diff --git a/(1) Community Patch/(1) Community Patch (v 149).modinfo b/(1) Community Patch/(1) Community Patch (v 149).modinfo index 94dfff8880..c8a191c869 100644 --- a/(1) Community Patch/(1) Community Patch (v 149).modinfo +++ b/(1) Community Patch/(1) Community Patch (v 149).modinfo @@ -2,7 +2,7 @@ (1) Community Patch - (5.2.6) Bugfixes, AI Improvements, Game Optimizations and API Extensions for Civ V + (5.2.7) Bugfixes, AI Improvements, Game Optimizations and API Extensions for Civ V Contains core AI improvements and bugfixes for the Civ 5 BNW DLL. Also contains support for the Vox Populi unofficial expansion and its associated mods. CivFanatics Community Patch Project Team 0 @@ -498,7 +498,7 @@ Core Files/Overrides/YieldIconManager.xml Core Files/Overrides/Includes/AssignStartingPlots.lua Core Files/Overrides/Includes/Bombardment.lua - Core Files/Overrides/Includes/InfoTooltipInclude.lua + Core Files/Overrides/Includes/InfoTooltipInclude.lua Core Files/Overrides/Includes/MapGenerator.lua Core Files/Overrides/Includes/TechButtonInclude.lua Core Files/Overrides/Includes/TechHelpInclude.lua @@ -508,7 +508,7 @@ Core Files/VPUI/VPUI_common.lua Core Files/VPUI/VPUI_common.xml Core Files/VPUI/VPUI_core.lua - CvGameCore_Expansion2.dll + CvGameCore_Expansion2.dll Database Changes/CommunityTables.xml Database Changes/CoreGameOptionChanges.sql Database Changes/CoreGameSpeedChanges.sql @@ -613,7 +613,7 @@ Database Changes/Text/en_US/Civilizations/CoreCivilizationTextChanges.sql Database Changes/Text/en_US/Civilizations/CoreLeaderTextChanges.sql Database Changes/Text/en_US/Civilizations/CoreNewCivilizationText.xml - Database Changes/Text/en_US/Concepts/CoreConceptTextChanges.sql + Database Changes/Text/en_US/Concepts/CoreConceptTextChanges.sql Database Changes/Text/en_US/Concepts/CoreNewConceptText.xml Database Changes/Text/en_US/Espionage/CoreNewEspionageText.xml Database Changes/Text/en_US/Minors/CoreMinorTextChanges.sql diff --git a/(1) Community Patch/Assets/FontIcons/CommunityFontIconAtlas.dds b/(1) Community Patch/Assets/FontIcons/CommunityFontIconAtlas.dds index 8ae4385254..8e996ab62f 100644 Binary files a/(1) Community Patch/Assets/FontIcons/CommunityFontIconAtlas.dds and b/(1) Community Patch/Assets/FontIcons/CommunityFontIconAtlas.dds differ diff --git a/(1) Community Patch/Assets/FontIcons/CommunityFontIconAtlas.ggxml b/(1) Community Patch/Assets/FontIcons/CommunityFontIconAtlas.ggxml index 34c3706774..f8d6b6be07 100644 --- a/(1) Community Patch/Assets/FontIcons/CommunityFontIconAtlas.ggxml +++ b/(1) Community Patch/Assets/FontIcons/CommunityFontIconAtlas.ggxml @@ -90,8 +90,9 @@ + - - + + diff --git a/(1) Community Patch/Community Patch.civ5proj b/(1) Community Patch/Community Patch.civ5proj index 6f4b44b886..9c5f080232 100644 --- a/(1) Community Patch/Community Patch.civ5proj +++ b/(1) Community Patch/Community Patch.civ5proj @@ -7,7 +7,7 @@ d1b6328c-ff44-4b0d-aad7-c657f83610cd 149 Stable - (5.2.6) Bugfixes, AI Improvements, Game Optimizations and API Extensions for Civ V + (5.2.7) Bugfixes, AI Improvements, Game Optimizations and API Extensions for Civ V Contains core AI improvements and bugfixes for the Civ 5 BNW DLL. Also contains support for the Vox Populi unofficial expansion and its associated mods. CivFanatics Community Patch Project Team @@ -1039,6 +1039,12 @@ EspionageChoice Core Files/New UI/EspionageChoicePopup.lua + + InGameUIAddin + CameraView + CameraView + Core Files/New UI/CameraView.xml + true @@ -2657,6 +2663,14 @@ Lua False + + Lua + False + + + Lua + False + Lua False @@ -3941,6 +3955,10 @@ Lua True + + Lua + True + Lua True @@ -3973,6 +3991,14 @@ Lua True + + Lua + True + + + Lua + True + Lua True @@ -3985,6 +4011,10 @@ Lua True + + Lua + True + Lua True @@ -3993,6 +4023,10 @@ Lua True + + Lua + True + Lua True diff --git a/(1) Community Patch/Core Files/New UI/CameraView.lua b/(1) Community Patch/Core Files/New UI/CameraView.lua new file mode 100644 index 0000000000..c37db8d2be --- /dev/null +++ b/(1) Community Patch/Core Files/New UI/CameraView.lua @@ -0,0 +1,417 @@ +--[[ + +# CameraView + +Viewport visibility tracker for Civ V world-space UI. + +Fires `LuaEvents.PlotEnterView(idx)` and `LuaEvents.PlotLeaveView(idx)` +as plots enter and leave the camera's visible area. Other contexts +(YieldIconManager, ResourceIconManager, etc.) subscribe to these events to +show or hide world-space UI without needing any camera math of their own. + +## Glossary + +**Directions:** L, R, T, B = Left, Right, Top, Bottom (screen edges) + +**Grid space** (hex tile coordinates, integer) +- `gx`, `gy` - tile column and row +- `gw`, `gh` - grid width and height (`MAP_GW`, `MAP_GH`) + +**World space** (3D engine units, float) +- `wx`, `wy` - world X and Y position on the ground plane + +**Screen space** +- `ex`, `ey` - signed NDC extents: (0,0) is screen center, + corners are near (+/-0.5, +/-0.5) + +## How it works + +A timer runs 10 times per second. `CameraViewChanged` updates the stored +camera matrix; `VisibilityUpdated` and `StrategicViewStateChanged` flag the +next tick for a recompute. On a flagged tick the timer builds a fresh set of +visible plot indices and diffs it against the previous set to emit events. + +**3D view:** Back-projects the four screen corners (TL, TR, BR, BL) through +the view matrix onto the ground plane (wz = 0) to get four grid-space corner +hexes. The frustum projects as a trapezoid - wide at the top (far edge) and +narrow at the bottom (near edge). A scanline sweep iterates each row between +the min and max corner rows; `intersect_edge` is called once per quad edge to +find the left/right x bounds for that row, then every tile in the range is +marked visible. Wrapping and out-of-bounds rejection are handled per-tile +inside `grid_to_index`. + +**Strategic view:** The camera is flat (top-down), so the projection math +is degenerate and back-projecting corners gives no useful frustum shape. +Instead the screen center is raycast to find the center hex, and a fixed +`STRATEGIC_*` hex rectangle around it is marked visible. + +## Tuning + +- `ADJUST_*` - fraction of the screen half-extent for 3D view. + 1.0 = full screen edge, above 1.0 adds a preload margin beyond the edge. +- `STRATEGIC_*` - hex count from center per edge in strategic view. + +## Late join + +Fire `LuaEvents.RequestViewPlots(caller)` to receive the full current +in-view set. The module queues the response and delivers it on the +next timer tick via `LuaEvents.RespondViewPlots(caller, plots)`, after +`PlotEnterView` and `PlotLeaveView` events for that tick have already fired. + +Fire `LuaEvents.RequestViewPlot(caller, idx)` to check a single plot. +The response arrives via +`LuaEvents.RespondViewPlot(caller, idx, isInView)`. + +In both cases `caller` is echoed back so you can ignore responses +triggered by other subscribers. Register your enter and leave handlers +before calling the request so you do not miss events in the intervening +tick. + +]] +local _docs +-- hover ^ for rendered markdown + +-------------------------------------------------------------------------------- + +include('CPK.lua') + +local lua_next = next +local lua_math_abs = math.abs +local lua_math_max = math.max +local lua_math_min = math.min +local lua_math_floor = math.floor +local lua_math_ceil = math.ceil +local lua_math_huge = math.huge + +local CivEvents = Events +local LuaEvents = LuaEvents + +local Timer = CPK.Util.Timer + +-------------------------------------------------------------------------------- + +-- Strategic view configuration + +local STRATEGIC_L = 14 +local STRATEGIC_T = 9 +local STRATEGIC_R = 14 +local STRATEGIC_B = 9 + +-------------------------------------------------------------------------------- + +-- 3D view configuration + +-- Finely tuned numbers :D +local ADJUST_L = 0.90 -- left edge +local ADJUST_T = 0.50 -- top edge (far from camera) +local ADJUST_R = 0.90 -- right edge +local ADJUST_B = 0.55 -- bottom edge (near camera) + +-- Dividing by negative z (camera looks along -Z) flips both screen axes, +-- so each extent uses the opposite ADJUST and negates the bound side. +local EXTENT_L = 0.5 * ADJUST_R * -1 +local EXTENT_R = 0.5 * ADJUST_L +local EXTENT_T = 0.5 * ADJUST_B +local EXTENT_B = 0.5 * ADJUST_T * -1 + +-------------------------------------------------------------------------------- + +-- Precalculate what we need + +local MAP_IS_WRAP_X = Map.IsWrapX() +local MAP_IS_WRAP_Y = Map.IsWrapY() + +local MAP_GW, MAP_GH = Map.GetGridSize() + +local world_to_grid + +do + local wx_00, wy_00 = GridToWorld(0, 0) + local wx_10, _ = GridToWorld(1, 0) + local wx_01, wy_01 = GridToWorld(0, 1) + + local inv_ww = 1.0 / (wx_10 - wx_00) + local inv_wh = 1.0 / (wy_01 - wy_00) + + local offset_wx = wx_01 - wx_00 + + --- Returns raw (unwrapped, unclamped) grid coordinates. + --- Used for frustum math where crossing the seam must be detectable. + --- @param wx number + --- @param wy number + --- @return integer # Grid X + --- @return integer # Grid Y + world_to_grid = function(wx, wy) + local dx = wx - wx_00 + local dy = wy - wy_00 + local gy = lua_math_floor(dy * inv_wh + 0.5) + + if lua_math_abs(gy % 2) == 1 then + dx = dx - offset_wx + end + + return lua_math_floor(dx * inv_ww + 0.5), gy + end +end + +--- Converts raw (possibly out-of-range) grid coordinates to a plot index. +--- Applies wrapping on enabled axes and returns nil for out-of-bounds on clamped axes. +--- @param gx integer +--- @param gy integer +--- @return integer | nil +local grid_to_index = function(gx, gy) + if MAP_IS_WRAP_X then + gx = gx % MAP_GW + elseif gx < 0 or gx >= MAP_GW then + return nil + end + + if MAP_IS_WRAP_Y then + gy = gy % MAP_GH + elseif gy < 0 or gy >= MAP_GH then + return nil + end + + return gx + gy * MAP_GW +end + +-------------------------------------------------------------------------------- + +--- Back-projects a screen point onto the ground plane (wz = 0). +--- ex and ey are signed NDC extents: screen center is (0, 0), corners are near (+/-0.5, +/-0.5). +--- @param cam number[][] # Civ V view matrix from CameraViewChanged +--- @param ex number # Screen X extent +--- @param ey number # Screen Y extent +--- @return number wx # World X coordinate +--- @return number wy # World Y coordinate +local raycast_ground = function(cam, ex, ey) + local wx_cx = cam[1][1] - ex * cam[1][3] + local wy_cx = cam[2][1] - ex * cam[2][3] + + local wx_cy = cam[1][2] - ey * cam[1][3] + local wy_cy = cam[2][2] - ey * cam[2][3] + + local dx = ex * cam[4][3] - cam[4][1] + local dy = ey * cam[4][3] - cam[4][2] + + local det = wx_cx * wy_cy - wx_cy * wy_cx + + if lua_math_abs(det) < 0.0001 then + return 0, 0 + end + + local inv = 1.0 / det + + local wx = inv * (dx * wy_cy - dy * wy_cx) + local wy = inv * (dy * wx_cx - dx * wx_cy) + + return wx, wy +end + +-------------------------------------------------------------------------------- + +--- Back-projects the 4 screen corners onto the ground plane and returns their +--- raw (unwrapped, unclamped) grid coordinates in clockwise order: +--- `TL->TR->BR->BL` +--- @param cam number[][] +--- @return integer tl_gx, integer tl_gy # Top-Left corner +--- @return integer tr_gx, integer tr_gy # Top-Right corner +--- @return integer br_gx, integer br_gy # Bottom-Right corner +--- @return integer bl_gx, integer bl_gy # Bottom-Left corner +local get_bounds = function(cam) + local tl_gx, tl_gy = world_to_grid(raycast_ground(cam, EXTENT_L, EXTENT_T)) + local tr_gx, tr_gy = world_to_grid(raycast_ground(cam, EXTENT_R, EXTENT_T)) + local br_gx, br_gy = world_to_grid(raycast_ground(cam, EXTENT_R, EXTENT_B)) + local bl_gx, bl_gy = world_to_grid(raycast_ground(cam, EXTENT_L, EXTENT_B)) + + return tl_gx, tl_gy, tr_gx, tr_gy, br_gx, br_gy, bl_gx, bl_gy +end + +-------------------------------------------------------------------------------- + +local is_strategic_view = false +local should_update = true +local camera_data = nil +local current = {} + +--- Fills vis with plot indices visible in strategic (top-down) view. +--- Uses a fixed STRATEGIC_* hex rectangle around the screen center. +--- @param cam number[][] # Civ V view matrix +--- @param vis table # Set to write visible plot indices into +local update_st_view = function(cam, vis) + local center_wx, center_wy = raycast_ground(cam, 0, 0) + local center_gx, center_gy = world_to_grid(center_wx, center_wy) + + local min_gy = lua_math_max(center_gy - STRATEGIC_B, 0) + local max_gy = lua_math_min(center_gy + STRATEGIC_T, MAP_GH - 1) + + for gy = min_gy, max_gy do + for gx = center_gx - STRATEGIC_L, center_gx + STRATEGIC_R do + local idx = grid_to_index(gx, gy) + if idx then vis[idx] = true end + end + end +end + +--- Intersects one edge of the frustum quad with a horizontal scanline and expands lo_gx/hi_gx. +--- @param fr_gx integer # Edge start X +--- @param fr_gy integer # Edge start Y +--- @param to_gx integer # Edge end X +--- @param to_gy integer # Edge end Y +--- @param row integer # Scanline row +--- @param lo_gx number # Current left bound +--- @param hi_gx number # Current right bound +--- @return number lo_gx # Updated left bound +--- @return number hi_gx # Updated right bound +local intersect_edge = function(fr_gx, fr_gy, to_gx, to_gy, row, lo_gx, hi_gx) + local lo_gy = fr_gy < to_gy and fr_gy or to_gy + local hi_gy = fr_gy < to_gy and to_gy or fr_gy + + if row >= lo_gy and row <= hi_gy then + local gx + + if fr_gy == to_gy then + -- horizontal edge: include both endpoints + if fr_gx < lo_gx then lo_gx = fr_gx end + if fr_gx > hi_gx then hi_gx = fr_gx end + gx = to_gx + else + gx = fr_gx + (row - fr_gy) * (to_gx - fr_gx) / (to_gy - fr_gy) + end + + if gx < lo_gx then lo_gx = gx end + if gx > hi_gx then hi_gx = gx end + end + + return lo_gx, hi_gx +end + +--- Fills vis with plot indices visible in 3D view. +--- Back-projects screen corners to get the four frustum corner hexes, then for +--- each row intersects the left/right X bounds against the quadrilateral edges. +--- Wrap and out-of-bounds handling is delegated to grid_to_index. +--- @param cam number[][] # Civ V view matrix +--- @param vis table # Set to write visible plot indices into +local update_3d_view = function(cam, vis) + -- corners in clockwise order: TL, TR, BR, BL + local tl_gx, tl_gy, tr_gx, tr_gy, br_gx, br_gy, bl_gx, bl_gy = get_bounds(cam) + + local min_gy = lua_math_min(tl_gy, tr_gy, br_gy, bl_gy) + local max_gy = lua_math_max(tl_gy, tr_gy, br_gy, bl_gy) + + if not MAP_IS_WRAP_Y then + min_gy = lua_math_max(min_gy, 0) + max_gy = lua_math_min(max_gy, MAP_GH - 1) + elseif max_gy - min_gy >= MAP_GH then + min_gy = 0 + max_gy = MAP_GH - 1 + end + + for row = min_gy, max_gy do + local lo_gx = lua_math_huge + local hi_gx = -lua_math_huge + + lo_gx, hi_gx = intersect_edge(tl_gx, tl_gy, tr_gx, tr_gy, row, lo_gx, hi_gx) + lo_gx, hi_gx = intersect_edge(tr_gx, tr_gy, br_gx, br_gy, row, lo_gx, hi_gx) + lo_gx, hi_gx = intersect_edge(br_gx, br_gy, bl_gx, bl_gy, row, lo_gx, hi_gx) + lo_gx, hi_gx = intersect_edge(bl_gx, bl_gy, tl_gx, tl_gy, row, lo_gx, hi_gx) + + for gx = lua_math_floor(lo_gx), lua_math_ceil(hi_gx) do + local idx = grid_to_index(gx, row) + if idx then + vis[idx] = true + end + end + end +end + +local EmitPlotEnterView = LuaEvents.PlotEnterView +local EmitPlotLeaveView = LuaEvents.PlotLeaveView + +local RequestViewPlots = LuaEvents.RequestViewPlots +local RespondViewPlots = LuaEvents.RespondViewPlots + +local pending_requests = {} + +RequestViewPlots.Add(function(caller) + pending_requests[#pending_requests + 1] = caller +end) + +--- Recomputes the visible plot set and emits `PlotEnterView` / `PlotLeaveView` +--- for any plots that entered or left the viewport since the last tick. +--- Also flushes any pending RequestViewPlots responses after the diff. +local update = function() + if should_update and camera_data then + should_update = false + + local cam = camera_data + local vis = {} + + if is_strategic_view then + update_st_view(cam, vis) + else + update_3d_view(cam, vis) + end + + for idx, _ in lua_next, current do + if not vis[idx] then + EmitPlotLeaveView(idx) + end + end + + for idx, _ in lua_next, vis do + if not current[idx] then + EmitPlotEnterView(idx) + end + end + + current = vis + end + + if #pending_requests > 0 then + local requests = pending_requests + pending_requests = {} + + for i = 1, #requests do + RespondViewPlots(requests[i], current) + end + end +end + +LuaEvents.RequestViewPlot.Add(function(caller, idx) + LuaEvents.RespondViewPlot(caller, idx, current[idx] == true) +end) + +CivEvents.CameraViewChanged.Add(function(camera) + camera_data = camera +end) + +CivEvents.VisibilityUpdated.Add(function() + should_update = true +end) + +CivEvents.StrategicViewStateChanged.Add(function(bStrategicView) + is_strategic_view = bStrategicView + should_update = true +end) + +-------------------------------------------------------------------------------- + +Timer.New({ + context = ContextPtr, + onReady = function(timer) + timer:CreateTask({ + interval = 0.1, -- 10 times per second + callback = update, + }) + end +}) + +--- Returns the number of plots currently tracked as visible. +function CountViewportPlots() + local count = 0 + for _ in lua_next, current do + count = count + 1 + end + return count +end diff --git a/(1) Community Patch/Core Files/New UI/CameraView.xml b/(1) Community Patch/Core Files/New UI/CameraView.xml new file mode 100644 index 0000000000..16e0b01ef9 --- /dev/null +++ b/(1) Community Patch/Core Files/New UI/CameraView.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/(1) Community Patch/Core Files/Overrides/EnemyUnitPanel.lua b/(1) Community Patch/Core Files/Overrides/EnemyUnitPanel.lua index 5b932d24b6..d1ab4bf792 100644 --- a/(1) Community Patch/Core Files/Overrides/EnemyUnitPanel.lua +++ b/(1) Community Patch/Core Files/Overrides/EnemyUnitPanel.lua @@ -726,11 +726,11 @@ function UpdateCombatSimulator(pMyUnit, pTheirUnit, pMyCity, pTheirCity) if pTheirUnit then -- Target is in friendly lands (+ following a different religion) - iModifier = pMyUnit:GetCombatVersusOtherReligionOwnLands(pTheirUnit); + iModifier = pMyUnit:GetReligionCombatBonusOwnLands(pTheirUnit); nBonus, iMiscModifier = ProcessModifier(iModifier, "TXT_KEY_EUPANEL_FRIENDLY_CITY_BELIEF_BONUS_CBP", nBonus, iMiscModifier, true, true); -- Target is in lands friendly to them (+ following a different religion) - iModifier = pMyUnit:GetCombatVersusOtherReligionTheirLands(pTheirUnit); + iModifier = pMyUnit:GetReligionCombatBonusTheirLands(pTheirUnit); nBonus, iMiscModifier = ProcessModifier(iModifier, "TXT_KEY_EUPANEL_ENEMY_CITY_BELIEF_BONUS_CBP", nBonus, iMiscModifier, true, true); end @@ -1150,7 +1150,7 @@ function UpdateCombatSimulator(pMyUnit, pTheirUnit, pMyCity, pTheirCity) -- Defending in friendly lands (+ following a different religion) if pMyUnit then - iModifier = pTheirUnit:GetCombatVersusOtherReligionOwnLands(pMyUnit); + iModifier = pTheirUnit:GetReligionCombatBonusOwnLands(pMyUnit); nBonus, iMiscModifier = ProcessModifier(iModifier, "TXT_KEY_EUPANEL_FRIENDLY_CITY_BELIEF_BONUS_CBP", nBonus, iMiscModifier, false, true); end else @@ -1165,7 +1165,7 @@ function UpdateCombatSimulator(pMyUnit, pTheirUnit, pMyCity, pTheirCity) -- Defending in lands friendly to the attacker (+ following a different religion) if pMyUnit and pToPlot:IsFriendlyTerritory(eMyPlayer) then - iModifier = pTheirUnit:GetCombatVersusOtherReligionTheirLands(pMyUnit); + iModifier = pTheirUnit:GetReligionCombatBonusTheirLands(pMyUnit); nBonus, iMiscModifier = ProcessModifier(iModifier, "TXT_KEY_EUPANEL_ENEMY_CITY_BELIEF_BONUS_CBP", nBonus, iMiscModifier, false, true); end diff --git a/(1) Community Patch/Core Files/Overrides/Includes/InfoTooltipInclude.lua b/(1) Community Patch/Core Files/Overrides/Includes/InfoTooltipInclude.lua index ebe8e7b87a..7c12147218 100644 --- a/(1) Community Patch/Core Files/Overrides/Includes/InfoTooltipInclude.lua +++ b/(1) Community Patch/Core Files/Overrides/Includes/InfoTooltipInclude.lua @@ -862,7 +862,13 @@ function GetHelpTextForUnit(eUnit, bIncludeRequirementsInfo, pCity, bExcludeName end if not kUnitInfo.PurchaseOnly then - AddTooltipPositive(tCosts, "TXT_KEY_PRODUCTION_COST_PRODUCTION", iProductionCost); + -- if production cost scales with era, it wouldn't be shown in pedia tooltip (no active city) + local iScalingProd = kUnitInfo.ProductionCostAddedPerEra; + if (iScalingProd > 0) and (not pActiveCity) then + AddTooltipPositive(tCosts, "TXT_KEY_PRODUCTION_COST_PRODUCTION_ADDED_PER_ERA", iProductionCost, iScalingProd); + else + AddTooltipPositive(tCosts, "TXT_KEY_PRODUCTION_COST_PRODUCTION", iProductionCost); + end end AddTooltipPositive(tCosts, "TXT_KEY_PRODUCTION_COST_GOLD", iGoldCost); AddTooltipPositive(tCosts, "TXT_KEY_PRODUCTION_COST_FAITH", iFaithCost); @@ -962,10 +968,11 @@ function GetHelpTextForUnit(eUnit, bIncludeRequirementsInfo, pCity, bExcludeName AddTooltipIfTrue(tAbilityLines, "TXT_KEY_MISSION_SPREAD_RELIGION", kUnitInfo.SpreadReligion); AddTooltipIfTrue(tAbilityLines, "TXT_KEY_MISSION_REMOVE_HERESY", kUnitInfo.RemoveHeresy); AddTooltipIfTrue(tAbilityLines, "TXT_KEY_MISSION_FOUND_RELIGION", kUnitInfo.FoundReligion); - AddTooltipIfTrue(tAbilityLines, "TXT_KEY_PRODUCTION_UNIT_CULTURE_ON_DISBAND_UPGRADE", kUnitInfo.CulExpOnDisbandUpgrade); AddTooltipIfTrue(tAbilityLines, "TXT_KEY_PRODUCTION_UNIT_EXTRA_PLUNDER_GOLD", kUnitInfo.HighSeaRaider); AddTooltipIfTrue(tAbilityLines, "TXT_KEY_PRODUCTION_UNIT_EXPEND_COPY_TILE_YIELD", kUnitInfo.CopyYieldsFromExpendTile); AddTooltipIfTrue(tAbilityLines, "TXT_KEY_PRODUCTION_UNIT_MOVE_AFTER_UPGRADE", kUnitInfo.MoveAfterUpgrade); + + AddTooltipPositive(tAbilityLines, "TXT_KEY_PRODUCTION_UNIT_CULTURE_ON_DISBAND_UPGRADE", kUnitInfo.CulExpOnDisbandUpgrade); -- Block/weaken active spread if kUnitInfo.ProhibitsSpread then @@ -1321,23 +1328,26 @@ function GetHelpTextForUnit(eUnit, bIncludeRequirementsInfo, pCity, bExcludeName -- Policy requirement if kUnitInfo.PolicyType then - -- Is this an opener or finisher? Assume openers and finishers are distinct across policy branches - local bOpenerOrFinisher = false; - for _, kPolicyBranchInfo in GameInfoCache("PolicyBranchTypes") do - if kPolicyBranchInfo.FreePolicy == kUnitInfo.PolicyType then - bOpenerOrFinisher = true; - AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY_BRANCH_OPENER", kPolicyBranchInfo.Description); - break; + -- Don't display dummy policies + if GameInfo.Policies[kUnitInfo.PolicyType].IsDummy == false then + -- Is this an opener or finisher? Assume openers and finishers are distinct across policy branches + local bOpenerOrFinisher = false; + for _, kPolicyBranchInfo in GameInfoCache("PolicyBranchTypes") do + if kPolicyBranchInfo.FreePolicy == kUnitInfo.PolicyType then + bOpenerOrFinisher = true; + AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY_BRANCH_OPENER", kPolicyBranchInfo.Description); + break; + end + if kPolicyBranchInfo.FreeFinishingPolicy == kUnitInfo.PolicyType then + bOpenerOrFinisher = true; + AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY_BRANCH_COMPLETION", kPolicyBranchInfo.Description); + break; + end end - if kPolicyBranchInfo.FreeFinishingPolicy == kUnitInfo.PolicyType then - bOpenerOrFinisher = true; - AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY_BRANCH_COMPLETION", kPolicyBranchInfo.Description); - break; + if not bOpenerOrFinisher then + AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY", GameInfo.Policies[kUnitInfo.PolicyType].Description); end end - if not bOpenerOrFinisher then - AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY", GameInfo.Policies[kUnitInfo.PolicyType].Description); - end end -- Belief requirement @@ -3319,23 +3329,26 @@ function GetHelpTextForBuilding(eBuilding, bExcludeName, _, bNoMaintenance, pCit -- Policy requirement if kBuildingInfo.PolicyType then - -- Is this an opener or finisher? Assume openers and finishers are distinct across policy branches - local bOpenerOrFinisher = false; - for _, kPolicyBranchInfo in GameInfoCache("PolicyBranchTypes") do - if kPolicyBranchInfo.FreePolicy == kBuildingInfo.PolicyType then - bOpenerOrFinisher = true; - AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY_BRANCH_OPENER", kPolicyBranchInfo.Description); - break; + -- Don't display dummy policies + if GameInfo.Policies[kBuildingInfo.PolicyType].IsDummy == false then + -- Is this an opener or finisher? Assume openers and finishers are distinct across policy branches + local bOpenerOrFinisher = false; + for _, kPolicyBranchInfo in GameInfoCache("PolicyBranchTypes") do + if kPolicyBranchInfo.FreePolicy == kBuildingInfo.PolicyType then + bOpenerOrFinisher = true; + AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY_BRANCH_OPENER", kPolicyBranchInfo.Description); + break; + end + if kPolicyBranchInfo.FreeFinishingPolicy == kBuildingInfo.PolicyType then + bOpenerOrFinisher = true; + AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY_BRANCH_COMPLETION", kPolicyBranchInfo.Description); + break; + end end - if kPolicyBranchInfo.FreeFinishingPolicy == kBuildingInfo.PolicyType then - bOpenerOrFinisher = true; - AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY_BRANCH_COMPLETION", kPolicyBranchInfo.Description); - break; + if not bOpenerOrFinisher then + AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY", GameInfo.Policies[kBuildingInfo.PolicyType].Description); end end - if not bOpenerOrFinisher then - AddTooltip(tReqLines, "TXT_KEY_PRODUCTION_REQUIRED_POLICY", GameInfo.Policies[kBuildingInfo.PolicyType].Description); - end end -- Policy branch requirement diff --git a/(1) Community Patch/Core Files/Overrides/Includes/TradeLogic.lua b/(1) Community Patch/Core Files/Overrides/Includes/TradeLogic.lua index 458080fb96..cee0556b80 100644 --- a/(1) Community Patch/Core Files/Overrides/Includes/TradeLogic.lua +++ b/(1) Community Patch/Core Files/Overrides/Includes/TradeLogic.lua @@ -2450,19 +2450,32 @@ function ResetDisplay() else strTooltip = Locale.ConvertTextKey("TXT_KEY_DIPLO_TO_TRADE_TECHNOLOGIES_TRADE_NO"); + -- No cities founded yet? + local bCityFoundingIssue = false; + if (not g_pUs:IsResearch()) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_TRADE_TECHNOLOGIES_NO_RESEARCH_PLAYER") .. "[ENDCOLOR]"; + bCityFoundingIssue = true; + end + if (not g_pThem:IsResearch()) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_TRADE_TECHNOLOGIES_NO_RESEARCH_OTHER_PLAYER") .. "[ENDCOLOR]"; + bCityFoundingIssue = true; + end + -- No tech? if (not g_pUsTeam:IsTechTrading()) then strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_TRADE_TECHNOLOGIES_NO_TECH_PLAYER") .. "[ENDCOLOR]"; end -- No embassy? - if ((not bAtWar) and ((not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) or (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam)))) then - if ((not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) and (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam))) then - strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_BOTH_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; - elseif (not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) then - strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_YOU_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; - elseif (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam)) then - strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_THEY_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; + if (not bCityFoundingIssue) then + if ((not bAtWar) and ((not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) or (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam)))) then + if ((not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) and (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam))) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_BOTH_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; + elseif (not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_YOU_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; + elseif (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam)) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_THEY_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; + end end end diff --git a/(1) Community Patch/Core Files/Overrides/NotificationLogPopup.lua b/(1) Community Patch/Core Files/Overrides/NotificationLogPopup.lua index aed443c839..a76af8b246 100644 --- a/(1) Community Patch/Core Files/Overrides/NotificationLogPopup.lua +++ b/(1) Community Patch/Core Files/Overrides/NotificationLogPopup.lua @@ -100,6 +100,7 @@ local GetInstantYieldKind = (function() local iyt_espionage = { IYT.INSTANT_YIELD_TYPE_COMBAT_EXPERIENCE, IYT.INSTANT_YIELD_TYPE_HEALING, + IYT.INSTANT_YIELD_TYPE_SCRAP_OR_UPGRADE, IYT.INSTANT_YIELD_TYPE_TR_PRODUCTION_SIPHON, IYT.INSTANT_YIELD_TYPE_CONVERSION_EXPO, IYT.INSTANT_YIELD_TYPE_PROMOTION_OBTAINED, diff --git a/(1) Community Patch/Core Files/Overrides/YieldIconManager.lua b/(1) Community Patch/Core Files/Overrides/YieldIconManager.lua index 2f7cb96dc0..7f2c1e6e53 100644 --- a/(1) Community Patch/Core Files/Overrides/YieldIconManager.lua +++ b/(1) Community Patch/Core Files/Overrides/YieldIconManager.lua @@ -1,206 +1,412 @@ -------------------------------------------------- --- Yield Icon Manager --- re-written by bc1, compatible with all Civ V and BE versions --- fix quantity erratic display & make code much more efficient --- add compatibility with "DLL - Various Mod Components" v63 --- fixed invisible yields bug -------------------------------------------------- - --- Minor optimizations -local pairs = pairs -local table_insert = table.insert -local table_remove = table.remove - -local ContextPtr = ContextPtr -local Controls_Scrap = Controls.Scrap -local Controls_YieldStore = Controls.YieldStore -local GridToWorld = GridToWorld -local Map_GetPlot = Map.GetPlot -local UI_RefreshYieldVisibleMode = UI.RefreshYieldVisibleMode - --- Globals -local g_ActiveAnchors = {} -local g_AvailableAnchors = {} -local g_AvailableImageInstances = {} -local g_maxYieldID = ((YieldTypes or {}).NUM_YIELD_TYPES or 5) -1 -local g_activeTeamID = Game.GetActiveTeam() -local g_isVanillaDLL = Map_GetPlot(0,0).GetCulture -local g_defaultTexture = "YieldAtlas.dds" -local g_Textures = { [0]=g_defaultTexture, g_defaultTexture, g_defaultTexture, g_defaultTexture, "YieldAtlas_128_Culture.dds", "YieldAtlas_128_Faith.dds", "YieldAtlas_128_Tourism.dds", "YieldAtlas_128_GoldenAge.dds" } -local g_OffsetXs = { [0]=0, 128 , 256, 384, 0, 0, 0, 0, 0 } --- compatibility with "DLL - Various Mod Components" extra yields -for row in GameInfo.Yields() do - for k,v in pairs(row) do - if k == "ImageTexture" then - g_Textures[row.ID] = v - elseif k == "ImageOffset" then - g_OffsetXs[row.ID] = v - end - end +include('CPK.lua') + +local lua_next = next +local lua_math_max = math.max +local lua_setmetatable = setmetatable + +local CivEvents = Events +local LuaEvents = LuaEvents + +local civ_db_query = DB.Query +local civ_grid_to_world = GridToWorld +local civ_to_grid_from_hex = ToGridFromHex +local civ_game_get_active_team = Game.GetActiveTeam +local civ_map_get_plot_by_index = Map.GetPlotByIndex + +local Stack = CPK.Util.Stack +local Always = CPK.FP.Always + +local MakeInstance = CPK.UI.Control.Instance.Make +local MoveInstance = CPK.UI.Control.Instance.Move + +local C = Controls +local CP = ContextPtr + +-------------------------------------------------------------------------------- + +local yield_max_id = 0 + +local yield_img_file = {} --[[@type table]] +local yield_img_offx = {} --[[@type table]] +local yield_img_offy = {} --[[@type table]] + +local yield_lbl_offx = {} --[[@type table]] +local yield_lbl_offy = {} --[[@type table]] + +for row in civ_db_query('SELECT ID, ImageTexture, ImageOffset FROM Yields') do + yield_img_file[row.ID] = row.ImageTexture + yield_img_offx[row.ID] = row.ImageOffset + + yield_max_id = lua_math_max(yield_max_id, row.ID) end ------------------------------------------------------------------- --- Update the controls to reflect the current known yield ------------------------------------------------------------------- -local function BuildAnchorYields( plot, anchor ) - - -- calculate the yields - local yieldInfo, amount = {} - for yieldID = 0, g_maxYieldID do - amount = plot:CalculateYield( yieldID, true ) --* 0 + math.random( 0, 3 ) * math.random( 0, 4 ) - if amount > 0 then - table_insert( yieldInfo, { yieldID, amount } ) +for count = 0, 32 do + yield_img_offy[count] = count > 4 + and 512 + or (128 * lua_math_max(0, count - 1)) + + yield_lbl_offy[count] = count > 9 + and 768 + or 640 + + yield_lbl_offx[count] = count > 12 + and 256 + or (128 * ((count - 6) % 4)) +end + +lua_setmetatable(yield_img_file, { __index = Always('YieldAtlas.dds') }) +lua_setmetatable(yield_img_offx, { __index = Always(0) }) +lua_setmetatable(yield_img_offy, { __index = Always(512) }) +lua_setmetatable(yield_lbl_offx, { __index = Always(256) }) +lua_setmetatable(yield_lbl_offy, { __index = Always(768) }) + +-------------------------------------------------------------------------------- + +local WORLD_GW = Map.GetGridSize() +local CALLER = CP:GetID() + +local active_icons = {} --[[@type table>]] +local active_labels = {} --[[@type table]] +local active_stacks = {} --[[@type table]] +local active_anchors = {} --[[@type table]] + +local hidden_icons = {} --[[@type table>]] +local hidden_labels = Stack.New() --[[@type Stack]] +local hidden_stacks = Stack.New() --[[@type Stack]] +local hidden_anchors = Stack.New() --[[@type Stack]] + +for _, texture in lua_next, yield_img_file do + hidden_icons[texture] = Stack.New() +end + +-------------------------------------------------------------------------------- + +--- @param idx integer +local RecycleHex = function(idx) + if not active_anchors[idx] then return end + + local labels = active_labels[idx] + if labels then + active_labels[idx] = nil + for i = 1, #labels do + labels[i].Image:SetHide(true) + hidden_labels:Push(labels[i]) end end - if g_isVanillaDLL then - amount = plot:GetCulture() - if amount > 0 then - table_insert( yieldInfo, { 4, amount } ) + + local icons = active_icons[idx] + if icons then + active_icons[idx] = nil + for yid, ico in lua_next, icons do + ico.Image:SetHide(true) + hidden_icons[yield_img_file[yid]]:Push(ico) end end - local n = #yieldInfo - if n > 0 then - local m = n<5 and 4 or (n+1)/2 - for i = 1, n do - local yieldID = yieldInfo[i][1] - local amount = yieldInfo[i][2] - - local iconInstance = table_remove( g_AvailableImageInstances ) - if iconInstance then - iconInstance.Image:ChangeParent( i <= m and anchor.Stack1 or anchor.Stack2 ) - else - iconInstance = {} - ContextPtr:BuildInstanceForControl( "ImageInstance", iconInstance, i <= m and anchor.Stack1 or anchor.Stack2 ) - end - table_insert( anchor, iconInstance ) - - iconInstance.Image:SetTexture( g_Textures[ yieldID ] or g_defaultTexture ) - iconInstance.Image:SetTextureOffsetVal( g_OffsetXs[ yieldID ] or 0, amount>4 and 512 or 128 * (amount - 1) ) - - if amount > 5 then - local textInstance = table_remove( g_AvailableImageInstances ) - if textInstance then - textInstance.Image:ChangeParent( iconInstance.Image ) - else - textInstance = {} - ContextPtr:BuildInstanceForControl( "ImageInstance", textInstance, iconInstance.Image ) - end - table_insert( anchor, textInstance ) - textInstance.Image:SetTexture( g_defaultTexture ) - textInstance.Image:SetTextureOffsetVal( amount > 12 and 256 or 128 * ((amount - 6) % 4), amount > 9 and 768 or 640 ) - end + + local stacks = active_stacks[idx] + if stacks then + active_stacks[idx] = nil + for i = 1, #stacks do + stacks[i].Stack:SetHide(true) + hidden_stacks:Push(stacks[i]) end + end + + local anc = active_anchors[idx] + active_anchors[idx] = nil + anc.Anchor:SetHide(true) + hidden_anchors:Push(anc) +end - anchor.Stack1:CalculateSize() - anchor.Stack2:CalculateSize() - anchor.Stack:ReprocessAnchoring() +local RecycleAll = function() + local indices = {} + for idx in lua_next, active_anchors do + indices[#indices + 1] = idx + end + for i = 1, #indices do + RecycleHex(indices[i]) end end ------------------------------------------------------------------- --- Update the controls to remove yield display ------------------------------------------------------------------- -local function RecycleAnchorYields( anchor ) +-------------------------------------------------------------------------------- + +--- @param parent Control +--- @param idx integer +--- @param cnt integer +--- @return ControlInstance +local AcquireHexLabel = function(parent, idx, cnt) + local lbl = hidden_labels:Pop() + + if lbl then + MoveInstance(lbl, parent) + else + lbl = MakeInstance('ImageInstance', parent, CP) + lbl.Image:SetTexture('YieldAtlas.dds') + end + + local labels = active_labels[idx] + + if not labels then + labels = {} + active_labels[idx] = labels + end + + labels[#labels + 1] = lbl + + lbl.Image:SetTextureOffsetVal(yield_lbl_offx[cnt], yield_lbl_offy[cnt]) + lbl.Image:SetHide(false) + + return lbl +end + +--- @param parent Control +--- @param idx integer +--- @param yid integer +--- @param cnt integer +--- @return ControlInstance +local AcquireHexIcon = function(parent, idx, yid, cnt) + local tex = yield_img_file[yid] - for i = 1, #anchor do - local instance = table_remove( anchor ) - instance.Image:ChangeParent( Controls_Scrap ) - table_insert( g_AvailableImageInstances, instance ) + local ico = hidden_icons[tex]:Pop() + + if ico then + MoveInstance(ico, parent) + else + ico = MakeInstance('ImageInstance', parent, CP) + ico.Image:SetTexture(tex) + end + + local icons = active_icons[idx] + + if not icons then + icons = {} + active_icons[idx] = icons end + + icons[yid] = ico + + ico.Image:SetTextureOffsetVal(yield_img_offx[yid], yield_img_offy[cnt]) + ico.Image:SetHide(false) + + return ico end ------------------------------------------------------------------- --- Plot Yield Show / Hide ------------------------------------------------------------------- -Events.ShowHexYield.Add( -function( x, y, isShown ) - - --workaround for bugged parameter isShown - isShown = isShown or OptionsManager.GetYieldOn(); - - local plot = Map_GetPlot( x, y ) - if plot and plot:IsRevealed( g_activeTeamID, false ) then - - local index = plot:GetPlotIndex() - local anchor = g_ActiveAnchors[ index ] - - if isShown then - if anchor then - -- just show it - anchor.Anchor:ChangeParent( Controls_YieldStore ) - -- updating world position to make it synchronized with current camera position - local a,b,c= GridToWorld( x, y ) - anchor.Anchor:SetWorldPositionVal( a,b,c ) - else - -- set up anchor - anchor = table_remove( g_AvailableAnchors ) - if anchor then - anchor.Anchor:ChangeParent( Controls_YieldStore ) - else - anchor = {} - ContextPtr:BuildInstanceForControl( "AnchorInstance", anchor, Controls_YieldStore ) - end - g_ActiveAnchors[ index ] = anchor - local a,b,c= GridToWorld( x, y ) - anchor.Anchor:SetWorldPositionVal( a,b,c ) -- compatibility with BE: SetHexPosition deprecated - -- add yields - BuildAnchorYields( plot, anchor ) +--- @param parent Control +--- @param idx integer +local AcquireHexStack = function(parent, idx) + local stk = hidden_stacks:Pop() + + if stk then + MoveInstance(stk, parent) + else + stk = MakeInstance('StackInstance', parent, CP) + end + + local stacks = active_stacks[idx] + + if not stacks then + stacks = {} + active_stacks[idx] = stacks + end + + stacks[#stacks + 1] = stk + + stk.Stack:SetHide(false) + + return stk +end + +--- @param parent Control +--- @param idx integer +--- @param wx number +--- @param wy number +--- @param wz number +--- @return ControlInstance +local AcquireHexAnchor = function(parent, idx, wx, wy, wz) + local anc = hidden_anchors:Pop() or MakeInstance('AnchorInstance', parent, CP) + + anc.Anchor:SetWorldPositionVal(wx, wy, wz) + anc.Anchor:SetHide(false) + + active_anchors[idx] = anc + + return anc +end + +-------------------------------------------------------------------------------- + +local RenderAnchor = function(idx) + RecycleHex(idx) + + local plot = civ_map_get_plot_by_index(idx) + + if not plot or not plot:IsRevealed(civ_game_get_active_team(), false) then + return + end + + local anc = nil --[[@type nil | ControlInstance]] + local stk = nil --[[@type nil | ControlInstance]] + local total = 0 + local stacks = {} + + for yid = 0, yield_max_id do + local cnt = plot:CalculateYield(yid, true) + + if cnt > 0 then + if not anc then + local wx, wy, wz = civ_grid_to_world(plot:GetX(), plot:GetY()) + anc = AcquireHexAnchor(C.Anchors, idx, wx, wy, wz) + end + + total = total + 1 + + if (total - 1) % 4 == 0 then + stk = AcquireHexStack(anc.Stacks, idx) + stacks[#stacks + 1] = stk end - anchor.IsVisible = true - elseif anchor then - -- just hide it - anchor.Anchor:ChangeParent( Controls_Scrap ) - anchor.IsVisible = false + local ico = AcquireHexIcon(stk.Stack, idx, yid, cnt) + + if cnt > 5 then + AcquireHexLabel(ico.Image, idx, cnt) + end end end -end) + if anc then + for i = #stacks, 1, -1 do + local stack = stacks[i].Stack + stack:CalculateSize() + stack:ReprocessAnchoring() + end ------------------------------------------------------------------- --- Plot Yield Update ------------------------------------------------------------------- -Events.HexYieldMightHaveChanged.Add( -function( x, y ) - - local plot = Map_GetPlot( x, y ) - if plot and plot:IsRevealed( g_activeTeamID, false ) then - local index = plot:GetPlotIndex() - local anchor = g_ActiveAnchors[ index ] - if anchor then - RecycleAnchorYields( anchor ) - if anchor.IsVisible then - BuildAnchorYields( plot, anchor ) - else - -- recycle anchor - table_insert( g_AvailableAnchors, anchor ) - g_ActiveAnchors[ index ] = nil - end + anc.Stacks:CalculateSize() + anc.Stacks:ReprocessAnchoring() + end +end + +-------------------------------------------------------------------------------- + +local updates_enabled = true + +local RequestViewPlot = LuaEvents.RequestViewPlot +local RequestViewPlots = LuaEvents.RequestViewPlots +local RespondViewPlots = LuaEvents.RespondViewPlots + +local RequestRender = function() + if updates_enabled then RequestViewPlots(CALLER) end +end + +RespondViewPlots.Add(function(caller, plots) + if caller == CALLER then + for idx in lua_next, plots do + RenderAnchor(idx) end end end) ------------------------------------------------------------------- --- 'Active' (local human) player has changed ------------------------------------------------------------------- -Events.GameplaySetActivePlayer.Add( -function()-- activePlayerID, prevActivePlayerID ) +LuaEvents.PlotEnterView.Add(function(idx) + if updates_enabled then RenderAnchor(idx) end +end) - g_activeTeamID = Game.GetActiveTeam() +LuaEvents.PlotLeaveView.Add(function(idx) + if updates_enabled then RecycleHex(idx) end +end) - for _, anchor in pairs( g_ActiveAnchors ) do - RecycleAnchorYields( anchor ) - -- recycle anchor - anchor.Anchor:ChangeParent( Controls_Scrap ) - table_insert( g_AvailableAnchors, anchor ) +LuaEvents.RespondViewPlot.Add(function(caller, idx, isInView) + if caller == CALLER and isInView and updates_enabled then + RenderAnchor(idx) end +end) - -- zap anchor cache - g_ActiveAnchors = {} +CivEvents.HexFOWStateChanged.Add(function(hex) + local gx, gy = civ_to_grid_from_hex(hex.x, hex.y) + local idx = gx + gy * WORLD_GW - UI_RefreshYieldVisibleMode() + if not active_anchors[idx] then + RequestViewPlot(CALLER, idx) + end end) --- Bit of a hack here, we want to ensure that the yield icons are properly refreshed --- when starting a new game. -UI_RefreshYieldVisibleMode() \ No newline at end of file +CivEvents.HexYieldMightHaveChanged.Add(function(gx, gy) + if updates_enabled then + local idx = gx + gy * WORLD_GW + + if active_anchors[idx] then + RenderAnchor(idx) + end + end +end) + +CivEvents.RequestYieldDisplay.Add(function(id) + if id == YieldDisplayTypes.USER_ALL_ON then + updates_enabled = true + C.Anchors:SetHide(false) + RequestRender() + elseif id == YieldDisplayTypes.USER_ALL_OFF then + updates_enabled = false + C.Anchors:SetHide(true) + RecycleAll() + end +end) + +CivEvents.GameplaySetActivePlayer.Add(function() + local last_state = updates_enabled + updates_enabled = false + RecycleAll() + updates_enabled = last_state + UI.RefreshYieldVisibleMode() + RequestRender() +end) + +UI.RefreshYieldVisibleMode() + +-------------------------------------------------------------------------------- + +function PrintYieldIconStats() + local active_anchor_count = 0 + local active_stack_count = 0 + local active_icon_count = 0 + local active_label_count = 0 + + for _, _ in lua_next, active_anchors do + active_anchor_count = active_anchor_count + 1 + end + + for _, stacks in lua_next, active_stacks do + active_stack_count = active_stack_count + #stacks + end + + for _, icons in lua_next, active_icons do + for _, _ in lua_next, icons do + active_icon_count = active_icon_count + 1 + end + end + + for _, labels in lua_next, active_labels do + active_label_count = active_label_count + #labels + end + + local pooled_icon_count = 0 + for _, stack in lua_next, hidden_icons do + pooled_icon_count = pooled_icon_count + stack:Size() + end + + local pooled_anchor_count = hidden_anchors:Size() + local pooled_stack_count = hidden_stacks:Size() + local pooled_label_count = hidden_labels:Size() + + print(string.format([[Yield Icon Stats: + active -> anchors: %d, stacks: %d, icons: %d, labels: %d + pooled -> anchors: %d, stacks: %d, icons: %d, labels: %d + total -> anchors: %d, stacks: %d, icons: %d, labels: %d]], + active_anchor_count, active_stack_count, + active_icon_count, active_label_count, + pooled_anchor_count, pooled_stack_count, + pooled_icon_count, pooled_label_count, + active_anchor_count + pooled_anchor_count, + active_stack_count + pooled_stack_count, + active_icon_count + pooled_icon_count, + active_label_count + pooled_label_count + )) +end diff --git a/(1) Community Patch/Core Files/Overrides/YieldIconManager.xml b/(1) Community Patch/Core Files/Overrides/YieldIconManager.xml index d83e600969..fbb5a9f5b5 100644 --- a/(1) Community Patch/Core Files/Overrides/YieldIconManager.xml +++ b/(1) Community Patch/Core Files/Overrides/YieldIconManager.xml @@ -1,24 +1,18 @@ - - + - - diff --git a/(1) Community Patch/Credits.txt b/(1) Community Patch/Credits.txt index 9dfb12d1c3..3ed0010861 100644 --- a/(1) Community Patch/Credits.txt +++ b/(1) Community Patch/Credits.txt @@ -305,3 +305,21 @@ Art Assets (General) -------------------------------------------------------------------------- SocialPoliciesIndustry.dds : from JFD's Rise to Power, image by Leugi Mikasa model converted from Civ 4 by Wolfshanze, icon by Janboruta +Ambassador icon by RawSasquatch, recolored by Hokath +Diplomat icon by Jarcast and Hokath +Ancient Great Diplomat icon by Hinin and Jarcast +Emissary icon adapted by Hokath from Weanew icon by RawSasquatch +Envoy icon by Janboruta +Elder Stateman model converted from Civ IV by Wolfdog +Envoy model from JFD's HRE civ by zwei833 +Envoy, Emissary, Diplomat, Ambassador, and Great Diplomat model reskins by Hokath +Chartarium icon from Israel (Solomon) custom civ by Lastsword +Jail icon by Janboruta +Casino icon by AsterixRage +Penitentiary, Sorting Office, Alamut Fortress, Port Royal, Chateau d'If, and Alcatraz icons by Hokath +Heavenly Grotto icon by Pineappledan +Grand Canal icon by Sukritact in JFD's Rise to Power +Daoguan icon by Sukritact +Order icon by Hokath and Jarcast +Saltworks model by Gwennog +Lake Fish icon by Gwennog diff --git a/(1) Community Patch/CvGameCore_Expansion2.dll b/(1) Community Patch/CvGameCore_Expansion2.dll index 24f052cc0d..c5fef38bde 100644 Binary files a/(1) Community Patch/CvGameCore_Expansion2.dll and b/(1) Community Patch/CvGameCore_Expansion2.dll differ diff --git a/(1) Community Patch/Database Changes/AI/CoreLeaderPersonalityChanges.sql b/(1) Community Patch/Database Changes/AI/CoreLeaderPersonalityChanges.sql index 746ce5bdfc..b1029b38ad 100644 --- a/(1) Community Patch/Database Changes/AI/CoreLeaderPersonalityChanges.sql +++ b/(1) Community Patch/Database Changes/AI/CoreLeaderPersonalityChanges.sql @@ -3,9 +3,6 @@ UPDATE Leaders SET WorkWithWillingness = DoFWillingness; UPDATE Leaders SET WorkAgainstWillingness = DenounceWillingness; --- FIXME: Remove this once Chattiness is fixed -UPDATE Leaders SET Chattiness = 4; - -- Sets default victory pursuits for all base game leaders CREATE TEMP TABLE Leader_VictoryPursuits ( TempLeaderType text, diff --git a/(1) Community Patch/Database Changes/Art/CoreNewIcons.xml b/(1) Community Patch/Database Changes/Art/CoreNewIcons.xml index fe6a303de1..ce6e5ec152 100644 --- a/(1) Community Patch/Database Changes/Art/CoreNewIcons.xml +++ b/(1) Community Patch/Database Changes/Art/CoreNewIcons.xml @@ -378,16 +378,21 @@ ICON_FONT_TEXTURE_CP 74 + + ICON_RES_LAKE_FISH + ICON_FONT_TEXTURE_CP + 75 + ICON_CULTURE_LOCAL ICON_FONT_TEXTURE_CP - 75 + 76 ICON_CITY_SECURITY ICON_FONT_TEXTURE_CP - 76 + 77 - \ No newline at end of file + diff --git a/(1) Community Patch/Database Changes/City/Processes/ProcessTableChanges.sql b/(1) Community Patch/Database Changes/City/Processes/ProcessTableChanges.sql index 6ce86a1e69..8259748785 100644 --- a/(1) Community Patch/Database Changes/City/Processes/ProcessTableChanges.sql +++ b/(1) Community Patch/Database Changes/City/Processes/ProcessTableChanges.sql @@ -4,5 +4,11 @@ ALTER TABLE Processes ADD RequiredPolicy text REFERENCES Policies (Type); -- Add a percentage of the city's production to its strength and healing ALTER TABLE Processes ADD DefenseValue integer DEFAULT 0; +-- Add a percentage of the city's production to its strength and healing per turn +ALTER TABLE Processes ADD DefenseValuePerTurn integer DEFAULT 0; + +-- Add a percentage of the city's production to its strength and healing +ALTER TABLE Processes ADD DefenseValueCap integer DEFAULT 0; + -- Only the specified civilization can use this process ALTER TABLE Processes ADD CivilizationType text REFERENCES Civilizations (Type); diff --git a/(1) Community Patch/Database Changes/Defines/CoreDefineChanges.sql b/(1) Community Patch/Database Changes/Defines/CoreDefineChanges.sql index 8817725c2d..bd3c6b6cf6 100644 --- a/(1) Community Patch/Database Changes/Defines/CoreDefineChanges.sql +++ b/(1) Community Patch/Database Changes/Defines/CoreDefineChanges.sql @@ -463,6 +463,9 @@ VALUES ('RELIGION_ADJACENT_CITY_DISTANCE', 9), -- Base trade route distance affected by religious pressure ('WOUNDED_DAMAGE_MULTIPLIER', 34), -- CP changed the wounded penalty from damage penalty to CS penalty. Adjust accordingly so it's actually 1% per 3HP lost. ('WLTKD_RESOURCE_RESET_TURNS', 0), -- WLTKD Reset Timer (<= 0 means disabled); scales with game speed + -- Research Agreements + ('RESEARCH_AGREEMENT_PER_TURN_YIELD_PERCENT', 0), -- percentage of RA yields that are given as per-turn yields, not as instant yield when the agreement ends. Default 0 (in BNW / Community Patch) + ('RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT', 0), -- RA yields are calculated as X*avg(SciencePlayer1, SciencePlayer2) + (1-X)*min(SciencePlayer1, SciencePlayer2). Default 0 (in BNW / Community Patch) -- These will be replaced by PostDefines in VP ('EMBASSY_IMPROVEMENT', -1), ('IDEOLOGY_PREREQ_ERA', -1), diff --git a/(1) Community Patch/Database Changes/NewCustomModOptions.xml b/(1) Community Patch/Database Changes/NewCustomModOptions.xml index 7aa9b476c4..0e8093a649 100644 --- a/(1) Community Patch/Database Changes/NewCustomModOptions.xml +++ b/(1) Community Patch/Database Changes/NewCustomModOptions.xml @@ -129,8 +129,8 @@ - - + + diff --git a/(1) Community Patch/Database Changes/Policies/NewPolicyTables.xml b/(1) Community Patch/Database Changes/Policies/NewPolicyTables.xml index f8d4a8da30..0558e9b1d4 100644 --- a/(1) Community Patch/Database Changes/Policies/NewPolicyTables.xml +++ b/(1) Community Patch/Database Changes/Policies/NewPolicyTables.xml @@ -130,6 +130,12 @@ + + + + + +
diff --git a/(1) Community Patch/Database Changes/Religions/BeliefTableChanges.sql b/(1) Community Patch/Database Changes/Religions/BeliefTableChanges.sql index 756753c6bd..f35b2fe5fd 100644 --- a/(1) Community Patch/Database Changes/Religions/BeliefTableChanges.sql +++ b/(1) Community Patch/Database Changes/Religions/BeliefTableChanges.sql @@ -40,11 +40,17 @@ ALTER TABLE Beliefs ADD PressureChangeTradeRoute integer DEFAULT 0; -- New Belief Element -- Assign Belief to Specific Civ only ALTER TABLE Beliefs ADD CivilizationType text REFERENCES Civilizations (Type); +-- Combat bonus in our lands +ALTER TABLE Beliefs ADD CombatBonusOwnLands integer DEFAULT 0; + -- Combat bonus v. other religions in our lands -ALTER TABLE Beliefs ADD CombatVersusOtherReligionOwnLands integer DEFAULT 0; +ALTER TABLE Beliefs ADD CombatBonusVersusOtherReligionOwnLands integer DEFAULT 0; + +-- Combat bonus in their lands +ALTER TABLE Beliefs ADD CombatBonusTheirLands integer DEFAULT 0; -- Combat bonus v. other religions in their lands -ALTER TABLE Beliefs ADD CombatVersusOtherReligionTheirLands integer DEFAULT 0; +ALTER TABLE Beliefs ADD CombatBonusVersusOtherReligionTheirLands integer DEFAULT 0; -- Production modifier for units ALTER TABLE Beliefs ADD UnitProductionModifier integer DEFAULT 0; @@ -72,3 +78,6 @@ ALTER TABLE Beliefs ADD HappinessFromForeignSpies integer DEFAULT 0; -- Decrease inquisitor cost. What it says on the tin ya git. ALTER TABLE Beliefs ADD InquisitorCostModifier integer DEFAULT 0; + +-- Increases civilian work rate by x% +ALTER TABLE Beliefs ADD CivilianWorkRate integer DEFAULT 0; diff --git a/(1) Community Patch/Database Changes/Religions/NewBeliefTables.xml b/(1) Community Patch/Database Changes/Religions/NewBeliefTables.xml index cdc6c537eb..e7567be330 100644 --- a/(1) Community Patch/Database Changes/Religions/NewBeliefTables.xml +++ b/(1) Community Patch/Database Changes/Religions/NewBeliefTables.xml @@ -20,6 +20,13 @@
+ + + + + + +
@@ -381,4 +388,12 @@
+ + + + + + + +
diff --git a/(1) Community Patch/Database Changes/Text/en_US/Concepts/CoreConceptTextChanges.sql b/(1) Community Patch/Database Changes/Text/en_US/Concepts/CoreConceptTextChanges.sql index e1d48b2a94..9ac74c59e6 100644 --- a/(1) Community Patch/Database Changes/Text/en_US/Concepts/CoreConceptTextChanges.sql +++ b/(1) Community Patch/Database Changes/Text/en_US/Concepts/CoreConceptTextChanges.sql @@ -33,6 +33,10 @@ UPDATE Language_en_US SET Text = 'At the end of melee combat, one or both units may have sustained damage and lost "hit points." If a unit''s hit points are reduced to 0, that unit is destroyed. If after melee combat the defending unit has been destroyed and the attacker survives, the attacking unit moves into the defender''s hex [COLOR_YELLOW]unless defending a Citadel, Fort, or City, at which point the melee unit remains in place[ENDCOLOR]. If it moves, the winner will capture any non-military units in that hex. If the defending unit survives, it retains possession of its hex and any other units in the hex.[NEWLINE][NEWLINE]Most units use up all of their movement when attacking. Some however have the ability to move after combat - if they survive the battle and have movement points left to expend.[NEWLINE][NEWLINE]Any surviving units involved in the combat will receive "experience points" (XPs), which may be expended to give the unit promotions.' WHERE Tag = 'TXT_KEY_COMBAT_MELEERESULTS_HEADING3_BODY'; +UPDATE Language_en_US +SET Text = 'Missiles are one-shot weapons. They perform a single air strike mission against a target, and then, win or lose, they are destroyed. They can be intercepted.' +WHERE Tag= 'TXT_KEY_AIRPOWER_MISSILES_HEADING2_BODY'; + -- Map UPDATE Language_en_US SET Text = 'The ruin provides a map of the nearest unrevealed City (lifting the fog of war from a number of tiles).' diff --git a/(1) Community Patch/Database Changes/Text/en_US/Notifications/CoreNewNotificationText.xml b/(1) Community Patch/Database Changes/Text/en_US/Notifications/CoreNewNotificationText.xml index 34c096faed..32067f662c 100644 --- a/(1) Community Patch/Database Changes/Text/en_US/Notifications/CoreNewNotificationText.xml +++ b/(1) Community Patch/Database Changes/Text/en_US/Notifications/CoreNewNotificationText.xml @@ -865,6 +865,9 @@ Restoring Hit Points + + Disbanding or Upgrading a Unit + Inflicting Damage on a City diff --git a/(1) Community Patch/Database Changes/Text/en_US/UI/CoreNewUIText.xml b/(1) Community Patch/Database Changes/Text/en_US/UI/CoreNewUIText.xml index cc2eb0b983..4bf024f44f 100644 --- a/(1) Community Patch/Database Changes/Text/en_US/UI/CoreNewUIText.xml +++ b/(1) Community Patch/Database Changes/Text/en_US/UI/CoreNewUIText.xml @@ -299,6 +299,9 @@ {1_Num} [ICON_PRODUCTION] + + {1_Num} [ICON_PRODUCTION] + {2_Num} [ICON_PRODUCTION] per Era + {1_Num} [ICON_GOLD] @@ -403,7 +406,7 @@ Max Hit Points: {1_Num}
- [ICON_BULLET] Sight: {1_Num} + [ICON_VISION] Sight: {1_Num} [ICON_AIRSTRIKE_DEFENSE] Air Strike Defense: {1_Num} @@ -484,7 +487,7 @@ Ignores [ICON_STRENGTH] City Strength from Buildings when Attacking - Gains [ICON_CULTURE] Culture on Disband or Upgrade + On Disband or Upgrade: Receive [ICON_CULTURE] Culture equal to {1_Num}x total XP {2_Sign}{1_Num}% Ancient Ruin Rewards @@ -737,7 +740,7 @@ Up to {1_YieldBoosts} when [COLOR_POSITIVE_TEXT]Themed[ENDCOLOR] - +{1_Num} [ICON_STRENGTH] City Strength for every National/World Wonder in City + [ICON_BULLET]+{1_Num} [ICON_STRENGTH] City Strength for every National/World Wonder in City {2_Sign}{1_Num}% [ICON_STRENGTH] City Strength from Buildings @@ -746,7 +749,7 @@ {2_Sign}{1_Num}% [ICON_STRENGTH] City Strength from Buildings in all Cities of your Team - [ICON_STRENGTH] Damage Reduction: {2_Sign}{1_Num} + [ICON_BULLET][ICON_STRENGTH] Damage Reduction: {2_Sign}{1_Num} {2_Sign}{1_Num} {3_ResourceIcon} {4_Resource:textkey} @@ -983,31 +986,31 @@ {2_Sign}{1_Num}% [ICON_PRODUCTION] Production from each [ICON_INTERNATIONAL_TRADE] Trade Route with City-States - {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for Non-Wonder Buildings + {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards Non-Wonder Buildings - {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for Wonders + {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards Wonders - {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for Military Units + {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards Military Units - {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for Military Units in all Cities for every Major Civilization at war with your Empire + {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards Military Units in all Cities for every Major Civilization at war with your Empire - {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for Spaceship Parts + {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards Spaceship Parts - {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for [COLOR_YELLOW]{3_Unit:textkey}[ENDCOLOR] + {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards [COLOR_YELLOW]{3_Unit:textkey}[ENDCOLOR] - {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for [COLOR_YELLOW]{3_UnitCombat:textkey} Units[ENDCOLOR] + {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards [COLOR_YELLOW]{3_UnitCombat:textkey} Units[ENDCOLOR] - {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for [COLOR_YELLOW]{3_Domain:textkey}[ENDCOLOR] + {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards [COLOR_YELLOW]{3_Domain:textkey}[ENDCOLOR] - {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for Units requiring {3_ResourceIcon} {4_Resource:textkey} + {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards Units requiring {3_ResourceIcon} {4_Resource:textkey} {2_Sign}{1_Num}% [ICON_PRODUCTION] Production/[ICON_GOLD] Gold from [ICON_CONNECTED] City Connections and [ICON_INDUSTRIAL_CONNECTED] Industrial City Connections @@ -1088,7 +1091,7 @@ {2_Sign}{1_Num}% Duration for [ICON_INTERNATIONAL_TRADE] Trade Routes originated from this City - {2_Sign}{1_Num} Sight for Trade Units + {2_Sign}{1_Num} [ICON_VISION] Sight for Trade Units {1_Num}x Speed for Trade Units @@ -1640,7 +1643,7 @@ [ICON_BULLET][COLOR_WATER_TEXT]{3_Accomplishment:textkey}[ENDCOLOR]: {2_Sign}{1_Num} XP for [COLOR_YELLOW]{4_Domain:textkey}[ENDCOLOR] - [ICON_BULLET][COLOR_WATER_TEXT]{3_Accomplishment:textkey}[ENDCOLOR]: {2_Sign}{1_Num}% [ICON_PRODUCTION] Production for [COLOR_YELLOW]{4_UnitCombat:textkey}[ENDCOLOR] Units + [ICON_BULLET][COLOR_WATER_TEXT]{3_Accomplishment:textkey}[ENDCOLOR]: {2_Sign}{1_Num}% [ICON_PRODUCTION] Production towards [COLOR_YELLOW]{4_UnitCombat:textkey}[ENDCOLOR] Units [ICON_BULLET][COLOR_WATER_TEXT]{2_Accomplishment:textkey}[ENDCOLOR]: May build {1_Num} more of this Building @@ -2058,6 +2061,9 @@ [NEWLINE][ICON_BULLET]Modifier for Unit Domain from Pledges: {1_Num}% + + [NEWLINE][ICON_BULLET]Modifier for Unit Combat Class from Religion: {1_Num}% + [NEWLINE][ICON_BULLET]Modifier for Military Units from Religion: {1_Num}% @@ -2968,7 +2974,7 @@ They fear your warmongering will plunge the world into nuclear winter! - [SPACE](They furiously abhor warmongers!) + [SPACE](They fervently abhor warmongers!) [SPACE](They hate warmongers!) @@ -3185,6 +3191,57 @@ The following cities currently demand this Resource: + + WORLD MAP + + + Reveals all land explored by this civilization.[NEWLINE][NEWLINE]Note: Will not receive contact with unmet civilizations and city-states. + + + Both players don't yet have the Technology required to trade this item ({1_Tech:textkey}). + + + They don't yet have the Technology required to trade this item ({1_Tech:textkey}). + + + You don't yet have the Technology required to trade this item ({1_Tech:textkey}). + + + They have already explored all of the lands revealed in your World Map. + + + We have already explored all of the lands revealed in their World Map. + + + TECHNOLOGIES + + + TECHNOLOGIES + + + We have Technologies available to trade. + + + We have no Technologies available to trade. + + + The other leader has Technologies available to trade. + + + The other leader has no Technologies available to trade. + + + You must found a City in order to begin research endeavors. + + + They must found a City in order to begin research endeavors. + + + They don't yet have the Technology to sell this item (Scientific Theory). + + + You don't yet have the Technology to sell this item (Scientific Theory). + Deal Value for Them: {1_Num}. @@ -3487,7 +3544,7 @@ Area Damage - Pilum + Fortified Strike Splash Damage diff --git a/(1) Community Patch/Database Changes/Units/UnitTableChanges.sql b/(1) Community Patch/Database Changes/Units/UnitTableChanges.sql index be1d8a5a7a..29ae572d8d 100644 --- a/(1) Community Patch/Database Changes/Units/UnitTableChanges.sql +++ b/(1) Community Patch/Database Changes/Units/UnitTableChanges.sql @@ -17,8 +17,8 @@ ALTER TABLE Units ADD FoundColony integer DEFAULT 0; -- Adds marker for city attack only units (for AI) ALTER TABLE Units ADD CityAttackOnly boolean DEFAULT 0; --- Adds Culture from experience to owner of unit when disbanded or upgraded -ALTER TABLE Units ADD CulExpOnDisbandUpgrade boolean DEFAULT 0; +-- Adds Culture to owner of disbanded or upgraded unit equal to unit XP * x +ALTER TABLE Units ADD CulExpOnDisbandUpgrade integer DEFAULT 0; -- Increases the cost by this amount every time you build the unit ALTER TABLE Units ADD CostScalerNumRepeats integer DEFAULT 0; diff --git a/(1) Community Patch/Kit/CPK.lua b/(1) Community Patch/Kit/CPK.lua index 9316800e00..79b623e180 100644 --- a/(1) Community Patch/Kit/CPK.lua +++ b/(1) Community Patch/Kit/CPK.lua @@ -16,6 +16,7 @@ local lua_type = type local lua_next = next + local lua_pcall = pcall local lua_error = error local lua_tostring = tostring @@ -87,7 +88,7 @@ error('Error in include emulation: dofile is not a function') end - local success, result = pcall(dofile, GetCandidatePath(filename)) + local success, result = lua_pcall(dofile, GetCandidatePath(filename)) if not success then if result:find('^cannot open') then @@ -356,7 +357,7 @@ }) end - local success, result = pcall(Import, filename) + local success, result = lua_pcall(Import, filename) if not success then Error(4, { diff --git a/(1) Community Patch/Kit/UI/Control/CPK.UI.Control.Inspect.lua b/(1) Community Patch/Kit/UI/Control/CPK.UI.Control.Inspect.lua new file mode 100644 index 0000000000..9e379a4952 --- /dev/null +++ b/(1) Community Patch/Kit/UI/Control/CPK.UI.Control.Inspect.lua @@ -0,0 +1,24 @@ +local lua_xpcall = xpcall +local lua_tostring = tostring + +local Void = CPK.FP.Void +local IsTable = CPK.Type.IsTable +local IsFunction = CPK.Type.IsFunction + +--- Formats a UI control as a string, appending its GetID() where available. +--- @param control any +--- @return string +local function ControlInspect(control) + local str = lua_tostring(control) + + if IsTable(control) and IsFunction(control.GetID) then + if not str:match(': 00000000$') then + local ok, id = lua_xpcall(function() return control:GetID() end, Void) + str = str .. ' (' .. lua_tostring(ok and id or nil) .. ')' + end + end + + return str +end + +CPK.UI.Control.Inspect = ControlInspect diff --git a/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Error.lua b/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Error.lua index 6dff6c08d8..8d08ba9688 100644 --- a/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Error.lua +++ b/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Error.lua @@ -1,37 +1,21 @@ local lua_error = error -local lua_xpcall = xpcall local lua_tostring = tostring local lua_loadstring = loadstring local lua_string_format = string.format -local Void = CPK.FP.Void -local IsTable = CPK.Type.IsTable -local IsFunction = CPK.Type.IsFunction +local ControlInspect = CPK.UI.Control.Inspect local template = [[InstanceError: %s - Instance name: %s - Instance root: %s - Instance parent: %s - Instance released: %s - Current state: %s - Current context: %s + Instance: + Name: %s + Root: %s + Parent: %s + Released: %s + Current: + State: %s + Context: %s ]] ---- @param control any ---- @return string -local function fc(control) - local str = lua_tostring(control) - - if IsTable(control) and IsFunction(control.GetID) then - if not str:match(': 00000000$') then - local ok, id = lua_xpcall(function() return control:GetID() end, Void) - str = str .. ' (' .. lua_tostring(ok and id or nil) .. ')' - end - end - - return str -end - --- @param inst ControlInstance --- @param info string local function InstanceError(inst, info) @@ -39,11 +23,11 @@ local function InstanceError(inst, info) template, lua_tostring(info), lua_tostring(inst[1]), - fc(inst[3]), - fc(inst[4]), + ControlInspect(inst[3]), + ControlInspect(inst[4]), lua_tostring(inst[2]), lua_tostring(lua_loadstring('return StateName')()), - fc(lua_loadstring('return ContextPtr')()) + ControlInspect(lua_loadstring('return ContextPtr')()) ) lua_error(msg, 3) diff --git a/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Make.lua b/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Make.lua index c34cdbe86e..8e178695a4 100644 --- a/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Make.lua +++ b/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Make.lua @@ -3,6 +3,7 @@ local lua_next = next local lua_tostring = tostring local lua_loadstring = loadstring local lua_setmetatable = setmetatable +local lua_string_format = string.format local IsString = CPK.Type.IsString @@ -10,6 +11,7 @@ local AssertIsTable = CPK.Assert.IsTable local AssertIsString = CPK.Assert.IsString local InstanceError = CPK.UI.Control.Instance.Error +local ControlInspect = CPK.UI.Control.Inspect --- @class ControlInstance --- @field [1] string # Instance name @@ -33,6 +35,15 @@ local ControlInstanceMeta = { end, __newindex = function(self) InstanceError(self, 'Instance modification is prohibited!') + end, + __tostring = function(self) + return lua_string_format( + 'ControlInstance(name: %s, root: %s, parent: %s, released: %s)', + lua_tostring(self[1]), + ControlInspect(self[3]), + ControlInspect(self[4]), + lua_tostring(self[2]) + ) end } @@ -52,12 +63,12 @@ local ControlInstanceMeta = { --- unless it will never be freed through this API.** --- --- @param name string # `` as defined in XML ---- @param parent Control | nil # Parent control (defaults to `ContextPtr`) +--- @param parent Control | nil # Parent control (defaults to context or `ContextPtr`) +--- @param context Control | nil # Context control --- @return ControlInstance # Table of child controls indexed by XML ID ---- @return Control # Root control for the created instance (Always ControlBase) -local function MakeControlInstance(name, parent) - local context = lua_loadstring('return ContextPtr')() - +--- @return Control # Root control for the created instance (Always `ControlBase`) +local function MakeControlInstance(name, parent, context) + context = context or lua_loadstring('return ContextPtr')() parent = parent or context AssertIsTable(context) diff --git a/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Manager.lua b/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Manager.lua new file mode 100644 index 0000000000..1adbd31200 --- /dev/null +++ b/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Manager.lua @@ -0,0 +1,292 @@ +local lua_next = next +local lua_error = error +local lua_tostring = tostring +local lua_loadstring = loadstring +local lua_setmetatable = setmetatable +local lua_string_format = string.format + +local Stack = CPK.Util.Stack +local Queue = CPK.Util.Queue + +local AssertIsTable = CPK.Assert.IsTable +local AssertIsString = CPK.Assert.IsString +local AssertIsFunction = CPK.Assert.IsFunction + +local ControlInspect = CPK.UI.Control.Inspect + +local MakeInstance = CPK.UI.Control.Instance.Make +local MoveInstance = CPK.UI.Control.Instance.Move +local FreeInstance = CPK.UI.Control.Instance.Free + +local errorTemplate = [[InstanceManagerError: %s + Manager: + Instance: %s + Context: %s + Mode: %s + Size: %d (acquired: %d, recycled: %d) + Current: + State: %s + Context: %s + Instance: %s + Instance state: %s +]] + +--- Configuration options for creating an InstanceManager. +--- @class InstanceManagerOpts +--- @field mode? 'LIFO' | 'FIFO' +--- @field context? Control +--- @field instance string +--- @field onCreated nil | (fun(inst: ControlInstance): nil) # Fires once when a new instance is first created. +--- @field onAcquire nil | (fun(inst: ControlInstance): nil) # Fires every time an instance is acquired, new or recycled. +--- @field onRecycle nil | (fun(inst: ControlInstance): nil) # Fires when an instance is returned to the pool. +--- @field onRelease nil | (fun(inst: ControlInstance): nil) # Fires before an instance is permanently destroyed. + +--- Manages the full lifecycle of UI control instances. +--- Handles creation, reuse, recycling and permanent release of ControlInstances. +--- Instances move through three states: acquired → recycled → acquired (reuse) or released (destroy). +--- @class InstanceManager : InstanceManagerOpts +--- @field instance string +--- @field mode 'LIFO' | 'FIFO' +--- @field protected context Control +--- @field protected recycled Stack | Queue +--- @field protected tracking table # true = acquired, false = recycled +local InstanceManagerImpl = {} + +--- @protected +--- @param inst ControlInstance +--- @param info string +function InstanceManagerImpl:Error(inst, info) + local state = self.tracking[inst] + local stateStr = state == nil + and 'not tracked' + or (state and 'acquired' or 'recycled') + + local msg = lua_string_format( + errorTemplate, + lua_tostring(info), + lua_tostring(self.instance), + ControlInspect(self.context), + lua_tostring(self.mode), + self:Size(), self:CountAcquired(), self:CountRecycled(), + lua_tostring(lua_loadstring('return StateName')()), + ControlInspect(lua_loadstring('return ContextPtr')()), + lua_tostring(inst), + stateStr + ) + + lua_error(msg, 3) +end + +--- Pops a recycled instance from the pool or creates a new one via MakeInstance. +--- Moves it to `parent`, fires `onCreated` (new instances only), +--- then `onAcquire`, then shows the root control. +--- @param parent Control +--- @return ControlInstance +function InstanceManagerImpl:Acquire(parent) + local inst = self.recycled:Pop() + + if inst then + MoveInstance(inst, parent) + else + inst = MakeInstance(self.instance, parent, self.context) + local cb = self.onCreated + if cb then cb(inst) end + end + + self.tracking[inst] = true + + local cb = self.onAcquire + if cb then cb(inst) end + + inst[3]:SetHide(false) + + return inst +end + +--- Returns an acquired instance to the pool. +--- Hides the root control, fires `onRecycle`, +--- then pushes the instance onto the recycle stack. +--- Errors if the instance is already recycled or not known to this manager. +--- @param inst ControlInstance +function InstanceManagerImpl:Recycle(inst) + if self.tracking[inst] ~= true then + local info = self.tracking[inst] == false + and 'Recycle called on already recycled instance' + or 'Recycle called on instance not known to this manager' + self:Error(inst, info) + end + + inst[3]:SetHide(true) + self.tracking[inst] = false + + local cb = self.onRecycle + if cb then cb(inst) end + + self.recycled:Push(inst) +end + +--- Permanently destroys an instance, freeing its underlying control. +--- Fires `onRelease` before destroying. +--- Errors if the instance is recycled or not known to this manager. +--- @param inst ControlInstance +function InstanceManagerImpl:Release(inst) + if self.tracking[inst] ~= true then + local info = self.tracking[inst] == false + and 'Release called on non-acquired instance' + or 'Release called on instance not known to this manager' + self:Error(inst, info) + end + + self.tracking[inst] = nil + + local cb = self.onRelease + if cb then cb(inst) end + + FreeInstance(inst) +end + +--- Returns true if this manager has ever acquired `inst` and it has not been permanently released. +--- @param inst ControlInstance +--- @return boolean +function InstanceManagerImpl:HasInstance(inst) + return self.tracking[inst] ~= nil +end + +--- Returns true if `inst` is currently acquired (not yet recycled or released). +--- @param inst ControlInstance +--- @return boolean +function InstanceManagerImpl:HasAcquired(inst) + return self.tracking[inst] == true +end + +--- Returns true if `inst` is currently sitting in the recycle pool, waiting to be reused. +--- @param inst ControlInstance +--- @return boolean +function InstanceManagerImpl:HasRecycled(inst) + return self.tracking[inst] == false +end + +--- Returns the total number of instances managed (acquired + recycled). +--- @return integer +function InstanceManagerImpl:Size() + local count = 0 + for _ in lua_next, self.tracking do + count = count + 1 + end + return count +end + +--- Returns the number of currently acquired instances. +--- @return integer +function InstanceManagerImpl:CountAcquired() + local count = 0 + for _, v in lua_next, self.tracking do + if v then count = count + 1 end + end + return count +end + +--- Returns the number of instances currently in the recycle pool. +--- @return integer +function InstanceManagerImpl:CountRecycled() + return self.recycled:Size() +end + +--- Recycles all currently acquired instances, hiding each root and firing `onRecycle`. +function InstanceManagerImpl:RecycleAcquired() + local tracking = self.tracking + local recycled = self.recycled + local cb = self.onRecycle + + for inst, v in lua_next, tracking do + if v then + inst[3]:SetHide(true) + tracking[inst] = false + if cb then cb(inst) end + recycled:Push(inst) + end + end +end + +--- Permanently releases all recycled instances, firing `onRelease` for each before destroying. +function InstanceManagerImpl:ReleaseRecycled() + local tracking = self.tracking + local recycled = self.recycled + local cb = self.onRelease + local inst = recycled:Pop() + + while inst do + tracking[inst] = nil + if cb then cb(inst) end + FreeInstance(inst) + inst = recycled:Pop() + end +end + +--- @class InstanceManagerMeta +local InstanceManagerMeta = {} + +InstanceManagerMeta.MODE_LIFO = 'LIFO' +InstanceManagerMeta.MODE_FIFO = 'FIFO' + +--- @package +InstanceManagerMeta.ModeStore = { + [InstanceManagerMeta.MODE_FIFO] = Queue, + [InstanceManagerMeta.MODE_LIFO] = Stack +} + +--- @package +InstanceManagerMeta.__index = InstanceManagerImpl + +--- @package +--- @param self InstanceManager +InstanceManagerMeta.__tostring = function(self) + return lua_string_format( + 'InstanceManager(instance: %s, mode: %s, total: %d, acquired: %d, recycled: %d)', + lua_tostring(self.instance), + lua_tostring(self.mode), + self:Size(), + self:CountAcquired(), + self:CountRecycled() + ) +end + +--- Creates a new InstanceManager. Validates all opts fields and errors on invalid input. +--- Defaults to LIFO ordering if `opts.mode` is not provided. +--- @param opts InstanceManagerOpts +--- @return InstanceManager +function InstanceManagerMeta.New(opts) + AssertIsTable(opts) --[[@cast opts table]] + AssertIsString(opts.instance) + + local context = opts.context or lua_loadstring('return ContextPtr')() + + AssertIsTable(context) + + local mode = opts.mode or 'LIFO' + + AssertIsString(mode) + + local store = InstanceManagerMeta.ModeStore[mode] + + AssertIsTable(store) + + if opts.onCreated then AssertIsFunction(opts.onCreated) end + if opts.onAcquire then AssertIsFunction(opts.onAcquire) end + if opts.onRecycle then AssertIsFunction(opts.onRecycle) end + if opts.onRelease then AssertIsFunction(opts.onRelease) end + + return lua_setmetatable({ + mode = mode, + context = context, + instance = opts.instance, + recycled = store.New(), + tracking = {}, + onCreated = opts.onCreated, + onAcquire = opts.onAcquire, + onRecycle = opts.onRecycle, + onRelease = opts.onRelease, + }, InstanceManagerMeta) +end + +CPK.UI.Control.Instance.Manager = InstanceManagerMeta diff --git a/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Move.lua b/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Move.lua new file mode 100644 index 0000000000..588ad58174 --- /dev/null +++ b/(1) Community Patch/Kit/UI/Control/Instance/CPK.UI.Control.Instance.Move.lua @@ -0,0 +1,28 @@ +local AssertIsTable = CPK.Assert.IsTable + +local InstanceError = CPK.UI.Control.Instance.Error + +--- Moves control instance to another parent. +--- @param inst ControlInstance +--- @param dest Control +local function MoveControlInstance(inst, dest) + AssertIsTable(inst) + AssertIsTable(dest) + + if inst[2] then + InstanceError(inst, 'Failed to move instance. Instance already released!') + end + + local root = inst[3] + + AssertIsTable(root) + + --[[@cast root Control]] + + root:ChangeParent(dest) + inst[4] = dest + + return inst +end + +CPK.UI.Control.Instance.Move = MoveControlInstance diff --git a/(1) Community Patch/Kit/Util/CPK.Util.Queue.lua b/(1) Community Patch/Kit/Util/CPK.Util.Queue.lua new file mode 100644 index 0000000000..8b6b0e0c68 --- /dev/null +++ b/(1) Community Patch/Kit/Util/CPK.Util.Queue.lua @@ -0,0 +1,109 @@ +local lua_setmetatable = setmetatable + +--- FIFO queue backed by two internal stacks. +--- Items are pushed onto one stack and popped from the other. +--- When the pop-side runs empty it is refilled by reversing the push-side, +--- keeping both Push and Pop amortized O(1). +--- @generic T +--- @class Queue +--- @field protected pushed_items T[] +--- @field protected pushed_count integer +--- @field protected popped_items T[] +--- @field protected popped_count integer +--- @field public Pop fun(self: Queue): T | nil +--- @field public Peek fun(self: Queue): T | nil +--- @field public Push fun(self: Queue, item: T): Queue +--- @field public Size fun(self: Queue): integer +--- @field public Empty fun(self: Queue): boolean +local QueueImpl = {} + +--- @package +function QueueImpl:Drain() + local pushed = self.pushed_items + local popped = self.popped_items + local count = self.pushed_count + + local j = 1 + + for i = count, 1, -1 do + popped[j] = pushed[i] + pushed[i] = nil + j = j + 1 + end + + self.popped_count = count + self.pushed_count = 0 +end + +--- Removes and returns the value from the front of the queue. +function QueueImpl:Pop() + if self.popped_count <= 0 then + if self.pushed_count <= 0 then + return nil + end + + self:Drain() + end + + local count = self.popped_count + local item = self.popped_items[count] + + self.popped_items[count] = nil + self.popped_count = count - 1 + + return item +end + +--- Returns the value at the front of the queue without removing it. +function QueueImpl:Peek() + if self.popped_count <= 0 then + if self.pushed_count <= 0 then + return nil + end + + self:Drain() + end + + return self.popped_items[self.popped_count] +end + +--- Adds one value to the back of the queue. +--- If value is nil then does nothing. +function QueueImpl:Push(item) + if item ~= nil then + local count = self.pushed_count + 1 + self.pushed_items[count] = item + self.pushed_count = count + end + + return self +end + +--- Returns the number of items currently in the queue. +function QueueImpl:Size() + return self.pushed_count + self.popped_count +end + +--- Checks if the queue is empty (has 0 items). +function QueueImpl:Empty() + return self.pushed_count <= 0 and self.popped_count <= 0 +end + +--- @class QueueMeta +local QueueMeta = {} + +--- @package +QueueMeta.__index = QueueImpl + +--- Creates a new, empty FIFO queue. +--- @return Queue +function QueueMeta.New() + return lua_setmetatable({ + pushed_items = {}, + pushed_count = 0, + popped_items = {}, + popped_count = 0, + }, QueueMeta) +end + +CPK.Util.Queue = QueueMeta diff --git a/(1) Community Patch/Kit/Util/CPK.Util.Stack.lua b/(1) Community Patch/Kit/Util/CPK.Util.Stack.lua index 1ce1c7fad4..9ddda4e3f0 100644 --- a/(1) Community Patch/Kit/Util/CPK.Util.Stack.lua +++ b/(1) Community Patch/Kit/Util/CPK.Util.Stack.lua @@ -1,34 +1,36 @@ local lua_setmetatable = setmetatable -local lua_table_remove = table.remove +--- LIFO stack. Items are pushed and popped from the same end (top). --- @generic T --- @class Stack ---- @field protected items any[] +--- @field protected items T[] --- @field protected count integer +--- @field public Pop fun(self: Stack): T | nil +--- @field public Peek fun(self: Stack): T | nil +--- @field public Push fun(self: Stack, item: T): Stack +--- @field public Size fun(self: Stack): integer +--- @field public Empty fun(self: Stack): boolean local StackImpl = {} --- Removes and returns the value from the top of the stack. ---- @return any function StackImpl:Pop() - if self.count <= 0 then - return nil - end + if self.count <= 0 then return nil end + + local item = self.items[self.count] - local item = lua_table_remove(self.items) + self.items[self.count] = nil self.count = self.count - 1 return item end --- Returns the value at the top of the stack without removing it. ---- @return T function StackImpl:Peek() return self.items[self.count] end --- Adds one value to the top of the stack. --- If value is nil then does nothing. ---- @param item any function StackImpl:Push(item) if item ~= nil then self.count = self.count + 1 @@ -39,15 +41,13 @@ function StackImpl:Push(item) end --- Returns the number of items currently in the stack. ---- @return integer # The number of items. function StackImpl:Size() return self.count end --- Checks if the stack is empty (has 0 items). ---- @return boolean function StackImpl:Empty() - return self.items[1] == nil + return self.count <= 0 end --- @class StackMeta @@ -57,10 +57,8 @@ local StackMeta = {} StackMeta.__index = StackImpl --- Creates a new, empty stack. ---- @generic T ---- @param _ nil | `T` ---- @return Stack -function StackMeta.New(_) +--- @return Stack +function StackMeta.New() local this = { count = 0, items = {} diff --git a/(1) Community Patch/Kit/Util/CPK.Util.Timer.lua b/(1) Community Patch/Kit/Util/CPK.Util.Timer.lua new file mode 100644 index 0000000000..588148a8bb --- /dev/null +++ b/(1) Community Patch/Kit/Util/CPK.Util.Timer.lua @@ -0,0 +1,280 @@ +local lua_next = next +local lua_pcall = pcall +local lua_error = error +local lua_tostring = tostring +local lua_os_clock = os.clock +local lua_math_abs = math.abs +local lua_setmetatable = setmetatable + +local Void = CPK.FP.Void + +local AssertError = CPK.Assert.Error +local AssertIsTable = CPK.Assert.IsTable +local AssertIsNumber = CPK.Assert.IsNumber +local AssertIsInteger = CPK.Assert.IsInteger +local AssertIsBoolean = CPK.Assert.IsBoolean +local AssertIsFunction = CPK.Assert.IsFunction + +local ControlIsKind = CPK.UI.Control.IsKind + +--- @class TimerTask +--- @field callback function # The function to execute when the interval is reached. +--- @field interval number # Time in seconds between executions (e.g., 1.5). +--- @field lifespan? number # Total number of times to run. If nil, runs indefinitely. +--- @field debounce? boolean # If true, resets elapsed time to 0 exactly. If false, preserves overflow for frequency accuracy. + +--- @class TimerOpts +--- @field context Control # The LuaContext (ContextPtr) to attach the heartbeat to. +--- @field onClose? fun(tasks: table) # Optional: Callback for the engine's SetShutdown event. +--- @field onReady? fun(timer: Timer) # Optional: Callback for the engine's SetPostInit event. +--- @field onError? fun(timer: Timer, error: string, taskId: integer, task: TimerTask) # Optional: Custom error handler for task crashes. + +--- @class TimerMeta +local TimerMeta = {} +TimerMeta.Symbol = {} + +--- @class Timer : TimerOpts +--- @field package last_id integer # An auto-incrementing counter used to assign unique IDs to new tasks. +--- @field package is_live boolean # Indicates if the Timer is currently attached to the context's SetUpdate heartbeat. +--- @field package is_busy boolean # Re-entrancy guard; prevents the update loop from running again if the previous tick is still processing. +--- @field package context Control # The underlying LuaContext providing the execution heartbeat. +--- @field package timeval number # The timestamp (via os.clock) of the most recent frame; used to calculate the delta time (dt). +--- @field package elapsed table # A map of taskId to accumulated seconds since the task's last execution. +--- @field package invokes table # A map of taskId to total successful executions; used to track progress against the lifespan. +--- @field package entries table # The primary registry containing the configuration for all active tasks. +local TimerImpl = {} +TimerMeta.__index = TimerImpl + +--- Retrieves the Timer instance associated with a context, or creates one if it doesn't exist. +--- @param context Control +--- @return Timer +function TimerMeta.For(context) + AssertIsTable(context) + local timer = context[TimerMeta.Symbol] + if not timer then + timer = TimerMeta.New({ context = context }) + end + return timer +end + +--- Creates a new Timer instance. +---
**Ticks only when context is visible.** +---
**This overrides SetUpdate, SetPostInit, and SetShutdown on the context.** +--- @param opts TimerOpts +--- @return Timer +function TimerMeta.New(opts) + AssertIsTable(opts) + + local context = opts.context + local onReady = opts.onReady or Void + local onClose = opts.onClose or Void + local onError = opts.onError or Void + + AssertIsFunction(onReady) + AssertIsFunction(onClose) + AssertIsFunction(onError) + + if not ControlIsKind('LuaContext', context) then + lua_error('Cannot attach Timer to ' .. lua_tostring(context) .. '. Only LuaContext allowed!') + end + + AssertIsFunction(context.GetID) + AssertIsFunction(context.SetUpdate) + AssertIsFunction(context.ClearUpdate) + AssertIsFunction(context.SetShutdown) + AssertIsFunction(context.SetPostInit) + + if context:GetID() == 'YieldIconManager' then + lua_error('Cannot attach Timer to ' .. lua_tostring(context) .. '. Context is restricted by engine!') + end + + if context[TimerMeta.Symbol] then + lua_error('Cannot attach Timer to ' .. lua_tostring(context) .. '. A Timer instance already exists!') + end + + --- @type Timer + local this = { + last_id = 0, + is_live = false, + is_busy = false, + timeval = lua_os_clock(), + entries = {}, + elapsed = {}, + invokes = {}, + onReady = onReady, + onClose = onClose, + onError = onError, + context = context, + } + + lua_setmetatable(this, TimerMeta) + context[TimerMeta.Symbol] = this + + context:SetShutdown(function() + local tasks = this:RemoveTasks() + lua_pcall(this.onClose, tasks) + end) + + context:SetPostInit(function() + lua_pcall(this.onReady, this) + end) + + return this +end + +--- Detaches the timer heartbeat from the context update loop. +--- @package +function TimerImpl:Detach() + self.is_live = false + self.context:ClearUpdate() +end + +--- Attaches the timer heartbeat to the context update loop. +--- @package +function TimerImpl:Attach() + if self.is_live then return end + + self.is_live = true + self.timeval = lua_os_clock() + + local this = self + + this.context:SetUpdate(function() + if not this.is_live then return this:Detach() end + + local now = lua_os_clock() + local dt = now - this.timeval + this.timeval = now + + if this.is_busy then return end + this.is_busy = true + + local removal_array = nil + + for id, task in lua_next, this.entries do + if not this.is_live then break end + + local time_spent = (this.elapsed[id] or 0) + dt + + if time_spent >= task.interval then + local ok, error = lua_pcall(task.callback) + if not ok then + lua_pcall(this.onError, this, error, id, task) + end + + if task.lifespan then + local executions = (this.invokes[id] or 0) + 1 + this.invokes[id] = executions + + if executions >= task.lifespan then + removal_array = removal_array or {} + removal_array[#removal_array + 1] = id + end + end + + time_spent = task.debounce and 0 or (time_spent - task.interval) + end + + this.elapsed[id] = time_spent + end + + -- Process bulk removals + if removal_array then + for i = 1, #removal_array do + this:RemoveTask(removal_array[i]) + end + end + + this.is_busy = false + end) +end + +--- Registers a new task to be executed. Starts the heartbeat if it's currently dormant. +--- @param opts TimerTask +--- @return integer # The unique ID of the created task. +function TimerImpl:CreateTask(opts) + AssertIsTable(opts) + + local interval = opts.interval + local lifespan = opts.lifespan + local debounce = opts.debounce + local callback = opts.callback + + AssertIsNumber(interval) + AssertIsFunction(callback) + + interval = lua_math_abs(interval) + + if interval < 0.005 then + AssertError(interval, '>=0.005', 'Minimum interval is 0.005s (5ms)') + end + + if lifespan ~= nil then + AssertIsInteger(lifespan) + lifespan = lua_math_abs(lifespan) + + if lifespan < 1 then + AssertError(lifespan, '>=1') + end + end + + if debounce ~= nil then + AssertIsBoolean(debounce) + else + debounce = true + end + + local id = self.last_id + 1 + self.last_id = id + + self.entries[id] = { + interval = interval, + callback = callback, + debounce = debounce, + lifespan = lifespan, + } + + self.elapsed[id] = 0 + self.invokes[id] = 0 + + if not self.is_live then + self:Attach() + end + + return id +end + +--- Removes a specific task by its ID. +--- @param id integer +--- @return TimerTask | nil # The removed task definition. +function TimerImpl:RemoveTask(id) + local task = self.entries[id] + + if task then + self.entries[id] = nil + self.elapsed[id] = nil + self.invokes[id] = nil + end + + if lua_next(self.entries) == nil and self.is_live then + self:Detach() + end + + return task +end + +--- Clears all tasks and shuts down the heartbeat. +--- @return table # A list of tasks that were orphaned by the clear. +function TimerImpl:RemoveTasks() + self:Detach() + + local orphaned = self.entries + + self.entries = {} + self.elapsed = {} + self.invokes = {} + + return orphaned +end + +CPK.Util.Timer = TimerMeta diff --git a/(2) Vox Populi/(2) Vox Populi (v 17).modinfo b/(2) Vox Populi/(2) Vox Populi (v 17).modinfo index c96f67393c..f61cb1de34 100644 --- a/(2) Vox Populi/(2) Vox Populi (v 17).modinfo +++ b/(2) Vox Populi/(2) Vox Populi (v 17).modinfo @@ -2,7 +2,7 @@ (2) Vox Populi - (5.2.6) Unofficial Expansion and Rebalancing Project for Civilization V: Brave New World + (5.2.7) Unofficial Expansion and Rebalancing Project for Civilization V: Brave New World Contains balance changes, gameplay improvements, and entirely new game systems to make Civ V a more enjoyable and exciting game. CivFanatics Community Patch Project Team 0 @@ -1078,7 +1078,7 @@ Database Changes/NewGameOptions.xml Database Changes/Triggers.sql Database Changes/WorldChanges.sql - Database Changes/AI/BuildingFlavorSweeps.sql + Database Changes/AI/BuildingFlavorSweeps.sql Database Changes/AI/CityEventFlavorChanges.sql Database Changes/AI/CitySpecializationFlavorChanges.sql Database Changes/AI/EraDiploEmphasisSweeps.sql @@ -1241,8 +1241,8 @@ Database Changes/Text/en_US/Tech/TechTextChanges.sql Database Changes/Text/en_US/UI/InterfaceModeTextChanges.sql Database Changes/Text/en_US/UI/NewUIText.sql - Database Changes/Text/en_US/UI/NewUIText.xml - Database Changes/Text/en_US/UI/UITextChanges.sql + Database Changes/Text/en_US/UI/NewUIText.xml + Database Changes/Text/en_US/UI/UITextChanges.sql Database Changes/Text/en_US/UnitPromotions/NewPromotionText.xml Database Changes/Text/en_US/UnitPromotions/PromotionTextChanges.sql Database Changes/Text/en_US/Units/PreUnitTextChanges.sql @@ -1307,9 +1307,9 @@ Database Changes/WorldMap/Resources/ResourceChanges.sql Database Changes/WorldMap/Resources/PreResourceChanges.sql Database Changes/WorldMap/Resources/ResourceSweeps.sql - LUA/CityBannerManager.lua + LUA/CityBannerManager.lua LUA/CityBannerManager.xml - LUA/CityStateDiploPopup.lua + LUA/CityStateDiploPopup.lua LUA/CityStateDiploPopup.xml LUA/CityStateGreetingPopup.lua LUA/CityStateGreetingPopup.xml @@ -1325,7 +1325,7 @@ LUA/NotificationPanel.xml LUA/SocialPolicyPopup.lua LUA/SocialPolicyPopup.xml - LUA/TopPanel.lua + LUA/TopPanel.lua LUA/TopPanel.xml LUA/UnitFlagManager.lua LUA/UnitFlagManager.xml diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_045.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_045.dds index 836db1a784..4335b7ba0a 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_045.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_045.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_064.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_064.dds index 77da13fbfb..af6e650625 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_064.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_064.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_080.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_080.dds index b50bfcc61b..1d5a5a075a 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_080.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_080.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_128.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_128.dds index 6c6ef5e8d5..d69e438fbf 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_128.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_128.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_256.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_256.dds index 2848749193..760ce01553 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_256.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_256.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_256.xcf b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_256.xcf index 0efa5f9867..099b2ef688 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_256.xcf and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas1_256.xcf differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_045.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_045.dds index 2d2fff955f..d43ea382e2 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_045.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_045.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_064.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_064.dds index 6413af0a3f..ef7a883ce9 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_064.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_064.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_080.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_080.dds index 561d1f6ec3..7c3179389d 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_080.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_080.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_128.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_128.dds index 98f44f9efa..f9caf3a675 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_128.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_128.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_256.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_256.dds index ecde1be747..a0dcff8013 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_256.dds and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_256.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_256.xcf b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_256.xcf index 9baa91baf1..de90237976 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_256.xcf and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas2_256.xcf differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_045.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_045.dds new file mode 100644 index 0000000000..124f94975a Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_045.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_064.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_064.dds new file mode 100644 index 0000000000..b48c178d93 Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_064.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_080.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_080.dds new file mode 100644 index 0000000000..43dede1eca Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_080.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_128.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_128.dds new file mode 100644 index 0000000000..fcd5c5caff Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_128.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_256.dds b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_256.dds new file mode 100644 index 0000000000..3becaa9944 Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_256.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_256.xcf b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_256.xcf new file mode 100644 index 0000000000..b4df4f4823 Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Buildings/VPBuildingAtlas3_256.xcf differ diff --git a/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_045.dds b/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_045.dds index a0adb7e44c..8497f9cc03 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_045.dds and b/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_045.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_064.dds b/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_064.dds index 7775ea51b6..80b13ded6c 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_064.dds and b/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_064.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_064.xcf b/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_064.xcf new file mode 100644 index 0000000000..9bd4b96bb3 Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Builds/VPBuildAtlas_064.xcf differ diff --git a/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_064.dds b/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_064.dds index 739f2b3f3e..51f62b6356 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_064.dds and b/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_064.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_256.dds b/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_256.dds index 16c574c8ca..a6f38751bf 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_256.dds and b/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_256.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_256.xcf b/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_256.xcf new file mode 100644 index 0000000000..30c6f4eb3f Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Improvements/VPImprovementAtlas_256.xcf differ diff --git a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_016.dds b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_016.dds index 1e6c727d8d..3b4ce44dde 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_016.dds and b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_016.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_032.dds b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_032.dds index 0dc827460d..e359c3c92e 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_032.dds and b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_032.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_045.dds b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_045.dds index fd166006cb..641493ad6f 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_045.dds and b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_045.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_064.dds b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_064.dds index 7adf40d659..92db35bc2f 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_064.dds and b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_064.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_256.dds b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_256.dds index c4850cf156..00a598ee21 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_256.dds and b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_256.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_256.xcf b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_256.xcf new file mode 100644 index 0000000000..53ea9d82db Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Promotions/promoVP_07_256.xcf differ diff --git a/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_045.dds b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_045.dds index d10db29ef2..6af8d36522 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_045.dds and b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_045.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_064.dds b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_064.dds index 08a982519b..49b4cc79de 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_064.dds and b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_064.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_080.dds b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_080.dds index a62ec73355..02a36de27f 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_080.dds and b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_080.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_256.dds b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_256.dds index 8ae3131771..b9280faf09 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_256.dds and b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_256.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_256.xcf b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_256.xcf new file mode 100644 index 0000000000..bf2b7ee259 Binary files /dev/null and b/(2) Vox Populi/Assets/Atlases/Resources/VPResourceAtlas_256.xcf differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_045.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_045.dds index 2af861e22d..72cf89345d 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_045.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_045.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_064.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_064.dds index 4f4860bdb6..96be768d93 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_064.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_064.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_080.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_080.dds index f0da29ecb9..40e7fb52be 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_080.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_080.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_128.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_128.dds index 4ab4adb540..1217e40872 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_128.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_128.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_256.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_256.dds index 821a25f31c..ec5c16bdc6 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_256.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_256.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_256.xcf b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_256.xcf index 481f5249bf..8db0db6345 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_256.xcf and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas1_256.xcf differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_045.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_045.dds index 16e9d01e42..7454ad6223 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_045.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_045.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_064.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_064.dds index c01fc860f4..bf30d38e86 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_064.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_064.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_080.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_080.dds index 43b83bc891..46b831691c 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_080.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_080.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_128.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_128.dds index 6ba1d11fd0..acc403d2a2 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_128.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_128.dds differ diff --git a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_256.dds b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_256.dds index 4701b43fa2..7b6c3ed7df 100644 Binary files a/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_256.dds and b/(2) Vox Populi/Assets/Atlases/Units/VPUnitAtlas2_256.dds differ diff --git a/(2) Vox Populi/Assets/Images/WonderSplash/Alamut_Splash.dds b/(2) Vox Populi/Assets/Images/WonderSplash/Alamut_Splash.dds new file mode 100644 index 0000000000..ed3bead1e9 Binary files /dev/null and b/(2) Vox Populi/Assets/Images/WonderSplash/Alamut_Splash.dds differ diff --git a/(2) Vox Populi/Assets/Images/WonderSplash/Alcatraz_Splash.dds b/(2) Vox Populi/Assets/Images/WonderSplash/Alcatraz_Splash.dds new file mode 100644 index 0000000000..84865a4579 Binary files /dev/null and b/(2) Vox Populi/Assets/Images/WonderSplash/Alcatraz_Splash.dds differ diff --git a/(2) Vox Populi/Assets/Images/WonderSplash/ChateaudIf_Splash.dds b/(2) Vox Populi/Assets/Images/WonderSplash/ChateaudIf_Splash.dds new file mode 100644 index 0000000000..e0a3b6fa96 Binary files /dev/null and b/(2) Vox Populi/Assets/Images/WonderSplash/ChateaudIf_Splash.dds differ diff --git a/(2) Vox Populi/Assets/Images/WonderSplash/PortRoyal_Splash.dds b/(2) Vox Populi/Assets/Images/WonderSplash/PortRoyal_Splash.dds new file mode 100644 index 0000000000..bc292f87ac Binary files /dev/null and b/(2) Vox Populi/Assets/Images/WonderSplash/PortRoyal_Splash.dds differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks.fxsxml b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks.fxsxml new file mode 100644 index 0000000000..c8b5807389 --- /dev/null +++ b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks.fxsxml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks.gr2 b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks.gr2 new file mode 100644 index 0000000000..3deea9180f Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks.gr2 differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_diff.dds b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_diff.dds new file mode 100644 index 0000000000..65a74b5103 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_diff.dds differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_grnd_diff.dds b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_grnd_diff.dds new file mode 100644 index 0000000000..e6b760f4ed Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_grnd_diff.dds differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_diff.dds b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_diff.dds new file mode 100644 index 0000000000..15109813b5 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_diff.dds differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_grnd.dds b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_grnd.dds new file mode 100644 index 0000000000..5cf4f35190 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_grnd.dds differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_grnd_h.dds b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_grnd_h.dds new file mode 100644 index 0000000000..b80069ea44 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_grnd_h.dds differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_sref.dds b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_sref.dds new file mode 100644 index 0000000000..da00b4b496 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_hb_sref.dds differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_sref.dds b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_sref.dds new file mode 100644 index 0000000000..f19102fcf7 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/Saltworks_sref.dds differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/hb_saltworks.fxsxml b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/hb_saltworks.fxsxml new file mode 100644 index 0000000000..7256e9c58e --- /dev/null +++ b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/hb_saltworks.fxsxml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/hb_saltworks.gr2 b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/hb_saltworks.gr2 new file mode 100644 index 0000000000..586d92e59c Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/hb_saltworks.gr2 differ diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/pl_saltworks.fxsxml b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/pl_saltworks.fxsxml new file mode 100644 index 0000000000..8b9388d7dd --- /dev/null +++ b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/pl_saltworks.fxsxml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/(2) Vox Populi/Assets/Models/Improvements/Saltworks/pl_saltworks.gr2 b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/pl_saltworks.gr2 new file mode 100644 index 0000000000..2f29acf82b Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Improvements/Saltworks/pl_saltworks.gr2 differ diff --git a/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador.fxsxml b/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador.fxsxml new file mode 100644 index 0000000000..afad3f2cfa --- /dev/null +++ b/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador.fxsxml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador.gr2 b/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador.gr2 new file mode 100644 index 0000000000..59ad900a6c Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador.gr2 differ diff --git a/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador_diff.dds b/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador_diff.dds new file mode 100644 index 0000000000..317de4b519 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador_diff.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador_sref.dds b/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador_sref.dds new file mode 100644 index 0000000000..69d2ab6463 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Ambassador/ambassador_sref.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP.fxsxml b/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP.fxsxml new file mode 100644 index 0000000000..3ab1d33776 --- /dev/null +++ b/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP.fxsxml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP.gr2 b/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP.gr2 new file mode 100644 index 0000000000..8029baa612 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP.gr2 differ diff --git a/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP_diff.dds b/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP_diff.dds new file mode 100644 index 0000000000..0c492b4cf0 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP_diff.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP_sref.dds b/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP_sref.dds new file mode 100644 index 0000000000..9ad266bc20 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Diplomat/DiplomatVP_sref.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout.dds b/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout.dds new file mode 100644 index 0000000000..0082581ce7 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout.fxsxml b/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout.fxsxml new file mode 100644 index 0000000000..987eccf487 --- /dev/null +++ b/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout.fxsxml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout.gr2 b/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout.gr2 new file mode 100644 index 0000000000..5762098535 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout.gr2 differ diff --git a/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout_sref.dds b/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout_sref.dds new file mode 100644 index 0000000000..4ff87c4e63 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Emissary/IncaScout_sref.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/Envoy/EnvoyVP.fxsxml b/(2) Vox Populi/Assets/Models/Units/Envoy/EnvoyVP.fxsxml new file mode 100644 index 0000000000..4dbe984267 --- /dev/null +++ b/(2) Vox Populi/Assets/Models/Units/Envoy/EnvoyVP.fxsxml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/(2) Vox Populi/Assets/Models/Units/Envoy/EnvoyVP.gr2 b/(2) Vox Populi/Assets/Models/Units/Envoy/EnvoyVP.gr2 new file mode 100644 index 0000000000..a0d400e5dd Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Envoy/EnvoyVP.gr2 differ diff --git a/(2) Vox Populi/Assets/Models/Units/Envoy/envoy_diff.dds b/(2) Vox Populi/Assets/Models/Units/Envoy/envoy_diff.dds new file mode 100644 index 0000000000..a5c3336ab2 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Envoy/envoy_diff.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/Envoy/envoy_sref.dds b/(2) Vox Populi/Assets/Models/Units/Envoy/envoy_sref.dds new file mode 100644 index 0000000000..3300dd8012 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Envoy/envoy_sref.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/Envoy/hat_envoy_diff.dds b/(2) Vox Populi/Assets/Models/Units/Envoy/hat_envoy_diff.dds new file mode 100644 index 0000000000..5f623ac5c1 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Envoy/hat_envoy_diff.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/Envoy/hat_envoy_sref.dds b/(2) Vox Populi/Assets/Models/Units/Envoy/hat_envoy_sref.dds new file mode 100644 index 0000000000..a0536d28e7 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/Envoy/hat_envoy_sref.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Modern/great_dignitary.dds b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Modern/great_dignitary.dds new file mode 100644 index 0000000000..6b964efb8a Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Modern/great_dignitary.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Modern/great_dignitary.fxsxml b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Modern/great_dignitary.fxsxml new file mode 100644 index 0000000000..b568cdca5e --- /dev/null +++ b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Modern/great_dignitary.fxsxml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Modern/great_dignitary.gr2 b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Modern/great_dignitary.gr2 new file mode 100644 index 0000000000..ce1ce74d19 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Modern/great_dignitary.gr2 differ diff --git a/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_Statesman.fxsxml b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_Statesman.fxsxml new file mode 100644 index 0000000000..cd41b1cbd0 --- /dev/null +++ b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_Statesman.fxsxml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_Statesman.gr2 b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_Statesman.gr2 new file mode 100644 index 0000000000..edf5bcf402 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_Statesman.gr2 differ diff --git a/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_statesman_diff.dds b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_statesman_diff.dds new file mode 100644 index 0000000000..8f2e87a9a0 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_statesman_diff.dds differ diff --git a/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_statesman_sref.dds b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_statesman_sref.dds new file mode 100644 index 0000000000..7b341fa312 Binary files /dev/null and b/(2) Vox Populi/Assets/Models/Units/GreatDiplomat/Renaissance/Elder_statesman_sref.dds differ diff --git a/(2) Vox Populi/Assets/StrategicView/Features/sv_salt_lake.dds b/(2) Vox Populi/Assets/StrategicView/Features/sv_salt_lake.dds new file mode 100644 index 0000000000..72eb495ec0 Binary files /dev/null and b/(2) Vox Populi/Assets/StrategicView/Features/sv_salt_lake.dds differ diff --git a/(2) Vox Populi/Assets/StrategicView/Improvements/SV_Saltworks.dds b/(2) Vox Populi/Assets/StrategicView/Improvements/SV_Saltworks.dds new file mode 100644 index 0000000000..e8cd390521 Binary files /dev/null and b/(2) Vox Populi/Assets/StrategicView/Improvements/SV_Saltworks.dds differ diff --git a/(2) Vox Populi/Assets/StrategicView/Improvements/SV_Torii.dds b/(2) Vox Populi/Assets/StrategicView/Improvements/SV_Torii.dds new file mode 100644 index 0000000000..e8d8b5d2c0 Binary files /dev/null and b/(2) Vox Populi/Assets/StrategicView/Improvements/SV_Torii.dds differ diff --git a/(2) Vox Populi/Assets/StrategicView/Resources/sv_lake_fish.dds b/(2) Vox Populi/Assets/StrategicView/Resources/sv_lake_fish.dds new file mode 100644 index 0000000000..150f2c929e Binary files /dev/null and b/(2) Vox Populi/Assets/StrategicView/Resources/sv_lake_fish.dds differ diff --git a/(2) Vox Populi/Core Files/Overrides/CivilopediaScreen.lua b/(2) Vox Populi/Core Files/Overrides/CivilopediaScreen.lua index dca1998293..ca806a9a2e 100644 --- a/(2) Vox Populi/Core Files/Overrides/CivilopediaScreen.lua +++ b/(2) Vox Populi/Core Files/Overrides/CivilopediaScreen.lua @@ -2774,7 +2774,7 @@ CivilopediaCategory[CategoryTech].SelectArticle = function( techID, shouldAddToL thisPlayer = Players[Game.GetActivePlayer()]; if thisPlayer ~= nil then leaderID = thisPlayer:GetLeaderType(); - for leaderTraits in DB.Query( "SELECT TraitType FROM Leader_Traits INNER JOIN Leaders on Leaders.Type = LeaderType WHERE Leaders.ID = " .. leaderID ) do + for leaderTraits in DB.Query( "SELECT TraitType FROM Leader_Traits INNER JOIN Leaders on Leaders.Type = LeaderType WHERE Leaders.ID = ?", leaderID ) do traitType = leaderTraits.TraitType; break; end @@ -6521,7 +6521,7 @@ CivilopediaCategory[CategoryResources].SelectArticle = function( resourceID, sho thisPlayer = Players[Game.GetActivePlayer()]; if thisPlayer ~= nil then leaderID = thisPlayer:GetLeaderType(); - for leaderTraits in DB.Query( "SELECT TraitType FROM Leader_Traits INNER JOIN Leaders on Leaders.Type = LeaderType WHERE Leaders.ID = " .. leaderID ) do + for leaderTraits in DB.Query( "SELECT TraitType FROM Leader_Traits INNER JOIN Leaders on Leaders.Type = LeaderType WHERE Leaders.ID = ?", leaderID ) do traitType = leaderTraits.TraitType; break; end diff --git a/(2) Vox Populi/Database Changes/AI/BuildingFlavorSweeps.sql b/(2) Vox Populi/Database Changes/AI/BuildingFlavorSweeps.sql index 9933f66e64..23d27f756d 100644 --- a/(2) Vox Populi/Database Changes/AI/BuildingFlavorSweeps.sql +++ b/(2) Vox Populi/Database Changes/AI/BuildingFlavorSweeps.sql @@ -31,6 +31,12 @@ VALUES ('BUILDING_FORGE', 'FLAVOR_PRODUCTION', 20), ('BUILDING_FORGE', 'FLAVOR_GREAT_PEOPLE', 2), + ('BUILDING_TATARA', 'FLAVOR_PRODUCTION', 30), + ('BUILDING_TATARA', 'FLAVOR_SCIENCE', 10), + ('BUILDING_TATARA', 'FLAVOR_CULTURE', 10), + ('BUILDING_TATARA', 'FLAVOR_EXPANSION', 15), + ('BUILDING_TATARA', 'FLAVOR_GREAT_PEOPLE', 4), + ('BUILDING_SIEGE_FOUNDRY', 'FLAVOR_MILITARY_TRAINING', 30), ('BUILDING_SIEGE_FOUNDRY', 'FLAVOR_PRODUCTION', 25), ('BUILDING_SIEGE_FOUNDRY', 'FLAVOR_RANGED', 20), @@ -78,6 +84,11 @@ VALUES -- Shrine ('BUILDING_SHRINE', 'FLAVOR_RELIGION', 25), + ('BUILDING_TOPHET', 'FLAVOR_GOLD', 10), + ('BUILDING_TOPHET', 'FLAVOR_CULTURE', 10), + ('BUILDING_TOPHET', 'FLAVOR_GROWTH', 10), + ('BUILDING_TOPHET', 'FLAVOR_RELIGION', 25), + -- Smokehouse ('BUILDING_SMOKEHOUSE', 'FLAVOR_GROWTH', 20), ('BUILDING_SMOKEHOUSE', 'FLAVOR_PRODUCTION', 20), @@ -100,13 +111,19 @@ VALUES -- Walls ('BUILDING_WALLS', 'FLAVOR_CITY_DEFENSE', 20), + ('BUILDING_WALLS', 'FLAVOR_MILITARY_TRAINING', 5), + ('BUILDING_WALLS', 'FLAVOR_HAPPINESS', 5), ('BUILDING_WALLS_OF_BABYLON', 'FLAVOR_CITY_DEFENSE', 30), ('BUILDING_WALLS_OF_BABYLON', 'FLAVOR_SCIENCE', 40), ('BUILDING_WALLS_OF_BABYLON', 'FLAVOR_GREAT_PEOPLE', 2), + ('BUILDING_WALLS_OF_BABYLON', 'FLAVOR_MILITARY_TRAINING', 5), + ('BUILDING_WALLS_OF_BABYLON', 'FLAVOR_HAPPINESS', 5), ('BUILDING_LAMASSU_GATE', 'FLAVOR_CITY_DEFENSE', 30), + ('BUILDING_LAMASSU_GATE', 'FLAVOR_MILITARY_TRAINING', 20), ('BUILDING_LAMASSU_GATE', 'FLAVOR_RELIGION', 10), + ('BUILDING_LAMASSU_GATE', 'FLAVOR_HAPPINESS', 5), -- Well ('BUILDING_WELL', 'FLAVOR_GROWTH', 15), @@ -120,19 +137,6 @@ VALUES -- Classical Era -------------------------------------- - -- Theater - ('BUILDING_AMPHITHEATER', 'FLAVOR_CULTURE', 20), - ('BUILDING_AMPHITHEATER', 'FLAVOR_GREAT_PEOPLE', 2), - - ('BUILDING_GYMNASION', 'FLAVOR_CULTURE', 25), - ('BUILDING_GYMNASION', 'FLAVOR_SCIENCE', 10), - ('BUILDING_GYMNASION', 'FLAVOR_OFFENSE', 10), - ('BUILDING_GYMNASION', 'FLAVOR_GREAT_PEOPLE', 2), - - ('BUILDING_IZIKO', 'FLAVOR_CULTURE', 30), - ('BUILDING_IZIKO', 'FLAVOR_OFFENSE', 15), - ('BUILDING_IZIKO', 'FLAVOR_GREAT_PEOPLE', 2), - -- Aqueduct ('BUILDING_AQUEDUCT', 'FLAVOR_GROWTH', 25), ('BUILDING_AQUEDUCT', 'FLAVOR_PRODUCTION', 5), @@ -211,13 +215,26 @@ VALUES ('BUILDING_TETRACONCH', 'FLAVOR_RELIGION', 100), ('BUILDING_TETRACONCH', 'FLAVOR_I_TRADE_ORIGIN', 20), + -- Theater + ('BUILDING_AMPHITHEATER', 'FLAVOR_CULTURE', 20), + ('BUILDING_AMPHITHEATER', 'FLAVOR_GREAT_PEOPLE', 2), + + ('BUILDING_GYMNASION', 'FLAVOR_CULTURE', 25), + ('BUILDING_GYMNASION', 'FLAVOR_SCIENCE', 10), + ('BUILDING_GYMNASION', 'FLAVOR_OFFENSE', 10), + ('BUILDING_GYMNASION', 'FLAVOR_GREAT_PEOPLE', 2), + + ('BUILDING_IZIKO', 'FLAVOR_CULTURE', 30), + ('BUILDING_IZIKO', 'FLAVOR_OFFENSE', 15), + ('BUILDING_IZIKO', 'FLAVOR_GREAT_PEOPLE', 2), + -- Water Mill ('BUILDING_WATERMILL', 'FLAVOR_GROWTH', 25), ('BUILDING_WATERMILL', 'FLAVOR_PRODUCTION', 25), ('BUILDING_NILOMETER', 'FLAVOR_GROWTH', 40), ('BUILDING_NILOMETER', 'FLAVOR_PRODUCTION', 30), - ('BUILDING_RUNESTONE', 'FLAVOR_CULTURE', 10), + ('BUILDING_NILOMETER', 'FLAVOR_CULTURE', 10), -- Writers' Guild ('BUILDING_WRITERS_GUILD', 'FLAVOR_CULTURE', 40), @@ -229,10 +246,6 @@ VALUES -- Armory ('BUILDING_ARMORY', 'FLAVOR_MILITARY_TRAINING', 20), - ('BUILDING_DOJO', 'FLAVOR_MILITARY_TRAINING', 25), - ('BUILDING_DOJO', 'FLAVOR_CULTURE', 15), - ('BUILDING_DOJO', 'FLAVOR_SCIENCE', 15), - ('BUILDING_BARBICAN', 'FLAVOR_MILITARY_TRAINING', 25), ('BUILDING_BARBICAN', 'FLAVOR_CITY_DEFENSE', 30), @@ -241,10 +254,14 @@ VALUES -- Castle ('BUILDING_CASTLE', 'FLAVOR_CITY_DEFENSE', 25), + ('BUILDING_CASTLE', 'FLAVOR_HAPPINESS', 5), + ('BUILDING_CASTLE', 'FLAVOR_MILITARY_TRAINING', 5), ('BUILDING_MUGHAL_FORT', 'FLAVOR_CITY_DEFENSE', 40), ('BUILDING_MUGHAL_FORT', 'FLAVOR_CULTURE', 15), ('BUILDING_MUGHAL_FORT', 'FLAVOR_WONDER', 10), + ('BUILDING_MUGHAL_FORT', 'FLAVOR_HAPPINESS', 5), + ('BUILDING_MUGHAL_FORT', 'FLAVOR_MILITARY_TRAINING', 5), -- Chancery ('BUILDING_CHANCERY', 'FLAVOR_DIPLOMACY', 35), @@ -353,10 +370,14 @@ VALUES -- Bastion Fort ('BUILDING_BASTION_FORT', 'FLAVOR_CITY_DEFENSE', 25), + ('BUILDING_BASTION_FORT', 'FLAVOR_HAPPINESS', 5), + ('BUILDING_BASTION_FORT', 'FLAVOR_MILITARY_TRAINING', 5), ('BUILDING_KREPOST', 'FLAVOR_CITY_DEFENSE', 25), --- Ostrog ('BUILDING_KREPOST', 'FLAVOR_PRODUCTION', 20), ('BUILDING_KREPOST', 'FLAVOR_GOLD', 20), + ('BUILDING_KREPOST', 'FLAVOR_HAPPINESS', 5), + ('BUILDING_KREPOST', 'FLAVOR_MILITARY_TRAINING', 5), -- Constabulary ('BUILDING_CONSTABLE', 'FLAVOR_ESPIONAGE', 25), @@ -441,6 +462,8 @@ VALUES ('BUILDING_MILITARY_ACADEMY', 'FLAVOR_MILITARY_TRAINING', 25), ('BUILDING_SCHUTZENSTAND', 'FLAVOR_MILITARY_TRAINING', 50), + ('BUILDING_SCHUTZENSTAND', 'FLAVOR_CULTURE', 10), + ('BUILDING_SCHUTZENSTAND', 'FLAVOR_SCIENCE', 10), -- Museum ('BUILDING_MUSEUM', 'FLAVOR_CULTURE', 40), @@ -470,6 +493,8 @@ VALUES -- Arsenal ('BUILDING_ARSENAL', 'FLAVOR_CITY_DEFENSE', 25), + ('BUILDING_ARSENAL', 'FLAVOR_HAPPINESS', 5), + ('BUILDING_ARSENAL', 'FLAVOR_MILITARY_TRAINING', 5), -- Broadcast Tower ('BUILDING_BROADCAST_TOWER', 'FLAVOR_CULTURE', 75), @@ -493,6 +518,11 @@ VALUES ('BUILDING_EMBRAPA', 'FLAVOR_GOLD', 10), ('BUILDING_EMBRAPA', 'FLAVOR_GREAT_PEOPLE', 2), + -- Shopping Mall + ('BUILDING_SHOPPING_MALL', 'FLAVOR_GOLD', 20), + ('BUILDING_SHOPPING_MALL', 'FLAVOR_I_TRADE_DESTINATION', 20), + ('BUILDING_SHOPPING_MALL', 'FLAVOR_CULTURE', 6), + -- Stock Exchange ('BUILDING_STOCK_EXCHANGE', 'FLAVOR_GOLD', 40), ('BUILDING_STOCK_EXCHANGE', 'FLAVOR_GREAT_PEOPLE', 2), @@ -507,11 +537,6 @@ VALUES ('BUILDING_WIRE_SERVICE', 'FLAVOR_DIPLOMACY', 35), ('BUILDING_WIRE_SERVICE', 'FLAVOR_CULTURE', 12), ('BUILDING_WIRE_SERVICE', 'FLAVOR_SCIENCE', 6), - - -- Shopping Mall - ('BUILDING_SHOPPING_MALL', 'FLAVOR_GOLD', 20), - ('BUILDING_SHOPPING_MALL', 'FLAVOR_I_TRADE_DESTINATION', 20), - ('BUILDING_WIRE_SERVICE', 'FLAVOR_CULTURE', 6), -------------------------------------- -- Atomic Era @@ -531,6 +556,8 @@ VALUES ('BUILDING_MILITARY_BASE', 'FLAVOR_AIR', 30), ('BUILDING_MILITARY_BASE', 'FLAVOR_ANTIAIR', 30), ('BUILDING_MILITARY_BASE', 'FLAVOR_ESPIONAGE', 25), + ('BUILDING_MILITARY_BASE', 'FLAVOR_HAPPINESS', 10), + ('BUILDING_MILITARY_BASE', 'FLAVOR_MILITARY_TRAINING', 5), -- Police Station ('BUILDING_POLICE_STATION', 'FLAVOR_ESPIONAGE', 50), @@ -566,18 +593,18 @@ VALUES ('BUILDING_SOLAR_PLANT', 'FLAVOR_PRODUCTION', 70), ('BUILDING_SOLAR_PLANT', 'FLAVOR_SCIENCE', 50), + -- Spaceship Factory + ('BUILDING_SPACESHIP_FACTORY', 'FLAVOR_PRODUCTION', 10), + ('BUILDING_SPACESHIP_FACTORY', 'FLAVOR_SPACESHIP', 100), + ('BUILDING_SPACESHIP_FACTORY', 'FLAVOR_SCIENCE', 50), + -- Tidal Power Plant ('BUILDING_TIDAL_PLANT', 'FLAVOR_PRODUCTION', 70), ('BUILDING_TIDAL_PLANT', 'FLAVOR_NAVAL_GROWTH', 10), ('BUILDING_TIDAL_PLANT', 'FLAVOR_NAVAL_TILE_IMPROVEMENT', 30), ('BUILDING_TIDAL_PLANT', 'FLAVOR_SCIENCE', 10), ('BUILDING_TIDAL_PLANT', 'FLAVOR_GOLD', 10), - - -- Spaceship Factory - ('BUILDING_SPACESHIP_FACTORY', 'FLAVOR_PRODUCTION', 10), - ('BUILDING_SPACESHIP_FACTORY', 'FLAVOR_SPACESHIP', 100), - ('BUILDING_SPACESHIP_FACTORY', 'FLAVOR_SCIENCE', 50), - + -- Wind Power Plant ('BUILDING_WIND_PLANT', 'FLAVOR_SCIENCE', 30), ('BUILDING_WIND_PLANT', 'FLAVOR_CULTURE', 30), @@ -610,7 +637,7 @@ VALUES ('BUILDING_ORDER', 'FLAVOR_RELIGION', 20), ('BUILDING_ORDER', 'FLAVOR_OFFENSE', 8), - ('BUILDING_ORDER', 'FLAVOR_CITY_DEFENSE', 15), + ('BUILDING_ORDER', 'FLAVOR_MILITARY_TRAINING', 15), ('BUILDING_ORDER', 'FLAVOR_GOLD', 8), ('BUILDING_PAGODA', 'FLAVOR_RELIGION', 10), @@ -628,11 +655,10 @@ VALUES ('BUILDING_TEOCALLI', 'FLAVOR_RELIGION', 20), ('BUILDING_TEOCALLI', 'FLAVOR_OFFENSE', 10), - ('BUILDING_TEOCALLI', 'FLAVOR_MILITARY_TRAINING', 15), ('BUILDING_GURDWARA', 'FLAVOR_RELIGION', 20), ('BUILDING_GURDWARA', 'FLAVOR_GROWTH', 10), - ('BUILDING_GURDWARA', 'FLAVOR_CITY_DEFENSE', 15), + ('BUILDING_GURDWARA', 'FLAVOR_CITY_DEFENSE', 20), -- Founder beliefs (Reformation buildings) ('BUILDING_APOSTOLIC_PALACE', 'FLAVOR_RELIGION', 200), @@ -919,19 +945,20 @@ VALUES -------------------------------------- -- Mausoleum of Halicarnassus - ('BUILDING_MAUSOLEUM_HALICARNASSUS', 'FLAVOR_GOLD', 5), - ('BUILDING_MAUSOLEUM_HALICARNASSUS', 'FLAVOR_PRODUCTION', 10), + ('BUILDING_MAUSOLEUM_HALICARNASSUS', 'FLAVOR_GOLD', 15), + ('BUILDING_MAUSOLEUM_HALICARNASSUS', 'FLAVOR_PRODUCTION', 20), ('BUILDING_MAUSOLEUM_HALICARNASSUS', 'FLAVOR_SCIENCE', 5), + ('BUILDING_MAUSOLEUM_HALICARNASSUS', 'FLAVOR_GROWTH', 5), ('BUILDING_MAUSOLEUM_HALICARNASSUS', 'FLAVOR_WONDER', 20), -- Petra ('BUILDING_PETRA', 'FLAVOR_GOLD', 20), ('BUILDING_PETRA', 'FLAVOR_CULTURE', 10), - ('BUILDING_PETRA', 'FLAVOR_I_LAND_TRADE_ROUTE', 20), + ('BUILDING_PETRA', 'FLAVOR_I_LAND_TRADE_ROUTE', 50), ('BUILDING_PETRA', 'FLAVOR_WONDER', 20), -- Pyramids - ('BUILDING_PYRAMID', 'FLAVOR_EXPANSION', 45), + ('BUILDING_PYRAMID', 'FLAVOR_EXPANSION', 60), ('BUILDING_PYRAMID', 'FLAVOR_GREAT_PEOPLE', 20), ('BUILDING_PYRAMID', 'FLAVOR_WONDER', 20), @@ -956,7 +983,7 @@ VALUES -- Colossus ('BUILDING_COLOSSUS', 'FLAVOR_GOLD', 25), - ('BUILDING_COLOSSUS', 'FLAVOR_I_SEA_TRADE_ROUTE', 20), + ('BUILDING_COLOSSUS', 'FLAVOR_I_SEA_TRADE_ROUTE', 45), ('BUILDING_COLOSSUS', 'FLAVOR_WONDER', 20), -- Great Library @@ -976,7 +1003,8 @@ VALUES ('BUILDING_GREAT_WALL', 'FLAVOR_WONDER', 20), -- Hagia Sophia - ('BUILDING_HAGIA_SOPHIA', 'FLAVOR_EXPANSION', 20), + ('BUILDING_HAGIA_SOPHIA', 'FLAVOR_EXPANSION', 50), + ('BUILDING_HAGIA_SOPHIA', 'FLAVOR_RELIGION', 10), ('BUILDING_HAGIA_SOPHIA', 'FLAVOR_CULTURE', 10), ('BUILDING_HAGIA_SOPHIA', 'FLAVOR_WONDER', 20), @@ -1041,7 +1069,7 @@ VALUES -- Notre Dame ('BUILDING_NOTRE_DAME', 'FLAVOR_HAPPINESS', 20), - ('BUILDING_NOTRE_DAME', 'FLAVOR_RELIGION', 10), + ('BUILDING_NOTRE_DAME', 'FLAVOR_RELIGION', 40), ('BUILDING_NOTRE_DAME', 'FLAVOR_GOLD', 10), ('BUILDING_NOTRE_DAME', 'FLAVOR_CULTURE', 25), ('BUILDING_NOTRE_DAME', 'FLAVOR_WONDER', 20), @@ -1059,11 +1087,12 @@ VALUES -- Chichen Itza ('BUILDING_CHICHEN_ITZA', 'FLAVOR_CULTURE', 10), - ('BUILDING_CHICHEN_ITZA', 'FLAVOR_HAPPINESS', 50), + ('BUILDING_CHICHEN_ITZA', 'FLAVOR_HAPPINESS', 70), ('BUILDING_CHICHEN_ITZA', 'FLAVOR_WONDER', 20), -- Globe Theatre - ('BUILDING_GLOBE_THEATER', 'FLAVOR_CULTURE', 75), + ('BUILDING_GLOBE_THEATER', 'FLAVOR_CULTURE', 55), + ('BUILDING_GLOBE_THEATER', 'FLAVOR_GREAT_PEOPLE', 25), ('BUILDING_GLOBE_THEATER', 'FLAVOR_HAPPINESS', 10), ('BUILDING_GLOBE_THEATER', 'FLAVOR_WONDER', 20), @@ -1087,7 +1116,7 @@ VALUES ('BUILDING_RED_FORT', 'FLAVOR_WONDER', 20), -- Sistine Chapel - ('BUILDING_SISTINE_CHAPEL', 'FLAVOR_CULTURE', 50), + ('BUILDING_SISTINE_CHAPEL', 'FLAVOR_CULTURE', 70), ('BUILDING_SISTINE_CHAPEL', 'FLAVOR_WONDER', 20), -- Summer Palace @@ -1098,12 +1127,13 @@ VALUES -- Taj Mahal ('BUILDING_TAJ_MAHAL', 'FLAVOR_CULTURE', 10), - ('BUILDING_TAJ_MAHAL', 'FLAVOR_GREAT_PEOPLE', 20), + ('BUILDING_TAJ_MAHAL', 'FLAVOR_HAPPINESS', 40), + ('BUILDING_TAJ_MAHAL', 'FLAVOR_RELIGION', 10), ('BUILDING_TAJ_MAHAL', 'FLAVOR_WONDER', 20), -- Uffizi ('BUILDING_UFFIZI', 'FLAVOR_CULTURE', 50), - ('BUILDING_UFFIZI', 'FLAVOR_GREAT_PEOPLE', 25), + ('BUILDING_UFFIZI', 'FLAVOR_GREAT_PEOPLE', 35), ('BUILDING_UFFIZI', 'FLAVOR_HAPPINESS', 10), ('BUILDING_UFFIZI', 'FLAVOR_WONDER', 20), @@ -1190,7 +1220,8 @@ VALUES ('BUILDING_PRORA_RESORT', 'FLAVOR_WONDER', 20), -- Statue of Liberty - ('BUILDING_STATUE_OF_LIBERTY', 'FLAVOR_PRODUCTION', 50), + ('BUILDING_STATUE_OF_LIBERTY', 'FLAVOR_PRODUCTION', 30), + ('BUILDING_STATUE_OF_LIBERTY', 'FLAVOR_GREAT_PEOPLE', 20), ('BUILDING_STATUE_OF_LIBERTY', 'FLAVOR_CULTURE', 50), ('BUILDING_STATUE_OF_LIBERTY', 'FLAVOR_WONDER', 20), @@ -1209,11 +1240,9 @@ VALUES ('BUILDING_PENTAGON', 'FLAVOR_WONDER', 20), -- The Motherland Calls - ('BUILDING_MOTHERLAND_CALLS', 'FLAVOR_GOLD', 30), + ('BUILDING_MOTHERLAND_CALLS', 'FLAVOR_GOLD', 40), ('BUILDING_MOTHERLAND_CALLS', 'FLAVOR_ESPIONAGE', 30), - ('BUILDING_MOTHERLAND_CALLS', 'FLAVOR_CITY_DEFENSE', 20), - ('BUILDING_MOTHERLAND_CALLS', 'FLAVOR_MILITARY_TRAINING', 20), - ('BUILDING_MOTHERLAND_CALLS', 'FLAVOR_HAPPINESS', 20), + ('BUILDING_MOTHERLAND_CALLS', 'FLAVOR_MILITARY_TRAINING', 40), ('BUILDING_MOTHERLAND_CALLS', 'FLAVOR_WONDER', 20), -------------------------------------- @@ -1226,7 +1255,7 @@ VALUES ('BUILDING_CERN', 'FLAVOR_WONDER', 20), -- CN Tower - ('BUILDING_CN_TOWER', 'FLAVOR_CULTURE', 75), + ('BUILDING_CN_TOWER', 'FLAVOR_CULTURE', 100), ('BUILDING_CN_TOWER', 'FLAVOR_GOLD', 20), ('BUILDING_CN_TOWER', 'FLAVOR_HAPPINESS', 30), ('BUILDING_CN_TOWER', 'FLAVOR_WONDER', 20), @@ -1243,7 +1272,7 @@ VALUES ('BUILDING_HUBBLE', 'FLAVOR_WONDER', 20), -- Sydney Opera House - ('BUILDING_SYDNEY_OPERA_HOUSE', 'FLAVOR_CULTURE', 70), + ('BUILDING_SYDNEY_OPERA_HOUSE', 'FLAVOR_CULTURE', 80), ('BUILDING_SYDNEY_OPERA_HOUSE', 'FLAVOR_WONDER', 20), -- World Trade Center @@ -1276,7 +1305,8 @@ VALUES ('BUILDING_MENIN_GATE', 'FLAVOR_WONDER', 25), -- Olympic Village - ('BUILDING_OLYMPIC_VILLAGE', 'FLAVOR_CULTURE', 25), + ('BUILDING_OLYMPIC_VILLAGE', 'FLAVOR_CULTURE', 45), + ('BUILDING_OLYMPIC_VILLAGE', 'FLAVOR_GOLD', 10), ('BUILDING_OLYMPIC_VILLAGE', 'FLAVOR_HAPPINESS', 30), ('BUILDING_OLYMPIC_VILLAGE', 'FLAVOR_WONDER', 25), diff --git a/(2) Vox Populi/Database Changes/AI/LeaderPersonalitySweeps.sql b/(2) Vox Populi/Database Changes/AI/LeaderPersonalitySweeps.sql index 9003a144c7..d70cd0873b 100644 --- a/(2) Vox Populi/Database Changes/AI/LeaderPersonalitySweeps.sql +++ b/(2) Vox Populi/Database Changes/AI/LeaderPersonalitySweeps.sql @@ -17,12 +17,13 @@ SET WarmongerHate = 5, DoFWillingness = 7, DenounceWillingness = 6, - WorkWithWillingness = 7, - WorkAgainstWillingness = 6, + WorkWithWillingness = 8, + WorkAgainstWillingness = 5, Loyalty = 4, Forgiveness = 5, Neediness = 6, - Meanness = 5 + Meanness = 5, + Chattiness = 4 WHERE Type = 'LEADER_AHMAD_ALMANSUR'; INSERT INTO Leader_MajorCivApproachBiases @@ -58,11 +59,12 @@ SET DoFWillingness = 5, DenounceWillingness = 7, WorkWithWillingness = 5, - WorkAgainstWillingness = 7, + WorkAgainstWillingness = 9, Loyalty = 7, Forgiveness = 4, Neediness = 3, - Meanness = 8 + Meanness = 8, + Chattiness = 7 WHERE Type = 'LEADER_ALEXANDER'; INSERT INTO Leader_MajorCivApproachBiases @@ -97,12 +99,13 @@ SET WarmongerHate = 7, DoFWillingness = 3, DenounceWillingness = 6, - WorkWithWillingness = 3, - WorkAgainstWillingness = 6, + WorkWithWillingness = 1, + WorkAgainstWillingness = 8, Loyalty = 3, Forgiveness = 5, Neediness = 9, - Meanness = 8 + Meanness = 8, + Chattiness = 5 WHERE Type = 'LEADER_ASHURBANIPAL'; INSERT INTO Leader_MajorCivApproachBiases @@ -136,12 +139,13 @@ SET WarmongerHate = 7, DoFWillingness = 4, DenounceWillingness = 7, - WorkWithWillingness = 4, - WorkAgainstWillingness = 7, + WorkWithWillingness = 2, + WorkAgainstWillingness = 8, Loyalty = 1, Forgiveness = 3, Neediness = 6, - Meanness = 9 + Meanness = 9, + Chattiness = 4 WHERE Type = 'LEADER_ASKIA'; INSERT INTO Leader_MajorCivApproachBiases @@ -176,11 +180,12 @@ SET DoFWillingness = 3, DenounceWillingness = 5, WorkWithWillingness = 3, - WorkAgainstWillingness = 5, + WorkAgainstWillingness = 9, Loyalty = 3, Forgiveness = 8, Neediness = 10, - Meanness = 10 + Meanness = 10, + Chattiness = 7 WHERE Type = 'LEADER_ATTILA'; INSERT INTO Leader_MajorCivApproachBiases @@ -205,41 +210,43 @@ VALUES -- Augustus Caesar (Rome) UPDATE Leaders SET - PrimaryVictoryPursuit = 'VICTORY_PURSUIT_DOMINATION', + PrimaryVictoryPursuit = 'VICTORY_PURSUIT_CULTURE', + SecondaryVictoryPursuit = 'VICTORY_PURSUIT_DOMINATION', VictoryCompetitiveness = 6, WonderCompetitiveness = 8, - MinorCivCompetitiveness = 2, + MinorCivCompetitiveness = 5, Boldness = 7, - DiploBalance = 3, - WarmongerHate = 5, - DoFWillingness = 2, - DenounceWillingness = 2, - WorkWithWillingness = 2, - WorkAgainstWillingness = 2, - Loyalty = 3, + DiploBalance = 5, + WarmongerHate = 6, + DoFWillingness = 6, + DenounceWillingness = 5, + WorkWithWillingness = 5, + WorkAgainstWillingness = 4, + Loyalty = 6, Forgiveness = 1, - Neediness = 4, - Meanness = 8 + Neediness = 6, + Meanness = 6, + Chattiness = 2 WHERE Type = 'LEADER_AUGUSTUS'; INSERT INTO Leader_MajorCivApproachBiases (LeaderType, MajorCivApproachType, Bias) VALUES - ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_WAR', 9), - ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_HOSTILE', 3), - ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_DECEPTIVE', 1), + ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_WAR', 6), + ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_HOSTILE', 6), + ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_DECEPTIVE', 5), ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_GUARDED', 5), ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_AFRAID', 5), ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_NEUTRAL', 8), - ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_FRIENDLY', 2); + ('LEADER_AUGUSTUS', 'MAJOR_CIV_APPROACH_FRIENDLY', 5); INSERT INTO Leader_MinorCivApproachBiases (LeaderType, MinorCivApproachType, Bias) VALUES ('LEADER_AUGUSTUS', 'MINOR_CIV_APPROACH_IGNORE', 3), - ('LEADER_AUGUSTUS', 'MINOR_CIV_APPROACH_PROTECTIVE', 2), + ('LEADER_AUGUSTUS', 'MINOR_CIV_APPROACH_PROTECTIVE', 8), ('LEADER_AUGUSTUS', 'MINOR_CIV_APPROACH_BULLY', 3), - ('LEADER_AUGUSTUS', 'MINOR_CIV_APPROACH_CONQUEST', 12); + ('LEADER_AUGUSTUS', 'MINOR_CIV_APPROACH_CONQUEST', 5); -- Bismarck (Germany) UPDATE Leaders @@ -253,12 +260,13 @@ SET WarmongerHate = 8, DoFWillingness = 8, DenounceWillingness = 5, - WorkWithWillingness = 8, - WorkAgainstWillingness = 5, + WorkWithWillingness = 10, + WorkAgainstWillingness = 10, Loyalty = 6, Forgiveness = 8, Neediness = 5, - Meanness = 9 + Meanness = 9, + Chattiness = 6 WHERE Type = 'LEADER_BISMARCK'; INSERT INTO Leader_MajorCivApproachBiases @@ -292,11 +300,12 @@ SET DoFWillingness = 6, DenounceWillingness = 6, WorkWithWillingness = 6, - WorkAgainstWillingness = 6, + WorkAgainstWillingness = 9, Loyalty = 6, Forgiveness = 6, Neediness = 7, - Meanness = 5 + Meanness = 5, + Chattiness = 4 WHERE Type = 'LEADER_BOUDICCA'; INSERT INTO Leader_MajorCivApproachBiases @@ -329,12 +338,13 @@ SET WarmongerHate = 7, DoFWillingness = 5, DenounceWillingness = 7, - WorkWithWillingness = 5, - WorkAgainstWillingness = 7, + WorkWithWillingness = 9, + WorkAgainstWillingness = 6, Loyalty = 9, Forgiveness = 2, Neediness = 8, - Meanness = 5 + Meanness = 5, + Chattiness = 6 WHERE Type = 'LEADER_CASIMIR'; INSERT INTO Leader_MajorCivApproachBiases @@ -369,12 +379,13 @@ SET WarmongerHate = 4, DoFWillingness = 6, DenounceWillingness = 9, - WorkWithWillingness = 6, - WorkAgainstWillingness = 9, + WorkWithWillingness = 5, + WorkAgainstWillingness = 8, Loyalty = 7, Forgiveness = 6, Neediness = 9, - Meanness = 9 + Meanness = 9, + Chattiness = 7 WHERE Type = 'LEADER_CATHERINE'; INSERT INTO Leader_MajorCivApproachBiases @@ -409,11 +420,12 @@ SET DoFWillingness = 5, DenounceWillingness = 6, WorkWithWillingness = 5, - WorkAgainstWillingness = 6, + WorkAgainstWillingness = 5, Loyalty = 5, Forgiveness = 5, Neediness = 5, - Meanness = 4 + Meanness = 4, + Chattiness = 2 WHERE Type = 'LEADER_DARIUS'; INSERT INTO Leader_MajorCivApproachBiases @@ -448,12 +460,13 @@ SET WarmongerHate = 6, DoFWillingness = 6, DenounceWillingness = 4, - WorkWithWillingness = 6, - WorkAgainstWillingness = 4, + WorkWithWillingness = 8, + WorkAgainstWillingness = 7, Loyalty = 8, Forgiveness = 3, Neediness = 8, - Meanness = 6 + Meanness = 6, + Chattiness = 5 WHERE Type = 'LEADER_DIDO'; INSERT INTO Leader_MajorCivApproachBiases @@ -488,12 +501,13 @@ SET WarmongerHate = 5, DoFWillingness = 6, DenounceWillingness = 8, - WorkWithWillingness = 6, - WorkAgainstWillingness = 8, + WorkWithWillingness = 4, + WorkAgainstWillingness = 9, Loyalty = 6, Forgiveness = 4, Neediness = 7, - Meanness = 8 + Meanness = 8, + Chattiness = 5 WHERE Type = 'LEADER_ELIZABETH'; INSERT INTO Leader_MajorCivApproachBiases @@ -528,11 +542,12 @@ SET DoFWillingness = 10, DenounceWillingness = 7, WorkWithWillingness = 10, - WorkAgainstWillingness = 7, + WorkAgainstWillingness = 10, Loyalty = 5, Forgiveness = 7, Neediness = 7, - Meanness = 3 + Meanness = 3, + Chattiness = 8 WHERE Type = 'LEADER_ENRICO_DANDOLO'; INSERT INTO Leader_MajorCivApproachBiases @@ -568,11 +583,12 @@ SET DoFWillingness = 7, DenounceWillingness = 6, WorkWithWillingness = 7, - WorkAgainstWillingness = 6, + WorkAgainstWillingness = 4, Loyalty = 10, Forgiveness = 7, Neediness = 3, - Meanness = 9 + Meanness = 9, + Chattiness = 4 WHERE Type = 'LEADER_GAJAH_MADA'; INSERT INTO Leader_MajorCivApproachBiases @@ -607,12 +623,13 @@ SET WarmongerHate = 12, DoFWillingness = 10, DenounceWillingness = 8, - WorkWithWillingness = 10, - WorkAgainstWillingness = 8, + WorkWithWillingness = 12, + WorkAgainstWillingness = 7, Loyalty = 10, Forgiveness = 7, Neediness = 5, - Meanness = 4 + Meanness = 4, + Chattiness = 7 WHERE Type = 'LEADER_GANDHI'; INSERT INTO Leader_MajorCivApproachBiases @@ -646,12 +663,13 @@ SET WarmongerHate = 3, DoFWillingness = 7, DenounceWillingness = 4, - WorkWithWillingness = 7, - WorkAgainstWillingness = 4, + WorkWithWillingness = 6, + WorkAgainstWillingness = 5, Loyalty = 8, Forgiveness = 1, Neediness = 8, - Meanness = 10 + Meanness = 10, + Chattiness = 6 WHERE Type = 'LEADER_GENGHIS_KHAN'; INSERT INTO Leader_MajorCivApproachBiases @@ -677,7 +695,7 @@ VALUES UPDATE Leaders SET PrimaryVictoryPursuit = 'VICTORY_PURSUIT_DOMINATION', - VictoryCompetitiveness = 8, + VictoryCompetitiveness = 9, WonderCompetitiveness = 6, MinorCivCompetitiveness = 4, Boldness = 10, @@ -685,12 +703,13 @@ SET WarmongerHate = 4, DoFWillingness = 2, DenounceWillingness = 6, - WorkWithWillingness = 2, - WorkAgainstWillingness = 6, + WorkWithWillingness = 1, + WorkAgainstWillingness = 5, Loyalty = 2, Forgiveness = 4, Neediness = 7, - Meanness = 8 + Meanness = 8, + Chattiness = 5 WHERE Type = 'LEADER_GUSTAVUS_ADOLPHUS'; INSERT INTO Leader_MajorCivApproachBiases @@ -725,12 +744,13 @@ SET WarmongerHate = 8, DoFWillingness = 6, DenounceWillingness = 6, - WorkWithWillingness = 6, - WorkAgainstWillingness = 6, + WorkWithWillingness = 5, + WorkAgainstWillingness = 5, Loyalty = 5, Forgiveness = 5, Neediness = 5, - Meanness = 5 + Meanness = 5, + Chattiness = 3 WHERE Type = 'LEADER_SELASSIE'; INSERT INTO Leader_MajorCivApproachBiases @@ -764,12 +784,13 @@ SET WarmongerHate = 1, DoFWillingness = 8, DenounceWillingness = 5, - WorkWithWillingness = 8, - WorkAgainstWillingness = 5, + WorkWithWillingness = 3, + WorkAgainstWillingness = 3, Loyalty = 3, Forgiveness = 4, Neediness = 8, - Meanness = 9 + Meanness = 9, + Chattiness = 5 WHERE Type = 'LEADER_HARALD'; INSERT INTO Leader_MajorCivApproachBiases @@ -803,12 +824,13 @@ SET WarmongerHate = 9, DoFWillingness = 8, DenounceWillingness = 8, - WorkWithWillingness = 8, + WorkWithWillingness = 9, WorkAgainstWillingness = 8, Loyalty = 3, Forgiveness = 4, Neediness = 8, - Meanness = 7 + Meanness = 7, + Chattiness = 8 WHERE Type = 'LEADER_HARUN_AL_RASHID'; INSERT INTO Leader_MajorCivApproachBiases @@ -848,7 +870,8 @@ SET Loyalty = 9, Forgiveness = 10, Neediness = 3, - Meanness = 8 + Meanness = 8, + Chattiness = 3 WHERE Type = 'LEADER_HIAWATHA'; INSERT INTO Leader_MajorCivApproachBiases @@ -884,11 +907,12 @@ SET DoFWillingness = 4, DenounceWillingness = 8, WorkWithWillingness = 4, - WorkAgainstWillingness = 8, + WorkAgainstWillingness = 6, Loyalty = 6, Forgiveness = 4, Neediness = 8, - Meanness = 8 + Meanness = 8, + Chattiness = 6 WHERE Type = 'LEADER_ISABELLA'; INSERT INTO Leader_MajorCivApproachBiases @@ -927,7 +951,8 @@ SET Loyalty = 8, Forgiveness = 6, Neediness = 4, - Meanness = 3 + Meanness = 3, + Chattiness = 3 WHERE Type = 'LEADER_KAMEHAMEHA'; INSERT INTO Leader_MajorCivApproachBiases @@ -962,11 +987,12 @@ SET DoFWillingness = 8, DenounceWillingness = 8, WorkWithWillingness = 8, - WorkAgainstWillingness = 8, + WorkAgainstWillingness = 3, Loyalty = 9, Forgiveness = 7, Neediness = 3, - Meanness = 6 + Meanness = 6, + Chattiness = 4 WHERE Type = 'LEADER_MARIA_I'; INSERT INTO Leader_MajorCivApproachBiases @@ -1001,12 +1027,13 @@ SET WarmongerHate = 10, DoFWillingness = 7, DenounceWillingness = 9, - WorkWithWillingness = 7, + WorkWithWillingness = 9, WorkAgainstWillingness = 9, Loyalty = 5, Forgiveness = 6, Neediness = 8, - Meanness = 4 + Meanness = 4, + Chattiness = 8 WHERE Type = 'LEADER_MARIA'; INSERT INTO Leader_MajorCivApproachBiases @@ -1040,12 +1067,13 @@ SET WarmongerHate = -1, DoFWillingness = 5, DenounceWillingness = 7, - WorkWithWillingness = 5, - WorkAgainstWillingness = 7, + WorkWithWillingness = 2, + WorkAgainstWillingness = 4, Loyalty = 4, Forgiveness = 3, Neediness = 9, - Meanness = 10 + Meanness = 10, + Chattiness = 9 WHERE Type = 'LEADER_MONTEZUMA'; INSERT INTO Leader_MajorCivApproachBiases @@ -1081,11 +1109,12 @@ SET DoFWillingness = 7, DenounceWillingness = 8, WorkWithWillingness = 7, - WorkAgainstWillingness = 8, + WorkAgainstWillingness = 5, Loyalty = 4, Forgiveness = 8, Neediness = 7, - Meanness = 6 + Meanness = 6, + Chattiness = 7 WHERE Type = 'LEADER_NAPOLEON'; INSERT INTO Leader_MajorCivApproachBiases @@ -1119,12 +1148,13 @@ SET WarmongerHate = 8, DoFWillingness = 3, DenounceWillingness = 5, - WorkWithWillingness = 3, - WorkAgainstWillingness = 5, + WorkWithWillingness = 7, + WorkAgainstWillingness = 4, Loyalty = 8, Forgiveness = 5, Neediness = 2, - Meanness = 7 + Meanness = 7, + Chattiness = 2 WHERE Type = 'LEADER_NEBUCHADNEZZAR'; INSERT INTO Leader_MajorCivApproachBiases @@ -1159,12 +1189,13 @@ SET WarmongerHate = 5, DoFWillingness = 6, DenounceWillingness = 6, - WorkWithWillingness = 6, + WorkWithWillingness = 3, WorkAgainstWillingness = 6, Loyalty = 2, Forgiveness = 1, Neediness = 7, - Meanness = 10 + Meanness = 10, + Chattiness = 5 WHERE Type = 'LEADER_ODA_NOBUNAGA'; INSERT INTO Leader_MajorCivApproachBiases @@ -1200,11 +1231,12 @@ SET DoFWillingness = 7, DenounceWillingness = 6, WorkWithWillingness = 7, - WorkAgainstWillingness = 6, + WorkAgainstWillingness = 3, Loyalty = 6, Forgiveness = 6, Neediness = 5, - Meanness = 7 + Meanness = 7, + Chattiness = 3 WHERE Type = 'LEADER_PACAL'; INSERT INTO Leader_MajorCivApproachBiases @@ -1238,12 +1270,13 @@ SET WarmongerHate = 7, DoFWillingness = 4, DenounceWillingness = 6, - WorkWithWillingness = 4, - WorkAgainstWillingness = 6, + WorkWithWillingness = 5, + WorkAgainstWillingness = 5, Loyalty = 7, Forgiveness = 5, Neediness = 5, - Meanness = 7 + Meanness = 7, + Chattiness = 4 WHERE Type = 'LEADER_PACHACUTI'; INSERT INTO Leader_MajorCivApproachBiases @@ -1282,7 +1315,8 @@ SET Loyalty = 9, Forgiveness = 3, Neediness = 8, - Meanness = 2 + Meanness = 2, + Chattiness = 8 WHERE Type = 'LEADER_PEDRO'; INSERT INTO Leader_MajorCivApproachBiases @@ -1316,11 +1350,12 @@ SET DoFWillingness = 5, DenounceWillingness = 7, WorkWithWillingness = 5, - WorkAgainstWillingness = 7, + WorkAgainstWillingness = 6, Loyalty = 9, Forgiveness = 2, Neediness = 6, - Meanness = 5 + Meanness = 5, + Chattiness = 4 WHERE Type = 'LEADER_POCATELLO'; INSERT INTO Leader_MajorCivApproachBiases @@ -1354,12 +1389,13 @@ SET WarmongerHate = 8, DoFWillingness = 3, DenounceWillingness = 8, - WorkWithWillingness = 3, - WorkAgainstWillingness = 8, + WorkWithWillingness = 5, + WorkAgainstWillingness = 1, Loyalty = 5, Forgiveness = 4, Neediness = 9, - Meanness = 8 + Meanness = 8, + Chattiness = 4 WHERE Type = 'LEADER_RAMESSES'; INSERT INTO Leader_MajorCivApproachBiases @@ -1393,12 +1429,13 @@ SET WarmongerHate = 9, DoFWillingness = 9, DenounceWillingness = 6, - WorkWithWillingness = 9, - WorkAgainstWillingness = 6, + WorkWithWillingness = 10, + WorkAgainstWillingness = 8, Loyalty = 7, Forgiveness = 7, Neediness = 4, - Meanness = 5 + Meanness = 5, + Chattiness = 8 WHERE Type = 'LEADER_RAMKHAMHAENG'; INSERT INTO Leader_MajorCivApproachBiases @@ -1432,12 +1469,13 @@ SET WarmongerHate = 10, DoFWillingness = 9, DenounceWillingness = 4, - WorkWithWillingness = 9, - WorkAgainstWillingness = 4, + WorkWithWillingness = 10, + WorkAgainstWillingness = 2, Loyalty = 9, Forgiveness = 4, Neediness = 3, - Meanness = 2 + Meanness = 2, + Chattiness = 1 WHERE Type = 'LEADER_SEJONG'; INSERT INTO Leader_MajorCivApproachBiases @@ -1471,12 +1509,13 @@ SET WarmongerHate = 1, DoFWillingness = 5, DenounceWillingness = 7, - WorkWithWillingness = 5, - WorkAgainstWillingness = 7, + WorkWithWillingness = 2, + WorkAgainstWillingness = 3, Loyalty = 3, Forgiveness = 2, Neediness = 5, - Meanness = 12 + Meanness = 12, + Chattiness = 9 WHERE Type = 'LEADER_SHAKA'; INSERT INTO Leader_MajorCivApproachBiases @@ -1511,12 +1550,13 @@ SET WarmongerHate = 4, DoFWillingness = 7, DenounceWillingness = 2, - WorkWithWillingness = 7, - WorkAgainstWillingness = 2, + WorkWithWillingness = 8, + WorkAgainstWillingness = 6, Loyalty = 8, Forgiveness = 7, Neediness = 8, - Meanness = 7 + Meanness = 7, + Chattiness = 4 WHERE Type = 'LEADER_SULEIMAN'; INSERT INTO Leader_MajorCivApproachBiases @@ -1549,12 +1589,13 @@ SET WarmongerHate = 8, DoFWillingness = 10, DenounceWillingness = 8, - WorkWithWillingness = 10, - WorkAgainstWillingness = 8, + WorkWithWillingness = 9, + WorkAgainstWillingness = 10, Loyalty = 5, Forgiveness = 7, Neediness = 9, - Meanness = 6 + Meanness = 6, + Chattiness = 10 WHERE Type = 'LEADER_THEODORA'; INSERT INTO Leader_MajorCivApproachBiases @@ -1592,7 +1633,8 @@ SET Loyalty = 5, Forgiveness = 4, Neediness = 10, - Meanness = 7 + Meanness = 7, + Chattiness = 7 WHERE Type = 'LEADER_WASHINGTON'; INSERT INTO Leader_MajorCivApproachBiases @@ -1627,11 +1669,12 @@ SET DoFWillingness = 10, DenounceWillingness = 3, WorkWithWillingness = 10, - WorkAgainstWillingness = 3, + WorkAgainstWillingness = 4, Loyalty = 6, Forgiveness = 8, Neediness = 8, - Meanness = 9 + Meanness = 9, + Chattiness = 6 WHERE Type = 'LEADER_WILLIAM'; INSERT INTO Leader_MajorCivApproachBiases @@ -1667,11 +1710,12 @@ SET DoFWillingness = 5, DenounceWillingness = 10, WorkWithWillingness = 5, - WorkAgainstWillingness = 10, + WorkAgainstWillingness = 12, Loyalty = 4, Forgiveness = 5, Neediness = 9, - Meanness = 9 + Meanness = 9, + Chattiness = 5 WHERE Type = 'LEADER_WU_ZETIAN'; INSERT INTO Leader_MajorCivApproachBiases diff --git a/(2) Vox Populi/Database Changes/AI/PolicyFlavorSweeps.sql b/(2) Vox Populi/Database Changes/AI/PolicyFlavorSweeps.sql index 31ea922836..4d71f9b4ca 100644 --- a/(2) Vox Populi/Database Changes/AI/PolicyFlavorSweeps.sql +++ b/(2) Vox Populi/Database Changes/AI/PolicyFlavorSweeps.sql @@ -103,7 +103,7 @@ VALUES ('POLICY_ENTREPRENEURSHIP', 'FLAVOR_GOLD', 24), ('POLICY_TRADE_UNIONS', 'FLAVOR_GOLD', 24), ('POLICY_TRADE_UNIONS', 'FLAVOR_PRODUCTION', 24), - ('POLICY_CARAVANS', 'FLAVOR_GOLD', 24), + ('POLICY_CARAVANS', 'FLAVOR_PRODUCTION', 24), ('POLICY_CARAVANS', 'FLAVOR_I_LAND_TRADE_ROUTE', 24), ('POLICY_MERCANTILISM', 'FLAVOR_SCIENCE', 24), ('POLICY_MERCANTILISM', 'FLAVOR_CULTURE', 24), diff --git a/(2) Vox Populi/Database Changes/AI/ProcessFlavorSweeps.sql b/(2) Vox Populi/Database Changes/AI/ProcessFlavorSweeps.sql index e0a6881030..e1620deb88 100644 --- a/(2) Vox Populi/Database Changes/AI/ProcessFlavorSweeps.sql +++ b/(2) Vox Populi/Database Changes/AI/ProcessFlavorSweeps.sql @@ -9,7 +9,7 @@ VALUES ('PROCESS_RESEARCH', 'FLAVOR_NUKE', 1), ('PROCESS_CULTURE', 'FLAVOR_CULTURE', 5), ('PROCESS_FOOD', 'FLAVOR_GROWTH', 5), - ('PROCESS_DEFENSE', 'FLAVOR_CITY_DEFENSE', 5), + ('PROCESS_DEFENSE', 'FLAVOR_CITY_DEFENSE', 7), -- World Congress processes ('PROCESS_WORLD_FAIR', 'FLAVOR_DIPLOMACY', 30), ('PROCESS_WORLD_FAIR', 'FLAVOR_CULTURE', 60), diff --git a/(2) Vox Populi/Database Changes/AI/TechFlavorSweeps.sql b/(2) Vox Populi/Database Changes/AI/TechFlavorSweeps.sql index a0e8b52972..75b2221ce9 100644 --- a/(2) Vox Populi/Database Changes/AI/TechFlavorSweeps.sql +++ b/(2) Vox Populi/Database Changes/AI/TechFlavorSweeps.sql @@ -109,7 +109,7 @@ VALUES ('TECH_DRAMA', 'FLAVOR_GREAT_PEOPLE', 10), ('TECH_DRAMA', 'FLAVOR_WONDER', 10), - ('TECH_PHILOSOPHY', 'FLAVOR_NAVAL', 15), -- Units: Trireme, Buildings: Courthouse, Temple, SchoolofPhilosophy, Wonders: Oracle + ('TECH_PHILOSOPHY', 'FLAVOR_NAVAL', 15), -- Units: Trireme, Buildings: Courthouse, Temple, SchoolofPhilosophy, Wonders: Oracle, Ability: OpenBorders ('TECH_PHILOSOPHY', 'FLAVOR_SCIENCE', 10), ('TECH_PHILOSOPHY', 'FLAVOR_RELIGION', 10), ('TECH_PHILOSOPHY', 'FLAVOR_WONDER', 10), @@ -160,7 +160,7 @@ VALUES ------------------------------------------------------ - ('TECH_CIVIL_SERVICE', 'FLAVOR_TILE_IMPROVEMENT', 10), -- Units: Envoy, Buildings: Workshop, Wonders: ForbiddenPalace, Ability: OpenBorders, Yields: Laborer +1G, Farm +1F, Pasture +2F + ('TECH_CIVIL_SERVICE', 'FLAVOR_TILE_IMPROVEMENT', 10), -- Units: Envoy, Buildings: Workshop, Wonders: ForbiddenPalace, Yields: Laborer +1G, Farm +1F, Pasture +2F ('TECH_CIVIL_SERVICE', 'FLAVOR_GROWTH', 10), ('TECH_CIVIL_SERVICE', 'FLAVOR_PRODUCTION', 10), ('TECH_CIVIL_SERVICE', 'FLAVOR_WONDER', 5), @@ -218,22 +218,23 @@ VALUES ('TECH_ARCHITECTURE', 'FLAVOR_WONDER', 20), -- Buildings: Royal Collection, Wonders: PorcelainTower, TajMahal, Yields: Artists +1C, Town +3F ('TECH_ARCHITECTURE', 'FLAVOR_CULTURE', 15), - ('TECH_ECONOMICS', 'FLAVOR_GOLD', 5), -- Buildings: Windmill, Wonders: Uffizi, Ability: +1 TR, Yields: Merchant&Plantation +1G + ('TECH_ACOUSTICS', 'FLAVOR_CULTURE', 25), -- Buildings: OperaHouse, Musicians' Guild, Wonders: Sistine, SummerPalace, Yields: HolySite +4Faith + ('TECH_ACOUSTICS', 'FLAVOR_WONDER', 20), + + ('TECH_ECONOMICS', 'FLAVOR_OFFENSE', 20), -- Units: Lancer, Buildings: Windmill, Wonders: Uffizi, Ability: +1 TR, Yields: Merchant&Plantation +1G + ('TECH_ECONOMICS', 'FLAVOR_MOBILE', 10), + ('TECH_ECONOMICS', 'FLAVOR_GOLD', 5), ('TECH_ECONOMICS', 'FLAVOR_GROWTH', 5), ('TECH_ECONOMICS', 'FLAVOR_PRODUCTION', 20), ('TECH_ECONOMICS', 'FLAVOR_WONDER', 10), ('TECH_ECONOMICS', 'FLAVOR_TILE_IMPROVEMENT', 5), - ('TECH_ACOUSTICS', 'FLAVOR_CULTURE', 25), -- Buildings: OperaHouse, Musicians' Guild, Wonders: Sistine, SummerPalace, Yields: HolySite +4Faith - ('TECH_ACOUSTICS', 'FLAVOR_WONDER', 20), - ('TECH_NAVIGATION', 'FLAVOR_NAVAL', 30), -- Units: Frigate, Corvette, Buildings: Bastion Fort, Ability: City embark costs 1 move, Yields: Servants +2G, Boats +1F ('TECH_NAVIGATION', 'FLAVOR_CITY_DEFENSE', 20), ('TECH_NAVIGATION', 'FLAVOR_GOLD', 10), ('TECH_NAVIGATION', 'FLAVOR_NAVAL_TILE_IMPROVEMENT', 5), - ('TECH_METALLURGY', 'FLAVOR_OFFENSE', 20), -- Units: Lancer, Musketman, Cuirassier, Wonders: RedFort, Yields: Lumber +1P +1G - ('TECH_METALLURGY', 'FLAVOR_MOBILE', 20), + ('TECH_METALLURGY', 'FLAVOR_MOBILE', 10), -- Units: Musketman, Cuirassier, Wonders: RedFort, Yields: Lumber +1P +1G ('TECH_METALLURGY', 'FLAVOR_RANGED', 20), ('TECH_METALLURGY', 'FLAVOR_TILE_IMPROVEMENT', 5), ('TECH_METALLURGY', 'FLAVOR_WONDER', 5), diff --git a/(2) Vox Populi/Database Changes/Art/NewFeatureArts.xml b/(2) Vox Populi/Database Changes/Art/NewFeatureArts.xml new file mode 100644 index 0000000000..ee832be7b8 --- /dev/null +++ b/(2) Vox Populi/Database Changes/Art/NewFeatureArts.xml @@ -0,0 +1,10 @@ + + + + + ART_DEF_FEATURE_LAKE_SALT + Feature + sv_salt_lake.dds + + + diff --git a/(2) Vox Populi/Database Changes/Art/NewIcons.xml b/(2) Vox Populi/Database Changes/Art/NewIcons.xml index 4c6bf2dcc8..00c59ad828 100644 --- a/(2) Vox Populi/Database Changes/Art/NewIcons.xml +++ b/(2) Vox Populi/Database Changes/Art/NewIcons.xml @@ -288,35 +288,35 @@ 16 promoVP_07_016.dds 8 - 2 + 5
PROMOTION_ATLAS_VP_07 32 promoVP_07_032.dds 8 - 2 + 5 PROMOTION_ATLAS_VP_07 45 promoVP_07_045.dds 8 - 2 + 5 PROMOTION_ATLAS_VP_07 64 promoVP_07_064.dds 8 - 2 + 5 PROMOTION_ATLAS_VP_07 256 promoVP_07_256.dds 8 - 2 + 5 CORPORATION_ATLAS_VP @@ -358,28 +358,28 @@ 45 VPResourceAtlas_045.dds 4 - 5 + 6 RESOURCE_ATLAS_VP 64 VPResourceAtlas_064.dds 4 - 5 + 6 RESOURCE_ATLAS_VP 80 VPResourceAtlas_080.dds 4 - 5 + 6 RESOURCE_ATLAS_VP 256 VPResourceAtlas_256.dds 4 - 5 + 6 BUILD_ATLAS_VP @@ -479,6 +479,41 @@ 8 8 + + BUILDING_ATLAS_VP_3 + 45 + VPBuildingAtlas3_045.dds + 8 + 3 + + + BUILDING_ATLAS_VP_3 + 64 + VPBuildingAtlas3_064.dds + 8 + 3 + + + BUILDING_ATLAS_VP_3 + 80 + VPBuildingAtlas3_080.dds + 8 + 3 + + + BUILDING_ATLAS_VP_3 + 128 + VPBuildingAtlas3_128.dds + 8 + 3 + + + BUILDING_ATLAS_VP_3 + 256 + VPBuildingAtlas3_256.dds + 8 + 3 + UNIT_ATLAS_VP_1 45 diff --git a/(2) Vox Populi/Database Changes/Art/NewImprovementArts.xml b/(2) Vox Populi/Database Changes/Art/NewImprovementArts.xml index ec73b4515c..0f0c176232 100644 --- a/(2) Vox Populi/Database Changes/Art/NewImprovementArts.xml +++ b/(2) Vox Populi/Database Changes/Art/NewImprovementArts.xml @@ -83,6 +83,16 @@ Improvement Aplekton + + ART_DEF_IMPROVEMENT_TORII + Improvement + Torii + + + ART_DEF_IMPROVEMENT_SALTWORKS + Improvement + Saltworks + @@ -523,6 +533,51 @@ SNAPSHOT Aplekton_pl.fxsxml 1 + + + ART_DEF_IMPROVEMENT_TORII + ART_DEF_RESOURCE_ALL + Constructed + 1.0 + SNAPSHOT + Assets/Buildings/Improvements/Monolith/Monolith_ASIA.fxsxml + 1 + + + ART_DEF_IMPROVEMENT_TORII + ART_DEF_RESOURCE_ALL + Pillaged + 1.0 + SNAPSHOT + Assets/Buildings/Improvements/Monolith/Monolith_PL_ASIA.fxsxml + 1 + + + ART_DEF_IMPROVEMENT_SALTWORKS + ART_DEF_RESOURCE_ALL + UnderConstruction + 1.0 + SNAPSHOT + hb_saltworks.fxsxml + 1 + + + ART_DEF_IMPROVEMENT_SALTWORKS + ART_DEF_RESOURCE_ALL + Constructed + 1.0 + SNAPSHOT + Saltworks.fxsxml + 1 + + + ART_DEF_IMPROVEMENT_SALTWORKS + ART_DEF_RESOURCE_ALL + Pillaged + 1.0 + SNAPSHOT + pl_saltworks.fxsxml + 1 @@ -607,5 +662,15 @@ Improvement SV_Aplekton.dds
+ + ART_DEF_IMPROVEMENT_TORII + Improvement + SV_Torii.dds + + + ART_DEF_IMPROVEMENT_SALTWORKS + Improvement + SV_Saltworks.dds + diff --git a/(2) Vox Populi/Database Changes/Art/NewResourceArts.xml b/(2) Vox Populi/Database Changes/Art/NewResourceArts.xml index 0d86bd04a7..0c84a33b72 100644 --- a/(2) Vox Populi/Database Changes/Art/NewResourceArts.xml +++ b/(2) Vox Populi/Database Changes/Art/NewResourceArts.xml @@ -81,6 +81,11 @@ Resource Figs
+ + ART_DEF_RESOURCE_LAKE_FISH + Resource + LakeFish + @@ -796,6 +801,43 @@ coral_industrial.fxsxml 1 + + + ART_DEF_RESOURCE_LAKE_FISH + ART_DEF_IMPROVEMENT_NONE + Ancient + 0.07 + ANIMATED + Assets/Buildings/Resources/Fish.fxsxml + 1 + + + ART_DEF_RESOURCE_LAKE_FISH + ART_DEF_IMPROVEMENT_NONE + Industrial + 0.07 + ANIMATED + Assets/Buildings/Resources/Fish_Industrial.fxsxml + 1 + + + ART_DEF_RESOURCE_LAKE_FISH + ART_DEF_IMPROVEMENT_FISHING_BOATS + Ancient + 0.07 + ANIMATED + Assets/Buildings/Resources/Fish.fxsxml + 1 + + + ART_DEF_RESOURCE_LAKE_FISH + ART_DEF_IMPROVEMENT_FISHING_BOATS + Industrial + 0.07 + ANIMATED + Assets/Buildings/Resources/Fish_Industrial.fxsxml + 1 + ART_DEF_RESOURCE_LAPIS @@ -1202,6 +1244,11 @@ Resource sv_coral.dds + + ART_DEF_RESOURCE_LAKE_FISH + Resource + sv_lake_fish.dds + ART_DEF_RESOURCE_LAPIS Resource @@ -1228,4 +1275,4 @@ sv_figs.dds - \ No newline at end of file + diff --git a/(2) Vox Populi/Database Changes/Art/NewUnitArts.xml b/(2) Vox Populi/Database Changes/Art/NewUnitArts.xml index ca2544fe52..cbe4a37bfe 100644 --- a/(2) Vox Populi/Database Changes/Art/NewUnitArts.xml +++ b/(2) Vox Populi/Database Changes/Art/NewUnitArts.xml @@ -801,6 +801,57 @@ CLOTH FLESH + + + ART_DEF_UNIT_MEMBER_EMISSARY + 0.15 + IncaScout.fxsxml + CLOTH + FLESH + + + ART_DEF_UNIT_MEMBER_ENVOY + 0.15 + EnvoyVP.fxsxml + CLOTH + FLESH + + + ART_DEF_UNIT_MEMBER_DIPLOMAT + 0.14 + DiplomatVP.fxsxml + CLOTH + FLESH + + + ART_DEF_UNIT_MEMBER_AMBASSADOR + 0.15 + ambassador.fxsxml + CLOTH + FLESH + + + + ART_DEF_UNIT_MEMBER_GREAT_DIPLOMAT_1 + 0.14 + Assets/Units/Settler/Settler_Euro/EuroMale45.fxsxml + CLOTH + FLESH + + + ART_DEF_UNIT_MEMBER_GREAT_DIPLOMAT_2 + 0.15 + Elder_Statesman.fxsxml + CLOTH + FLESH + + + ART_DEF_UNIT_MEMBER_GREAT_DIPLOMAT_3 + 0.14 + great_dignitary.fxsxml + CLOTH + FLESH + ART_DEF_UNIT_MEMBER_TADODAHO 0.14 @@ -808,6 +859,7 @@ CLOTH FLESH + ART_DEF_UNIT_MEMBER_FCOMPANY_1 0.14 @@ -1447,6 +1499,41 @@ 1 50.0 + + ART_DEF_UNIT_MEMBER_EMISSARY + Idle Death BombardDefend Run + 1 + + + ART_DEF_UNIT_MEMBER_ENVOY + Idle Death BombardDefend Run + 1 + + + ART_DEF_UNIT_MEMBER_DIPLOMAT + Idle Death BombardDefend Run + 1 + + + ART_DEF_UNIT_MEMBER_AMBASSADOR + Idle Death BombardDefend Run + 1 + + + ART_DEF_UNIT_MEMBER_GREAT_DIPLOMAT_1 + Idle Death BombardDefend Run + 1 + + + ART_DEF_UNIT_MEMBER_GREAT_DIPLOMAT_2 + Idle Death BombardDefend Run + 1 + + + ART_DEF_UNIT_MEMBER_GREAT_DIPLOMAT_3 + Idle Death BombardDefend Run + 1 + ART_DEF_UNIT_MEMBER_TADODAHO Idle Death BombardDefend Run diff --git a/(2) Vox Populi/Database Changes/Art/UnitArtChanges.sql b/(2) Vox Populi/Database Changes/Art/UnitArtChanges.sql index 93e69dad57..c224a972d9 100644 --- a/(2) Vox Populi/Database Changes/Art/UnitArtChanges.sql +++ b/(2) Vox Populi/Database Changes/Art/UnitArtChanges.sql @@ -117,14 +117,14 @@ VALUES ('ART_DEF_UNIT_COLONIST', 'ART_DEF_UNIT_MEMBER_COLONIST2', 1), ('ART_DEF_UNIT_COLONIST', 'ART_DEF_UNIT_MEMBER_COLONIST3', 1), ('ART_DEF_UNIT_COLONIST', 'ART_DEF_UNIT_MEMBER_COLONIST4', 1), - ('ART_DEF_UNIT_EMISSARY', 'ART_DEF_UNIT_MEMBER_SETTLERS_ASIAN_F2', 1), - ('ART_DEF_UNIT_ENVOY', 'ART_DEF_UNIT_MEMBER_MISSIONARY_01', 1), - ('ART_DEF_UNIT_DIPLOMAT', 'ART_DEF_UNIT_MEMBER_EUROFEMALE18', 1), - ('ART_DEF_UNIT_AMBASSADOR', 'ART_DEF_UNIT_MEMBER_ARCHAEOLOGIST', 1), + ('ART_DEF_UNIT_EMISSARY', 'ART_DEF_UNIT_MEMBER_EMISSARY', 1), + ('ART_DEF_UNIT_ENVOY', 'ART_DEF_UNIT_MEMBER_ENVOY', 1), + ('ART_DEF_UNIT_DIPLOMAT', 'ART_DEF_UNIT_MEMBER_DIPLOMAT', 1), + ('ART_DEF_UNIT_AMBASSADOR', 'ART_DEF_UNIT_MEMBER_AMBASSADOR', 1), ('ART_DEF_UNIT_INDUNA', 'ART_DEF_UNIT_MEMBER_U_ZULU_IMPI_WARRIOR_V2', 1), - ('ART_DEF_UNIT_GREAT_DIPLOMAT', 'ART_DEF_UNIT_MEMBER_AFRIMALE3', 1), - ('ART_DEF_UNIT_GREAT_DIPLOMAT_RENAISSANCE', 'ART_DEF_UNIT_MEMBER_EUROFEMALE18', 1), - ('ART_DEF_UNIT_GREAT_DIPLOMAT_MODERN', 'ART_DEF_UNIT_MEMBER_GREAT_ARTIST_F2', 1), + ('ART_DEF_UNIT_GREAT_DIPLOMAT', 'ART_DEF_UNIT_MEMBER_GREAT_DIPLOMAT_1', 1), + ('ART_DEF_UNIT_GREAT_DIPLOMAT_RENAISSANCE', 'ART_DEF_UNIT_MEMBER_GREAT_DIPLOMAT_2', 1), + ('ART_DEF_UNIT_GREAT_DIPLOMAT_MODERN', 'ART_DEF_UNIT_MEMBER_GREAT_DIPLOMAT_3', 1), ('ART_DEF_UNIT_TADODAHO', 'ART_DEF_UNIT_MEMBER_TADODAHO', 1), ('ART_DEF_UNIT_FCOMPANY', 'ART_DEF_UNIT_MEMBER_FCOMPANY_4', 3), ('ART_DEF_UNIT_FCOMPANY', 'ART_DEF_UNIT_MEMBER_FCOMPANY_1', 1), diff --git a/(2) Vox Populi/Database Changes/Beliefs/FounderChanges.sql b/(2) Vox Populi/Database Changes/Beliefs/FounderChanges.sql index 3b8213f664..5815b811df 100644 --- a/(2) Vox Populi/Database Changes/Beliefs/FounderChanges.sql +++ b/(2) Vox Populi/Database Changes/Beliefs/FounderChanges.sql @@ -22,7 +22,8 @@ INSERT INTO Belief_YieldFromConversion -- scaler = +(X^2)% where X is the number (BeliefType, YieldType, Yield) VALUES ('BELIEF_PAPAL_PRIMACY', 'YIELD_PRODUCTION', 20), - ('BELIEF_PAPAL_PRIMACY', 'YIELD_SCIENCE', 20); + ('BELIEF_PAPAL_PRIMACY', 'YIELD_SCIENCE', 20), + ('BELIEF_PAPAL_PRIMACY', 'YIELD_FAITH', 10); -- Peace Loving (now Divine Inheritance) UPDATE Beliefs SET HappinessPerXPeacefulForeignFollowers = 0 WHERE Type = 'BELIEF_PEACE_LOVING'; @@ -66,9 +67,9 @@ WHERE Type = 'BELIEF_CHURCH_PROPERTY'; INSERT INTO Belief_YieldFromPolicyUnlock (BeliefType, YieldType, Yield) VALUES - ('BELIEF_CHURCH_PROPERTY', 'YIELD_GOLD', 5), - ('BELIEF_CHURCH_PROPERTY', 'YIELD_SCIENCE', 5), - ('BELIEF_CHURCH_PROPERTY', 'YIELD_FAITH', 5); + ('BELIEF_CHURCH_PROPERTY', 'YIELD_GOLD', 6), + ('BELIEF_CHURCH_PROPERTY', 'YIELD_SCIENCE', 6), + ('BELIEF_CHURCH_PROPERTY', 'YIELD_FAITH', 6); -- Tithe (now Revelation) UPDATE Beliefs @@ -102,7 +103,7 @@ WHERE Type = 'BELIEF_INITIATION_RITES'; INSERT INTO Belief_YieldFromEraUnlock (BeliefType, YieldType, Yield) SELECT - 'BELIEF_INITIATION_RITES', Type, 12 + 'BELIEF_INITIATION_RITES', Type, 15 FROM Yields WHERE ID < 6; -- "All" yields diff --git a/(2) Vox Populi/Database Changes/Beliefs/NewBeliefs.xml b/(2) Vox Populi/Database Changes/Beliefs/NewBeliefs.xml index 3ea19dba2a..5f267a9186 100644 --- a/(2) Vox Populi/Database Changes/Beliefs/NewBeliefs.xml +++ b/(2) Vox Populi/Database Changes/Beliefs/NewBeliefs.xml @@ -98,5 +98,12 @@ TXT_KEY_BELIEF_COMMUNALISM_SHORT true + + + BELIEF_WORK_ETHIC + TXT_KEY_BELIEF_WORK_ETHIC + TXT_KEY_BELIEF_WORK_ETHIC_SHORT + true + - \ No newline at end of file + diff --git a/(2) Vox Populi/Database Changes/Beliefs/PantheonChanges.sql b/(2) Vox Populi/Database Changes/Beliefs/PantheonChanges.sql index 2b98382103..ca38167ad7 100644 --- a/(2) Vox Populi/Database Changes/Beliefs/PantheonChanges.sql +++ b/(2) Vox Populi/Database Changes/Beliefs/PantheonChanges.sql @@ -48,6 +48,7 @@ INSERT INTO Belief_ImprovementYieldChanges (BeliefType, ImprovementType, YieldType, Yield) VALUES ('BELIEF_STONE_CIRCLES', 'IMPROVEMENT_QUARRY', 'YIELD_PRODUCTION', 1), + ('BELIEF_STONE_CIRCLES', 'IMPROVEMENT_QUARRY', 'YIELD_SCIENCE', 1), ('BELIEF_STONE_CIRCLES', 'IMPROVEMENT_QUARRY', 'YIELD_FAITH', 1); INSERT INTO Belief_BuildingClassYieldChanges @@ -124,7 +125,7 @@ VALUES -- Dance of the Aurora (now God of the Stars and Sky) UPDATE Beliefs -SET RequiresResource = 1 +SET RequiresResource = 1, RequiresImprovement = 1 WHERE Type = 'BELIEF_DANCE_AURORA'; INSERT INTO Belief_TerrainYieldChanges @@ -137,6 +138,14 @@ VALUES ('BELIEF_DANCE_AURORA', 'TERRAIN_SNOW', 'YIELD_CULTURE', 1), ('BELIEF_DANCE_AURORA', 'TERRAIN_SNOW', 'YIELD_FAITH', 1); +INSERT INTO Belief_NearbyTerrainYieldChanges + (BeliefType, TerrainType, YieldType, Yield) +VALUES + ('BELIEF_DANCE_AURORA', 'TERRAIN_TUNDRA', 'YIELD_FOOD', 1), + ('BELIEF_DANCE_AURORA', 'TERRAIN_TUNDRA', 'YIELD_FAITH', 1), + ('BELIEF_DANCE_AURORA', 'TERRAIN_SNOW', 'YIELD_FOOD', 1), + ('BELIEF_DANCE_AURORA', 'TERRAIN_SNOW', 'YIELD_FAITH', 1); + -- Sun God (now God of the Sun) UPDATE Beliefs SET RequiresResource = 1 WHERE Type = 'BELIEF_SUN_GOD'; @@ -150,7 +159,8 @@ VALUES INSERT INTO Belief_BuildingClassYieldChanges (BeliefType, BuildingClassType, YieldType, YieldChange) VALUES - ('BELIEF_SUN_GOD', 'BUILDINGCLASS_GRANARY', 'YIELD_GOLD', 2), + ('BELIEF_SUN_GOD', 'BUILDINGCLASS_GRANARY', 'YIELD_PRODUCTION', 1), + ('BELIEF_SUN_GOD', 'BUILDINGCLASS_GRANARY', 'YIELD_GOLD', 1), ('BELIEF_SUN_GOD', 'BUILDINGCLASS_GRANARY', 'YIELD_FAITH', 2); -- God of War @@ -298,12 +308,16 @@ VALUES -- Fertility Rites (now Goddess of the Home) UPDATE Beliefs -SET CityGrowthModifier = 25 +SET + CityGrowthModifier = 25, + HappinessPerCity = 1 WHERE Type = 'BELIEF_FERTILITY_RITES'; INSERT INTO Belief_BuildingClassYieldChanges (BeliefType, BuildingClassType, YieldType, YieldChange) VALUES + ('BELIEF_FERTILITY_RITES', 'BUILDINGCLASS_PALACE', 'YIELD_FOOD', 1), + ('BELIEF_FERTILITY_RITES', 'BUILDINGCLASS_PALACE', 'YIELD_FAITH', 1), ('BELIEF_FERTILITY_RITES', 'BUILDINGCLASS_SHRINE', 'YIELD_FOOD', 1), ('BELIEF_FERTILITY_RITES', 'BUILDINGCLASS_SHRINE', 'YIELD_FAITH', 1); @@ -324,7 +338,8 @@ VALUES INSERT INTO Belief_BuildingClassYieldChanges (BeliefType, BuildingClassType, YieldType, YieldChange) VALUES - ('BELIEF_GODDESS_HUNT', 'BUILDINGCLASS_SMOKEHOUSE', 'YIELD_FOOD', 2); + ('BELIEF_GODDESS_HUNT', 'BUILDINGCLASS_SMOKEHOUSE', 'YIELD_FAITH', 1), + ('BELIEF_GODDESS_HUNT', 'BUILDINGCLASS_SMOKEHOUSE', 'YIELD_FOOD', 1); -- Religious Idols (now Goddess of Wisdom) INSERT INTO Belief_CityYieldChanges @@ -434,22 +449,20 @@ VALUES INSERT INTO Belief_YieldFromWLTKD (BeliefType, YieldType, Yield) VALUES - ('BELIEF_NUADA', 'YIELD_GOLD', 15), - ('BELIEF_NUADA', 'YIELD_SCIENCE', 15); + ('BELIEF_NUADA', 'YIELD_GOLD', 10), + ('BELIEF_NUADA', 'YIELD_SCIENCE', 10); INSERT INTO Belief_YieldPerLux (BeliefType, YieldType, Yield) VALUES - ('BELIEF_NUADA', 'YIELD_GOLD', 3), - ('BELIEF_NUADA', 'YIELD_SCIENCE', 3), - ('BELIEF_NUADA', 'YIELD_GOLDEN_AGE_POINTS', 3); + ('BELIEF_NUADA', 'YIELD_GOLD', 2), + ('BELIEF_NUADA', 'YIELD_SCIENCE', 2); INSERT INTO Belief_YieldPerActiveTR (BeliefType, YieldType, Yield) VALUES - ('BELIEF_NUADA', 'YIELD_GOLD', 3), - ('BELIEF_NUADA', 'YIELD_SCIENCE', 3), - ('BELIEF_NUADA', 'YIELD_GOLDEN_AGE_POINTS', 3); + ('BELIEF_NUADA', 'YIELD_GOLD', 2), + ('BELIEF_NUADA', 'YIELD_SCIENCE', 2); -- Cernunnos, the Horned Stag INSERT INTO Belief_ImprovementYieldChanges diff --git a/(2) Vox Populi/Database Changes/Beliefs/ReformationChanges.sql b/(2) Vox Populi/Database Changes/Beliefs/ReformationChanges.sql index 14a72f4b92..8ca67fd881 100644 --- a/(2) Vox Populi/Database Changes/Beliefs/ReformationChanges.sql +++ b/(2) Vox Populi/Database Changes/Beliefs/ReformationChanges.sql @@ -2,14 +2,38 @@ UPDATE Beliefs SET OtherReligionPressureErosion = 0, - CombatVersusOtherReligionTheirLands = 20 + CombatBonusTheirLands = 10, + CombatBonusVersusOtherReligionTheirLands = 5 WHERE Type = 'BELIEF_EVANGELISM'; -INSERT INTO Belief_YieldFromConquest - (BeliefType, YieldType, Yield) +CREATE TEMP TABLE Helper ( + BuildingClassType TEXT +); + +CREATE TEMP TABLE Helper2 ( + YieldType TEXT, + YieldChange INTEGER +); + +INSERT INTO Helper VALUES - ('BELIEF_EVANGELISM', 'YIELD_GOLD', 50), - ('BELIEF_EVANGELISM', 'YIELD_CULTURE', 50); + ('BUILDINGCLASS_BARRACKS'), + ('BUILDINGCLASS_ARMORY'), + ('BUILDINGCLASS_MILITARY_ACADEMY'); + +INSERT INTO Helper2 +VALUES + ('YIELD_GOLD', 2), + ('YIELD_CULTURE', 2); + +INSERT INTO Belief_BuildingClassYieldChanges + (BeliefType, BuildingClassType, YieldType, YieldChange) +SELECT + 'BELIEF_EVANGELISM', a.BuildingClassType, b.YieldType, b.YieldChange +FROM Helper a, Helper2 b; + +DROP TABLE Helper; +DROP TABLE Helper2; -- Defender of the Faith UPDATE Beliefs @@ -17,7 +41,8 @@ SET Enhancer = 0, Reformation = 1, CombatModifierFriendlyCities = 0, - CombatVersusOtherReligionOwnLands = 20 + CombatBonusOwnLands = 10, + CombatBonusVersusOtherReligionOwnLands = 5 WHERE Type = 'BELIEF_DEFENDER_FAITH'; CREATE TEMP TABLE Helper ( @@ -50,8 +75,8 @@ SELECT 'BELIEF_DEFENDER_FAITH', a.BuildingClassType, b.YieldType, b.YieldChange FROM Helper a, Helper2 b; -DROP TABLE Helper; -DROP TABLE Helper2; +DELETE FROM Helper; +DELETE FROM Helper2; -- Jesuit Education (now Divine Teachings) UPDATE Beliefs SET GreatPersonExpendedFaith = 20 WHERE Type = 'BELIEF_JESUIT_EDUCATION'; @@ -96,11 +121,41 @@ VALUES ('BELIEF_UNDERGROUND_SECT', 'BUILDINGCLASS_BROADCAST_TOWER'); -- Charitable Missions (now Global Commandments) -UPDATE Beliefs -SET - CityStateInfluenceModifier = 0, - SpreadStrengthModifier = 15 -WHERE Type = 'BELIEF_CHARITABLE_MISSIONS'; +UPDATE Beliefs SET CityStateInfluenceModifier = 0 WHERE Type = 'BELIEF_CHARITABLE_MISSIONS'; + +INSERT INTO Belief_UnitCombatProductionModifiers + (BeliefType, UnitCombatType, Modifier) +VALUES + ('BELIEF_CHARITABLE_MISSIONS', 'UNITCOMBAT_DIPLOMACY', 20); + +INSERT INTO Helper +VALUES + ('BUILDINGCLASS_CHANCERY'), + ('BUILDINGCLASS_PRINTING_HOUSE'), + ('BUILDINGCLASS_WIRE_SERVICE'); + +INSERT INTO Helper2 +VALUES + ('YIELD_GOLD', 1), + ('YIELD_SCIENCE', 1), + ('YIELD_CULTURE', 1); + +INSERT INTO Belief_BuildingClassYieldChanges + (BeliefType, BuildingClassType, YieldType, YieldChange) +SELECT + 'BELIEF_CHARITABLE_MISSIONS', a.Type, b.Type, b.YieldChange +FROM + FROM Helper a, Helper2 b; + +DROP TABLE Helper; +DROP TABLE Helper2; + +INSERT INTO Belief_BuildingClassFaithPurchase + (BeliefType, BuildingClassType) +VALUES + ('BELIEF_CHARITABLE_MISSIONS', 'BUILDINGCLASS_CHANCERY'), + ('BELIEF_CHARITABLE_MISSIONS', 'BUILDINGCLASS_PRINTING_HOUSE'), + ('BELIEF_CHARITABLE_MISSIONS', 'BUILDINGCLASS_WIRE_SERVICE'); INSERT INTO Belief_YieldFromProposal (BeliefType, YieldType, Yield) @@ -126,18 +181,19 @@ SET InquisitorPressureRetention = 0 WHERE Type = 'BELIEF_UNITY_OF_PROPHETS'; CREATE TEMP TABLE Helper3 ( - YieldType TEXT + YieldType TEXT, + Yield integer ); INSERT INTO Helper3 VALUES - ('YIELD_SCIENCE'), - ('YIELD_FAITH'); + ('YIELD_SCIENCE', 3), + ('YIELD_FAITH', 2); INSERT INTO Belief_ImprovementYieldChanges (BeliefType, ImprovementType, YieldType, Yield) SELECT - 'BELIEF_UNITY_OF_PROPHETS', a.Type, b.YieldType, 2 + 'BELIEF_UNITY_OF_PROPHETS', a.Type, b.YieldType, b.Yield FROM Improvements a, Helper3 b WHERE a.CreatedByGreatPerson = 1 OR a.Type = 'IMPROVEMENT_LANDMARK'; @@ -153,7 +209,7 @@ WHERE Class = 'UNITCLASS_ARCHAEOLOGIST'; INSERT INTO Belief_GreatWorkYieldChanges (BeliefType, YieldType, Yield) VALUES - ('BELIEF_UNITY_OF_PROPHETS', 'YIELD_CULTURE', 2); + ('BELIEF_UNITY_OF_PROPHETS', 'YIELD_CULTURE', 3); -- Sacred Sites UPDATE Beliefs SET FaithBuildingTourism = 3 WHERE Type = 'BELIEF_SACRED_SITES'; @@ -199,3 +255,20 @@ SELECT FROM GreatPersons a, Helper4 b; DROP TABLE Helper4; + +-- Work Ethic +UPDATE Beliefs SET CivilianWorkRate = 25 WHERE Type = 'BELIEF_WORK_ETHIC'; + +INSERT INTO Belief_BuildingClassYieldChanges + (BeliefType, BuildingClassType, YieldType, YieldChange) +VALUES + ('BELIEF_WORK_ETHIC', 'BUILDINGCLASS_WORKSHOP', 'YIELD_PRODUCTION', 2), + ('BELIEF_WORK_ETHIC', 'BUILDINGCLASS_WINDMILL', 'YIELD_PRODUCTION', 2), + ('BELIEF_WORK_ETHIC', 'BUILDINGCLASS_FACTORY', 'YIELD_PRODUCTION', 2); + +INSERT INTO Belief_BuildingClassFaithPurchase + (BeliefType, BuildingClassType) +VALUES + ('BELIEF_WORK_ETHIC', 'BUILDINGCLASS_WORKSHOP'), + ('BELIEF_WORK_ETHIC', 'BUILDINGCLASS_WINDMILL'), + ('BELIEF_WORK_ETHIC', 'BUILDINGCLASS_FACTORY'); diff --git a/(2) Vox Populi/Database Changes/City/Buildings/BuildingChanges.sql b/(2) Vox Populi/Database Changes/City/Buildings/BuildingChanges.sql index eb7b63f565..3f981c3fe6 100644 --- a/(2) Vox Populi/Database Changes/City/Buildings/BuildingChanges.sql +++ b/(2) Vox Populi/Database Changes/City/Buildings/BuildingChanges.sql @@ -323,10 +323,10 @@ SELECT FROM Buildings WHERE BuildingClass = 'BUILDINGCLASS_AQUEDUCT'; -INSERT INTO Building_GrowthExtraYield - (BuildingType, YieldType, Yield) +INSERT INTO Building_YieldFromBirth + (BuildingType, YieldType, Yield, IsEraScaling) SELECT - Type, 'YIELD_PRODUCTION', 60 + Type, 'YIELD_PRODUCTION', 20, 1 FROM Buildings WHERE BuildingClass = 'BUILDINGCLASS_AQUEDUCT'; @@ -1096,13 +1096,6 @@ SELECT FROM Buildings WHERE BuildingClass = 'BUILDINGCLASS_COLOSSEUM'; -INSERT INTO Building_YieldChangesPerPop - (BuildingType, YieldType, Yield) -SELECT - Type, 'YIELD_TOURISM', 25 -FROM Buildings -WHERE BuildingClass = 'BUILDINGCLASS_COLOSSEUM'; - INSERT INTO Helper (BuildingClassType) VALUES @@ -2093,12 +2086,20 @@ WHERE a.BuildingClass = 'BUILDINGCLASS_CIRCUS_MAXIMUS'; DELETE FROM Helper; +INSERT INTO Helper + (YieldType) +VALUES + ('YIELD_GOLD'), + ('YIELD_TOURISM'); + INSERT INTO Building_BuildingClassYieldChanges (BuildingType, BuildingClassType, YieldType, YieldChange) SELECT - Type, 'BUILDINGCLASS_COLOSSEUM', 'YIELD_GOLD', 2 -FROM Buildings -WHERE BuildingClass = 'BUILDINGCLASS_CIRCUS_MAXIMUS'; + a.Type, 'BUILDINGCLASS_COLOSSEUM', b.YieldType, 2 +FROM Buildings a, Helper b +WHERE a.BuildingClass = 'BUILDINGCLASS_CIRCUS_MAXIMUS'; + +DELETE FROM Helper; -- Imperial College UPDATE Buildings @@ -2435,7 +2436,7 @@ VALUES -- World Wonders ('BUILDINGCLASS_RED_FORT', 600, 100, 0), -- Beliefs - ('BUILDINGCLASS_ORDER', 300, 0, 0); + ('BUILDINGCLASS_GURDWARA', 300, 0, 0); UPDATE Buildings SET diff --git a/(2) Vox Populi/Database Changes/City/Buildings/BuildingChanges2.sql b/(2) Vox Populi/Database Changes/City/Buildings/BuildingChanges2.sql index 425c409c70..30d10e77a4 100644 --- a/(2) Vox Populi/Database Changes/City/Buildings/BuildingChanges2.sql +++ b/(2) Vox Populi/Database Changes/City/Buildings/BuildingChanges2.sql @@ -183,7 +183,14 @@ VALUES INSERT INTO Building_YieldFromUnitProduction (BuildingType, YieldType, Yield) VALUES - ('BUILDING_ORDER', 'YIELD_FAITH', 10); + ('BUILDING_ORDER', 'YIELD_GOLD', 15); + +INSERT INTO Building_DomainFreeExperiences + (BuildingType, DomainType, Experience) +VALUES + ('BUILDING_ORDER', 'DOMAIN_LAND', 15), + ('BUILDING_ORDER', 'DOMAIN_SEA', 15), + ('BUILDING_ORDER', 'DOMAIN_AIR', 15); -- Teocalli INSERT INTO Building_YieldChanges @@ -201,13 +208,6 @@ INSERT INTO Building_YieldFromVictory VALUES ('BUILDING_TEOCALLI', 'YIELD_FAITH', 8, 0); -INSERT INTO Building_DomainFreeExperiences - (BuildingType, DomainType, Experience) -VALUES - ('BUILDING_TEOCALLI', 'DOMAIN_LAND', 10), - ('BUILDING_TEOCALLI', 'DOMAIN_SEA', 10), - ('BUILDING_TEOCALLI', 'DOMAIN_AIR', 10); - -- Gurdwara INSERT INTO Building_YieldChanges (BuildingType, YieldType, Yield) @@ -220,6 +220,10 @@ INSERT INTO Building_YieldModifiers VALUES ('BUILDING_GURDWARA', 'YIELD_FOOD', 10); +UPDATE Buildings +SET AlwaysHeal = 5 +WHERE BuildingClass = 'BUILDINGCLASS_GURDWARA'; + ---------------------------------------------------------------------------- -- Reformation Buildings ---------------------------------------------------------------------------- @@ -1394,12 +1398,6 @@ INSERT INTO Building_YieldChanges VALUES ('BUILDING_MENIN_GATE', 'YIELD_CULTURE', 2); -INSERT INTO Building_YieldFromDeath - (BuildingType, YieldType, Yield) -VALUES - ('BUILDING_MENIN_GATE', 'YIELD_CULTURE', 2), - ('BUILDING_MENIN_GATE', 'YIELD_GOLDEN_AGE_POINTS', 2); - INSERT INTO Building_ImprovementYieldChangesGlobal (BuildingType, ImprovementType, YieldType, Yield) VALUES diff --git a/(2) Vox Populi/Database Changes/City/Buildings/BuildingCostSweeps.sql b/(2) Vox Populi/Database Changes/City/Buildings/BuildingCostSweeps.sql index 8974ae35a1..eaf5e53bdb 100644 --- a/(2) Vox Populi/Database Changes/City/Buildings/BuildingCostSweeps.sql +++ b/(2) Vox Populi/Database Changes/City/Buildings/BuildingCostSweeps.sql @@ -238,6 +238,16 @@ UPDATE Buildings SET FaithCost = 400, UnlockedByBelief = 1 WHERE BuildingClass = UPDATE Buildings SET FaithCost = 600, UnlockedByBelief = 1 WHERE BuildingClass = 'BUILDINGCLASS_PUBLIC_SCHOOL'; UPDATE Buildings SET FaithCost = 800, UnlockedByBelief = 1 WHERE BuildingClass = 'BUILDINGCLASS_LABORATORY'; +-- Diplomacy line (Global Commandments) +UPDATE Buildings SET FaithCost = 400, UnlockedByBelief = 1 WHERE BuildingClass = 'BUILDINGCLASS_CHANCERY'; +UPDATE Buildings SET FaithCost = 500, UnlockedByBelief = 1 WHERE BuildingClass = 'BUILDINGCLASS_PRINTING_HOUSE'; +UPDATE Buildings SET FaithCost = 800, UnlockedByBelief = 1 WHERE BuildingClass = 'BUILDINGCLASS_WIRE_SERVICE'; + +-- Production line (Work Ethic) +UPDATE Buildings SET FaithCost = 400, UnlockedByBelief = 1 WHERE BuildingClass = 'BUILDINGCLASS_WORKSHOP'; +UPDATE Buildings SET FaithCost = 500, UnlockedByBelief = 1 WHERE BuildingClass = 'BUILDINGCLASS_WINDMILL'; +UPDATE Buildings SET FaithCost = 600, UnlockedByBelief = 1 WHERE BuildingClass = 'BUILDINGCLASS_FACTORY'; + -- Religious buildings UPDATE Buildings SET Cost = -1, FaithCost = 200 WHERE BuildingClass IN ( diff --git a/(2) Vox Populi/Database Changes/City/Buildings/NewBuildings.xml b/(2) Vox Populi/Database Changes/City/Buildings/NewBuildings.xml index d1b740dc4c..72d993f454 100644 --- a/(2) Vox Populi/Database Changes/City/Buildings/NewBuildings.xml +++ b/(2) Vox Populi/Database Changes/City/Buildings/NewBuildings.xml @@ -1126,15 +1126,15 @@ BUILDING_ATLAS_VP_1 26 - + - BUILDING_DOJO - BUILDINGCLASS_ARMORY - TXT_KEY_BUILDING_DOJO - TXT_KEY_BUILDING_DOJO_TEXT - TXT_KEY_BUILDING_DOJO_HELP - TXT_KEY_BUILDING_DOJO_STRATEGY - TECH_STEEL + BUILDING_TATARA + BUILDINGCLASS_FORGE + TXT_KEY_BUILDING_TATARA + TXT_KEY_BUILDING_TATARA_TEXT + TXT_KEY_BUILDING_TATARA_HELP + TXT_KEY_BUILDING_TATARA_STRATEGY + TECH_BRONZE_WORKING ART_DEF_BUILDING_BARRACKS BUILDING_ATLAS_VP_1 27 @@ -1428,7 +1428,7 @@ 1 1 BUILDING_ATLAS_VP_2 - 52 + 24 @@ -1749,8 +1749,8 @@ 1 PROMOTION_IMPERIAL_SEAL WonderForum.dds - BUILDING_ATLAS_VP_2 - 33 + BUILDING_ATLAS_VP_3 + 9 @@ -1768,8 +1768,8 @@ 2 GREAT_WORK_BODHISATTVA WonderNalandaSplash.dds - BUILDING_ATLAS_VP_2 - 31 + BUILDING_ATLAS_VP_3 + 7 @@ -1782,10 +1782,9 @@ TECH_GUILDS true 5 - BUILDINGCLASS_ORDER Karlstejn_WonderSplash.dds - BUILDING_ATLAS_VP_2 - 29 + BUILDING_ATLAS_VP_3 + 5 @@ -1802,8 +1801,8 @@ 1 PROMOTION_NOBILITY WonderSummerPalace.dds - BUILDING_ATLAS_VP_2 - 35 + BUILDING_ATLAS_VP_3 + 11 @@ -1819,8 +1818,8 @@ 1 BUILDINGCLASS_FACTORY SlaterSplash.dds - BUILDING_ATLAS_VP_2 - 34 + BUILDING_ATLAS_VP_3 + 10 @@ -1837,8 +1836,8 @@ BUILDINGCLASS_POLICE_STATION motherlandsplash.dds AS2D_WONDER_SPEECH_MOTHERLAND_CALLS - BUILDING_ATLAS_VP_2 - 36 + BUILDING_ATLAS_VP_3 + 12 @@ -1855,8 +1854,8 @@ 2 true bletchleysplash.dds - BUILDING_ATLAS_VP_2 - 24 + BUILDING_ATLAS_VP_3 + 0 @@ -1870,8 +1869,8 @@ BUILDINGCLASS_STOCK_EXCHANGE 1 empiresplash.dds - BUILDING_ATLAS_VP_2 - 27 + BUILDING_ATLAS_VP_3 + 3 @@ -1885,8 +1884,8 @@ 2 100 CernSplash.dds - BUILDING_ATLAS_VP_2 - 25 + BUILDING_ATLAS_VP_3 + 1 @@ -1983,8 +1982,8 @@ 1 PROMOTION_TREASURE_FLEET WonderAdmiralty.dds - BUILDING_ATLAS_VP_2 - 28 + BUILDING_ATLAS_VP_3 + 4 @@ -2000,8 +1999,8 @@ GREAT_WORK_SLOT_ART_ARTIFACT 1 CrystalPalace.dds - BUILDING_ATLAS_VP_2 - 26 + BUILDING_ATLAS_VP_3 + 2 @@ -2017,8 +2016,8 @@ 1 2 MeninGateSplash.dds - BUILDING_ATLAS_VP_2 - 30 + BUILDING_ATLAS_VP_3 + 6 @@ -2034,8 +2033,8 @@ 25 3 OlympicVillage.dds - BUILDING_ATLAS_VP_2 - 32 + BUILDING_ATLAS_VP_3 + 8 @@ -2231,8 +2230,6 @@ TXT_KEY_BUILDING_ORDER_HELP TXT_KEY_BUILDING_ORDER_STRATEGY PROMOTION_MORALE - 10 - 1 25 1 BUILDING_ATLAS_VP_2 @@ -2247,6 +2244,7 @@ TXT_KEY_BUILDING_TEOCALLI_TEXT TXT_KEY_BUILDING_TEOCALLI_HELP TXT_KEY_BUILDING_TEOCALLI_STRATEGY + 1 25 1 EXPANSION_BW_ATLAS_1 @@ -2262,6 +2260,7 @@ TXT_KEY_BUILDING_GURDWARA_HELP TXT_KEY_BUILDING_GURDWARA_STRATEGY 10 + 10 25 1 BUILDING_ATLAS_VP_2 diff --git a/(2) Vox Populi/Database Changes/City/Buildings/PreBuildingChanges.sql b/(2) Vox Populi/Database Changes/City/Buildings/PreBuildingChanges.sql index 969d571bdf..6f0e0abcab 100644 --- a/(2) Vox Populi/Database Changes/City/Buildings/PreBuildingChanges.sql +++ b/(2) Vox Populi/Database Changes/City/Buildings/PreBuildingChanges.sql @@ -194,13 +194,11 @@ INSERT INTO Building_ResourceQuantityRequirements VALUES ('BUILDING_STEAM_MILL', 'RESOURCE_COAL', 1); --- Japan: Armory -INSERT INTO Building_DomainFreeExperiences - (BuildingType, DomainType, Experience) +-- Japan: Forge +INSERT INTO Building_DomainProductionModifiers + (BuildingType, DomainType, Modifier) VALUES - ('BUILDING_DOJO', 'DOMAIN_LAND', 15), - ('BUILDING_DOJO', 'DOMAIN_SEA', 15), - ('BUILDING_DOJO', 'DOMAIN_AIR', 15); + ('BUILDING_TATARA', 'DOMAIN_LAND', 15); -- Korea: University INSERT INTO Building_FeatureYieldChanges diff --git a/(2) Vox Populi/Database Changes/City/Processes/ProcessChanges.sql b/(2) Vox Populi/Database Changes/City/Processes/ProcessChanges.sql index 7eea77f8aa..d250f83c0d 100644 --- a/(2) Vox Populi/Database Changes/City/Processes/ProcessChanges.sql +++ b/(2) Vox Populi/Database Changes/City/Processes/ProcessChanges.sql @@ -1,5 +1,10 @@ -- Process yields -UPDATE Processes SET DefenseValue = 10 WHERE Type = 'PROCESS_DEFENSE'; +UPDATE Processes +SET + DefenseValue = 10, + DefenseValuePerTurn = 1, + DefenseValueCap = 15 +WHERE Type = 'PROCESS_DEFENSE'; INSERT INTO Process_ProductionYields (ProcessType, YieldType, Yield) diff --git a/(2) Vox Populi/Database Changes/Civilizations/Aztec.sql b/(2) Vox Populi/Database Changes/Civilizations/Aztec.sql index 875ff41325..9b73dde67c 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Aztec.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Aztec.sql @@ -27,7 +27,7 @@ SET ) ) ), - Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_LONGSWORDSMAN') + 1 + Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_LONGSWORDSMAN') WHERE Type = 'UNIT_AZTEC_JAGUAR'; DELETE FROM Unit_ResourceQuantityRequirements WHERE UnitType = 'UNIT_AZTEC_JAGUAR'; diff --git a/(2) Vox Populi/Database Changes/Civilizations/Babylon.sql b/(2) Vox Populi/Database Changes/Civilizations/Babylon.sql index 0c3e70cb6a..3005fa754d 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Babylon.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Babylon.sql @@ -49,8 +49,8 @@ WHERE Type = 'UNIT_SABUM_KIBITUM'; INSERT INTO Unit_FreePromotions (UnitType, PromotionType) VALUES - ('UNIT_SABUM_KIBITUM', 'PROMOTION_GAIN_EXPERIENCE'), - ('UNIT_SABUM_KIBITUM', 'PROMOTION_LEGACY'); + ('UNIT_SABUM_KIBITUM', 'PROMOTION_FORMATION_2'), + ('UNIT_SABUM_KIBITUM', 'PROMOTION_SPEAR_WALL'); ---------------------------------------------------------- -- Unique Building: Walls of Babylon (Walls) diff --git a/(2) Vox Populi/Database Changes/Civilizations/Byzantium.sql b/(2) Vox Populi/Database Changes/Civilizations/Byzantium.sql index 83bf1f8360..deb96a1034 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Byzantium.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Byzantium.sql @@ -116,7 +116,7 @@ VALUES ('IMPROVEMENT_APLEKTON', 'YIELD_FOOD', 2), ('IMPROVEMENT_APLEKTON', 'YIELD_PRODUCTION', 2), ('IMPROVEMENT_APLEKTON', 'YIELD_SCIENCE', 1), - ('IMPROVEMENT_APLEKTON', 'YIELD_CULTURE_LOCAL', 1); + ('IMPROVEMENT_APLEKTON', 'YIELD_CULTURE_LOCAL', 2); INSERT INTO Improvement_TechYieldChanges (ImprovementType, TechType, YieldType, Yield) diff --git a/(2) Vox Populi/Database Changes/Civilizations/Celts.sql b/(2) Vox Populi/Database Changes/Civilizations/Celts.sql index 7d25088fb7..b30e9fc3f7 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Celts.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Celts.sql @@ -131,6 +131,8 @@ VALUES ('IMPROVEMENT_OPPIDUM', 'TECH_METAL_CASTING', 'YIELD_CULTURE', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_PHYSICS', 'YIELD_GOLD', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_PHYSICS', 'YIELD_SCIENCE', 1), + ('IMPROVEMENT_OPPIDUM', 'TECH_GUILDS', 'YIELD_FOOD', 1), + ('IMPROVEMENT_OPPIDUM', 'TECH_GUILDS', 'YIELD_CULTURE', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_CHEMISTRY', 'YIELD_SCIENCE', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_CHEMISTRY', 'YIELD_CULTURE', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_ARCHITECTURE', 'YIELD_FOOD', 1), @@ -139,8 +141,12 @@ VALUES ('IMPROVEMENT_OPPIDUM', 'TECH_RAILROAD', 'YIELD_CULTURE', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_FERTILIZER', 'YIELD_FOOD', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_FERTILIZER', 'YIELD_SCIENCE', 1), + ('IMPROVEMENT_OPPIDUM', 'TECH_ELECTRICITY', 'YIELD_PRODUCTION', 1), + ('IMPROVEMENT_OPPIDUM', 'TECH_ELECTRICITY', 'YIELD_GOLD', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_FLIGHT', 'YIELD_GOLD', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_FLIGHT', 'YIELD_CULTURE', 1), + ('IMPROVEMENT_OPPIDUM', 'TECH_REFRIGERATION', 'YIELD_FOOD', 1), + ('IMPROVEMENT_OPPIDUM', 'TECH_REFRIGERATION', 'YIELD_SCIENCE', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_ELECTRONICS', 'YIELD_PRODUCTION', 1), ('IMPROVEMENT_OPPIDUM', 'TECH_ELECTRONICS', 'YIELD_SCIENCE', 1); diff --git a/(2) Vox Populi/Database Changes/Civilizations/Denmark.sql b/(2) Vox Populi/Database Changes/Civilizations/Denmark.sql index db69053d54..412f895e1f 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Denmark.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Denmark.sql @@ -32,7 +32,7 @@ SET ) ) ), - Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_PIKEMAN') + 2, + Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_PIKEMAN') + 1, Moves = (SELECT Moves FROM Units WHERE Type = 'UNIT_PIKEMAN') + 1 WHERE Type = 'UNIT_DANISH_BERSERKER'; diff --git a/(2) Vox Populi/Database Changes/Civilizations/France.sql b/(2) Vox Populi/Database Changes/Civilizations/France.sql index 53a0f23a80..985f74629c 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/France.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/France.sql @@ -33,7 +33,7 @@ SET ) ) ), - Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_SPANISH_TERCIO') + 5 + Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_SPANISH_TERCIO') + 4 WHERE Type = 'UNIT_FRENCH_MUSKETEER'; INSERT INTO Unit_FreePromotions diff --git a/(2) Vox Populi/Database Changes/Civilizations/India.sql b/(2) Vox Populi/Database Changes/Civilizations/India.sql index 1801b65c25..07e805a955 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/India.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/India.sql @@ -34,8 +34,7 @@ SET ) ) ), - Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_CUIRASSIER') + 3, - RangedCombat = (SELECT RangedCombat FROM Units WHERE Type = 'UNIT_CUIRASSIER') + 8, + RangedCombat = (SELECT RangedCombat FROM Units WHERE Type = 'UNIT_CUIRASSIER') + 5, Moves = 3 WHERE Type = 'UNIT_INDIAN_WARELEPHANT'; @@ -47,7 +46,8 @@ DELETE FROM Unit_FreePromotions WHERE UnitType = 'UNIT_INDIAN_WARELEPHANT' AND P INSERT INTO Unit_FreePromotions (UnitType, PromotionType) VALUES - ('UNIT_INDIAN_WARELEPHANT', 'PROMOTION_ACCURACY_1'), + ('UNIT_INDIAN_WARELEPHANT', 'PROMOTION_STALWART'), + ('UNIT_INDIAN_WARELEPHANT', 'PROMOTION_RETALIATION'), ('UNIT_INDIAN_WARELEPHANT', 'PROMOTION_FEARED_ELEPHANT'); ---------------------------------------------------------- diff --git a/(2) Vox Populi/Database Changes/Civilizations/Indonesia.sql b/(2) Vox Populi/Database Changes/Civilizations/Indonesia.sql index 11345fbc7b..c9852f7c3d 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Indonesia.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Indonesia.sql @@ -59,7 +59,7 @@ WHERE UnitType = 'UNIT_DJONG' AND PromotionType IN ( INSERT INTO Unit_FreePromotions (UnitType, PromotionType) VALUES - ('UNIT_DJONG', 'PROMOTION_CETBANG'); + ('UNIT_DJONG', 'PROMOTION_RETALIATION'); ---------------------------------------------------------- -- Unique Improvement: Kampong diff --git a/(2) Vox Populi/Database Changes/Civilizations/Japan.sql b/(2) Vox Populi/Database Changes/Civilizations/Japan.sql index 5892017901..11878f951f 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Japan.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Japan.sql @@ -8,19 +8,13 @@ WHERE Type = 'TRAIT_FIGHT_WELL_DAMAGED'; DELETE FROM Trait_ImprovementYieldChanges WHERE TraitType = 'TRAIT_FIGHT_WELL_DAMAGED'; DELETE FROM Trait_UnimprovedFeatureYieldChanges WHERE TraitType = 'TRAIT_FIGHT_WELL_DAMAGED'; -INSERT INTO Trait_BuildingClassYieldChanges - (TraitType, BuildingClassType, YieldType, YieldChange) -VALUES - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_WALLS', 'YIELD_CULTURE', 1), - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_WALLS', 'YIELD_FAITH', 1), - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_CASTLE', 'YIELD_CULTURE', 1), - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_CASTLE', 'YIELD_FAITH', 1), - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_BASTION_FORT', 'YIELD_CULTURE', 1), - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_BASTION_FORT', 'YIELD_FAITH', 1), - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_ARSENAL', 'YIELD_CULTURE', 1), - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_ARSENAL', 'YIELD_FAITH', 1), - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_MILITARY_BASE', 'YIELD_CULTURE', 1), - ('TRAIT_FIGHT_WELL_DAMAGED', 'BUILDINGCLASS_MILITARY_BASE', 'YIELD_FAITH', 1); +INSERT INTO Trait_FreePromotionUnitCombats + (TraitType, UnitCombatType, PromotionType) +VALUES + ('TRAIT_FIGHT_WELL_DAMAGED', 'UNITCOMBAT_MELEE', 'PROMOTION_BUSHIDO'), + ('TRAIT_FIGHT_WELL_DAMAGED', 'UNITCOMBAT_GUN', 'PROMOTION_BUSHIDO'), + ('TRAIT_FIGHT_WELL_DAMAGED', 'UNITCOMBAT_MOUNTED', 'PROMOTION_BUSHIDO'), + ('TRAIT_FIGHT_WELL_DAMAGED', 'UNITCOMBAT_ARMOR', 'PROMOTION_BUSHIDO'); INSERT INTO Trait_GreatPersonBirthGWAM (TraitType, GreatPersonType, Value) @@ -40,7 +34,7 @@ SET ) ) ), - Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_LONGSWORDSMAN') + 3 + Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_LONGSWORDSMAN') + 2 WHERE Type = 'UNIT_JAPANESE_SAMURAI'; INSERT INTO Unit_FreePromotions @@ -83,37 +77,46 @@ VALUES ('UNIT_MIKASA', 'PROMOTION_KANTAI_KESSEN'); ---------------------------------------------------------- --- Unique Building: Dojo (Armory) +-- Unique Building: Tatara (Forge) ---------------------------------------------------------- INSERT INTO Civilization_BuildingClassOverrides (CivilizationType, BuildingClassType, BuildingType) VALUES - ('CIVILIZATION_JAPAN', 'BUILDINGCLASS_ARMORY', 'BUILDING_DOJO'); + ('CIVILIZATION_JAPAN', 'BUILDINGCLASS_FORGE', 'BUILDING_TATARA'); -UPDATE Buildings -SET - CitySupplyFlat = (SELECT CitySupplyFlat FROM Buildings WHERE Type = 'BUILDING_ARMORY') + 1, - TrainedFreePromotion = 'PROMOTION_BUSHIDO' -WHERE Type = 'BUILDING_DOJO'; +INSERT INTO Building_YieldChanges + (BuildingType, YieldType, Yield) +VALUES + ('BUILDING_TATARA', 'YIELD_PRODUCTION', 2); UPDATE Building_YieldChanges -SET Yield = (SELECT Yield FROM Building_YieldChanges WHERE BuildingType = 'BUILDING_ARMORY' AND YieldType = 'YIELD_SCIENCE') + 3 -WHERE BuildingType = 'BUILDING_DOJO' AND YieldType = 'YIELD_SCIENCE'; +SET Yield = (SELECT Yield FROM Building_YieldChanges WHERE BuildingType = 'BUILDING_FORGE' AND YieldType = 'YIELD_SCIENCE') + 1 +WHERE BuildingType = 'BUILDING_TATARA' AND YieldType = 'YIELD_SCIENCE'; -INSERT INTO Building_YieldChanges - (BuildingType, YieldType, Yield) +UPDATE Buildings +SET SpecialistCount = (SELECT SpecialistCount FROM Buildings WHERE Type = 'BUILDING_FORGE') + 1 +WHERE Type = 'BUILDING_TATARA'; + +INSERT INTO Building_ResourcePlotsToPlace + (BuildingType, ResourceType, NumPlots, ResourceQuantityToPlace) VALUES - ('BUILDING_DOJO', 'YIELD_CULTURE', 3); + ('BUILDING_TATARA', 'RESOURCE_IRON', 1, 1); -UPDATE Building_DomainFreeExperiences -SET Experience = (SELECT Experience FROM Building_DomainFreeExperiences WHERE BuildingType = 'BUILDING_ARMORY') + 5 -WHERE BuildingType = 'BUILDING_DOJO'; +INSERT INTO Building_ResourceClaim + (BuildingType, ResourceType, IncludeOwnedByOtherPlayer) +VALUES + ('BUILDING_TATARA', 'RESOURCE_IRON', 0); + +INSERT INTO Building_ResourceYieldChanges + (BuildingType, ResourceType, YieldType, Yield) +VALUES + ('BUILDING_TATARA', 'RESOURCE_IRON', 'YIELD_CULTURE', 1); INSERT INTO Building_YieldFromCombatExperienceTimes100 (BuildingType, YieldType, Yield) VALUES - ('BUILDING_DOJO', 'YIELD_CULTURE', 140), - ('BUILDING_DOJO', 'YIELD_SCIENCE', 140); + ('BUILDING_TATARA', 'YIELD_CULTURE', 140), + ('BUILDING_TATARA', 'YIELD_SCIENCE', 140); ---------------------------------------------------------- -- Unique Building: Kabuki Theater (Opera House) diff --git a/(2) Vox Populi/Database Changes/Civilizations/Mongolia.sql b/(2) Vox Populi/Database Changes/Civilizations/Mongolia.sql index 6ffcbc6aa2..3ff232ba47 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Mongolia.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Mongolia.sql @@ -34,7 +34,7 @@ WHERE Type = 'UNIT_MONGOLIAN_KHAN'; INSERT INTO Unit_FreePromotions (UnitType, PromotionType) VALUES - ('UNIT_MONGOLIAN_KHAN', 'PROMOTION_MEDIC_GENERAL'), + ('UNIT_MONGOLIAN_KHAN', 'PROMOTION_RETALIATION'), ('UNIT_MONGOLIAN_KHAN', 'PROMOTION_MEDIC'), ('UNIT_MONGOLIAN_KHAN', 'PROMOTION_MEDIC_II'); diff --git a/(2) Vox Populi/Database Changes/Civilizations/Morocco.sql b/(2) Vox Populi/Database Changes/Civilizations/Morocco.sql index b455c6aaf5..125dec344a 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Morocco.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Morocco.sql @@ -3,8 +3,7 @@ ---------------------------------------------------------- UPDATE Traits SET - NoTradeRouteProximityPenalty = 1, - CanPlunderWithoutWar = 1 + NoTradeRouteProximityPenalty = 1 WHERE Type = 'TRAIT_GATEWAY_AFRICA'; DELETE FROM Trait_YieldChangesPerTradePartner WHERE TraitType = 'TRAIT_GATEWAY_AFRICA'; @@ -13,7 +12,7 @@ DELETE FROM Trait_YieldChangesIncomingTradeRoute WHERE TraitType = 'TRAIT_GATEWA INSERT INTO Trait_YieldChangesPerTradePartner (TraitType, YieldType, Yield) SELECT - 'TRAIT_GATEWAY_AFRICA', Type, 1 + 'TRAIT_GATEWAY_AFRICA', Type, 2 FROM Yields WHERE ID < 6; -- "All" yields @@ -29,8 +28,9 @@ SET ) ) ), + Moves = (SELECT Moves FROM Units WHERE Type = 'UNIT_CAVALRY') + 1, Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_CAVALRY') + 2, - RangedCombat = (SELECT RangedCombat FROM Units WHERE Type = 'UNIT_CAVALRY') + 3 + RangedCombat = (SELECT RangedCombat FROM Units WHERE Type = 'UNIT_CAVALRY') + 5 WHERE Type = 'UNIT_BERBER_CAVALRY'; INSERT INTO Unit_FreePromotions @@ -68,7 +68,8 @@ WHERE Type = 'UNIT_CORSAIR'; INSERT INTO Unit_FreePromotions (UnitType, PromotionType) VALUES - ('UNIT_CORSAIR', 'PROMOTION_RAZZIA'); + ('UNIT_CORSAIR', 'PROMOTION_RAZZIA'), + ('UNIT_CORSAIR', 'PROMOTION_NAVIGATOR_1'); ---------------------------------------------------------- -- Unique Improvement: Kasbah @@ -129,17 +130,11 @@ UPDATE Buildings SET LandmarksTourismPercent = (SELECT LandmarksTourismPercent FROM Buildings WHERE Type = 'BUILDING_HOTEL') * 133 / 100 WHERE Type = 'BUILDING_RIAD'; -INSERT INTO Building_YieldChangesEraScalingTimes100 - (BuildingType, YieldType, Yield) -VALUES - ('BUILDING_RIAD', 'YIELD_GOLD', 100), - ('BUILDING_RIAD', 'YIELD_CULTURE', 100); - INSERT INTO Building_YieldChangesPerXBuilding (BuildingType, YieldType, Yield, NumRequired) VALUES - ('BUILDING_RIAD', 'YIELD_GOLD', 1, 8), - ('BUILDING_RIAD', 'YIELD_CULTURE', 1, 8); + ('BUILDING_RIAD', 'YIELD_GOLD', 1, 3), + ('BUILDING_RIAD', 'YIELD_CULTURE', 1, 3); INSERT INTO Building_YieldFromPurchase (BuildingType, YieldType, Yield) diff --git a/(2) Vox Populi/Database Changes/Civilizations/Netherlands.sql b/(2) Vox Populi/Database Changes/Civilizations/Netherlands.sql index 38c4c5fa3d..12b7b74366 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Netherlands.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Netherlands.sql @@ -54,7 +54,7 @@ SET ) ) ), - Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_PIKEMAN') + 3 + Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_PIKEMAN') + 2 WHERE Type = 'UNIT_GOEDENDAG'; INSERT INTO Unit_FreePromotions diff --git a/(2) Vox Populi/Database Changes/Civilizations/Polynesia.sql b/(2) Vox Populi/Database Changes/Civilizations/Polynesia.sql index 8d5fd06744..3fa8801cfa 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Polynesia.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Polynesia.sql @@ -30,7 +30,7 @@ SET ) ) ), - Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_PIKEMAN') + 5 + Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_PIKEMAN') + 4 WHERE Type = 'UNIT_POLYNESIAN_MAORI_WARRIOR'; INSERT INTO Unit_FreePromotions diff --git a/(2) Vox Populi/Database Changes/Civilizations/StartBiasSweeps.sql b/(2) Vox Populi/Database Changes/Civilizations/StartBiasSweeps.sql index e00e96ec8f..c651133652 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/StartBiasSweeps.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/StartBiasSweeps.sql @@ -46,7 +46,7 @@ VALUES ('CIVILIZATION_SWEDEN', 'REGION_TUNDRA'), -- UB wants tundra (historical) - ('CIVILIZATION_ROME', 'REGION_HILLS'); -- UU wants Iron, UA needs early production (historical) + ('CIVILIZATION_ETHIOPIA', 'REGION_HILLS'); -- UI and UU need hills (historical) INSERT INTO Civilization_Start_Region_Avoid (CivilizationType, RegionType) @@ -55,7 +55,8 @@ VALUES ('CIVILIZATION_INDIA', 'REGION_HILLS'), -- Classical UB wants farms ('CIVILIZATION_SHOSHONE', 'REGION_HILLS'), -- UI needs flat land (historical) ('CIVILIZATION_POLAND', 'REGION_JUNGLE'), -- Medieval UB wants Sheep/Cattle/Horses - ('CIVILIZATION_POLAND', 'REGION_FOREST'); + ('CIVILIZATION_POLAND', 'REGION_FOREST'), + ('CIVILIZATION_JAPAN', 'REGION_JUNGLE'); -- UB wants Iron, can't spawn Iron in Jungle tiles INSERT INTO Civilization_Start_Prefer_Mountain (CivilizationType, StartPreferMountain) diff --git a/(2) Vox Populi/Database Changes/Civilizations/Sweden.sql b/(2) Vox Populi/Database Changes/Civilizations/Sweden.sql index 63644a5e8e..b241c195b5 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Sweden.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Sweden.sql @@ -6,7 +6,7 @@ SET GreatPersonGiftInfluence = 0, DOFGreatPersonModifier = 0, GreatGeneralExtraBonus = 15, - XPBonusFromGreatPersonBirth = 2, + XPBonusFromGreatPersonBirth = 3, UnitHealFromGreatPersonBirth = 10 WHERE Type = 'TRAIT_DIPLOMACY_GREAT_PEOPLE'; @@ -143,4 +143,4 @@ VALUES INSERT INTO Building_YieldFromGPExpend (BuildingType, YieldType, Yield) VALUES - ('BUILDING_BASTU', 'YIELD_CULTURE', 8); + ('BUILDING_BASTU', 'YIELD_CULTURE', 12); diff --git a/(2) Vox Populi/Database Changes/Civilizations/Zulu.sql b/(2) Vox Populi/Database Changes/Civilizations/Zulu.sql index d69716d974..08a248d136 100644 --- a/(2) Vox Populi/Database Changes/Civilizations/Zulu.sql +++ b/(2) Vox Populi/Database Changes/Civilizations/Zulu.sql @@ -27,7 +27,7 @@ SET ) ) ), - Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_SPANISH_TERCIO') + 3 + Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_SPANISH_TERCIO') + 2 WHERE Type = 'UNIT_ZULU_IMPI'; INSERT INTO Unit_FreePromotions diff --git a/(2) Vox Populi/Database Changes/DefineChanges.sql b/(2) Vox Populi/Database Changes/DefineChanges.sql index 94c8857d72..5dbce5d34f 100644 --- a/(2) Vox Populi/Database Changes/DefineChanges.sql +++ b/(2) Vox Populi/Database Changes/DefineChanges.sql @@ -43,6 +43,14 @@ UPDATE Defines SET Value = 10 WHERE Name = 'DEFENSIVE_PACT_LIMIT_SCALER'; UPDATE Defines SET Value = 2 WHERE Name = 'AI_DEFENSIVE_PACT_LIMIT_BASE'; UPDATE Defines SET Value = 10 WHERE Name = 'AI_DEFENSIVE_PACT_LIMIT_SCALER'; +------------------------------------------------------------------------------------------------------------------- +-- Research Agreements +------------------------------------------------------------------------------------------------------------------- +-- Percentage of RA yields that are given as per-turn yields, not as instant yield when the agreement ends. +UPDATE Defines SET Value = 50 WHERE Name = 'RESEARCH_AGREEMENT_PER_TURN_YIELD_PERCENT'; +-- RA yields are calculated as X*avg(SciencePlayer1, SciencePlayer2) + (1-X)*min(SciencePlayer1, SciencePlayer2). +UPDATE Defines SET Value = 50 WHERE Name = 'RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT'; + ------------------------------------------------------------------------------------------------------------------- -- War ------------------------------------------------------------------------------------------------------------------- diff --git a/(2) Vox Populi/Database Changes/Difficulty/DifficultyChanges.sql b/(2) Vox Populi/Database Changes/Difficulty/DifficultyChanges.sql index 86ab60f69b..28973e1af5 100644 --- a/(2) Vox Populi/Database Changes/Difficulty/DifficultyChanges.sql +++ b/(2) Vox Populi/Database Changes/Difficulty/DifficultyChanges.sql @@ -74,8 +74,7 @@ VALUES ('HISTORIC_EVENT_GOLDEN_AGE', 'YIELD_CULTURE'), ('HISTORIC_EVENT_GOLDEN_AGE', 'YIELD_SCIENCE'), ('HISTORIC_EVENT_WORLD_WONDER', 'YIELD_GOLD'), - ('HISTORIC_EVENT_WORLD_WONDER', 'YIELD_CULTURE'), - ('HISTORIC_EVENT_WORLD_WONDER', 'YIELD_SCIENCE'), + ('HISTORIC_EVENT_WORLD_WONDER', 'YIELD_TOURISM'), ('DIFFICULTY_BONUS_RESEARCHED_TECH', 'YIELD_FOOD'), ('DIFFICULTY_BONUS_RESEARCHED_TECH', 'YIELD_GOLD'), ('DIFFICULTY_BONUS_RESEARCHED_TECH', 'YIELD_CULTURE'), @@ -169,6 +168,8 @@ CREATE TEMP TABLE DifficultyBonusMultipliers ( IsNotGold boolean, IsFoodOrGold boolean, IsFoodOrGoldBeforeRenaissance boolean, + IsTourism boolean, + IsTourismBeforeRenaissance boolean, IsAny boolean, MultiplierTimes100 integer ); @@ -177,7 +178,7 @@ INSERT INTO DifficultyBonusMultipliers (HistoricEventTypeTemp, IsGold, IsNotGold, MultiplierTimes100) VALUES ('HISTORIC_EVENT_GOLDEN_AGE', 1, 0, 300), -- 3x Gold for Golden Ages - ('HISTORIC_EVENT_WORLD_WONDER', 1, 0, 300), -- 3x Gold for World Wonders + ('HISTORIC_EVENT_WORLD_WONDER', 1, 0, 250), -- 2.5x Gold for World Wonders ('DIFFICULTY_BONUS_KILLED_MAJOR_UNIT', 1, 0, 20), -- 0.2x Gold for killing a major civ unit ('DIFFICULTY_BONUS_KILLED_MAJOR_UNIT', 0, 1, 10), -- 0.1x other yields for killing a major civ unit ('DIFFICULTY_BONUS_KILLED_CITY_STATE_UNIT', 1, 0, 20), -- 0.2x Gold for killing a City-State unit @@ -199,6 +200,12 @@ VALUES ('DIFFICULTY_BONUS_CITY_FOUND', 1, 0, 200), -- 2x Food and Gold for founding a city ('DIFFICULTY_BONUS_CITY_FOUND', 0, 1, 0); -- 0x Food and Gold for founding a city before Renaissance Era +INSERT INTO DifficultyBonusMultipliers + (HistoricEventTypeTemp, IsTourism, IsTourismBeforeRenaissance, MultiplierTimes100) +VALUES + ('HISTORIC_EVENT_WORLD_WONDER', 1, 0, 90), -- 0.9x Tourism for World Wonders + ('HISTORIC_EVENT_WORLD_WONDER', 0, 1, 0); -- 0x Tourism for World Wonders before Renaissance Era + UPDATE HandicapInfo_AIDifficultyBonus SET Amount = Amount * (SELECT MultiplierTimes100 FROM DifficultyBonusMultipliers WHERE HistoricEventTypeTemp = HistoricEventType AND IsGold = 1) / 100 WHERE YieldType = 'YIELD_GOLD' @@ -221,6 +228,18 @@ WHERE YieldType IN ('YIELD_FOOD', 'YIELD_GOLD') AND EraType IN ( ) AND EXISTS (SELECT 1 FROM DifficultyBonusMultipliers WHERE HistoricEventTypeTemp = HistoricEventType AND IsFoodOrGoldBeforeRenaissance = 1); +UPDATE HandicapInfo_AIDifficultyBonus +SET Amount = Amount * (SELECT MultiplierTimes100 FROM DifficultyBonusMultipliers WHERE HistoricEventTypeTemp = HistoricEventType AND IsTourism = 1) / 100 +WHERE YieldType = 'YIELD_TOURISM' +AND EXISTS (SELECT 1 FROM DifficultyBonusMultipliers WHERE HistoricEventTypeTemp = HistoricEventType AND IsTourism = 1); + +UPDATE HandicapInfo_AIDifficultyBonus +SET Amount = Amount * (SELECT MultiplierTimes100 FROM DifficultyBonusMultipliers WHERE HistoricEventTypeTemp = HistoricEventType AND IsTourismBeforeRenaissance = 1) / 100 +WHERE YieldType = 'YIELD_TOURISM' AND EraType IN ( + SELECT Type FROM Eras WHERE ID < (SELECT ID FROM Eras WHERE Type = 'ERA_RENAISSANCE') +) +AND EXISTS (SELECT 1 FROM DifficultyBonusMultipliers WHERE HistoricEventTypeTemp = HistoricEventType AND IsTourismBeforeRenaissance = 1); + UPDATE HandicapInfo_AIDifficultyBonus SET Amount = Amount * (SELECT MultiplierTimes100 FROM DifficultyBonusMultipliers WHERE HistoricEventTypeTemp = HistoricEventType AND IsAny = 1) / 100 WHERE EXISTS (SELECT 1 FROM DifficultyBonusMultipliers WHERE HistoricEventTypeTemp = HistoricEventType AND IsAny = 1); @@ -237,7 +256,6 @@ CREATE TEMP TABLE AncientDifficultyBonusMultipliers ( INSERT INTO AncientDifficultyBonusMultipliers VALUES ('DIFFICULTY_BONUS_CITY_FOUND', 'YIELD_SCIENCE', 89), -- 0.89x Science for founding a city - ('HISTORIC_EVENT_WORLD_WONDER', 'YIELD_SCIENCE', 89), -- 0.89x Science for World Wonders ('DIFFICULTY_BONUS_ADOPTED_POLICY', 'YIELD_SCIENCE', 78), -- 0.78x Science for adopting a policy ('DIFFICULTY_BONUS_RESEARCHED_TECH', 'YIELD_CULTURE', 79); -- 0.79x Culture for researching a tech diff --git a/(2) Vox Populi/Database Changes/GameOptionChanges.sql b/(2) Vox Populi/Database Changes/GameOptionChanges.sql index 9492aac4c1..ed9998671e 100644 --- a/(2) Vox Populi/Database Changes/GameOptionChanges.sql +++ b/(2) Vox Populi/Database Changes/GameOptionChanges.sql @@ -14,7 +14,5 @@ WHERE Type IN ( UPDATE GameOptions SET "Default" = 1 WHERE Type IN ( - 'GAMEOPTION_DISABLE_RESEARCH_AGREEMENTS', - 'GAMEOPTION_ENABLE_TECH_TRADING', 'GAMEOPTION_NO_TECH_BROKERING' ); diff --git a/(2) Vox Populi/Database Changes/Policies/Imperialism.sql b/(2) Vox Populi/Database Changes/Policies/Imperialism.sql index 93c6226c89..9cf8b91fae 100644 --- a/(2) Vox Populi/Database Changes/Policies/Imperialism.sql +++ b/(2) Vox Populi/Database Changes/Policies/Imperialism.sql @@ -90,8 +90,8 @@ VALUES -- Naval Tradition (now Colonialism) UPDATE Policies SET - MonopolyModPercent = 10, - MonopolyModFlat = 3 + MonopolyModPercent = 5, + MonopolyModFlat = 5 WHERE Type = 'POLICY_NAVAL_TRADITION'; DELETE FROM Policy_BuildingClassHappiness diff --git a/(2) Vox Populi/Database Changes/Policies/Industry.sql b/(2) Vox Populi/Database Changes/Policies/Industry.sql index b002265a9d..d7913925d3 100644 --- a/(2) Vox Populi/Database Changes/Policies/Industry.sql +++ b/(2) Vox Populi/Database Changes/Policies/Industry.sql @@ -68,7 +68,16 @@ WHERE b.Type IN ('BUILDINGCLASS_WORKSHOP', 'BUILDINGCLASS_WINDMILL', 'BUILDINGCL AND y.Type IN ('YIELD_PRODUCTION', 'YIELD_CULTURE'); -- Wagon Trains (now Subsidies) -UPDATE Policies SET LandTradeRouteGoldChange = 0 WHERE Type = 'POLICY_CARAVANS'; +UPDATE Policies +SET + LandTradeRouteGoldChange = 0, + RouteGoldMaintenanceMod = 0 +WHERE Type = 'POLICY_CARAVANS'; + +INSERT INTO Policy_FreeResource + (PolicyType, ResourceType, Number) +VALUES + ('POLICY_CARAVANS', 'RESOURCE_COAL', 4); INSERT INTO Policy_BuildingClassHappiness (PolicyType, BuildingClassType, Happiness) diff --git a/(2) Vox Populi/Database Changes/Policies/Progress.sql b/(2) Vox Populi/Database Changes/Policies/Progress.sql index e1c2fd2bd8..1222c79e88 100644 --- a/(2) Vox Populi/Database Changes/Policies/Progress.sql +++ b/(2) Vox Populi/Database Changes/Policies/Progress.sql @@ -25,7 +25,7 @@ WHERE Type = 'POLICY_LIBERTY'; INSERT INTO Policy_YieldFromBirth (PolicyType, YieldType, Yield) VALUES - ('POLICY_LIBERTY', 'YIELD_SCIENCE', 10); + ('POLICY_LIBERTY', 'YIELD_SCIENCE', 8); INSERT INTO Policy_InstantYield (PolicyType, YieldType, Yield) diff --git a/(2) Vox Populi/Database Changes/Tech/TechChanges.sql b/(2) Vox Populi/Database Changes/Tech/TechChanges.sql index 1a1c7901ec..0a02550eae 100644 --- a/(2) Vox Populi/Database Changes/Tech/TechChanges.sql +++ b/(2) Vox Populi/Database Changes/Tech/TechChanges.sql @@ -84,6 +84,15 @@ WHERE Type IN ( 'TECH_TELECOM' ); +-- Move Open Borders treaties to Philosophy +UPDATE Technologies +SET OpenBordersTradingAllowed = 0 +WHERE Type = 'TECH_CIVIL_SERVICE'; + +UPDATE Technologies +SET OpenBordersTradingAllowed = 1 +WHERE Type = 'TECH_PHILOSOPHY'; + -- Enable Corporations UPDATE Technologies SET CorporationsEnabled = 1 WHERE Type = 'TECH_CORPORATIONS'; diff --git a/(2) Vox Populi/Database Changes/Tech/TechTreeSweeps.sql b/(2) Vox Populi/Database Changes/Tech/TechTreeSweeps.sql index f21864ea17..4578c38f42 100644 --- a/(2) Vox Populi/Database Changes/Tech/TechTreeSweeps.sql +++ b/(2) Vox Populi/Database Changes/Tech/TechTreeSweeps.sql @@ -81,21 +81,21 @@ VALUES ('TECH_DYNAMITE', 'TECH_RIFLING'), ('TECH_MILITARY_SCIENCE', 'TECH_RIFLING'), ('TECH_SCIENTIFIC_THEORY', 'TECH_ARCHITECTURE'), - ('TECH_SCIENTIFIC_THEORY', 'TECH_ECONOMICS'), - ('TECH_RAILROAD', 'TECH_ECONOMICS'), + ('TECH_SCIENTIFIC_THEORY', 'TECH_ACOUSTICS'), ('TECH_RAILROAD', 'TECH_ACOUSTICS'), - ('TECH_STEAM_POWER', 'TECH_ACOUSTICS'), + ('TECH_RAILROAD', 'TECH_ECONOMICS'), + ('TECH_STEAM_POWER', 'TECH_ECONOMICS'), ('TECH_STEAM_POWER', 'TECH_NAVIGATION'), ('TECH_RIFLING', 'TECH_NAVIGATION'), ('TECH_RIFLING', 'TECH_METALLURGY'), ('TECH_ARCHITECTURE', 'TECH_BANKING'), ('TECH_ARCHITECTURE', 'TECH_PRINTING_PRESS'), - ('TECH_ECONOMICS', 'TECH_BANKING'), - ('TECH_ECONOMICS', 'TECH_PRINTING_PRESS'), - ('TECH_ECONOMICS', 'TECH_ASTRONOMY'), + ('TECH_ACOUSTICS', 'TECH_BANKING'), ('TECH_ACOUSTICS', 'TECH_PRINTING_PRESS'), ('TECH_ACOUSTICS', 'TECH_ASTRONOMY'), - ('TECH_ACOUSTICS', 'TECH_GUNPOWDER'), + ('TECH_ECONOMICS', 'TECH_PRINTING_PRESS'), + ('TECH_ECONOMICS', 'TECH_ASTRONOMY'), + ('TECH_ECONOMICS', 'TECH_GUNPOWDER'), ('TECH_NAVIGATION', 'TECH_ASTRONOMY'), ('TECH_NAVIGATION', 'TECH_GUNPOWDER'), ('TECH_NAVIGATION', 'TECH_CHEMISTRY'), @@ -224,8 +224,8 @@ VALUES ('TECH_CHEMISTRY', 7, 9), -- Renaissance T2 ('TECH_ARCHITECTURE', 8, 1), - ('TECH_ECONOMICS', 8, 3), - ('TECH_ACOUSTICS', 8, 5), + ('TECH_ACOUSTICS', 8, 3), + ('TECH_ECONOMICS', 8, 5), ('TECH_NAVIGATION', 8, 7), ('TECH_METALLURGY', 8, 9), -- Industrial T1 diff --git a/(2) Vox Populi/Database Changes/Text/en_US/Beliefs/BeliefTextChanges.sql b/(2) Vox Populi/Database Changes/Text/en_US/Beliefs/BeliefTextChanges.sql index b9c09349a8..798c75c134 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/Beliefs/BeliefTextChanges.sql +++ b/(2) Vox Populi/Database Changes/Text/en_US/Beliefs/BeliefTextChanges.sql @@ -36,7 +36,7 @@ SET Text = 'God of Craftsmen' WHERE Tag = 'TXT_KEY_BELIEF_STONE_CIRCLES_SHORT'; UPDATE Language_en_US -SET Text = '+1 [ICON_PEACE] Faith and [ICON_PRODUCTION] Production from Quarries. +2 [ICON_PEACE] Faith and [ICON_RESEARCH] Science from the Palace. +2 [ICON_PEACE] Faith and +1 [ICON_CULTURE] Culture from Stone Works.' +SET Text = '+1 [ICON_PEACE] Faith, [ICON_PRODUCTION] Production, and [ICON_RESEARCH] Science from Quarries. +2 [ICON_PEACE] Faith and [ICON_RESEARCH] Science from the Palace. +2 [ICON_PEACE] Faith and +1 [ICON_CULTURE] Culture from Stone Works.' WHERE Tag = 'TXT_KEY_BELIEF_STONE_CIRCLES'; UPDATE Language_en_US @@ -62,7 +62,7 @@ SET Text = 'God of the Stars and Sky' WHERE Tag = 'TXT_KEY_BELIEF_DANCE_AURORA_SHORT'; UPDATE Language_en_US -SET Text = '+1 [ICON_PEACE] Faith, [ICON_FOOD] Food, and [ICON_CULTURE] Culture from Tundra and Snow tiles with Resources.' +SET Text = '+1 [ICON_PEACE] Faith, [ICON_FOOD] Food, and [ICON_CULTURE] Culture from Tundra and Snow tiles with improved Resources. +1 [ICON_PEACE] Faith and +1 [ICON_FOOD] Food from Cities on or adjacent to Snow or Tundra.' WHERE Tag = 'TXT_KEY_BELIEF_DANCE_AURORA'; UPDATE Language_en_US @@ -70,7 +70,7 @@ SET Text = 'God of the Sun' WHERE Tag = 'TXT_KEY_BELIEF_SUN_GOD_SHORT'; UPDATE Language_en_US -SET Text = '+2 [ICON_PEACE] Faith and [ICON_GOLD] Gold from Granaries. +1 [ICON_PEACE] Faith, [ICON_FOOD] Food, and [ICON_RESEARCH] Science from Farms on improved Resources.' +SET Text = '+2 [ICON_PEACE] Faith, +1 [ICON_PRODUCTION] Production, and +1 [ICON_GOLD] Gold from Granaries. +1 [ICON_PEACE] Faith, [ICON_FOOD] Food, and [ICON_RESEARCH] Science from Farms on improved Resources.' WHERE Tag = 'TXT_KEY_BELIEF_SUN_GOD'; -- God of War @@ -138,12 +138,12 @@ SET Text = 'Goddess of the Home' WHERE Tag = 'TXT_KEY_BELIEF_FERTILITY_RITES_SHORT'; UPDATE Language_en_US -SET Text = '+25% [ICON_FOOD] Growth. +1 [ICON_PEACE] Faith and [ICON_FOOD] Food from Shrines. +8 [ICON_PEACE] Faith and [ICON_FOOD] Food when a Building is constructed, scaling with Era.' +SET Text = '+25% [ICON_FOOD] Growth and +1 [ICON_HAPPINESS_1] Happiness. +1 [ICON_PEACE] Faith and [ICON_FOOD] Food from Shrines and Palace. +8 [ICON_PEACE] Faith and [ICON_FOOD] Food when a Building is constructed, scaling with Era.' WHERE Tag = 'TXT_KEY_BELIEF_FERTILITY_RITES'; -- Goddess of the Hunt UPDATE Language_en_US -SET Text = '+1 [ICON_PEACE] Faith, [ICON_GOLD] Gold, and [ICON_CULTURE] Culture from Camps. +2 [ICON_FOOD] Food from Smokehouses.' +SET Text = '+1 [ICON_PEACE] Faith, [ICON_GOLD] Gold, and [ICON_CULTURE] Culture from Camps. +1 [ICON_PEACE] Faith and [ICON_FOOD] Food from Smokehouses.' WHERE Tag = 'TXT_KEY_BELIEF_GODDESS_HUNT'; UPDATE Language_en_US @@ -191,7 +191,7 @@ SET Text = 'Council of Elders' WHERE Tag = 'TXT_KEY_BELIEF_PAPAL_PRIMACY_SHORT'; UPDATE Language_en_US -SET Text = 'When a City adopts your [ICON_RELIGION] Religion for the first time, gain 20 [ICON_RESEARCH] Science and [ICON_PRODUCTION] Production in your Holy City, scaling gradually based on the number of Cities following your [ICON_RELIGION] Religion (bonus caps at 25 Cities).[NEWLINE]Unlocks [COLOR_POSITIVE_TEXT]Holy Council National Wonder[ENDCOLOR] (+4 [ICON_PEACE] Faith, +5 [ICON_FOOD] Food; +5 [ICON_RESEARCH] Science from [ICON_RELIGION] Holy Sites; unlocks [COLOR_POSITIVE_TEXT]Reformation Belief[ENDCOLOR]).' +SET Text = 'When a City adopts your [ICON_RELIGION] Religion for the first time, gain 20 [ICON_RESEARCH] Science, 20 [ICON_PRODUCTION] Production, and 10 [ICON_PEACE] Faith in your Holy City, scaling gradually based on the number of Cities following your [ICON_RELIGION] Religion (bonus caps at 25 Cities).[NEWLINE]Unlocks [COLOR_POSITIVE_TEXT]Holy Council National Wonder[ENDCOLOR] (+4 [ICON_PEACE] Faith, +5 [ICON_FOOD] Food; +5 [ICON_RESEARCH] Science from [ICON_RELIGION] Holy Sites; unlocks [COLOR_POSITIVE_TEXT]Reformation Belief[ENDCOLOR]).' WHERE Tag = 'TXT_KEY_BELIEF_PAPAL_PRIMACY'; UPDATE Language_en_US @@ -215,7 +215,7 @@ SET Text = 'Holy Law' WHERE Tag = 'TXT_KEY_BELIEF_CHURCH_PROPERTY_SHORT'; UPDATE Language_en_US -SET Text = 'When you unlock a Policy, gain 5 [ICON_PEACE] Faith, [ICON_RESEARCH] Science, and [ICON_GOLD] Gold for every Follower of your [ICON_RELIGION] Religion (max 250 Followers).[NEWLINE]Unlocks [COLOR_POSITIVE_TEXT]Divine Court National Wonder[ENDCOLOR] (+4 [ICON_PEACE] Faith, +6 [ICON_GOLD] Gold; +5 [ICON_CULTURE] Culture from [ICON_RELIGION] Holy Sites; unlocks [COLOR_POSITIVE_TEXT]Reformation Belief[ENDCOLOR]).' +SET Text = 'When you unlock a Policy, gain 6 [ICON_PEACE] Faith, [ICON_RESEARCH] Science, and [ICON_GOLD] Gold for every Follower of your [ICON_RELIGION] Religion (max 250 Followers).[NEWLINE]Unlocks [COLOR_POSITIVE_TEXT]Divine Court National Wonder[ENDCOLOR] (+4 [ICON_PEACE] Faith, +6 [ICON_GOLD] Gold; +5 [ICON_CULTURE] Culture from [ICON_RELIGION] Holy Sites; unlocks [COLOR_POSITIVE_TEXT]Reformation Belief[ENDCOLOR]).' WHERE Tag = 'TXT_KEY_BELIEF_CHURCH_PROPERTY'; UPDATE Language_en_US @@ -231,7 +231,7 @@ SET Text = 'Transcendent Thoughts' WHERE Tag = 'TXT_KEY_BELIEF_INITIATION_RITES_SHORT'; UPDATE Language_en_US -SET Text = 'When you enter a new Era, Holy City gains 12 of every Yield for each City following your [ICON_RELIGION] Religion (max 25 Cities), scaling with Era.[NEWLINE]Unlocks [COLOR_POSITIVE_TEXT]Sacred Garden National Wonder[ENDCOLOR] (+3 [ICON_PEACE] Faith, +5 [ICON_CULTURE] Culture; +5 [ICON_FOOD] Food from [ICON_RELIGION] Holy Sites; unlocks [COLOR_POSITIVE_TEXT]Reformation Belief[ENDCOLOR]).' +SET Text = 'When you enter a new Era, Holy City gains 15 of every Yield for each City following your [ICON_RELIGION] Religion (max 25 Cities), scaling with Era.[NEWLINE]Unlocks [COLOR_POSITIVE_TEXT]Sacred Garden National Wonder[ENDCOLOR] (+3 [ICON_PEACE] Faith, +5 [ICON_CULTURE] Culture; +5 [ICON_FOOD] Food from [ICON_RELIGION] Holy Sites; unlocks [COLOR_POSITIVE_TEXT]Reformation Belief[ENDCOLOR]).' WHERE Tag = 'TXT_KEY_BELIEF_INITIATION_RITES'; UPDATE Language_en_US @@ -445,12 +445,12 @@ SET Text = 'Crusader Spirit' WHERE Tag = 'TXT_KEY_BELIEF_EVANGELISM_SHORT'; UPDATE Language_en_US -SET Text = 'Land Units gain +10% [ICON_STRENGTH] Combat Strength versus Land Units in enemy territory, and an additional +10% versus Land Units of players that do not follow your Religion. Receive 50 [ICON_CULTURE] Culture and [ICON_GOLD] Gold when you conquer Cities, scaling with Era and City [ICON_CITIZEN] Population.' +SET Text = 'Land Units gain +10% [ICON_STRENGTH] Combat Strength versus Land Units in enemy territory, and an additional +5% versus Land Units of players that do not follow your Religion. +2 [ICON_Gold] Gold and [ICON_CULTURE] Culture from Barracks, Armories, and Military Academies.' WHERE Tag = 'TXT_KEY_BELIEF_EVANGELISM'; -- Defender of the Faith UPDATE Language_en_US -SET Text = 'Land Units gain +10% [ICON_STRENGTH] Combat Strength versus Land Units in friendly territory, and an additional +10% versus Land Units of players that do not follow your Religion. +1 [ICON_PEACE] Faith and +2 [ICON_CULTURE] Culture from all Defensive Buildings.' +SET Text = 'Land Units gain +10% [ICON_STRENGTH] Combat Strength versus Land Units in friendly territory, and an additional +5% versus Land Units of players that do not follow your Religion. +1 [ICON_PEACE] Faith and +2 [ICON_CULTURE] Culture from all Defensive Buildings.' WHERE Tag = 'TXT_KEY_BELIEF_DEFENDER_FAITH'; UPDATE Language_en_US @@ -474,7 +474,7 @@ SET Text = 'Global Commandments' WHERE Tag = 'TXT_KEY_BELIEF_CHARITABLE_MISSIONS_SHORT'; UPDATE Language_en_US -SET Text = '[ICON_RELIGION] Religion spreads 15% faster (30% with Printing Press). Receive 350 [ICON_RESEARCH] Science, [ICON_CULTURE] Culture, [ICON_GOLD] Gold, [ICON_PEACE] Faith, and [ICON_GOLDEN_AGE] Golden Age Points when you pass a Proposal in the World Congress or United Nations, scaling with Era.' +SET Text = 'May build Chanceries, Printing Houses, and Wire Services with [ICON_PEACE] Faith. These buildings produce +1 [ICON_GOLD] Gold, [ICON_RESEARCH] Science, and [ICON_CULTURE] Culture each. +20% [ICON_PRODUCTION] Production for [COLOR_YELLOW]Diplomacy Units[ENDCOLOR]. Receive 350 [ICON_RESEARCH] Science, [ICON_CULTURE] Culture, [ICON_GOLD] Gold, [ICON_PEACE] Faith, and [ICON_GOLDEN_AGE] Golden Age Points when you pass a Proposal in the World Congress or United Nations, scaling with Era.' WHERE Tag = 'TXT_KEY_BELIEF_CHARITABLE_MISSIONS'; UPDATE Language_en_US @@ -490,7 +490,7 @@ SET Text = 'Inspired Works' WHERE Tag = 'TXT_KEY_BELIEF_UNITY_OF_PROPHETS_SHORT'; UPDATE Language_en_US -SET Text = 'Landmarks and Great Person Improvements produce +2 [ICON_PEACE] Faith and [ICON_RESEARCH] Science. [ICON_GREAT_WORK] Great Works produce +2 [ICON_CULTURE] Culture. Can purchase Archaeologists with [ICON_PEACE] Faith.' +SET Text = 'Landmarks and Great Person Improvements produce +2 [ICON_PEACE] Faith and +3 [ICON_RESEARCH] Science. [ICON_GREAT_WORK] Great Works produce +3 [ICON_CULTURE] Culture. Can purchase Archaeologists with [ICON_PEACE] Faith.' WHERE Tag = 'TXT_KEY_BELIEF_UNITY_OF_PROPHETS'; -- Sacred Sites diff --git a/(2) Vox Populi/Database Changes/Text/en_US/Beliefs/NewBeliefText.xml b/(2) Vox Populi/Database Changes/Text/en_US/Beliefs/NewBeliefText.xml index dc790af5e5..74fe60a647 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/Beliefs/NewBeliefText.xml +++ b/(2) Vox Populi/Database Changes/Text/en_US/Beliefs/NewBeliefText.xml @@ -14,7 +14,7 @@ Epona, the Great Mare - +15% [ICON_GOLD] Gold and [ICON_RESEARCH] Science during "We Love the King Days". +3 [ICON_GOLD] Gold, [ICON_RESEARCH] Science, and [ICON_GOLDEN_AGE] Golden Age Points in your [ICON_CAPITAL] Capital/Holy City for every unique Luxury Resource owned or imported. +3 [ICON_GOLD] Gold, [ICON_RESEARCH] Science, and [ICON_GOLDEN_AGE] Golden Age Points per active Trade Route to or from the City. +5 [ICON_GOLD] Gold from Ceilidh Hall. + +10% [ICON_GOLD] Gold and [ICON_RESEARCH] Science during "We Love the King Days". +2 [ICON_GOLD] Gold and [ICON_RESEARCH] Science in your [ICON_CAPITAL] Capital/Holy City for every unique Luxury Resource owned or imported. +2 [ICON_GOLD] Gold and [ICON_RESEARCH] Science per active Trade Route to or from the City. +5 [ICON_GOLD] Gold from Ceilidh Hall. Nuada, the Silver-Handed @@ -89,6 +89,12 @@ Cooperation + + May build Workshops, Windmills, and Factories with [ICON_PEACE] Faith. These buildings produce +2 [ICON_PRODUCTION] Production each. +25% [ICON_WORKER] Tile Improvement Rate for [COLOR_YELLOW]Civilian Units[ENDCOLOR]. + + + Work Ethic + +2 [ICON_PEACE] Faith[NEWLINE]+2 [ICON_GOLD] Gold[NEWLINE][NEWLINE]+1 [ICON_GOLD] Gold to Farms, Pastures, and Quarries worked by the City.[NEWLINE][NEWLINE]+10 [ICON_GOLD] Gold when the City naturally expands, scaling with Era.[NEWLINE][NEWLINE]+25% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_GOLD] Poverty. @@ -97,7 +103,7 @@ +4 [ICON_PEACE] Faith[NEWLINE]+2 [ICON_CULTURE] Culture[NEWLINE][NEWLINE]+1 [ICON_PEACE] Faith to all [ICON_GREAT_WORK] Great Works in the City.[NEWLINE][NEWLINE]Start 15 Turns of [ICON_HAPPINESS_1] "We Love the King Day" when built.[NEWLINE][NEWLINE]+40% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_CULTURE] Boredom. - +2 [ICON_PEACE] Faith[NEWLINE]+3 [ICON_FOOD] Food[NEWLINE]+10% [ICON_FOOD] Food[NEWLINE][NEWLINE]+10% [ICON_STRENGTH] City Strength from Buildings.[NEWLINE][NEWLINE]+25% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_FOOD] and [ICON_PRODUCTION] Distress. + +2 [ICON_PEACE] Faith[NEWLINE]+3 [ICON_FOOD] Food[NEWLINE]+10% [ICON_FOOD] Food[NEWLINE][NEWLINE]+3 [ICON_STRENGTH] City Strength.[NEWLINE]+10% [ICON_STRENGTH] City Strength from Buildings.[NEWLINE]+10% [ICON_RANGE_STRENGTH] City Strike Strength.[NEWLINE][NEWLINE]Land Military Units garrisoned in this City recover an additional 5 Health regardless of action.[NEWLINE][NEWLINE]+25% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_FOOD] and [ICON_PRODUCTION] Distress. +3 [ICON_PEACE] Faith[NEWLINE]+2 [ICON_FOOD] Food[NEWLINE][NEWLINE]+5 of all Yields when a [ICON_CITIZEN] Citizen is born in this City, scaling with Era.[NEWLINE][NEWLINE]+15% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City, and -10% Religious Pressure towards this City from Cities following other Religions or active spreads of other Religions.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_GOLD] Poverty. @@ -106,7 +112,7 @@ +3 [ICON_PEACE] Faith[NEWLINE]+2 [ICON_RESEARCH] Science[NEWLINE][NEWLINE]+15% [ICON_CULTURE] Culture during [ICON_GOLDEN_AGE] Golden Age.[NEWLINE][NEWLINE]+25% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_RESEARCH] Illiteracy. - +2 [ICON_PEACE] Faith[NEWLINE]+3 [ICON_GOLD] Gold[NEWLINE]+1 [ICON_WAR] Military Supply[NEWLINE][NEWLINE]Gain [ICON_PEACE] Faith equal to 10% of [ICON_PRODUCTION] Production cost when completing production of a Military Unit.[NEWLINE][NEWLINE]+10% [ICON_RANGE_STRENGTH] City Strike Strength.[NEWLINE][NEWLINE]All existing and future eligible Units trained in this City gain the [COLOR_POSITIVE_TEXT]{TXT_KEY_PROMOTION_MORALE}[ENDCOLOR] Promotion.[NEWLINE][NEWLINE]+25% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_FOOD] and [ICON_PRODUCTION] Distress. + +2 [ICON_PEACE] Faith[NEWLINE]+3 [ICON_GOLD] Gold[NEWLINE][NEWLINE]Gain [ICON_GOLD] Gold equal to 15% of [ICON_PRODUCTION] Production cost when completing production of a Military Unit.[NEWLINE][NEWLINE]+15 XP for Military Units trained in the City.[NEWLINE][NEWLINE]All existing and future eligible Units trained in this City gain the [COLOR_POSITIVE_TEXT]{TXT_KEY_PROMOTION_MORALE}[ENDCOLOR] Promotion.[NEWLINE][NEWLINE]+25% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_FOOD] and [ICON_PRODUCTION] Distress. +2 [ICON_PEACE] Faith[NEWLINE][NEWLINE]+1 to [COLOR_POSITIVE_TEXT]All Yields[ENDCOLOR] for each [ICON_RELIGION] Religion with at least one Follower in the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_CULTURE] Boredom and -2 [ICON_HAPPINESS_3] Unhappiness from [ICON_RELIGION] Religious Unrest. @@ -118,7 +124,7 @@ +2 [ICON_PEACE] Faith[NEWLINE]+3 [ICON_PRODUCTION] Production[NEWLINE][NEWLINE]+10% [ICON_RESEARCH] Science during [ICON_HAPPINESS_1] "We Love the King Day".[NEWLINE][NEWLINE]-15% Religious Pressure towards this City from Cities following other Religions or active spreads of other Religions.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_FOOD] and [ICON_PRODUCTION] Distress. - +2 [ICON_PEACE] Faith[NEWLINE][NEWLINE]+2 [ICON_PEACE] Faith when an owned Unit defeats a Military Unit, and an additional +8 [ICON_PEACE] Faith if the Unit was created by this City.[NEWLINE][NEWLINE]+10 XP for Military Units trained in the City.[NEWLINE][NEWLINE]+25% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_FOOD] and [ICON_PRODUCTION] Distress. + +2 [ICON_PEACE] Faith[NEWLINE]+1 [ICON_WAR] Military Supply[NEWLINE][NEWLINE]+2 [ICON_PEACE] Faith when an owned Unit defeats a Military Unit, and an additional +8 [ICON_PEACE] Faith if the Unit was created by this City.[NEWLINE][NEWLINE]+25% Religious Pressure towards other Cities if this Religion is the Majority Religion of the City.[NEWLINE][NEWLINE]-1 [ICON_HAPPINESS_3] Unhappiness from [ICON_FOOD] and [ICON_PRODUCTION] Distress. diff --git a/(2) Vox Populi/Database Changes/Text/en_US/City/BuildingTextChanges.sql b/(2) Vox Populi/Database Changes/Text/en_US/City/BuildingTextChanges.sql index 2b63d0afbf..5d1de6732b 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/City/BuildingTextChanges.sql +++ b/(2) Vox Populi/Database Changes/Text/en_US/City/BuildingTextChanges.sql @@ -5,7 +5,7 @@ WHERE Tag = 'TXT_KEY_BUILDING_WATERMILL_STRATEGY'; -- Aqueduct UPDATE Language_en_US -SET Text = 'The Aqueduct decreases the amount of [ICON_FOOD] Food a City needs to increase in size by 15%. Build Aqueducts in cities that you want to grow large over time. The City needs to have a Granary in order to construct the Aqueduct.' +SET Text = 'The Aqueduct decreases the amount of [ICON_FOOD] Food a City needs to for [ICON_CITIZEN] Citizen Birth by 15%, and provides [ICON_PRODUCTION] Production when that happens. Build Aqueducts in cities that can grow large over time. When built early while citizens are born the fastest, the production bonus will be most impactful. The City needs to have a Granary in order to construct the Aqueduct.' WHERE Tag = 'TXT_KEY_BUILDING_AQUEDUCT_STRATEGY'; -- Medical Lab @@ -185,7 +185,7 @@ SET Text = 'Arena' WHERE Tag = 'TXT_KEY_BUILDING_COLOSSEUM'; UPDATE Language_en_US -SET Text = 'The Arena provides additional Culture, and generates Tourism from City''s population. Reduces Boredom in a City, and improves the output of nearby [ICON_RES_PERFUME] Perfume and [ICON_RES_OLIVE] Olives. Build these to combat Unhappiness from Boredom, to increase your Culture, and to improve the production of your military buildings.' +SET Text = 'The Arena provides additional Culture, reduces Boredom in a City, and improves the output of nearby [ICON_RES_PERFUME] Perfume and [ICON_RES_OLIVE] Olives. Build these to combat Unhappiness from Boredom, to increase your Culture, and to improve the production of your military buildings.' WHERE Tag = 'TXT_KEY_BUILDING_COLOSSEUM_STRATEGY'; UPDATE Language_en_US diff --git a/(2) Vox Populi/Database Changes/Text/en_US/City/NewBuildingText.xml b/(2) Vox Populi/Database Changes/Text/en_US/City/NewBuildingText.xml index f54b8814ab..7c11ee4d16 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/City/NewBuildingText.xml +++ b/(2) Vox Populi/Database Changes/Text/en_US/City/NewBuildingText.xml @@ -514,7 +514,7 @@ Menin Gate - A defensive wonder. Strengthens the yields from Forts, Citadels, and Landmarks. The Menin Gate also gives a small bonus for each unit that falls in battle. May only be built collaboratively through the World Congress. + A defensive wonder. Strengthens the yields from Forts, Citadels, and Landmarks. May only be built collaboratively through the World Congress. @@ -912,7 +912,7 @@ Order - Can only be built in cities following a religion with the Orders belief. Construct this building by purchasing it with [ICON_PEACE] Faith. Build the Order to gain Faith, Gold and the Morale promotion for all Military units trained in the city. + Can only be built in cities following a religion with the Orders belief. Construct this building by purchasing it with [ICON_PEACE] Faith. Build the Order to gain Gold, Experience, and the Morale promotion for Military units trained in the city. @@ -925,7 +925,7 @@ Teocalli - Can only be built in cities following a religion with the Teocallis belief. Construct this building by purchasing it with [ICON_PEACE] Faith. The Teocalli offers bonuses towards military training and faith generation. + Can only be built in cities following a religion with the Teocallis belief. Construct this building by purchasing it with [ICON_PEACE] Faith. The Teocalli offers bonuses towards Military Supply and Faith generation. diff --git a/(2) Vox Populi/Database Changes/Text/en_US/City/NewProcessText.xml b/(2) Vox Populi/Database Changes/Text/en_US/City/NewProcessText.xml index 374af67e29..8a07f435e7 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/City/NewProcessText.xml +++ b/(2) Vox Populi/Database Changes/Text/en_US/City/NewProcessText.xml @@ -15,7 +15,7 @@ Defense - [COLOR_POSITIVE_TEXT]Defense[ENDCOLOR] boosts City [ICON_STRENGTH] Defense and increases Hit Point recovery by 10% of the City's [ICON_PRODUCTION] Production. + [COLOR_POSITIVE_TEXT]Defense[ENDCOLOR] boosts City [ICON_STRENGTH] Defense and increases Hit Point recovery by 10% of the City's [ICON_PRODUCTION] Production, increasing by 1% for every turn the defense process is running, up to a maximum value of 15%. Investing production in defense is a last-ditch method to stave off your city's conquest by a foreign power. diff --git a/(2) Vox Populi/Database Changes/Text/en_US/Civilizations/CivilizationTextChanges.sql b/(2) Vox Populi/Database Changes/Text/en_US/Civilizations/CivilizationTextChanges.sql index dae2442901..d5079d07b6 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/Civilizations/CivilizationTextChanges.sql +++ b/(2) Vox Populi/Database Changes/Text/en_US/Civilizations/CivilizationTextChanges.sql @@ -423,7 +423,7 @@ SET Text = 'Naga-Malla' WHERE Tag = 'TXT_KEY_UNIT_INDIAN_WARELEPHANT'; UPDATE Language_en_US -SET Text = 'The {TXT_KEY_UNIT_INDIAN_WARELEPHANT} is the Indian unique unit, replacing the {TXT_KEY_UNIT_CUIRASSIER}. As an elephant unit, it strikes fear into nearby enemy units and weakens them, has vastly superior [ICON_STRENGTH] Combat Strength and [ICON_RANGE_STRENGTH] Ranged Combat Strength, but moves slower. It does not require [ICON_RES_HORSE] {TXT_KEY_RESOURCE_HORSES} to be trained, and is available with the discovery of [COLOR_CYAN]{TXT_KEY_TECH_GUNPOWDER_TITLE}[ENDCOLOR], earlier than the {TXT_KEY_UNIT_CUIRASSIER}. Does not start with the {TXT_KEY_PROMOTION_SKIRMISHER_DOCTRINE} promotion.' +SET Text = 'The {TXT_KEY_UNIT_INDIAN_WARELEPHANT} is the Indian unique unit, replacing the {TXT_KEY_UNIT_CUIRASSIER}. As an elephant unit, it strikes fear into nearby enemy units and weakens them, has vastly superior [ICON_RANGE_STRENGTH] Ranged Combat Strength, but moves slower. It does not require [ICON_RES_HORSE] {TXT_KEY_RESOURCE_HORSES} to be trained, and is available with the discovery of [COLOR_CYAN]{TXT_KEY_TECH_GUNPOWDER_TITLE}[ENDCOLOR], earlier than the {TXT_KEY_UNIT_CUIRASSIER}. Does not start with the {TXT_KEY_PROMOTION_SKIRMISHER_DOCTRINE} promotion, but starts with [COLOR_POSITIVE_TEXT]{TXT_KEY_PROMOTION_STALWART}[ENDCOLOR] and [COLOR_POSITIVE_TEXT]{TXT_KEY_PROMOTION_RETALIATION}[ENDCOLOR].' WHERE Tag = 'TXT_KEY_UNIT_INDIAN_WARELEPHANT_STRATEGY'; UPDATE Language_en_US @@ -450,7 +450,7 @@ SET Text = 'Sumpah Palapa' WHERE Tag = 'TXT_KEY_TRAIT_SPICE_SHORT'; UPDATE Language_en_US -SET Text = 'When you gain a City, one of 3 unique Luxuries ([ICON_RES_CLOVES]/[ICON_RES_PEPPER]/[ICON_RES_NUTMEG]) will appear nearby. +5% to unique Yield and [ICON_GOLDEN_AGE] Golden Age duration modifiers from [ICON_MONOPOLY] Global Monopolies; +2 to Yields and [ICON_HAPPINESS_1] Happiness from [ICON_MONOPOLY] Global Monopolies.' +SET Text = 'When you gain a City, one of 3 unique Luxuries ([ICON_RES_CLOVES]/[ICON_RES_PEPPER]/[ICON_RES_NUTMEG]) will appear nearby. Each [ICON_MONOPOLY] Global Monopoly Bonus is increased by an additional 5% if it''s percentage-based, or by +2 otherwise.' WHERE Tag = 'TXT_KEY_TRAIT_SPICE'; UPDATE Language_en_US @@ -484,7 +484,7 @@ SET Text = 'Shogunate' WHERE Tag = 'TXT_KEY_TRAIT_FIGHT_WELL_DAMAGED_SHORT'; UPDATE Language_en_US -SET Text = '+1 [ICON_CULTURE] Culture and [ICON_PEACE] Faith from Defense Buildings. When a [ICON_GREAT_ADMIRAL] Great Admiral or [ICON_GREAT_GENERAL] Great General is born, gain 50% progress toward a [ICON_GREAT_WORK] Great Artist, Writer, and Musician in your [ICON_CAPITAL] Capital.' +SET Text = 'Non-Recon Melee Land Units start with the [COLOR_POSITIVE_TEXT]{TXT_KEY_PROMOTION_BUSHIDO}[ENDCOLOR] Promotion. When a [ICON_GREAT_ADMIRAL] Great Admiral or [ICON_GREAT_GENERAL] Great General is born, gain 50% progress toward a [ICON_GREAT_WORK] Great Writer, Artist, and Musician in your [ICON_CAPITAL] Capital.' WHERE Tag = 'TXT_KEY_TRAIT_FIGHT_WELL_DAMAGED'; UPDATE Language_en_US @@ -540,7 +540,7 @@ WHERE Tag = 'TXT_KEY_CIV5_MONGOLIA_KHAN_STRATEGY'; -- Morocco -------------------- UPDATE Language_en_US -SET Text = '+1 to All Yields in [ICON_CAPITAL] Capital per unique [ICON_INTERNATIONAL_TRADE] Trade Route partner, scaling with Era. Distance does not reduce [ICON_INTERNATIONAL_TRADE] Trade Route Yields to or from Moroccan Cities. Can plunder [ICON_INTERNATIONAL_TRADE] Trade Routes between foreign Cities without declaring war.' +SET Text = '+2 to All Yields in [ICON_CAPITAL] Capital per unique [ICON_INTERNATIONAL_TRADE] Trade Route partner, scaling with Era. Distance does not reduce [ICON_INTERNATIONAL_TRADE] Trade Route Yields to or from Moroccan Cities.' WHERE Tag = 'TXT_KEY_TRAIT_GATEWAY_AFRICA'; UPDATE Language_en_US @@ -799,7 +799,7 @@ SET Text = 'Lion of the North' WHERE Tag = 'TXT_KEY_TRAIT_DIPLOMACY_GREAT_PEOPLE_SHORT'; UPDATE Language_en_US -SET Text = 'Melee Land Units start with the [COLOR_POSITIVE_TEXT]{TXT_KEY_PROMOTION_ATTACK_BONUS_SWEDEN}[ENDCOLOR] Promotion, and Siege Units start with the [COLOR_POSITIVE_TEXT]{TXT_KEY_PROMOTION_MOBILITY}[ENDCOLOR] Promotion. All Military Units heal 10 HP and earn 2 XP when a [ICON_GREAT_PEOPLE] Great Person is born. +15% [ICON_STRENGTH] Combat Strength from [ICON_GREAT_GENERAL]/[ICON_GREAT_ADMIRAL] [COLOR_POSITIVE_TEXT]Leadership[ENDCOLOR].' +SET Text = 'Melee Land Units start with the [COLOR_POSITIVE_TEXT]{TXT_KEY_PROMOTION_ATTACK_BONUS_SWEDEN}[ENDCOLOR] Promotion, and Siege Units start with the [COLOR_POSITIVE_TEXT]{TXT_KEY_PROMOTION_MOBILITY}[ENDCOLOR] Promotion. All Military Units heal 10 HP and earn 3 XP when a [ICON_GREAT_PEOPLE] Great Person is born. +15% [ICON_STRENGTH] Combat Strength from [ICON_GREAT_GENERAL]/[ICON_GREAT_ADMIRAL] [COLOR_POSITIVE_TEXT]Leadership[ENDCOLOR].' WHERE Tag = 'TXT_KEY_TRAIT_DIPLOMACY_GREAT_PEOPLE'; UPDATE Language_en_US diff --git a/(2) Vox Populi/Database Changes/Text/en_US/Civilizations/NewCivilizationText.xml b/(2) Vox Populi/Database Changes/Text/en_US/Civilizations/NewCivilizationText.xml index bfae23d516..d1b6842396 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/Civilizations/NewCivilizationText.xml +++ b/(2) Vox Populi/Database Changes/Text/en_US/Civilizations/NewCivilizationText.xml @@ -880,17 +880,17 @@ - - Dojo + + Tatara - - A dojo is a Japanese term which literally means "place of the way". Initially, dojos were adjunct to temples. The term can refer to a formal training place for any of the Japanese do arts but typically it is considered the formal gathering place for students of any Japanese martial arts style such as karate, judo, or samurai, to conduct training, examinations and other related encounters. The concept of a dojo only referring to a training place specifically for Asian martial arts is a Western concept; in Japan, any physical training facility, including professional wrestling schools, may be called dojo because of its close martial arts roots. + + Tatara was the technology for producing iron and steel used in Japan since ancient times. Sometimes called a bloomery, sometimes forge, tatara are most commonly known as furnaces.[NEWLINE][NEWLINE]The word tatara and the ancient technology of iron production used in East Asia seems to have originated in central Asia. The first iron used in Japan was imported, most probably from China, though later from what is now Korea, and took the form of ready-made iron items or as ingots. Metal-working spread across Japan, most probably from northern Kyushu, but the quantity of iron was not great.[NEWLINE][NEWLINE]Things changed in probably the 5th or 6th century when indigenous sources of iron were discovered. Archaeological and mythological sources suggest this was near the Sea of Japan Coast in western Japan centered on what is now Shimane Prefecture.[NEWLINE][NEWLINE]What was discovered was iron sand, though very small amounts of iron ore were also found. The tatara, probably introduced from Siberia via Korea, was used to smelt the iron sand to produce iron. Tatara appeared across Japan, but by the Medieval Period of Japanese history (13th to 16th centuries) the Chugoku region was the most important iron-producing region, and especially the Okuizumo area as it had the highest grade of iron sand.[NEWLINE][NEWLINE]Enough iron and steel was being produced that Japan was exporting swords to China by the thousand. The technology of iron production improved and became more efficient so that during the Edo Period the Okuizumo area can be said to have mass produced iron and it was exported all over Japan. - + - - Unique Japanese replacement for the {TXT_KEY_BUILDING_ARMORY}. In addition to the {TXT_KEY_BUILDING_ARMORY}'s bonuses, {TXT_KEY_BUILDING_DOJO} provides more training experience and more military supply; grants the {TXT_KEY_PROMOTION_BUSHIDO} promotion to all melee, gunpowder, mounted and armored units, which offers one of eight possible promotions to these unit types after their first combat and increases the unit's strength as it takes damage; and produces [ICON_RESEARCH] Science and [ICON_CULTURE] Culture when units gain experience in combat. + + Unique Japanese replacement for the {TXT_KEY_BUILDING_FORGE}. It provides an extra [ICON_RES_IRON] Iron resource in the map, further enhances yields from Iron tiles, and has an extra [ICON_ENGINEER] Engineer slot that can be used to generate Great Engineers for a Manufactory in that tile, should Iron prove scarce in your surroundings. It can also generate [ICON_RESEARCH] Science and [ICON_CULTURE] Culture from Combat XP earned by Units trained in the City. Combine an early expansion prioritizing the {TXT_KEY_BUILDING_TATARA} with and an early military force to secure key resources for your armies. @@ -1039,7 +1039,7 @@ Adopt the Yassa - [ICON_BULLET]+20% [ICON_PRODUCTION] Production for [COLOR_YELLOW]{TXT_KEY_UNITCOMBAT_ARCHER} Units[ENDCOLOR] in all Cities[NEWLINE][ICON_BULLET]+20% [ICON_PRODUCTION] Production for [COLOR_YELLOW]{TXT_KEY_UNITCOMBAT_SIEGE} Units[ENDCOLOR] in all Cities[NEWLINE][ICON_BULLET]+20% [ICON_PRODUCTION] Production for [COLOR_YELLOW]{TXT_KEY_UNITCOMBAT_MOUNTED} Units[ENDCOLOR] in all Cities[NEWLINE][ICON_BULLET]+20% [ICON_PRODUCTION] Production for [COLOR_YELLOW]{TXT_KEY_UNITCOMBAT_ARMOR} Units[ENDCOLOR] in all Cities[NEWLINE][ICON_BULLET]-40% [ICON_HAPPINESS_3] Empire Size Modifier from number of Cities in all Cities[NEWLINE][ICON_BULLET]When conquering a City: +10 [ICON_GOLD] +10 [ICON_RESEARCH] +10 [ICON_CULTURE] in all Cities, scaling with Era[NEWLINE]----------------[NEWLINE]Prerequisite Technology: [COLOR_CYAN]{TXT_KEY_TECH_PHILOSOPHY_TITLE}[ENDCOLOR][NEWLINE]Max 1 per Player + [ICON_BULLET]+20% [ICON_PRODUCTION] Production towards [COLOR_YELLOW]{TXT_KEY_UNITCOMBAT_ARCHER} Units[ENDCOLOR] in all Cities[NEWLINE][ICON_BULLET]+20% [ICON_PRODUCTION] Production towards [COLOR_YELLOW]{TXT_KEY_UNITCOMBAT_SIEGE} Units[ENDCOLOR] in all Cities[NEWLINE][ICON_BULLET]+20% [ICON_PRODUCTION] Production towards [COLOR_YELLOW]{TXT_KEY_UNITCOMBAT_MOUNTED} Units[ENDCOLOR] in all Cities[NEWLINE][ICON_BULLET]+20% [ICON_PRODUCTION] Production towards [COLOR_YELLOW]{TXT_KEY_UNITCOMBAT_ARMOR} Units[ENDCOLOR] in all Cities[NEWLINE][ICON_BULLET]-40% [ICON_HAPPINESS_3] Empire Size Modifier from number of Cities in all Cities[NEWLINE][ICON_BULLET]When conquering a City: +10 [ICON_GOLD] +10 [ICON_RESEARCH] +10 [ICON_CULTURE] in all Cities, scaling with Era[NEWLINE]----------------[NEWLINE]Prerequisite Technology: [COLOR_CYAN]{TXT_KEY_TECH_PHILOSOPHY_TITLE}[ENDCOLOR][NEWLINE]Max 1 per Player Yassa was a secret written code of law created by Genghis Khan. The word Yassa translates into "order" or "decree". It was the de facto law of the Mongol Empire even though the "law" was never actually made public. The Yassa seems to have originated from decrees issued in wartime, and was later codified and expanded to include cultural and life-style conventions. By keeping the Yassa secret, the decrees could be modified and used selectively. It is believed that the Yassa was supervised by Genghis Khan himself with the help of the high judge of the Mongol Empire: His stepbrother, Shihihutag. Genghis Khan appointed his second son Chagatai (a.k.a. Chagatai Khan) to oversee the execution of the laws. diff --git a/(2) Vox Populi/Database Changes/Text/en_US/Concepts/ConceptTextChanges.sql b/(2) Vox Populi/Database Changes/Text/en_US/Concepts/ConceptTextChanges.sql index ed378a03bc..1f33a350b6 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/Concepts/ConceptTextChanges.sql +++ b/(2) Vox Populi/Database Changes/Text/en_US/Concepts/ConceptTextChanges.sql @@ -355,6 +355,11 @@ UPDATE Language_en_US SET Text = 'The three Ideology trees, Freedom, Order, and Autocracy, have greatly expanded in Brave New World. All civilizations in the game, [COLOR_YELLOW]on acquiring 18 Policies and having advanced at least to the Industrial Era, or upon reaching the Atomic Era,[ENDCOLOR] will be required to choose an Ideology for their civilization. Each Ideology tree contains 3 tiers of "tenets" that you use to customize your Ideology, with the third and final tier holding the most powerful benefits. As with regular Social Policies, players use Culture to purchase additional tenets as they move through the game.[NEWLINE][NEWLINE]Civilizations that share a common Ideology will receive benefits with their diplomatic relationships. Civilizations that have conflicting Ideologies have multiple side-effects. For example, a negative effect on their diplomatic relationship and happiness penalties take effect if an opposing Ideology has a stronger Cultural influence on your people. If you let your people become too unhappy, there is a chance that your cities may declare that they are joining your opponents empire. As a last resort, you, or other players, can resort to a "Revolution" to switch Ideologies to one that is preferable to your people.[NEWLINE][NEWLINE]For more information on Ideological Tenets, click on the "Social Policies" tab along the top of the Civilopedia, and scroll down to the Order, Freedom, and Autocracy sections.' WHERE Tag = 'TXT_KEY_SOCIALPOLICY_IDEOLOGY_HEADING3_BODY'; +-- Open Borders Agreement +UPDATE Language_en_US +SET Text = 'Once you or another civ have discovered Philosophy, you can enter into an Open Borders agreement with any civ where you have established an Embassy. (City-States can''t make Open Borders agreements.) While an Open Borders agreement is in effect, the other civ''s units can enter your territory without automatically triggering war. If the agreement is mutual, either civ''s units can enter the other''s territory freely; however it doesn''t have to be mutual: one civ can grant another Open Borders without automatically receiving it in return.[NEWLINE][NEWLINE]An Open Borders agreement lasts for 50 turns. When 50 turns have passed, the agreement must be renegotiated or it lapses.' +WHERE Tag = 'TXT_KEY_DIPLOMACY_OPENBORDERS_HEADING3_BODY'; + -- Fortification UPDATE Language_en_US SET Text = 'Many units have the ability to "fortify." This means that the unit "digs in" and creates defensive works in its current location. This gives the unit certain defensive bonuses, making it much tougher to kill. However, fortifications are strictly defensive: if the unit moves or attacks, the fortifications are destroyed. While fortified, a unit will not activate. It will remain inactive until you manually activate it by clicking on the unit.[NEWLINE][NEWLINE]An improvement may also contain [COLOR_POSITIVE_TEXT]Fortifications[ENDCOLOR], which means units stationed on this improvement, like those garrisoning a city, will not move out of the tile after a victory in melee combat.' diff --git a/(2) Vox Populi/Database Changes/Text/en_US/Concepts/NewConceptText.xml b/(2) Vox Populi/Database Changes/Text/en_US/Concepts/NewConceptText.xml index ffc2368679..0497ff42e8 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/Concepts/NewConceptText.xml +++ b/(2) Vox Populi/Database Changes/Text/en_US/Concepts/NewConceptText.xml @@ -266,7 +266,7 @@ [COLOR_GREEN]Tech Trading[ENDCOLOR] - Once you have acquired the Replaceable Parts tech, you may sell your technological discoveries with another civ, as long as you have an Embassy in their capital. + Once you have acquired the Scientific Theory tech, you may sell your technological discoveries with another civ, as long as you have an Embassy in their capital. {TXT_KEY_CONCEPT_DIPLOMACY_TECH_TRADING_TOPIC} diff --git a/(2) Vox Populi/Database Changes/Text/en_US/Corporations/NewCorporationText.xml b/(2) Vox Populi/Database Changes/Text/en_US/Corporations/NewCorporationText.xml index fd59bb7334..04dd91369e 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/Corporations/NewCorporationText.xml +++ b/(2) Vox Populi/Database Changes/Text/en_US/Corporations/NewCorporationText.xml @@ -53,7 +53,7 @@ +3 [ICON_CULTURE_LOCAL] Border Growth Points for every Global [ICON_FRANCHISE] Franchise. +15% [ICON_PRODUCTION] Production towards Military Units. When you train a Military Unit in this City, gain [ICON_RESEARCH] Science equal to 25% of the Unit's [ICON_PRODUCTION] Production cost. - [ICON_INTERNATIONAL_TRADE] Trade Routes from Cities with a Hexxon Refinery Office to Cities with a Hexxon Refinery Franchise produce +50% additional [ICON_SCIENCE] Science. + [ICON_INTERNATIONAL_TRADE] Trade Routes from Cities with a Hexxon Refinery Office to Cities with a Hexxon Refinery Franchise produce +50% additional [ICON_RESEARCH] Science. Current Office Bonus: {1_String} diff --git a/(2) Vox Populi/Database Changes/Text/en_US/Policies/PolicyTextChanges.sql b/(2) Vox Populi/Database Changes/Text/en_US/Policies/PolicyTextChanges.sql index 2fed481108..6a4a59f5fd 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/Policies/PolicyTextChanges.sql +++ b/(2) Vox Populi/Database Changes/Text/en_US/Policies/PolicyTextChanges.sql @@ -79,7 +79,7 @@ SET Text = 'Progress' WHERE Tag = 'TXT_KEY_POLICY_BRANCH_LIBERTY'; UPDATE Language_en_US -SET Text = '[COLOR_POSITIVE_TEXT]Progress[ENDCOLOR] plans to found a sprawl of self-sufficient Cities in the [COLOR_CYAN]early game[ENDCOLOR] with free starting yields and faster [ICON_WORKER] rate of tile improvement. Constructing Buildings and birthing [ICON_CITIZEN] Citizens supports the economy [COLOR_CYAN]throughout the game[ENDCOLOR].[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Adopting Progress grants:[ENDCOLOR][NEWLINE][ICON_BULLET]Receive an instant boost of 75 [ICON_RESEARCH] Science.[NEWLINE][ICON_BULLET]Receive 10 [ICON_RESEARCH] Science when a [ICON_CITIZEN] Citizen is born, scaling with Era.[NEWLINE][ICON_BULLET]Receive 15 [ICON_CULTURE] Culture when a Technology is researched, scaling with Era, and for every Technology already unlocked (not scaling with Era).[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Each Progress policy unlocked grants:[ENDCOLOR][NEWLINE][ICON_BULLET]Gain an additional 15 [ICON_CULTURE] Culture when a Technology is researched, scaling with Era.[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Adopting all policies in Progress grants:[ENDCOLOR][NEWLINE][ICON_BULLET]Unlocks building the [COLOR_POSITIVE_TEXT]Forbidden Palace[ENDCOLOR].[NEWLINE][ICON_BULLET]Receive 25 [ICON_GOLD] Gold when a [ICON_CITIZEN] Citizen is born in any city, scaling with Era.[NEWLINE][ICON_BULLET]Allows for the purchase of [ICON_GREAT_WRITER] Great Writers with [ICON_PEACE] Faith starting in the Industrial Era.' +SET Text = '[COLOR_POSITIVE_TEXT]Progress[ENDCOLOR] plans to found a sprawl of self-sufficient Cities in the [COLOR_CYAN]early game[ENDCOLOR] with free starting yields and faster [ICON_WORKER] rate of tile improvement. Constructing Buildings and birthing [ICON_CITIZEN] Citizens supports the economy [COLOR_CYAN]throughout the game[ENDCOLOR].[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Adopting Progress grants:[ENDCOLOR][NEWLINE][ICON_BULLET]Receive an instant boost of 75 [ICON_RESEARCH] Science.[NEWLINE][ICON_BULLET]Receive 8 [ICON_RESEARCH] Science when a [ICON_CITIZEN] Citizen is born, scaling with Era.[NEWLINE][ICON_BULLET]Receive 15 [ICON_CULTURE] Culture when a Technology is researched, scaling with Era, and for every Technology already unlocked (not scaling with Era).[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Each Progress policy unlocked grants:[ENDCOLOR][NEWLINE][ICON_BULLET]Gain an additional 15 [ICON_CULTURE] Culture when a Technology is researched, scaling with Era.[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Adopting all policies in Progress grants:[ENDCOLOR][NEWLINE][ICON_BULLET]Unlocks building the [COLOR_POSITIVE_TEXT]Forbidden Palace[ENDCOLOR].[NEWLINE][ICON_BULLET]Receive 25 [ICON_GOLD] Gold when a [ICON_CITIZEN] Citizen is born in any city, scaling with Era.[NEWLINE][ICON_BULLET]Allows for the purchase of [ICON_GREAT_WRITER] Great Writers with [ICON_PEACE] Faith starting in the Industrial Era.' WHERE Tag = 'TXT_KEY_POLICY_BRANCH_LIBERTY_HELP'; UPDATE Language_en_US @@ -103,7 +103,7 @@ SET Text = 'Liberty' WHERE Tag = 'TXT_KEY_POLICY_CITIZENSHIP'; UPDATE Language_en_US -SET Text = '[COLOR_POSITIVE_TEXT]Liberty[ENDCOLOR][NEWLINE][ICON_BULLET]+2 [ICON_GOLD] Gold in every City.[NEWLINE][ICON_BULLET]+25% Tile improvement rate.[NEWLINE][ICON_BULLET]A [COLOR_POSITIVE_TEXT]Worker[ENDCOLOR] appears near the [ICON_CAPITAL] Capital.' +SET Text = '[COLOR_POSITIVE_TEXT]Liberty[ENDCOLOR][NEWLINE][ICON_BULLET]+2 [ICON_GOLD] Gold in every City.[NEWLINE][ICON_BULLET]+25% Tile Improvement Rate.[NEWLINE][ICON_BULLET]A [COLOR_POSITIVE_TEXT]Worker[ENDCOLOR] appears near the [ICON_CAPITAL] Capital.' WHERE Tag = 'TXT_KEY_POLICY_CITIZENSHIP_HELP'; UPDATE Language_en_US @@ -350,7 +350,7 @@ SET Text = 'Exchange Markets' WHERE Tag = 'TXT_KEY_POLICY_CULTURAL_DIPLOMACY'; UPDATE Language_en_US -SET Text = '[COLOR_POSITIVE_TEXT]Exchange Markets[ENDCOLOR][NEWLINE][ICON_BULLET]+1 [ICON_INTERNATIONAL_TRADE] Trade Route.[NEWLINE][ICON_BULLET]+15% [ICON_TOURISM] Tourism modifier for [COLOR_POSITIVE_TEXT]Trade Routes[ENDCOLOR].[NEWLINE][ICON_BULLET]+1 of every Strategic Resource for every three City-State Alliances you maintain.[NEWLINE][ICON_BULLET]Resources from City-States count towards Global Monopolies.' +SET Text = '[COLOR_POSITIVE_TEXT]Exchange Markets[ENDCOLOR][NEWLINE][ICON_BULLET]+1 [ICON_INTERNATIONAL_TRADE] Trade Route.[NEWLINE][ICON_BULLET]+15% [ICON_TOURISM] Tourism modifier for [COLOR_POSITIVE_TEXT]Trade Routes[ENDCOLOR].[NEWLINE][ICON_BULLET]+1 of every Strategic Resource for every three [ICON_CITY_STATE] City-State Alliances you maintain.[NEWLINE][ICON_BULLET]Resources from [ICON_CITY_STATE] City-States count towards [ICON_MONOPOLY] Global Monopolies.' WHERE Tag = 'TXT_KEY_POLICY_CULTURAL_DIPLOMACY_HELP'; UPDATE Language_en_US @@ -362,7 +362,7 @@ SET Text = 'Trade Confederacy' WHERE Tag = 'TXT_KEY_POLICY_MERCHANT_CONFEDERACY'; UPDATE Language_en_US -SET Text = '[COLOR_POSITIVE_TEXT]Trade Confederacy[ENDCOLOR][NEWLINE][ICON_BULLET]+1 [ICON_HAPPINESS_1] Happiness for every active [ICON_INTERNATIONAL_TRADE] Trade Route.[NEWLINE][ICON_BULLET]+25% Yields for International [ICON_INTERNATIONAL_TRADE] Trade Routes.[NEWLINE][ICON_BULLET][ICON_INTERNATIONAL_TRADE] Trade Routes to City-States generate +1 [ICON_INFLUENCE] Influence per turn (with the target City-State) per each owned City-State Trade Route (up to +5).' +SET Text = '[COLOR_POSITIVE_TEXT]Trade Confederacy[ENDCOLOR][NEWLINE][ICON_BULLET]+1 [ICON_HAPPINESS_1] Happiness for every active [ICON_INTERNATIONAL_TRADE] Trade Route.[NEWLINE][ICON_BULLET]+25% Yields for International [ICON_INTERNATIONAL_TRADE] Trade Routes.[NEWLINE][ICON_BULLET][ICON_INTERNATIONAL_TRADE] Trade Routes to City-States generate +1 [ICON_INFLUENCE] Influence per turn (with the target [ICON_CITY_STATE] City-State) per each owned City-State Trade Route (up to +5).' WHERE Tag = 'TXT_KEY_POLICY_MERCHANT_CONFEDERACY_HELP'; UPDATE Language_en_US @@ -484,7 +484,7 @@ SET Text = 'Subsidies' WHERE Tag = 'TXT_KEY_POLICY_CARAVANS'; UPDATE Language_en_US -SET Text = '[COLOR_POSITIVE_TEXT]Subsidies[ENDCOLOR][NEWLINE][ICON_BULLET]-50% [ICON_GOLD] Route Maintenance.[NEWLINE][ICON_BULLET]Cities earn +15 [ICON_RESEARCH] Science when they construct Buildings, scaling with Era.[NEWLINE][ICON_BULLET]+1 [ICON_HAPPINESS_1] Happiness from {TXT_KEY_BUILDING_WINDMILL}.' +SET Text = '[COLOR_POSITIVE_TEXT]Subsidies[ENDCOLOR][NEWLINE][ICON_BULLET]Gain 4 Free Coal.[NEWLINE][ICON_BULLET]Cities earn +15 [ICON_RESEARCH] Science when they construct Buildings, scaling with Era.[NEWLINE][ICON_BULLET]+1 [ICON_HAPPINESS_1] Happiness from {TXT_KEY_BUILDING_WINDMILL}.' WHERE Tag = 'TXT_KEY_POLICY_CARAVANS_HELP'; UPDATE Language_en_US @@ -525,7 +525,7 @@ SET Text = 'Imperialism' WHERE Tag = 'TXT_KEY_POLICY_BRANCH_EXPLORATION'; UPDATE Language_en_US -SET Text = '[COLOR_POSITIVE_TEXT]Imperialism[ENDCOLOR] provides additional yields from common Improvements (including Forts), Military Buildings, and Water tiles. It also provides large bonuses to [ICON_PUPPET] Puppet Cities. Militarily, it provides [ICON_PRODUCTION][ICON_GOLD] discounts for obtaining the newest Units, and enhances [ICON_VISION] Sight, [ICON_MOVES] Movement, and [ICON_STRENGTH] Combat Strength, particularly of [ICON_GREAT_ADMIRAL] Admirals and their navies.[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Adopting Imperialism grants:[ENDCOLOR][NEWLINE][ICON_BULLET]+1 [ICON_MOVES] Movement for Naval Units, Embarked Units, and [ICON_GREAT_GENERAL] Great Generals, as well as +1 Sight for Melee, Recon, Naval Melee and Gunpowder Units.[NEWLINE][ICON_BULLET]+10% [ICON_PRODUCTION] Production toward Military Units, -10% [ICON_GOLD] Gold required for upgrades.[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Each Imperialism policy unlocked grants:[ENDCOLOR][NEWLINE][ICON_BULLET]+5% [ICON_PRODUCTION] Production for Military Units, -5% [ICON_GOLD] Gold required for upgrades.[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Adopting all policies in Imperialism grants:[ENDCOLOR][NEWLINE][ICON_BULLET]Unlocks building the [COLOR_POSITIVE_TEXT]Pentagon[ENDCOLOR].[NEWLINE][ICON_BULLET]All Naval and Air units gain the [COLOR_POSITIVE_TEXT]Banzai![ENDCOLOR] Promotion (become stronger as they take damage).[NEWLINE][ICON_BULLET]Allows for the purchase of [ICON_GREAT_ADMIRAL] Great Admirals with [ICON_PEACE] Faith starting in the Industrial Era.' +SET Text = '[COLOR_POSITIVE_TEXT]Imperialism[ENDCOLOR] provides additional yields from common Improvements (including Forts), Military Buildings, and Water tiles. It also provides large bonuses to [ICON_PUPPET] Puppet Cities. Militarily, it provides [ICON_PRODUCTION][ICON_GOLD] discounts for obtaining the newest Units, and enhances [ICON_VISION] Sight, [ICON_MOVES] Movement, and [ICON_STRENGTH] Combat Strength, particularly of [ICON_GREAT_ADMIRAL] Admirals and their navies.[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Adopting Imperialism grants:[ENDCOLOR][NEWLINE][ICON_BULLET]+1 [ICON_MOVES] Movement for Naval Units, Embarked Units, and [ICON_GREAT_GENERAL] Great Generals, as well as +1 Sight for Melee, Recon, Naval Melee and Gunpowder Units.[NEWLINE][ICON_BULLET]+10% [ICON_PRODUCTION] Production towards Military Units, -10% [ICON_GOLD] Gold required for upgrades.[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Each Imperialism policy unlocked grants:[ENDCOLOR][NEWLINE][ICON_BULLET]+5% [ICON_PRODUCTION] Production towards Military Units, -5% [ICON_GOLD] Gold required for upgrades.[NEWLINE][NEWLINE][COLOR_POSITIVE_TEXT]Adopting all policies in Imperialism grants:[ENDCOLOR][NEWLINE][ICON_BULLET]Unlocks building the [COLOR_POSITIVE_TEXT]Pentagon[ENDCOLOR].[NEWLINE][ICON_BULLET]All Naval and Air units gain the [COLOR_POSITIVE_TEXT]Banzai![ENDCOLOR] Promotion (become stronger as they take damage).[NEWLINE][ICON_BULLET]Allows for the purchase of [ICON_GREAT_ADMIRAL] Great Admirals with [ICON_PEACE] Faith starting in the Industrial Era.' WHERE Tag = 'TXT_KEY_POLICY_BRANCH_EXPLORATION_HELP'; UPDATE Language_en_US @@ -569,7 +569,7 @@ SET Text = 'Colonialism' WHERE Tag = 'TXT_KEY_POLICY_NAVAL_TRADITION'; UPDATE Language_en_US -SET Text = '[COLOR_POSITIVE_TEXT]Colonialism[ENDCOLOR][NEWLINE][ICON_BULLET]+2 [ICON_RESEARCH] Science and +1 [ICON_CULTURE] Culture from Barracks, Armories, Military Academies, Forts, and Citadels.[NEWLINE][ICON_BULLET]Each unique [ICON_MONOPOLY] Global Monopoly modifier is increased by an additional 10% if it''s percentage-based, or +3 otherwise.' +SET Text = '[COLOR_POSITIVE_TEXT]Colonialism[ENDCOLOR][NEWLINE][ICON_BULLET]+2 [ICON_RESEARCH] Science and +1 [ICON_CULTURE] Culture from Barracks, Armories, Military Academies, Forts, and Citadels.[NEWLINE][ICON_BULLET]Each [ICON_MONOPOLY] Global Monopoly Bonus is increased by an additional 5% if it''s percentage-based, or by +5 otherwise.' WHERE Tag = 'TXT_KEY_POLICY_NAVAL_TRADITION_HELP'; UPDATE Language_en_US diff --git a/(2) Vox Populi/Database Changes/Text/en_US/Tech/TechTextChanges.sql b/(2) Vox Populi/Database Changes/Text/en_US/Tech/TechTextChanges.sql index 3743ae24e5..548e3a1fc7 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/Tech/TechTextChanges.sql +++ b/(2) Vox Populi/Database Changes/Text/en_US/Tech/TechTextChanges.sql @@ -66,7 +66,7 @@ WHERE Tag = 'TXT_KEY_TECH_CHEMISTRY_HELP'; -- Metallurgy UPDATE Language_en_US -SET Text = 'Allows you to build the [COLOR_POSITIVE_TEXT]Lancer[ENDCOLOR], a powerful Renaissance-Era mounted unit.' +SET Text = 'Allows you to build the [COLOR_POSITIVE_TEXT]Musketman[ENDCOLOR], a powerful Renaissance-Era ranged unit.' WHERE Tag = 'TXT_KEY_TECH_METALLURGY_HELP'; -- Industrialization diff --git a/(2) Vox Populi/Database Changes/Text/en_US/UI/NewUIText.xml b/(2) Vox Populi/Database Changes/Text/en_US/UI/NewUIText.xml index f86a3cfe9f..a6794e4077 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/UI/NewUIText.xml +++ b/(2) Vox Populi/Database Changes/Text/en_US/UI/NewUIText.xml @@ -298,6 +298,12 @@ Contender: + + (Contender: {1_Info}) + + + (You are the contender for Ally) + You are receiving [COLOR_POSITIVE_TEXT]{1_Num} [ICON_GOLD] Gold[ENDCOLOR] per turn from them. @@ -657,6 +663,9 @@ {1_Happiness} from Events. + + {1_Num} from Improvements. + {1_Num} from [ICON_RESEARCH] Illiteracy. @@ -1791,51 +1800,6 @@ - - WORLD MAP - - - Reveals all land explored by this civilization.[NEWLINE][NEWLINE]Note: Will not receive contact with unmet civilizations and city-states. - - - Both players don't yet have the Technology required to trade this item ({1_Tech:textkey}). - - - They don't yet have the Technology required to trade this item ({1_Tech:textkey}). - - - You don't yet have the Technology required to trade this item ({1_Tech:textkey}). - - - They have already explored all of the lands revealed in your World Map. - - - We have already explored all of the lands revealed in their World Map. - - - TECHNOLOGIES - - - TECHNOLOGIES - - - We have Technologies available to trade. - - - We have no Technologies available to trade. - - - The other leader has Technologies available to trade. - - - The other leader has no Technologies available to trade. - - - They don't yet have the Technology to sell this item (Scientific Theory). - - - You don't yet have the Technology to sell this item (Scientific Theory). - VASSAL STATE diff --git a/(2) Vox Populi/Database Changes/Text/en_US/UI/UITextChanges.sql b/(2) Vox Populi/Database Changes/Text/en_US/UI/UITextChanges.sql index 19a47a67d8..0029a2a211 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/UI/UITextChanges.sql +++ b/(2) Vox Populi/Database Changes/Text/en_US/UI/UITextChanges.sql @@ -127,10 +127,6 @@ UPDATE Language_en_US SET Text = 'Empire-Wide [ICON_HAPPINESS_1] Happiness Total: {1_Num}' WHERE Tag = 'TXT_KEY_TP_HAPPINESS_SOURCES'; -UPDATE Language_en_US -SET Text = '{1_Num} from Natural Wonders and Landmarks.' -WHERE Tag = 'TXT_KEY_TP_HAPPINESS_NATURAL_WONDERS'; - UPDATE Language_en_US SET Text = 'Empire-Wide [ICON_HAPPINESS_3] Unhappiness Total: {1_Num} (capped by City Population)' WHERE Tag = 'TXT_KEY_TP_UNHAPPINESS_TOTAL'; diff --git a/(2) Vox Populi/Database Changes/Text/en_US/UnitPromotions/NewPromotionText.xml b/(2) Vox Populi/Database Changes/Text/en_US/UnitPromotions/NewPromotionText.xml index 7c425ecfb6..222533ed76 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/UnitPromotions/NewPromotionText.xml +++ b/(2) Vox Populi/Database Changes/Text/en_US/UnitPromotions/NewPromotionText.xml @@ -13,6 +13,11 @@ +1 [ICON_WAR] Attack. + + + All tiles cost 1 [ICON_MOVES] Movement.[NEWLINE][COLOR_NEGATIVE_TEXT]Cannot use Roads and Railroads.[ENDCOLOR] + + +50% [ICON_STRENGTH] Combat Strength VS [COLOR_POSITIVE_TEXT]Armored Units[ENDCOLOR]. @@ -348,14 +353,14 @@ Shrapnel Rounds I - +35% [ICON_STRENGTH] Combat Strength when attacking [COLOR_POSITIVE_TEXT]Land Units[ENDCOLOR]. + +50% [ICON_STRENGTH] Combat Strength when attacking [COLOR_POSITIVE_TEXT]Land Units[ENDCOLOR]. Shrapnel Rounds II - +35% [ICON_STRENGTH] Combat Strength when attacking [COLOR_POSITIVE_TEXT]Land Units[ENDCOLOR]. + +50% [ICON_STRENGTH] Combat Strength when attacking [COLOR_POSITIVE_TEXT]Land Units[ENDCOLOR]. @@ -1058,11 +1063,11 @@ +40% [ICON_STRENGTH] Combat Strength VS [COLOR_POSITIVE_TEXT]Barbarians[ENDCOLOR]. - - Legacy + + Spear Wall - - +5% [ICON_STRENGTH] Combat Strength per Level beyond 1. + + Deal [COLOR_POSITIVE_TEXT]5 Damage[ENDCOLOR] to Enemy Units ending their turn adjacent to this Unit.[NEWLINE]This Damage ignores damage reduction.[NEWLINE][NEWLINE]Deal [COLOR_POSITIVE_TEXT]5 Damage[ENDCOLOR] to all adjacent Enemy Units if this Unit is [COLOR_POSITIVE_TEXT]Fortified[ENDCOLOR] when starting its turn.[NEWLINE]This Damage ignores damage reduction, but does not affect Units in [COLOR_NEGATIVE_TEXT]Cities[ENDCOLOR] and [COLOR_NEGATIVE_TEXT]Fortifications[ENDCOLOR]. @@ -1198,11 +1203,11 @@ Gain 0.5 [ICON_CULTURE] Culture and [ICON_PEACE] Faith for each XP gained through Combat. - - Cetbang + + Retaliation - - Deal 10 Damage to Enemy Units ending their turn adjacent to this Unit.[NEWLINE]This Damage ignores damage reduction. + + Deal [COLOR_POSITIVE_TEXT]10 Damage[ENDCOLOR] to Enemy Units ending their turn adjacent to this Unit.[NEWLINE]This Damage ignores damage reduction. @@ -1244,7 +1249,7 @@ Razzia - Gain 200% of the [ICON_STRENGTH] Combat Strength of defeated Enemy Units as [ICON_FOOD] Food and [ICON_PRODUCTION] Production in this unit's origin City.[NEWLINE]Gain 100 [ICON_FOOD] Food and [ICON_PRODUCTION] Production in this unit's origin City when [ICON_RAZING] Pillaging Improvements.[NEWLINE]Gain 200 [ICON_FOOD] Food and [ICON_PRODUCTION] Production in this unit's origin City when plundering [ICON_INTERNATIONAL_TRADE] Trade Routes. + Gain 200% of the [ICON_STRENGTH] Combat Strength of defeated Enemy Units as [ICON_PRODUCTION] Production and [ICON_GOLD] Gold in this unit's origin City.[NEWLINE]Gain 100 [ICON_PRODUCTION] Production and [ICON_GOLD] Gold in this unit's origin City when [ICON_RAZING] Pillaging Improvements.[NEWLINE]Gain 400 [ICON_PRODUCTION] Production and [ICON_GOLD] Gold in this unit's origin City when plundering [ICON_INTERNATIONAL_TRADE] Trade Routes. diff --git a/(2) Vox Populi/Database Changes/Text/en_US/UnitPromotions/PromotionTextChanges.sql b/(2) Vox Populi/Database Changes/Text/en_US/UnitPromotions/PromotionTextChanges.sql index a9748fb3e6..7535ca0102 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/UnitPromotions/PromotionTextChanges.sql +++ b/(2) Vox Populi/Database Changes/Text/en_US/UnitPromotions/PromotionTextChanges.sql @@ -494,7 +494,7 @@ SET Text = 'Can enter [COLOR_POSITIVE_TEXT]Impassable[ENDCOLOR] tiles, and can e WHERE Tag = 'TXT_KEY_PROMOTION_HOVERING_UNIT_HELP'; UPDATE Language_en_US -SET Text = 'All tiles cost 1 [ICON_MOVES] Movement.[NEWLINE][COLOR_NEGATIVE_TEXT]Cannot use Roads and Railroads.[ENDCOLOR]' +SET Text = 'Flat Movement Cost' WHERE Tag = 'TXT_KEY_PROMOTION_FLAT_MOVEMENT_COST'; UPDATE Language_en_US @@ -658,14 +658,6 @@ UPDATE Language_en_US SET Text = '-20% [ICON_STRENGTH] Combat Strength for [COLOR_POSITIVE_TEXT]Adjacent Enemy Units[ENDCOLOR].[NEWLINE]Embarking and Disembarking cost only 1 [ICON_MOVES] Movement.' WHERE Tag = 'TXT_KEY_PROMOTION_HAKA_WAR_DANCE_HELP'; -UPDATE Language_en_US -SET Text = 'Khaaaan!' -WHERE Tag = 'TXT_KEY_PROMOTION_MEDIC_GENERAL'; - -UPDATE Language_en_US -SET Text = 'Deal 10 Damage to Enemy Units ending their turn adjacent to this Unit.[NEWLINE]This Damage ignores damage reduction.' -WHERE Tag = 'TXT_KEY_PROMOTION_MEDIC_GENERAL_HELP'; - -- Quick Study UPDATE Language_en_US SET Text = '+50% Experience gained from combat.' diff --git a/(2) Vox Populi/Database Changes/Text/en_US/WorldCongress/NewProjectText.xml b/(2) Vox Populi/Database Changes/Text/en_US/WorldCongress/NewProjectText.xml index ba09a31f07..f2e5bc11c2 100644 --- a/(2) Vox Populi/Database Changes/Text/en_US/WorldCongress/NewProjectText.xml +++ b/(2) Vox Populi/Database Changes/Text/en_US/WorldCongress/NewProjectText.xml @@ -54,7 +54,7 @@ Field Marshal - Menin Gate wonder appears in [ICON_CAPITAL] Capital. (Receive +2 [ICON_HAPPINESS_1] Happiness in the City in which it is built. Receive 2 [ICON_CULTURE] Culture and [ICON_GOLDEN_AGE] Golden Age Points whenever an owned unit is killed, scaling with Era. Forts, Citadels and Landmarks provide +2 [ICON_CULTURE] Culture and [ICON_GOLDEN_AGE] Golden Age Points when worked.) + Menin Gate wonder appears in [ICON_CAPITAL] Capital. (Receive +2 [ICON_HAPPINESS_1] Happiness in the City in which it is built. Forts, Citadels and Landmarks provide +2 [ICON_CULTURE] Culture and [ICON_GOLDEN_AGE] Golden Age Points when worked.) diff --git a/(2) Vox Populi/Database Changes/UnitPromotions/NewPromotions.xml b/(2) Vox Populi/Database Changes/UnitPromotions/NewPromotions.xml index 7313d37dbd..7e9a49c08b 100644 --- a/(2) Vox Populi/Database Changes/UnitPromotions/NewPromotions.xml +++ b/(2) Vox Populi/Database Changes/UnitPromotions/NewPromotions.xml @@ -467,6 +467,14 @@ PEDIA_ATTRIBUTES TXT_KEY_PROMOTION_SUN_NEVER_SETS + + PROMOTION_BUSHIDO + TXT_KEY_PROMOTION_BUSHIDO + TXT_KEY_PROMOTION_BUSHIDO_HELP + PEDIA_MELEE + TXT_KEY_PROMOTION_BUSHIDO + true + PROMOTION_FASTER_WORKER @@ -642,14 +650,6 @@ PEDIA_MELEE TXT_KEY_PROMOTION_IKLWA - - PROMOTION_BUSHIDO - TXT_KEY_PROMOTION_BUSHIDO - TXT_KEY_PROMOTION_BUSHIDO_HELP - PEDIA_MELEE - TXT_KEY_PROMOTION_BUSHIDO - true - PROMOTION_SCHUTZENKONIG TXT_KEY_PROMOTION_SCHUTZENKONIG @@ -1045,11 +1045,11 @@ TXT_KEY_PROMOTION_BRUTE_STRENGTH - PROMOTION_LEGACY - TXT_KEY_PROMOTION_LEGACY - TXT_KEY_PROMOTION_LEGACY_HELP + PROMOTION_SPEAR_WALL + TXT_KEY_PROMOTION_SPEAR_WALL + TXT_KEY_PROMOTION_SPEAR_WALL_HELP PEDIA_MELEE - TXT_KEY_PROMOTION_LEGACY + TXT_KEY_PROMOTION_SPEAR_WALL PROMOTION_RIACHUELO @@ -1178,11 +1178,11 @@ TXT_KEY_PROMOTION_DHANURVIDYA - PROMOTION_CETBANG - TXT_KEY_PROMOTION_CETBANG - TXT_KEY_PROMOTION_CETBANG_HELP - PEDIA_NAVAL - TXT_KEY_PROMOTION_CETBANG + PROMOTION_RETALIATION + TXT_KEY_PROMOTION_RETALIATION + TXT_KEY_PROMOTION_RETALIATION_HELP + PEDIA_SHARED + TXT_KEY_PROMOTION_RETALIATION PROMOTION_KANTAI_KESSEN diff --git a/(2) Vox Populi/Database Changes/UnitPromotions/OldPromotions.xml b/(2) Vox Populi/Database Changes/UnitPromotions/OldPromotions.xml index 20c1013a9d..2a8ef10d28 100644 --- a/(2) Vox Populi/Database Changes/UnitPromotions/OldPromotions.xml +++ b/(2) Vox Populi/Database Changes/UnitPromotions/OldPromotions.xml @@ -912,9 +912,9 @@ PROMOTION_FLAT_MOVEMENT_COST TXT_KEY_PROMOTION_FLAT_MOVEMENT_COST - TXT_KEY_PROMOTION_FLAT_MOVEMENT_COST + TXT_KEY_PROMOTION_FLAT_MOVEMENT_COST_HELP PEDIA_MOUNTED - TXT_KEY_PEDIA_PROMOTION_FLAT_MOVEMENT_COST + TXT_KEY_PROMOTION_FLAT_MOVEMENT_COST PROMOTION_MUST_SET_UP @@ -1239,13 +1239,6 @@ PEDIA_MELEE TXT_KEY_PROMOTION_HAKA_WAR_DANCE - - PROMOTION_MEDIC_GENERAL - TXT_KEY_PROMOTION_MEDIC_GENERAL - TXT_KEY_PROMOTION_MEDIC_GENERAL_HELP - PEDIA_HEAL - TXT_KEY_PROMOTION_MEDIC_GENERAL - PROMOTION_GAIN_EXPERIENCE TXT_KEY_PROMOTION_GAIN_EXPERIENCE diff --git a/(2) Vox Populi/Database Changes/UnitPromotions/PromotionChanges.sql b/(2) Vox Populi/Database Changes/UnitPromotions/PromotionChanges.sql index 686e801976..a252a49c5e 100644 --- a/(2) Vox Populi/Database Changes/UnitPromotions/PromotionChanges.sql +++ b/(2) Vox Populi/Database Changes/UnitPromotions/PromotionChanges.sql @@ -314,7 +314,7 @@ UPDATE UnitPromotions SET HealOutsideFriendly = 1, FriendlyHealChange = 5, Neutr ---------------------------------------------------------------------------------------------------------------------------- -- Naval Ranged promotion tree drawn using ASCIIFlow --- Splash I +-- Splash I ──► Splash II -- Targeting I ──┬───► Targeting II ───┬───► Targeting III ───┬─► Indomitable ─────────────────────────┐ -- │ │ │ │ -- │ │ └───────────────────────────┐ │ @@ -368,8 +368,8 @@ UPDATE UnitPromotions SET CityAttack = 40 WHERE Type = 'PROMOTION_BROADSIDE'; INSERT INTO UnitPromotions_Domains (PromotionType, DomainType, Attack) VALUES - ('PROMOTION_SHRAPNEL_ROUNDS_1', 'DOMAIN_LAND', 35), - ('PROMOTION_SHRAPNEL_ROUNDS_2', 'DOMAIN_LAND', 35); + ('PROMOTION_SHRAPNEL_ROUNDS_1', 'DOMAIN_LAND', 50), + ('PROMOTION_SHRAPNEL_ROUNDS_2', 'DOMAIN_LAND', 50); ---------------------------------------------------------------------------------------------------------------------------- -- Submarine promotion tree drawn using ASCIIFlow @@ -566,6 +566,20 @@ UPDATE UnitPromotions SET RiverDoubleMove = 1, River = 1 WHERE Type = 'PROMOTION UPDATE UnitPromotions SET AttackMod = 20 WHERE Type = 'PROMOTION_ATTACK_BONUS_SWEDEN'; +-- Eight Virtues of Bushido +UPDATE UnitPromotions SET HasPostCombatPromotions = 1 WHERE Type = 'PROMOTION_BUSHIDO'; +INSERT INTO UnitPromotions_PostCombatRandomPromotion + (PromotionType, NewPromotion) +VALUES + ('PROMOTION_BUSHIDO', 'PROMOTION_RIGHTEOUSNESS'), + ('PROMOTION_BUSHIDO', 'PROMOTION_COURAGE'), + ('PROMOTION_BUSHIDO', 'PROMOTION_BENEVOLENCE'), + ('PROMOTION_BUSHIDO', 'PROMOTION_RESPECT'), + ('PROMOTION_BUSHIDO', 'PROMOTION_SINCERITY'), + ('PROMOTION_BUSHIDO', 'PROMOTION_HONOR'), + ('PROMOTION_BUSHIDO', 'PROMOTION_LOYALTY'), + ('PROMOTION_BUSHIDO', 'PROMOTION_SELF_CONTROL'); + -------------------------------------------- -- Policy free promotions -------------------------------------------- @@ -637,20 +651,6 @@ UPDATE UnitPromotions SET AttackMod = 10 WHERE Type = 'PROMOTION_IKLWA'; UPDATE UnitPromotions SET DiplomaticMissionAccomplishment = 1 WHERE Type = 'PROMOTION_PROXENOS'; --- Eight Virtues of Bushido -UPDATE UnitPromotions SET HasPostCombatPromotions = 1 WHERE Type = 'PROMOTION_BUSHIDO'; -INSERT INTO UnitPromotions_PostCombatRandomPromotion - (PromotionType, NewPromotion) -VALUES - ('PROMOTION_BUSHIDO', 'PROMOTION_RIGHTEOUSNESS'), - ('PROMOTION_BUSHIDO', 'PROMOTION_COURAGE'), - ('PROMOTION_BUSHIDO', 'PROMOTION_BENEVOLENCE'), - ('PROMOTION_BUSHIDO', 'PROMOTION_RESPECT'), - ('PROMOTION_BUSHIDO', 'PROMOTION_SINCERITY'), - ('PROMOTION_BUSHIDO', 'PROMOTION_HONOR'), - ('PROMOTION_BUSHIDO', 'PROMOTION_LOYALTY'), - ('PROMOTION_BUSHIDO', 'PROMOTION_SELF_CONTROL'); - UPDATE UnitPromotions SET DiploMissionInfluence = 10 WHERE RankList = 'DIPLO_BOOST'; UPDATE UnitPromotions SET DiploMissionInfluence = 15, RivalTerritory = 1 WHERE Type = 'PROMOTION_NOBILITY'; UPDATE UnitPromotions SET MovesChange = 1, ExtraNavalMovement = 1 WHERE Type IN ('PROMOTION_WIRE_SERVICE', 'PROMOTION_IMPERIAL_SEAL', 'PROMOTION_LITERACY'); @@ -922,9 +922,6 @@ SET DisembarkFlatCost = 1 WHERE Type = 'PROMOTION_HAKA_WAR_DANCE'; --- Khan: Khaaaan! -UPDATE UnitPromotions SET NearbyEnemyDamage = 10 WHERE Type = 'PROMOTION_MEDIC_GENERAL'; - -- Samurai, Sabum Kibitum, Treasure Ship: Quick Study UPDATE UnitPromotions SET ExperiencePercent = 50 WHERE Type = 'PROMOTION_GAIN_EXPERIENCE'; @@ -1061,8 +1058,8 @@ UPDATE UnitPromotions SET BorderMod = 40 WHERE Type = 'PROMOTION_FRONTIERSMAN'; -- Otomi: Brute Strength UPDATE UnitPromotions SET BarbarianCombatBonus = 40 WHERE Type = 'PROMOTION_BRUTE_STRENGTH'; --- Sabum Kibitum: Legacy -UPDATE UnitPromotions SET CombatModPerLevel = 5 WHERE Type = 'PROMOTION_LEGACY'; +-- Sabum Kibitum: Spear Wall +UPDATE UnitPromotions SET NearbyEnemyDamage = 5, AoEWhileFortified = 5 WHERE Type = 'PROMOTION_SPEAR_WALL'; -- Amazonas: Riachuelo UPDATE UnitPromotions SET GoldenAgeValueFromKills = 100 WHERE Type = 'PROMOTION_RIACHUELO'; @@ -1157,8 +1154,8 @@ VALUES ('PROMOTION_DHANURVIDYA', 'YIELD_CULTURE', 50), ('PROMOTION_DHANURVIDYA', 'YIELD_FAITH', 50); --- Djong: Cetbang -UPDATE UnitPromotions SET NearbyEnemyDamage = 10 WHERE Type = 'PROMOTION_CETBANG'; +-- Djong, Khan, Naga-Malla: Retaliation +UPDATE UnitPromotions SET NearbyEnemyDamage = 10 WHERE Type = 'PROMOTION_RETALIATION'; -- Mikasa: Kantai Kessen UPDATE UnitPromotions SET AttackFullyHealedMod = 25 WHERE Type = 'PROMOTION_KANTAI_KESSEN'; @@ -1194,18 +1191,18 @@ UPDATE UnitPromotions SET XPFromPillaging = 3, PillageBonusStrength = 20 WHERE T INSERT INTO UnitPromotions_YieldFromKills (PromotionType, YieldType, Yield) VALUES - ('PROMOTION_RAZZIA', 'YIELD_FOOD', 200), - ('PROMOTION_RAZZIA', 'YIELD_PRODUCTION', 200); + ('PROMOTION_RAZZIA', 'YIELD_PRODUCTION', 200), + ('PROMOTION_RAZZIA', 'YIELD_GOLD', 200); INSERT INTO UnitPromotions_YieldFromPillage (PromotionType, YieldType, YieldNoScale) VALUES - ('PROMOTION_RAZZIA', 'YIELD_FOOD', 100), - ('PROMOTION_RAZZIA', 'YIELD_PRODUCTION', 100); + ('PROMOTION_RAZZIA', 'YIELD_PRODUCTION', 100), + ('PROMOTION_RAZZIA', 'YIELD_GOLD', 100); INSERT INTO UnitPromotions_YieldFromTRPlunder (PromotionType, YieldType, Yield) VALUES - ('PROMOTION_RAZZIA', 'YIELD_FOOD', 200), - ('PROMOTION_RAZZIA', 'YIELD_PRODUCTION', 200); + ('PROMOTION_RAZZIA', 'YIELD_PRODUCTION', 400), + ('PROMOTION_RAZZIA', 'YIELD_GOLD', 400); -- Goedendag: Burgher UPDATE UnitPromotions SET NearbyFriendlyCityCombatMod = 33, NearbyRange = 2 WHERE Type = 'PROMOTION_BURGHER'; diff --git a/(2) Vox Populi/Database Changes/UnitPromotions/PromotionDisplaySweeps.sql b/(2) Vox Populi/Database Changes/UnitPromotions/PromotionDisplaySweeps.sql index 4c00af3dc7..6fb20abc37 100644 --- a/(2) Vox Populi/Database Changes/UnitPromotions/PromotionDisplaySweeps.sql +++ b/(2) Vox Populi/Database Changes/UnitPromotions/PromotionDisplaySweeps.sql @@ -119,7 +119,6 @@ VALUES ('PROMOTION_SKI_INFANTRY', 129), ('PROMOTION_WITHDRAW_BEFORE_MELEE', 130), ('PROMOTION_HAKA_WAR_DANCE', 131), - ('PROMOTION_MEDIC_GENERAL', 132), ('PROMOTION_GAIN_EXPERIENCE', 133), ('PROMOTION_TRADE_MISSION_BONUS', 134), ('PROMOTION_HEAVY_CHARGE', 135), @@ -154,7 +153,7 @@ VALUES ('PROMOTION_SHOCK_CAVALRY', 164), ('PROMOTION_FRONTIERSMAN', 165), ('PROMOTION_BRUTE_STRENGTH', 166), - ('PROMOTION_LEGACY', 167), + ('PROMOTION_SPEAR_WALL', 167), ('PROMOTION_RIACHUELO', 168), ('PROMOTION_GREEK_FIRE', 169), ('PROMOTION_AT_THE_GATES', 171), @@ -168,7 +167,7 @@ VALUES ('PROMOTION_BELLUM_ALET', 179), ('PROMOTION_SENTINEL_OF_THE_SUN', 180), ('PROMOTION_DHANURVIDYA', 181), - ('PROMOTION_CETBANG', 182), + ('PROMOTION_RETALIATION', 182), ('PROMOTION_TAIKAN_KYOHO', 183), ('PROMOTION_DECK_SPIKES', 184), ('PROMOTION_LOST_CODEX', 185), diff --git a/(2) Vox Populi/Database Changes/UnitPromotions/PromotionIconChanges.sql b/(2) Vox Populi/Database Changes/UnitPromotions/PromotionIconChanges.sql index 19b47b8f78..47594a0cc8 100644 --- a/(2) Vox Populi/Database Changes/UnitPromotions/PromotionIconChanges.sql +++ b/(2) Vox Populi/Database Changes/UnitPromotions/PromotionIconChanges.sql @@ -134,7 +134,7 @@ VALUES ('PROMOTION_ATLAS_VP_01', 43, 'PROMOTION_GAIN_EXPERIENCE'), ('PROMOTION_ATLAS_VP_01', 44, 'PROMOTION_MEDIC'), ('PROMOTION_ATLAS_VP_01', 45, 'PROMOTION_FASTER_HEAL'), - ('PROMOTION_ATLAS_VP_01', 46, 'PROMOTION_MEDIC_GENERAL'), + -- ('PROMOTION_ATLAS_VP_01', 46, ''), ('PROMOTION_ATLAS_VP_01', 47, 'PROMOTION_EVERLASTING_YOUTH'), -- 7th row ('PROMOTION_ATLAS_VP_01', 48, 'PROMOTION_PARTIAL_HEAL_IF_DESTROY_ENEMY'), @@ -354,7 +354,7 @@ VALUES ('PROMOTION_ATLAS_VP_04', 42, 'PROMOTION_INVINCIBLE'), ('PROMOTION_ATLAS_VP_04', 43, 'PROMOTION_SANTA_MARIA'), -- ('PROMOTION_ATLAS_VP_04', 44, ''), - ('PROMOTION_ATLAS_VP_04', 45, 'PROMOTION_CETBANG'), + ('PROMOTION_ATLAS_VP_04', 45, 'PROMOTION_RETALIATION'), -- ('PROMOTION_ATLAS_VP_04', 46, ''), -- ('PROMOTION_ATLAS_VP_04', 47, ''), -- 7th row @@ -376,7 +376,7 @@ VALUES ('PROMOTION_ATLAS_VP_05', 3, 'PROMOTION_GARLAND_MINE'), ('PROMOTION_ATLAS_VP_05', 4, 'PROMOTION_SHOCK_CAVALRY'), -- 3rd row - ('PROMOTION_ATLAS_VP_05', 18, 'PROMOTION_LEGACY'), + ('PROMOTION_ATLAS_VP_05', 18, 'PROMOTION_SPEAR_WALL'), -- 4th row ('PROMOTION_ATLAS_VP_05', 28, 'PROMOTION_GREEK_FIRE'), ('PROMOTION_ATLAS_VP_05', 31, 'PROMOTION_ON_FIRE'), diff --git a/(2) Vox Populi/Database Changes/UnitPromotions/PromotionSweeps.sql b/(2) Vox Populi/Database Changes/UnitPromotions/PromotionSweeps.sql index f615b21076..3c59678204 100644 --- a/(2) Vox Populi/Database Changes/UnitPromotions/PromotionSweeps.sql +++ b/(2) Vox Populi/Database Changes/UnitPromotions/PromotionSweeps.sql @@ -65,6 +65,7 @@ VALUES ('TARGETING', 'UNITCOMBAT_NAVALRANGED'), ('BOMBARDMENT', 'UNITCOMBAT_NAVALRANGED'), ('AIR_DEFENSE', 'UNITCOMBAT_NAVALRANGED'), + ('SPLASH', 'UNITCOMBAT_NAVALRANGED'), ('SHRAPNEL_ROUNDS', 'UNITCOMBAT_NAVALRANGED'), -- Submarine tree ('TORPEDO', 'UNITCOMBAT_SUBMARINE'), @@ -169,7 +170,6 @@ VALUES -- Naval Ranged tree ('PROMOTION_INDOMITABLE', 'UNITCOMBAT_NAVALRANGED'), ('PROMOTION_BROADSIDE', 'UNITCOMBAT_NAVALRANGED'), - ('PROMOTION_SPLASH_1', 'UNITCOMBAT_NAVALRANGED'), ('PROMOTION_SENTRY', 'UNITCOMBAT_NAVALRANGED'), ('PROMOTION_MOBILITY', 'UNITCOMBAT_NAVALRANGED'), ('PROMOTION_SUPPLY', 'UNITCOMBAT_NAVALRANGED'), @@ -266,15 +266,6 @@ SELECT FROM UnitCombatInfos WHERE IsMilitary = 1 AND IsNaval = 0 AND IsAerial = 0; --- Dojo -INSERT INTO UnitPromotions_UnitCombats - (PromotionType, UnitCombatType) -VALUES - ('PROMOTION_BUSHIDO', 'UNITCOMBAT_MELEE'), - ('PROMOTION_BUSHIDO', 'UNITCOMBAT_GUN'), - ('PROMOTION_BUSHIDO', 'UNITCOMBAT_MOUNTED'), - ('PROMOTION_BUSHIDO', 'UNITCOMBAT_ARMOR'); - -- Schützenstand INSERT INTO UnitPromotions_UnitCombats (PromotionType, UnitCombatType) @@ -542,7 +533,6 @@ OR Type IN ( 'PROMOTION_HIMEJI_CASTLE', -- Himeji Castle 'PROMOTION_STATUE_ZEUS', -- Statue of Zeus 'PROMOTION_PROXENOS', -- Parthenon - 'PROMOTION_BUSHIDO', -- Dojo 'PROMOTION_IKLWA', -- Ikanda 'PROMOTION_SCHUTZENKONIG', -- Schützenstand 'PROMOTION_VENETIAN_CRAFTSMANSHIP', -- Arsenale di Venezia diff --git a/(2) Vox Populi/Database Changes/Units/NewUnits.xml b/(2) Vox Populi/Database Changes/Units/NewUnits.xml index a013a89487..c8c73005a9 100644 --- a/(2) Vox Populi/Database Changes/Units/NewUnits.xml +++ b/(2) Vox Populi/Database Changes/Units/NewUnits.xml @@ -1509,8 +1509,8 @@ ART_DEF_UNIT_INQUISITOR EXPANSION_UNIT_FLAG_ATLAS 17 - EXPANSION_UNIT_ATLAS_1 - 17 + UNIT_ATLAS_VP_1 + 42 false diff --git a/(2) Vox Populi/Database Changes/Units/PolicyUnitChanges.sql b/(2) Vox Populi/Database Changes/Units/PolicyUnitChanges.sql index e202ad0c4a..d2fd2a3f0c 100644 --- a/(2) Vox Populi/Database Changes/Units/PolicyUnitChanges.sql +++ b/(2) Vox Populi/Database Changes/Units/PolicyUnitChanges.sql @@ -31,7 +31,7 @@ SET Class = 'UNITCLASS_FREE_COMPANY', ObsoleteTech = (SELECT PrereqTech FROM Units WHERE Type = 'UNIT_FRENCH_FOREIGNLEGION'), PolicyType = 'POLICY_HONOR_FINISHER', - Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_PIKEMAN') + 2, + Combat = (SELECT Combat FROM Units WHERE Type = 'UNIT_PIKEMAN') + 1, DefaultUnitAI = 'UNITAI_ATTACK', PurchaseOnly = 1, MoveAfterPurchase = 1 diff --git a/(2) Vox Populi/Database Changes/Units/UnitChanges.sql b/(2) Vox Populi/Database Changes/Units/UnitChanges.sql index d15cf20e96..1c644cc8e7 100644 --- a/(2) Vox Populi/Database Changes/Units/UnitChanges.sql +++ b/(2) Vox Populi/Database Changes/Units/UnitChanges.sql @@ -155,12 +155,12 @@ SET PrereqTech = 'TECH_ARCHERY' -- Military Strategy WHERE Class = 'UNITCLASS_HORSEMAN'; -- Knight -UPDATE Units SET ObsoleteTech = 'TECH_METALLURGY' WHERE Class = 'UNITCLASS_KNIGHT'; +UPDATE Units SET ObsoleteTech = 'TECH_ECONOMICS' WHERE Class = 'UNITCLASS_KNIGHT'; -- Lancer UPDATE Units SET - PrereqTech = 'TECH_METALLURGY', + PrereqTech = 'TECH_ECONOMICS', ObsoleteTech = 'TECH_COMBUSTION' WHERE Class = 'UNITCLASS_LANCER'; diff --git a/(2) Vox Populi/Database Changes/Units/UnitCostSweeps.sql b/(2) Vox Populi/Database Changes/Units/UnitCostSweeps.sql index 1fc2cdd11b..10afa90562 100644 --- a/(2) Vox Populi/Database Changes/Units/UnitCostSweeps.sql +++ b/(2) Vox Populi/Database Changes/Units/UnitCostSweeps.sql @@ -47,17 +47,17 @@ VALUES (4, 110, 130, 250), (5, 135, 175, 300), (6, 160, 200, 350), - (7, 300, 350, 400), - (8, 325, 350, 500), - (9, 625, 900, 600), - (10, 700, 800, 700), - (11, 900, 1000, 800), - (12, 950, 1300, 900), - (13, 1300, 1800, 1000), - (14, 1500, 2000, 1200), - (15, 1800, 2250, 1400), - (16, 2600, 3000, 1600), - (17, 2600, 3000, 1600); + (7, 300, 350, 500), + (8, 325, 350, 600), + (9, 625, 900, 800), + (10, 700, 800, 900), + (11, 900, 1000, 1100), + (12, 950, 1300, 1200), + (13, 1300, 1800, 1400), + (14, 1500, 2000, 1500), + (15, 1800, 2250, 1700), + (16, 2600, 3000, 1800), + (17, 2600, 3000, 1800); -- Melee, Ranged, Recon UPDATE Units @@ -183,12 +183,12 @@ UPDATE Units SET Cost = 125, FaithCost = 250 WHERE Type = 'UNIT_FUSTA'; -- cheap UPDATE Units SET Cost = 185 WHERE Type = 'UNIT_KOREAN_TURTLE_SHIP'; -- more expensive UPDATE Units SET Cost = 200 WHERE Type = 'UNIT_DJONG'; -- more expensive UPDATE Units SET Cost = 250 WHERE Type = 'UNIT_VENETIAN_GALLEASS'; -- more expensive -UPDATE Units SET Cost = 275, FaithCost = 350 WHERE Type = 'UNIT_GERMAN_LANDSKNECHT'; -- cheaper -UPDATE Units SET Cost = 400, FaithCost = 450 WHERE Type = 'UNIT_GREAT_BOMBARD'; -- unique class -UPDATE Units SET Cost = 325, FaithCost = 500 WHERE Type = 'UNIT_OTTOMAN_JANISSARY'; -- earlier but same cost -UPDATE Units SET Cost = 350, FaithCost = 500 WHERE Type = 'UNIT_INDIAN_WARELEPHANT'; -- earlier but same cost -UPDATE Units SET Cost = 500, FaithCost = 550 WHERE Type = 'UNIT_TREASURE_SHIP'; -- earlier but more expensive -UPDATE Units SET FaithCost = 450 WHERE Type = 'UNIT_QIZILBASH'; -- cheaper faith cost -UPDATE Units SET Cost = 1300, FaithCost = 900 WHERE Type = 'UNIT_HASHEMITE_RAIDER'; -- earlier but same cost -UPDATE Units SET Cost = 1300, FaithCost = 900 WHERE Type = 'UNIT_KRUPP_GUN'; -- earlier but same cost -UPDATE Units SET FaithCost = 900 WHERE Type = 'UNIT_MIKASA'; -- earlier but same cost +UPDATE Units SET Cost = 275, FaithCost = 450 WHERE Type = 'UNIT_GERMAN_LANDSKNECHT'; -- cheaper +UPDATE Units SET Cost = 400, FaithCost = 550 WHERE Type = 'UNIT_GREAT_BOMBARD'; -- unique class +UPDATE Units SET Cost = 325, FaithCost = 600 WHERE Type = 'UNIT_OTTOMAN_JANISSARY'; -- earlier but same cost +UPDATE Units SET Cost = 350, FaithCost = 600 WHERE Type = 'UNIT_INDIAN_WARELEPHANT'; -- earlier but same cost +UPDATE Units SET Cost = 500, FaithCost = 650 WHERE Type = 'UNIT_TREASURE_SHIP'; -- earlier but more expensive +UPDATE Units SET FaithCost = 550 WHERE Type = 'UNIT_QIZILBASH'; -- cheaper faith cost +UPDATE Units SET Cost = 1300, FaithCost = 1200 WHERE Type = 'UNIT_HASHEMITE_RAIDER'; -- earlier but same cost +UPDATE Units SET Cost = 1300, FaithCost = 1200 WHERE Type = 'UNIT_KRUPP_GUN'; -- earlier but same cost +UPDATE Units SET FaithCost = 1200 WHERE Type = 'UNIT_MIKASA'; -- earlier but same cost diff --git a/(2) Vox Populi/Database Changes/Units/UnitStatChanges.sql b/(2) Vox Populi/Database Changes/Units/UnitStatChanges.sql index 91e7550e87..3cd3bf0c04 100644 --- a/(2) Vox Populi/Database Changes/Units/UnitStatChanges.sql +++ b/(2) Vox Populi/Database Changes/Units/UnitStatChanges.sql @@ -163,9 +163,9 @@ VALUES ('UNITCLASS_WARRIOR', 8), ('UNITCLASS_SPEARMAN', 12), ('UNITCLASS_SWORDSMAN', 16), - ('UNITCLASS_PIKEMAN', 17), - ('UNITCLASS_LONGSWORDSMAN', 22), - ('UNITCLASS_TERCIO', 25), + ('UNITCLASS_PIKEMAN', 18), + ('UNITCLASS_LONGSWORDSMAN', 23), + ('UNITCLASS_TERCIO', 26), ('UNITCLASS_RIFLEMAN', 38), ('UNITCLASS_GREAT_WAR_INFANTRY', 50), ('UNITCLASS_INFANTRY', 62), diff --git a/(2) Vox Populi/Database Changes/WorldMap/Improvements/ImprovementChanges.sql b/(2) Vox Populi/Database Changes/WorldMap/Improvements/ImprovementChanges.sql index b105be35a7..2c5f866d36 100644 --- a/(2) Vox Populi/Database Changes/WorldMap/Improvements/ImprovementChanges.sql +++ b/(2) Vox Populi/Database Changes/WorldMap/Improvements/ImprovementChanges.sql @@ -80,7 +80,7 @@ VALUES INSERT INTO Improvement_Yields (ImprovementType, YieldType, Yield) VALUES - ('IMPROVEMENT_FORT', 'YIELD_CULTURE_LOCAL', 1); + ('IMPROVEMENT_FORT', 'YIELD_CULTURE_LOCAL', 2); INSERT INTO Improvement_ResourceExtractionIncrease (ImprovementType, ResourceType, Num) diff --git a/(2) Vox Populi/LUA/CityBannerManager.lua b/(2) Vox Populi/LUA/CityBannerManager.lua index 6a2c263e20..d1c05d6f2e 100644 --- a/(2) Vox Populi/LUA/CityBannerManager.lua +++ b/(2) Vox Populi/LUA/CityBannerManager.lua @@ -322,7 +322,7 @@ function RefreshCityBanner(cityBanner, iActiveTeam, iActivePlayer) controls.PuppetIcon:SetHide(true); end - -- Rome UA (Annexed City-States) + -- Annexed City-States controls.CityStateIcon:SetHide ( not Players[city:GetOriginalOwner()]:IsMinorCiv() or not player:IsAnnexedCityStatesGiveYields()) if Players[city:GetOriginalOwner()]:IsMinorCiv() and player:IsAnnexedCityStatesGiveYields() then local cityOriginalOwner = Players[city:GetOriginalOwner()]; diff --git a/(2) Vox Populi/LUA/CityStateDiploPopup.lua b/(2) Vox Populi/LUA/CityStateDiploPopup.lua index 8553551b7f..553ba6a331 100644 --- a/(2) Vox Populi/LUA/CityStateDiploPopup.lua +++ b/(2) Vox Populi/LUA/CityStateDiploPopup.lua @@ -1260,7 +1260,7 @@ end Controls.UnitTributeButton:RegisterCallback( Mouse.eLClick, OnUnitTributeButtonClicked ); ---------------------------------------------------------------- --- CBP: Forced Annex (Rome UA) +-- CBP: Forced Annex ---------------------------------------------------------------- function OnBullyAnnexButtonClicked() local pPlayer = Players[g_iMinorCivID]; diff --git a/(2) Vox Populi/LUA/TopPanel.lua b/(2) Vox Populi/LUA/TopPanel.lua index 99411f95d8..c7472954d9 100644 --- a/(2) Vox Populi/LUA/TopPanel.lua +++ b/(2) Vox Populi/LUA/TopPanel.lua @@ -901,9 +901,14 @@ function HappinessTipHandler( control ) strText = strText .. "[NEWLINE][ICON_BULLET]" .. Locale.ConvertTextKey("TXT_KEY_TP_HAPPINESS_RESOURCE_CITY", ResourceHappiness, AvgResourceHappiness); end - local NaturalWonderAndLandmarkHappiness = pPlayer:GetHappinessFromNaturalWonders(); - if (NaturalWonderAndLandmarkHappiness ~= 0) then - strText = strText .. "[NEWLINE][ICON_BULLET]" .. Locale.ConvertTextKey("TXT_KEY_TP_HAPPINESS_NATURAL_WONDERS", NaturalWonderAndLandmarkHappiness); + local NaturalWonderHappiness = pPlayer:GetHappinessFromNaturalWonders(); + if (NaturalWonderHappiness ~= 0) then + strText = strText .. "[NEWLINE][ICON_BULLET]" .. Locale.ConvertTextKey("TXT_KEY_TP_HAPPINESS_NATURAL_WONDERS", NaturalWonderHappiness); + end + + local ImprovementHappiness = pPlayer:GetHappinessFromImprovements(); + if (ImprovementHappiness ~= 0) then + strText = strText .. "[NEWLINE][ICON_BULLET]" .. Locale.ConvertTextKey("TXT_KEY_TP_HAPPINESS_IMPROVEMENTS", ImprovementHappiness); end local ReligionHappiness = pPlayer:GetHappinessFromReligion(); diff --git a/(2) Vox Populi/Vox Populi.civ5proj b/(2) Vox Populi/Vox Populi.civ5proj index ca0fa6bf53..fddb87f2ba 100644 --- a/(2) Vox Populi/Vox Populi.civ5proj +++ b/(2) Vox Populi/Vox Populi.civ5proj @@ -8,7 +8,7 @@ 8411a7a8-dad3-4622-a18e-fcc18324c799 17 Stable - (5.2.6) Unofficial Expansion and Rebalancing Project for Civilization V: Brave New World + (5.2.7) Unofficial Expansion and Rebalancing Project for Civilization V: Brave New World Contains balance changes, gameplay improvements, and entirely new game systems to make Civ V a more enjoyable and exciting game. CivFanatics Community Patch Project Team @@ -58,6 +58,11 @@ UpdateDatabase Database Changes/Art/NewImprovementArts.xml + + OnModActivated + UpdateDatabase + Database Changes/Art/NewFeatureArts.xml + OnModActivated UpdateDatabase @@ -1569,6 +1574,7 @@ + @@ -1590,6 +1596,7 @@ + @@ -1603,8 +1610,11 @@ + + + @@ -1612,6 +1622,8 @@ + + @@ -1647,6 +1659,7 @@ + @@ -1746,6 +1759,26 @@ Lua True
+ + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + Lua True @@ -2122,6 +2155,22 @@ Lua True + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + Lua True @@ -2718,6 +2767,58 @@ Lua True + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + Lua True @@ -3898,6 +3999,22 @@ Lua True + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + Lua True @@ -4226,6 +4343,22 @@ Lua True + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + Lua True @@ -4262,6 +4395,46 @@ Lua True + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + Lua True @@ -4438,6 +4611,34 @@ Lua True + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + + + Lua + True + Lua True @@ -5186,6 +5387,10 @@ Lua True + + Lua + True + Lua True @@ -5242,6 +5447,10 @@ Lua True + + Lua + True + Lua True @@ -5250,6 +5459,10 @@ Lua True + + Lua + True + Lua True @@ -5278,6 +5491,10 @@ Lua True + + Lua + True + Lua True @@ -5943,6 +6160,10 @@ Lua False + + Lua + False + Lua False diff --git a/(3a) VP - EUI Compatibility Files/(3a) VP - EUI Compatibility Files (v 1).modinfo b/(3a) VP - EUI Compatibility Files/(3a) VP - EUI Compatibility Files (v 1).modinfo index 17eb4f85d0..63993bec5b 100644 --- a/(3a) VP - EUI Compatibility Files/(3a) VP - EUI Compatibility Files (v 1).modinfo +++ b/(3a) VP - EUI Compatibility Files/(3a) VP - EUI Compatibility Files (v 1).modinfo @@ -2,7 +2,7 @@ (3a) VP - EUI Compatibility Files - (5.2.6) Vox Populi Compatibility and LUA (EUI Version) + (5.2.7) Vox Populi Compatibility and LUA (EUI Version) Contains LUA and mod compatibility files for Vox Populi if using EUI. Gazebo 0 @@ -31,11 +31,11 @@ EUI/DC45_VassalOverview.dds EUI/EUI_text_en_us.xml EUI/NeededText.xml - LUA/CityBannerManager.lua + LUA/CityBannerManager.lua LUA/CityBannerManager.xml - LUA/CityStateDiploPopup.lua + LUA/CityStateDiploPopup.lua LUA/CityStateDiploPopup.xml - LUA/CityStateStatusHelper.lua + LUA/CityStateStatusHelper.lua LUA/CityView.lua LUA/CityView.xml LUA/CityView_small.xml @@ -65,7 +65,7 @@ LUA/UnitPanel.xml LUA/UnitPanel_small.xml ImprovedTopPanel/Text.xml - ImprovedTopPanel/TopPanel.lua + ImprovedTopPanel/TopPanel.lua ImprovedTopPanel/TopPanel.xml ImprovedTechTree/TechTree.lua ImprovedTechTree/TechTree.xml diff --git a/(3a) VP - EUI Compatibility Files/EUI/NeededText.xml b/(3a) VP - EUI Compatibility Files/EUI/NeededText.xml index ee2251d8e3..596f380cc9 100644 --- a/(3a) VP - EUI Compatibility Files/EUI/NeededText.xml +++ b/(3a) VP - EUI Compatibility Files/EUI/NeededText.xml @@ -17,6 +17,9 @@ From Great People + + From Policies + From Corporation diff --git a/(3a) VP - EUI Compatibility Files/ImprovedTopPanel/TopPanel.lua b/(3a) VP - EUI Compatibility Files/ImprovedTopPanel/TopPanel.lua index 6948835bc7..dcf72ce075 100644 --- a/(3a) VP - EUI Compatibility Files/ImprovedTopPanel/TopPanel.lua +++ b/(3a) VP - EUI Compatibility Files/ImprovedTopPanel/TopPanel.lua @@ -467,7 +467,7 @@ local function UpdateTopPanelNow() local traitType = ""; if g_activePlayer ~= nil then leaderID = g_activePlayer:GetLeaderType(); - for leaderTraits in DB.Query( "SELECT TraitType FROM Leader_Traits INNER JOIN Leaders on Leaders.Type = LeaderType WHERE Leaders.ID = " .. leaderID ) do + for leaderTraits in DB.Query( "SELECT TraitType FROM Leader_Traits INNER JOIN Leaders on Leaders.Type = LeaderType WHERE Leaders.ID = ?", leaderID ) do traitType = leaderTraits.TraitType; break; end @@ -1467,7 +1467,8 @@ if civ5_mode then local ResourceHappiness = g_activePlayer:GetBonusHappinessFromLuxuriesFlat(); local AvgResourceHappiness = g_activePlayer:GetBonusHappinessFromLuxuriesFlatForUI(); local LocalCityHappiness = g_activePlayer:GetEmpireHappinessFromCities(); - local NaturalWonderAndLandmarkHappiness = g_activePlayer:GetHappinessFromNaturalWonders(); + local NaturalWonderHappiness = g_activePlayer:GetHappinessFromNaturalWonders(); + local ImprovementHappiness = g_activePlayer:GetHappinessFromImprovements(); local ReligionHappiness = g_activePlayer:GetHappinessFromReligion(); local LeagueHappiness = g_activePlayer:GetHappinessFromLeagues(); local EventHappiness = g_activePlayer:GetEventHappiness(); @@ -1481,7 +1482,8 @@ if civ5_mode then tips:insert( "[ENDCOLOR][COLOR:150:255:150:255]" ) tips:insertLocalizedBulletIfNonZero( "TXT_KEY_TP_HAPPINESS_RESOURCE_CITY", ResourceHappiness, AvgResourceHappiness ) - tips:insertLocalizedBulletIfNonZero( "TXT_KEY_TP_HAPPINESS_NATURAL_WONDERS", NaturalWonderAndLandmarkHappiness ) + tips:insertLocalizedBulletIfNonZero( "TXT_KEY_TP_HAPPINESS_NATURAL_WONDERS", NaturalWonderHappiness ) + tips:insertLocalizedBulletIfNonZero( "TXT_KEY_TP_HAPPINESS_IMPROVEMENTS", ImprovementHappiness ) tips:insertLocalizedBulletIfNonZero( "TXT_KEY_TP_HAPPINESS_STATE_RELIGION_VP", ReligionHappiness ) tips:insertLocalizedBulletIfNonZero( "TXT_KEY_TP_HAPPINESS_LEAGUES", LeagueHappiness ) tips:insertLocalizedBulletIfNonZero( "TXT_KEY_TP_HAPPINESS_EVENT", EventHappiness ) @@ -1512,7 +1514,7 @@ if civ5_mode then local BoredomUnhappiness = g_activePlayer:GetUnhappinessFromBoredom(); local ReligiousUnrestUnhappiness = g_activePlayer:GetUnhappinessFromReligiousUnrest(); local BuildingsUnhappiness = g_activePlayer:GetUnhappinessFromBuildings(); - local UrbanizationUnhappiness = g_activePlayer:GetUnhappinessFromCitySpecialists() / 100; + local UrbanizationUnhappiness = g_activePlayer:GetUnhappinessFromCitySpecialists(); local UrbanizationPuppetUnhappiness = g_activePlayer:GetUnhappinessFromPuppetCitySpecialists(); tips:insert( "[COLOR:255:150:150:255]" ) @@ -2030,7 +2032,7 @@ if civ5_mode and gk_mode then local tip = "" for _, resource in pairs( g_luxuries) do local resourceID = resource.ID - local quantity = g_activePlayer:GetNumResourceTotal( resourceID, false ) + g_activePlayer:GetResourceExport( resourceID ) - g_activePlayer:GetResourcesFromGP(resourceID) + local quantity = g_activePlayer:GetNumResourceTotal( resourceID, false ) + g_activePlayer:GetResourceExport( resourceID ) - g_activePlayer:GetResourcesFromGP(resourceID) - g_activePlayer:GetFreeResourceFromPolicies(resourceID) if quantity > 0 then tip = tip .. " " .. ColorizeAbs( quantity ) .. resource.IconString end @@ -2085,6 +2087,20 @@ if civ5_mode and gk_mode then tips:insert( "[COLOR_POSITIVE_TEXT]" .. L"TXT_KEY_EO_GP_RESOURCES" .. "[ENDCOLOR]" .. GPtip) end + ---------------------------- + -- Policy Resources + ---------------------------- + local PolicyTip = "" + for _, resource in pairs( g_luxuries) do + local numResourcePolicy = g_activePlayer:GetFreeResourceFromPolicies(resource.ID) + if numResourcePolicy > 0 then + PolicyTip = PolicyTip .. "[NEWLINE][ICON_BULLET]" .. ColorizeAbs( numResourcePolicy ) .. resource.IconString + end + end + if #PolicyTip > 0 and (LuxuryResourcesMem == 0 or LuxuryResourcesMem == 4) then + tips:insert( "[COLOR_POSITIVE_TEXT]" .. L"TXT_KEY_EO_POLICY_RESOURCES" .. "[ENDCOLOR]" .. PolicyTip) + end + ---------------------------- -- Import & Export Breakdown ---------------------------- @@ -2464,12 +2480,13 @@ local function ResourcesToolTip( control ) local numResourceGP = g_activePlayer:GetResourcesFromGP(resourceID) + local numResourcePolicy = g_activePlayer:GetFreeResourceFromPolicies(resourceID) local numResourceCorp = g_activePlayer:GetResourcesFromCorporation(resourceID) local numResourceFranchises = g_activePlayer:GetResourcesFromFranchises(resourceID) local numResourceCSAlly = g_activePlayer:GetResourceFromCSAlliances(resourceID) --want the total, but before GetStrategicResourceMod and GetResourceModFromReligion are applied, so have to remove Misc then add back in parts of it - local totalBeforeMod = g_activePlayer:GetNumResourceTotal( resourceID, false ) - numResourceMisc + numResourceGP + numResourceCorp + numResourceFranchises + numResourceCSAlly + local totalBeforeMod = g_activePlayer:GetNumResourceTotal( resourceID, false ) - numResourceMisc + numResourceGP + numResourcePolicy + numResourceCorp + numResourceFranchises + numResourceCSAlly local stratResMod = g_activePlayer:GetStrategicResourceMod() local resourceModRel = g_activePlayer:GetResourceModFromReligion(resourceID) @@ -2477,6 +2494,9 @@ local function ResourcesToolTip( control ) if numResourceGP > 0 then tips:insert( "[ICON_BULLET]" .. Colorize(numResourceGP) .. resource.IconString .. " " .. L"TXT_KEY_EO_GP_RESOURCES" ) end + if numResourcePolicy > 0 then + tips:insert( "[ICON_BULLET]" .. Colorize(numResourcePolicy) .. resource.IconString .. " " .. L"TXT_KEY_EO_POLICY_RESOURCES" ) + end if numResourceCorp > 0 then tips:insert( "[ICON_BULLET]" .. Colorize(numResourceCorp) .. resource.IconString .. " " .. L"TXT_KEY_EO_CORP_RESOURCES" ) end diff --git a/(3a) VP - EUI Compatibility Files/LUA/CityBannerManager.lua b/(3a) VP - EUI Compatibility Files/LUA/CityBannerManager.lua index a770167f95..7e6c46287c 100644 --- a/(3a) VP - EUI Compatibility Files/LUA/CityBannerManager.lua +++ b/(3a) VP - EUI Compatibility Files/LUA/CityBannerManager.lua @@ -1090,7 +1090,7 @@ local function RefreshCityBannersNow() -- Puppet ? instance.CityIsPuppet:SetHide( not isPuppet ) - -- Rome UA (Annexed City-States) + -- Annexed City-States if Players[city:GetOriginalOwner()]:IsMinorCiv() then instance.CityIsCityState:SetHide ( not cityOwner:IsAnnexedCityStatesGiveYields()) end diff --git a/(3a) VP - EUI Compatibility Files/LUA/CityStateDiploPopup.lua b/(3a) VP - EUI Compatibility Files/LUA/CityStateDiploPopup.lua index da9d29ba51..d461f3b2fc 100644 --- a/(3a) VP - EUI Compatibility Files/LUA/CityStateDiploPopup.lua +++ b/(3a) VP - EUI Compatibility Files/LUA/CityStateDiploPopup.lua @@ -1309,7 +1309,7 @@ end Controls.UnitTributeButton:RegisterCallback( Mouse.eLClick, OnUnitTributeButtonClicked ) ---------------------------------------------------------------- --- CBP: Forced Annex (Rome UA) +-- CBP: Forced Annex ---------------------------------------------------------------- function OnBullyAnnexButtonClicked() local minorPlayer = Players[g_minorCivID] @@ -1394,4 +1394,4 @@ function OnNoBully( ) Controls.BullyConfirm:SetHide(true) Controls.BGBlock:SetHide(false) end -Controls.NoBully:RegisterCallback( Mouse.eLClick, OnNoBully ) \ No newline at end of file +Controls.NoBully:RegisterCallback( Mouse.eLClick, OnNoBully ) diff --git a/(3a) VP - EUI Compatibility Files/LUA/CityStateStatusHelper.lua b/(3a) VP - EUI Compatibility Files/LUA/CityStateStatusHelper.lua index af7e96983b..389e14c6f6 100644 --- a/(3a) VP - EUI Compatibility Files/LUA/CityStateStatusHelper.lua +++ b/(3a) VP - EUI Compatibility Files/LUA/CityStateStatusHelper.lua @@ -451,6 +451,15 @@ function GetCityStateStatusToolTip( majorPlayerID, minorPlayerID, isFullInfo ) -- Status tip = tip .. " " .. GetCityStateStatusText( majorPlayerID, minorPlayerID ) table_insert( tips, tip ) + -- How far behind second place is the leader (and is it us?) + if GetContenderInfo(majorPlayerID, minorPlayerID) ~= influenceAccumulated .. "[ICON_INFLUENCE]" then + tip = L( "TXT_KEY_CSTATE_CONTENDER_INFO", GetContenderInfo(majorPlayerID, minorPlayerID) ) + table_insert( tips, tip ) + else + tip = L"TXT_KEY_CSTATE_CONTENDER_YOU" + table_insert( tips, tip ) + end + -- Embassy available? local bIsEmbassyCheck = false for k, v in pairs(tIsEmbassyImprovement) do if minorPlayer:GetImprovementCount(k) > 0 then diff --git a/(3a) VP - EUI Compatibility Files/LUA/TradeLogic.lua b/(3a) VP - EUI Compatibility Files/LUA/TradeLogic.lua index 776157fcbc..fb324a3669 100644 --- a/(3a) VP - EUI Compatibility Files/LUA/TradeLogic.lua +++ b/(3a) VP - EUI Compatibility Files/LUA/TradeLogic.lua @@ -2442,19 +2442,32 @@ function ResetDisplay() else strTooltip = Locale.ConvertTextKey("TXT_KEY_DIPLO_TO_TRADE_TECHNOLOGIES_TRADE_NO"); + -- No cities founded yet? + local bCityFoundingIssue = false; + if (not g_pUs:IsResearch()) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_TRADE_TECHNOLOGIES_NO_RESEARCH_PLAYER") .. "[ENDCOLOR]"; + bCityFoundingIssue = true; + end + if (not g_pThem:IsResearch()) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_TRADE_TECHNOLOGIES_NO_RESEARCH_OTHER_PLAYER") .. "[ENDCOLOR]"; + bCityFoundingIssue = true; + end + -- No tech? if (not g_pUsTeam:IsTechTrading()) then strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_TRADE_TECHNOLOGIES_NO_TECH_PLAYER") .. "[ENDCOLOR]"; end -- No embassy? - if ((not bAtWar) and ((not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) or (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam)))) then - if ((not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) and (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam))) then - strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_BOTH_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; - elseif (not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) then - strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_YOU_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; - elseif (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam)) then - strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_THEY_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; + if (not bCityFoundingIssue) then + if ((not bAtWar) and ((not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) or (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam)))) then + if ((not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) and (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam))) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_BOTH_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; + elseif (not g_pUsTeam:HasEmbassyAtTeam(g_iThemTeam)) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_YOU_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; + elseif (not g_pThemTeam:HasEmbassyAtTeam(g_iUsTeam)) then + strTooltip = strTooltip .. "[NEWLINE][NEWLINE][COLOR_NEGATIVE_TEXT]" .. Locale.ConvertTextKey("TXT_KEY_DIPLO_THEY_NEED_EMBASSY_TT") .. "[ENDCOLOR]"; + end end end diff --git a/(3a) VP - EUI Compatibility Files/VP EUI Compatibility.civ5proj b/(3a) VP - EUI Compatibility Files/VP EUI Compatibility.civ5proj index ec6e629b01..71b61b4e76 100644 --- a/(3a) VP - EUI Compatibility Files/VP EUI Compatibility.civ5proj +++ b/(3a) VP - EUI Compatibility Files/VP EUI Compatibility.civ5proj @@ -7,7 +7,7 @@ 24923240-e4fb-4bf6-8f0e-6e5b6cf4d3c2 1 Stable - (5.2.6) Vox Populi Compatibility and LUA (EUI Version) + (5.2.7) Vox Populi Compatibility and LUA (EUI Version) Contains LUA and mod compatibility files for Vox Populi if using EUI. Gazebo diff --git a/(3b) 43 Civs Community Patch/(3b) 43 Civs Community Patch (v 1).modinfo b/(3b) 43 Civs Community Patch/(3b) 43 Civs Community Patch (v 1).modinfo index a2cc8a4881..a82636fda6 100644 --- a/(3b) 43 Civs Community Patch/(3b) 43 Civs Community Patch (v 1).modinfo +++ b/(3b) 43 Civs Community Patch/(3b) 43 Civs Community Patch (v 1).modinfo @@ -2,7 +2,7 @@ (3b) 43 Civs Community Patch - (5.2.6) Supports up to 43 Major Civilizations + (5.2.7) Supports up to 43 Major Civilizations This version of the Community Patch supports up to 43 major civilizations. The maximum number of total players (civilizations and City-States) is 63.[NEWLINE][NEWLINE] If you downloaded this mod manually, see MANUAL INSTALL.txt for installation instructions. Gazebo, Ilteroi @@ -24,6 +24,6 @@ If you downloaded this mod manually, see MANUAL INSTALL.txt for installation ins AdvancedSetup.lua - CvGameCore_Expansion2.dll + CvGameCore_Expansion2.dll diff --git a/(3b) 43 Civs Community Patch/43 Civs Community Patch.civ5proj b/(3b) 43 Civs Community Patch/43 Civs Community Patch.civ5proj index 0fd9053567..61085050b4 100644 --- a/(3b) 43 Civs Community Patch/43 Civs Community Patch.civ5proj +++ b/(3b) 43 Civs Community Patch/43 Civs Community Patch.civ5proj @@ -7,7 +7,7 @@ 6521c02b-1c78-4826-a5b0-faed86235e98 1 Stable - (5.2.6) Supports up to 43 Major Civilizations + (5.2.7) Supports up to 43 Major Civilizations This version of the Community Patch supports up to 43 major civilizations. The maximum number of total players (civilizations and City-States) is 63.[NEWLINE][NEWLINE] If you downloaded this mod manually, see MANUAL INSTALL.txt for installation instructions. Gazebo, Ilteroi diff --git a/(3b) 43 Civs Community Patch/CvGameCore_Expansion2.dll b/(3b) 43 Civs Community Patch/CvGameCore_Expansion2.dll index 53dcf23faa..5c1cc08b8a 100644 Binary files a/(3b) 43 Civs Community Patch/CvGameCore_Expansion2.dll and b/(3b) 43 Civs Community Patch/CvGameCore_Expansion2.dll differ diff --git a/(4a) Squads for VP/(4a) Squads for VP (v 1).modinfo b/(4a) Squads for VP/(4a) Squads for VP (v 1).modinfo index 548f975159..ba84849ca4 100644 --- a/(4a) Squads for VP/(4a) Squads for VP (v 1).modinfo +++ b/(4a) Squads for VP/(4a) Squads for VP (v 1).modinfo @@ -2,7 +2,7 @@ (4a) Squads for VP - (5.2.6) RTS Style Control Groups and Group Movement + (5.2.7) RTS Style Control Groups and Group Movement QoL mod for organizing and moving groups of units at once. Squads is used in two modes: [COLOR_GREEN]Management Mode[ENDCOLOR] and [COLOR_GREEN]Movement Mode[ENDCOLOR].[NEWLINE][NEWLINE] In [COLOR_GREEN]Management Mode[ENDCOLOR], units can be added or removed with [COLOR_YELLOW]LCLICK[ENDCOLOR] on the unit or their flag. Groups of units can be added to a squad at once by holding [COLOR_YELLOW]RCLICK[ENDCOLOR] and dragging a box over units to be added. [COLOR_GREEN]Management Mode[ENDCOLOR] can be entered for a squad by pressing the [COLOR_BLUE]Manage Squad[ENDCOLOR] button on the UI with that squad selected, or using the key combination [COLOR_YELLOW]CTRL+NUM[ENDCOLOR] where num is the squad number key.[NEWLINE][NEWLINE] diff --git a/(4a) Squads for VP/Squads.civ5proj b/(4a) Squads for VP/Squads.civ5proj index 21fe0a9038..d3f53b5f16 100644 --- a/(4a) Squads for VP/Squads.civ5proj +++ b/(4a) Squads for VP/Squads.civ5proj @@ -7,7 +7,7 @@ 3645dbca-bdfb-4d86-bdc5-46cb5a426cc2 1 Stable - (5.2.6) RTS Style Control Groups and Group Movement + (5.2.7) RTS Style Control Groups and Group Movement QoL mod for organizing and moving groups of units at once. Squads is used in two modes: [COLOR_GREEN]Management Mode[ENDCOLOR] and [COLOR_GREEN]Movement Mode[ENDCOLOR].[NEWLINE][NEWLINE] In [COLOR_GREEN]Management Mode[ENDCOLOR], units can be added or removed with [COLOR_YELLOW]LCLICK[ENDCOLOR] on the unit or their flag. Groups of units can be added to a squad at once by holding [COLOR_YELLOW]RCLICK[ENDCOLOR] and dragging a box over units to be added. [COLOR_GREEN]Management Mode[ENDCOLOR] can be entered for a squad by pressing the [COLOR_BLUE]Manage Squad[ENDCOLOR] button on the UI with that squad selected, or using the key combination [COLOR_YELLOW]CTRL+NUM[ENDCOLOR] where num is the squad number key.[NEWLINE][NEWLINE] diff --git a/.github/ISSUE_TEMPLATE/bug_report_v5.yml b/.github/ISSUE_TEMPLATE/bug_report_v5.yml index 943073a078..08233813bf 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_v5.yml +++ b/.github/ISSUE_TEMPLATE/bug_report_v5.yml @@ -14,6 +14,7 @@ body: label: 1. Mod version description: "Please select which version you are using. Current Version: 5.2" options: + - 5.2.7 - 5.2.6 - 5.2.5 - 5.2.4 diff --git a/.luacheckrc b/.luacheckrc index 0db1d65093..d450c3d2fb 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -1,13 +1,210 @@ +-- Luacheck configuration for Community Patch DLL (Civilization V mod) +-- Globals sourced from: CvGameCoreDLL C++, Civ5XP, LuaCATS + +std = "lua51+CIV" + +allow_defined_top = true + ignore = { - '.luacheckrc', - '614', --[[ Trailing whitespace in a comment. ]] - '631', --[[ Line is too long. ]] + "611", -- Line contains only whitespace. + "612", -- Line contains trailing whitespace. + "614", -- Trailing whitespace in a comment. + "621", -- Inconsistent indentation (SPACE followed by TAB). + "631", -- Line is too long. } + +-- Engine-provided Lua environment for Civilization V stds.CIV = { + -- read_globals: provided by the engine, mods should not overwrite these + read_globals = { + -- Core game singletons (CvLuaStaticInstance / per-context) + "Game", "Map", "Players", "Teams", + "ContextPtr", "Controls", "Events", "LuaEvents", + + -- Engine library tables (ExposeLibraryToThread) + "ContentManager", "DB", "GameDefines", "GameInfo", + "Locale", "Matchmaking", "Modding", "Network", + "OptionsManager", "Path", "Steam", "UI", + + -- Engine globals (lua_setfield LUA_GLOBALSINDEX) + "ExposedMembers", "ExposedTypes", + "GameCore", "GameEventFactory", "GameEvents", + "LuaEventFactory", "PreGame", + "UIManager", "TTManager", + + -- Engine-registered data tables (lua_setglobal) + "Fractal", "GameInfoActions", "GameInfoTypes", + + -- DLL enum tables (CvLuaEnums::EnumStart) — all 62 tables + "AccomplishmentTypes", "ActionSubTypes", "ActivityTypes", + "AdvisorTypes", "AggressivePostureTypes", + "BeliefTypes", "ButtonPopupTypes", + "ChatTargetTypes", "CityAIFocusTypes", "CityUpdateTypes", + "CivApproachTypes", "CivOpinionTypes", "CombatPredictionTypes", + "CommandTypes", "ContractTypes", "ControlTypes", "CoopWarStates", + "CorporationTypes", "DiploUIStateTypes", "DirectionTypes", + "DisputeLevelTypes", "DomainTypes", "EndTurnBlockingTypes", + "FaithPurchaseTypes", "FeatureTypes", "FlowDirectionTypes", + "FogOfWarModeTypes", "FromUIDiploEventTypes", + "GameMessageTypes", "GameOptionTypes", "GameStates", + "GameplayGameStateTypes", "GenericWorldAnchorTypes", + "InfluenceLevelTrend", "InfluenceLevelTypes", + "InstantYieldType", "InterfaceDirtyBits", "InterfaceModeTypes", + "LeaderheadAnimationTypes", "MajorCivApproachTypes", + "MinorCivPersonalityTypes", "MinorCivQuestTypes", + "MinorCivTraitTypes", "MissionTypes", "NotificationTypes", + "OrderTypes", "PeaceTreatyTypes", "PlotTypes", + "PolicyBranchTypes", "PublicOpinionTypes", "ReligionTypes", + "ResolutionDecisionTypes", "ResourceUsageTypes", + "RoutePlanTypes", + "StrengthTypes", "TaskTypes", "TerraformingEventTypes", + "TerrainTypes", "ThreatTypes", "TradeConnectionTypes", + "TradeableItems", "YieldTypes", + + -- Binary-only enum tables (cvLuaUIEnumsLibrary) + "ButtonStates", "ContentType", "EndGameTypes", + "GameStateTypes", "GameTypes", "GameViewTypes", + "InfoCornerID", "KeyEvents", "Keys", + "Mouse", "MouseEvents", "MultiplayerLobbyMode", + "NetErrors", "NetKicked", + "PopupPriorities", "PopupPriority", + "SlotClaim", "SlotStatus", + "SystemUpdateUIType", "YieldDisplayTypes", + + -- Standalone engine functions (LuaThunk) + "GridToWorld", "HexToWorld", "HexVertexToWorld", + "ToGridFromHex", "ToHexFromGrid", + "InStrategicView", "IsGameCoreBusy", + "MouseOverStrategicViewResource", + "ProcessStrategicViewMouseClick", + "StrategicViewShowFeatures", "StrategicViewShowFogOfWar", + "SetStrategicViewOverlay", "SetStrategicViewIconSetting", + "GetStrategicViewOverlays", "GetStrategicViewIconSettings", + "GetGameViewRenderType", "SetGameViewRenderType", + "GetOverlayLegend", "SystemBuildYield", + "GetVolumeKnobValue", "SetVolumeKnobValue", + "GetVolumeKnobIDFromName", + "ToggleStrategicView", "UnitMoving", + "TruncateString", "LookUpControl", + "IsNull", + + -- Engine utility globals + "IncludeFileList", "RefreshIncludeFileList", + "Profiler", "Automation", "FLua", + "ContentManagerEnums", + + -- Firaxis extensions to standard libraries + math = { fields = { "clamp" } }, + table = { fields = { "fill", "count" } }, + + -- Vanilla game Lua utility classes/functions (from base game includes) + "FractalWorld", "MultilayeredFractal", "TerrainGenerator", + "GenerationalInstanceManager", + "GetShuffledCopyOfTable", + "ApplyGenericEntrySettings", + "PrintContentsOfTable", + "GameplayUtilities", + + -- FLuaVector (include("FLuaVector")) — constructors + vector math + "Vector2", "Vector3", "Vector4", "Color", + "VecAdd", "VecSub", "VecSubtract", + }, + + -- globals: things mod code legitimately reads AND writes globals = { - 'Locale', - 'include', - 'StateName' - } + "include", -- engine include function + "InstanceManager", -- LuaCATS InstanceManager class + "MapModData", -- cross-context shared data table + "StateName", -- context state tracking + "R", -- localization shortcut table + "LuaTypes", -- type registry + }, +} + +--------------------------------------------------------------------------- +-- Per-path overrides +--------------------------------------------------------------------------- + +-- LuaCATS type-definition stubs are not runnable code +files["LuaCATS/**"] = { exclude_files = { "**" } } + +-- Map scripts use many vanilla globals from base-game includes +files["**/Mapscripts/**"] = { + globals = { + "MG", + "GetNumNaturalWondersToPlace", + "AdjacentToSaltWater", + "GetMatchingPlots", + "ObtainLandmassBoundaries", + "PlaceBonusResources", "PlaceFish", "PlacePossibleFish", + "PlaceResourcesAndCityStates", "PlaceStrategicAndBonusResources", + "MeasureStartPlacementFertilityOfPlot", + "GenerateNextToCoastalLandDataTables", + "GetNumStartRegionAvoidForCiv", "GetNumStartRegionPriorityForCiv", + "GetStartRegionAvoidListForCiv_GetIDs", + "GetStartRegionPriorityListForCiv_GetIDs", + "CivNeedsCoastalStart", "CivNeedsPlaceFirstCoastalStart", + "CivNeedsRiverStart", + "CreateAxisChainWithDots", "CreateAxisChainWithDoubleDots", + "CreateAxisChainWithShiftedDots", "CreateSingleAxisIslandChain", + "CreateLineSegment", + "BuffIslands", "ApplyHexAdjustment", "ShiftMultiplier", + "PolarToCartesian", "VecSub", + "NO_RESOURCE", + }, +} + +-- UI code from base game (UI_bc1) uses vanilla UI utility includes +files["UI_bc1/**"] = { + globals = { + "SwapWorkLineInstance", + "RegisterCollapseBehavior", "RegisterTabBehavior", + "SizeParentToChildContent", + "PopulateAndAdd", "AddToPullDown", "PopulatePullDown", + "PopulateButton", + "GetBonusTips", "AddArticle", + "IdentifyTableIndex", + "GetPlayerAndTeamInfo", + "TestMembership", "TestEspionageStarted", + "IsUniqueColor", + "GetCivName", + "SetCurrentSelection", + "GetHighlightTexture", + "CityAvailableForRelocate", + "CityScreenClosed", + "AddRequiredBuildingButton", + }, +} + +-- EUI compatibility files +files["**/VP - EUI Compatibility Files/**"] = { + globals = { + "CPK", + "SwapWorkLineInstance", + "RegisterCollapseBehavior", + "SizeParentToChildContent", + "IdentifyTableIndex", + "GetBonusTips", "AddArticle", + "GetPlayerAndTeamInfo", + "TestMembership", + }, +} + +-- Kit modules define the CPK global +files["**/Kit/**"] = { + globals = { "CPK" }, +} + +-- Override Lua files that extend vanilla includes +files["**/Overrides/**"] = { + globals = { + "SwapWorkLineInstance", + "RegisterCollapseBehavior", + "SizeParentToChildContent", + "PopulateAndAdd", "PopulatePullDown", + "GetBonusTips", "AddArticle", + "IdentifyTableIndex", + "GetPlayerAndTeamInfo", + "TestMembership", + }, } -std = 'min+CIV' diff --git a/CvGameCoreDLLUtil/include/CvEnums.h b/CvGameCoreDLLUtil/include/CvEnums.h index e6d34857ef..d82e5c28fc 100644 --- a/CvGameCoreDLLUtil/include/CvEnums.h +++ b/CvGameCoreDLLUtil/include/CvEnums.h @@ -65,7 +65,6 @@ enum CLOSED_ENUM DiploStatementTypes DIPLO_STATEMENT_WORK_WITH_US_RANDFAILED, DIPLO_STATEMENT_END_WORK_WITH_US, DIPLO_STATEMENT_DENOUNCE, - DIPLO_STATEMENT_DENOUNCE_RANDFAILED, DIPLO_STATEMENT_END_WORK_AGAINST_SOMEONE, DIPLO_STATEMENT_COOP_WAR_REQUEST, DIPLO_STATEMENT_COOP_WAR_TIME, @@ -107,22 +106,18 @@ enum CLOSED_ENUM DiploStatementTypes DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE_RANDFAILED, DIPLO_STATEMENT_REQUEST_FRIEND_WAR, + DIPLO_STATEMENT_COMPLIMENT_THROTTLE, + DIPLO_STATEMENT_INSULT_THROTTLE, + DIPLO_STATEMENT_TAUNT_THROTTLE, + DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY, - DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY_RANDFAILED, DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND, - DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND_RANDFAILED, DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY, - DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY_RANDFAILED, DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND, - DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND_RANDFAILED, DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY, - DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY_RANDFAILED, DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND, - DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND_RANDFAILED, DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY, - DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY_RANDFAILED, DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND, - DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND_RANDFAILED, DIPLO_STATEMENT_SAME_POLICIES_FREEDOM, DIPLO_STATEMENT_SAME_POLICIES_ORDER, @@ -3625,7 +3620,7 @@ enum CLOSED_ENUM InstantYieldType INSTANT_YIELD_TYPE_TR_MOVEMENT_IN_FOREIGN = 41, INSTANT_YIELD_TYPE_IMPROVEMENT_BUILD = 42, INSTANT_YIELD_TYPE_LUA = 43, - INSTANT_YIELD_TYPE_RESEARCH_AGREMEENT = 44, + INSTANT_YIELD_TYPE_RESEARCH_AGREEMENT = 44, INSTANT_YIELD_TYPE_REFUND = 45, INSTANT_YIELD_TYPE_FAITH_REFUND = 46, INSTANT_YIELD_TYPE_BIRTH_HOLY_CITY = 47, @@ -3644,6 +3639,7 @@ enum CLOSED_ENUM InstantYieldType INSTANT_YIELD_TYPE_ANCIENT_RUIN = 60, INSTANT_YIELD_TYPE_PLUNDER_TRADE_ROUTE = 61, INSTANT_YIELD_TYPE_HEALING = 62, + INSTANT_YIELD_TYPE_SCRAP_OR_UPGRADE = 63, NUM_INSTANT_YIELD_TYPES ENUM_META_VALUE }; diff --git a/CvGameCoreDLL_Expansion2/CustomMods.cpp b/CvGameCoreDLL_Expansion2/CustomMods.cpp index e0e209277a..8d4aa5ccb3 100644 --- a/CvGameCoreDLL_Expansion2/CustomMods.cpp +++ b/CvGameCoreDLL_Expansion2/CustomMods.cpp @@ -178,9 +178,11 @@ void CustomMods::prefetchCache() { while (kPostDefines.Step()) { Database::Results kLookup; char szSQL[512]; - sprintf_s(szSQL, "select ROWID from %s where Type = '%s' LIMIT 1", kPostDefines.GetText("Table"), kPostDefines.GetText("Type")); + // Table name must be concatenated (SQLite doesn't support parameterized identifiers) + sprintf_s(szSQL, "select ROWID from %s where Type = ? LIMIT 1", kPostDefines.GetText("Table")); if (db->Execute(kLookup, szSQL)) { + kLookup.Bind(1, kPostDefines.GetText("Type")); if (kLookup.Step()) { kInsert.Bind(1, kPostDefines.GetText("Name")); kInsert.Bind(2, kLookup.GetInt(0)); diff --git a/CvGameCoreDLL_Expansion2/CvAIOperation.cpp b/CvGameCoreDLL_Expansion2/CvAIOperation.cpp index d0df0cf824..55ccddfe4a 100644 --- a/CvGameCoreDLL_Expansion2/CvAIOperation.cpp +++ b/CvGameCoreDLL_Expansion2/CvAIOperation.cpp @@ -954,7 +954,7 @@ bool CvAIOperation::BuyFinalUnit() CvUnit* pUnit = GET_PLAYER(m_eOwner).GetMilitaryAI()->BuyEmergencyUnit(thisSlotEntry.m_primaryUnitType, pCity); if(pUnit != NULL) { - pArmy->AddUnit(pUnit->GetID(), thisSlot.m_iSlotID, true); + pArmy->AddUnit(pUnit->GetID(), thisSlot.m_iSlotID, thisSlotEntry.m_requiredSlot); m_viListOfUnitsWeStillNeedToBuild.pop_front(); if (m_viListOfUnitsWeStillNeedToBuild.empty() && m_viListOfUnitsCitiesHaveCommittedToBuild.empty()) @@ -2901,7 +2901,7 @@ bool CvAIOperationNukeAttack::FindBestFitReserveUnit(OperationSlot thisOperation if(pLoopUnit && pLoopUnit->canNuke() && pLoopUnit->plot()==GetMusterPlot() && pLoopUnit->getArmyID()==-1) { CvArmyAI* pThisArmy = ownerPlayer.getArmyAI(thisOperationSlot.m_iArmyID); - pThisArmy->AddUnit(pLoopUnit->GetID(), thisOperationSlot.m_iSlotID,true); + pThisArmy->AddUnit(pLoopUnit->GetID(), thisOperationSlot.m_iSlotID, pThisArmy->GetSlotInfo(thisOperationSlot.m_iSlotID).m_requiredSlot); return true; } } diff --git a/CvGameCoreDLL_Expansion2/CvAStar.cpp b/CvGameCoreDLL_Expansion2/CvAStar.cpp index 119a3af052..41b5dfce58 100644 --- a/CvGameCoreDLL_Expansion2/CvAStar.cpp +++ b/CvGameCoreDLL_Expansion2/CvAStar.cpp @@ -1310,10 +1310,13 @@ int PathEndTurnCost(CvPlot* pToPlot, const CvPathNodeCacheData& kToNodeCacheData //be extra careful if requested but don't really abort, else we might not find a path at all int iScale = bAbortInDanger ? 2 : 1; + if (bAbortInDanger && iPlotDanger > pUnit->GetCurrHitPoints() * 3 / 2 && iTurnsInFuture < 2) + return -1; // is there ever a good reason to allow this? + //combat units can still tolerate some danger //embarkation is handled implicitly because danger value will be higher //GetDanger returns MAX_INT for "fatal" plots (city about to fall, etc) - handle without overflow - int iScaledDanger = (iPlotDanger >= INT_MAX / 2) ? INT_MAX : iPlotDanger * iScale; + int iScaledDanger = (iPlotDanger >= INT_MAX / 2 && iScale > 1) ? INT_MAX : iPlotDanger * iScale; if (iScaledDanger >= pUnit->GetCurrHitPoints()*3) iCost += PATH_END_TURN_MORTAL_DANGER_WEIGHT*iFutureFactor; else if (iScaledDanger >= pUnit->GetCurrHitPoints()) diff --git a/CvGameCoreDLL_Expansion2/CvAdvisorCounsel.cpp b/CvGameCoreDLL_Expansion2/CvAdvisorCounsel.cpp index 210ef579d0..3c99cefc79 100644 --- a/CvGameCoreDLL_Expansion2/CvAdvisorCounsel.cpp +++ b/CvGameCoreDLL_Expansion2/CvAdvisorCounsel.cpp @@ -993,34 +993,25 @@ void CvAdvisorCounsel::BuildCounselList(PlayerTypes ePlayer) PlayerTypes eOtherPlayer = (PlayerTypes)iPlayer; TeamTypes eOtherTeam = GET_PLAYER(eOtherPlayer).getTeam(); - // don't evaluate yourself - if(eOtherPlayer == ePlayer) - { + // don't evaluate yourself or teammates + if (eOtherTeam == eTeam) continue; - } - - // don't evaluate teammates - if(eOtherTeam == eTeam) - { - continue; - } // don't evaluate dead people - if(!GET_PLAYER(eOtherPlayer).isAlive()) - { + if (!GET_PLAYER(eOtherPlayer).isAlive()) continue; - } // don't evaluate players we haven't met - if(!GET_TEAM(eTeam).isHasMet(eOtherTeam)) - { + if (!GET_TEAM(eTeam).isHasMet(eOtherTeam)) continue; - } - if(GET_TEAM(eTeam).isAtWar(eOtherTeam)) - { + // don't evaluate players we're at war with + if (GET_TEAM(eTeam).isAtWar(eOtherTeam)) + continue; + + // don't evaluate based on hidden diplomacy modifiers + if (GET_PLAYER(eOtherPlayer).GetDiplomacyAI()->ShouldHideDisputeMods(ePlayer)) continue; - } DisputeLevelTypes eMinorCivDisputeLevel = pDiplomacyAI->GetMinorCivDisputeLevel(eOtherPlayer); switch(eMinorCivDisputeLevel) diff --git a/CvGameCoreDLL_Expansion2/CvBeliefClasses.cpp b/CvGameCoreDLL_Expansion2/CvBeliefClasses.cpp index af4320970f..54bd2504a0 100644 --- a/CvGameCoreDLL_Expansion2/CvBeliefClasses.cpp +++ b/CvGameCoreDLL_Expansion2/CvBeliefClasses.cpp @@ -105,8 +105,10 @@ CvBeliefEntry::CvBeliefEntry() : m_piYieldFromHost(NULL), m_piYieldFromFaithPurchase(NULL), m_piYieldFromKnownPantheons(NULL), - m_iCombatVersusOtherReligionOwnLands(0), - m_iCombatVersusOtherReligionTheirLands(0), + m_iCombatBonusOwnLands(0), + m_iCombatBonusVersusOtherReligionOwnLands(0), + m_iCombatBonusTheirLands(0), + m_iCombatBonusVersusOtherReligionTheirLands(0), m_iMissionaryInfluenceCS(0), m_iHappinessPerPantheon(0), m_iExtraVotes(0), @@ -117,7 +119,9 @@ CvBeliefEntry::CvBeliefEntry() : m_bAIGoodStartingPantheon(false), m_piMaxYieldPerFollower(NULL), m_piMaxYieldPerFollowerPercent(NULL), + m_iCivilianWorkRate(0), m_piImprovementVoteChange(NULL), + m_piUnitCombatProductionModifiers(NULL), m_iReducePolicyRequirements(0), m_iCSYieldBonus(0), @@ -154,6 +158,7 @@ CvBeliefEntry::CvBeliefEntry() : m_piGreatPersonPoints(NULL), m_piCapitalYieldChange(NULL), m_piCoastalCityYieldChange(NULL), + m_ppiNearbyTerrainYieldChange(NULL), m_piGreatWorkYieldChange(NULL), m_piYieldFromKills(NULL), m_piYieldFromRemoveHeresy(NULL), @@ -245,6 +250,7 @@ CvBeliefEntry::~CvBeliefEntry() SAFE_DELETE_ARRAY(m_piMaxYieldPerFollower); SAFE_DELETE_ARRAY(m_piMaxYieldPerFollowerPercent); SAFE_DELETE_ARRAY(m_piImprovementVoteChange); + SAFE_DELETE_ARRAY(m_piUnitCombatProductionModifiers); CvDatabaseUtility::SafeDelete2DArray(m_ppiImprovementYieldChanges); CvDatabaseUtility::SafeDelete2DArray(m_ppiBuildingClassYieldChanges); @@ -255,6 +261,7 @@ CvBeliefEntry::~CvBeliefEntry() CvDatabaseUtility::SafeDelete2DArray(m_ppiUnimprovedFeatureYieldChanges); CvDatabaseUtility::SafeDelete2DArray(m_ppaiResourceYieldChange); CvDatabaseUtility::SafeDelete2DArray(m_ppaiTerrainYieldChange); + CvDatabaseUtility::SafeDelete2DArray(m_ppiNearbyTerrainYieldChange); CvDatabaseUtility::SafeDelete2DArray(m_ppiTradeRouteYieldChange); CvDatabaseUtility::SafeDelete2DArray(m_ppiSpecialistYieldChange); CvDatabaseUtility::SafeDelete2DArray(m_ppiGreatPersonExpendedYield); @@ -818,24 +825,43 @@ int CvBeliefEntry::GetCSYieldBonus() const { return m_iCSYieldBonus; } - -/// Accessor:: Extra yield from an improvement +int CvBeliefEntry::GetCivilianWorkRate() const +{ + return m_iCivilianWorkRate; +} +/// Accessor:: Extra votes from an improvement int CvBeliefEntry::GetImprovementVoteChange(ImprovementTypes eIndex1) const { PRECONDITION(eIndex1 < GC.getNumImprovementInfos(), "Index out of bounds"); PRECONDITION(eIndex1 > -1, "Index out of bounds"); return m_piImprovementVoteChange ? m_piImprovementVoteChange[eIndex1] : 0; } - -/// Accessor: combat bonus v. other in own lands -int CvBeliefEntry::GetCombatVersusOtherReligionOwnLands() const +/// Accessor:: Extra unit combat production modifer +int CvBeliefEntry::GetUnitCombatProductionModifiers(int i) const { - return m_iCombatVersusOtherReligionOwnLands; + PRECONDITION(i < GC.getNumUnitCombatClassInfos(), "Index out of bounds"); + PRECONDITION(i > -1, "Index out of bounds"); + return m_piUnitCombatProductionModifiers ? m_piUnitCombatProductionModifiers[i] : -1; +} +/// Accessor: combat bonus in own lands +int CvBeliefEntry::GetCombatBonusOwnLands() const +{ + return m_iCombatBonusOwnLands; } -/// Accessor: combat bonus v. other in enemy lands -int CvBeliefEntry::GetCombatVersusOtherReligionTheirLands() const +/// Accessor: combat bonus v. other religion in own lands +int CvBeliefEntry::GetCombatBonusVersusOtherReligionOwnLands() const { - return m_iCombatVersusOtherReligionTheirLands; + return m_iCombatBonusVersusOtherReligionOwnLands; +} +/// Accessor: combat bonus in enemy lands +int CvBeliefEntry::GetCombatBonusTheirLands() const +{ + return m_iCombatBonusTheirLands; +} +/// Accessor: combat bonus v. other religion in enemy lands +int CvBeliefEntry::GetCombatBonusVersusOtherReligionTheirLands() const +{ + return m_iCombatBonusVersusOtherReligionTheirLands; } /// Accessor: missionary CS influence int CvBeliefEntry::GetMissionaryInfluenceCS() const @@ -1117,6 +1143,11 @@ int CvBeliefEntry::GetCoastalCityYieldChange(int i) const return m_piCoastalCityYieldChange ? m_piCoastalCityYieldChange[i] : 0; } +int CvBeliefEntry::GetNearbyTerrainYieldChange(int i, int j) const +{ + return m_ppiNearbyTerrainYieldChange ? m_ppiNearbyTerrainYieldChange[i][j] : 0; +} + int CvBeliefEntry::GetGreatWorkYieldChange(int i) const { return m_piGreatWorkYieldChange ? m_piGreatWorkYieldChange[i] : 0; @@ -1340,6 +1371,7 @@ bool CvBeliefEntry::CacheResults(Database::Results& kResults, CvDatabaseUtility& m_iInquisitorPressureRetention = kResults.GetInt("InquisitorPressureRetention"); m_iFaithBuildingTourism = kResults.GetInt("FaithBuildingTourism"); m_iFullyConvertedHappiness = kResults.GetInt("FullyConvertedHappiness"); + m_iCivilianWorkRate = kResults.GetInt("CivilianWorkRate"); m_bPantheon = kResults.GetBool("Pantheon"); m_bFounder = kResults.GetBool("Founder"); @@ -1358,8 +1390,10 @@ bool CvBeliefEntry::CacheResults(Database::Results& kResults, CvDatabaseUtility& m_iHappinessFromForeignSpies = kResults.GetInt("HappinessFromForeignSpies"); m_iGetPressureChangeTradeRoute = kResults.GetInt("PressureChangeTradeRoute"); - m_iCombatVersusOtherReligionOwnLands = kResults.GetInt("CombatVersusOtherReligionOwnLands"); - m_iCombatVersusOtherReligionTheirLands = kResults.GetInt("CombatVersusOtherReligionTheirLands"); + m_iCombatBonusOwnLands = kResults.GetInt("CombatBonusOwnLands"); + m_iCombatBonusVersusOtherReligionOwnLands = kResults.GetInt("CombatBonusVersusOtherReligionOwnLands"); + m_iCombatBonusTheirLands = kResults.GetInt("CombatBonusTheirLands"); + m_iCombatBonusVersusOtherReligionTheirLands = kResults.GetInt("CombatBonusVersusOtherReligionTheirLands"); m_iMissionaryInfluenceCS = kResults.GetInt("MissionaryInfluenceCS"); m_iHappinessPerPantheon = kResults.GetInt("HappinessPerPantheon"); m_iExtraVotes = kResults.GetInt("ExtraVotes"); @@ -1417,6 +1451,8 @@ bool CvBeliefEntry::CacheResults(Database::Results& kResults, CvDatabaseUtility& kUtility.PopulateArrayByValue(m_piMaxYieldPerFollowerPercent, "Yields", "Belief_MaxYieldPerFollowerPercent", "YieldType", "BeliefType", szBeliefType, "Max"); kUtility.PopulateArrayByValue(m_piImprovementVoteChange, "Improvements", "Belief_VotePerXImprovementOwned", "ImprovementType", "BeliefType", szBeliefType, "Amount"); + + kUtility.PopulateArrayByValue(m_piUnitCombatProductionModifiers, "UnitCombatInfos", "Belief_UnitCombatProductionModifiers", "UnitCombatType", "BeliefType", szBeliefType, "Modifier"); m_iReducePolicyRequirements = kResults.GetInt("ReducePolicyRequirements"); m_iCSYieldBonus = kResults.GetInt("CSYieldBonusFromSharedReligion"); @@ -1781,6 +1817,30 @@ bool CvBeliefEntry::CacheResults(Database::Results& kResults, CvDatabaseUtility& kUtility.PopulateArrayByValue(m_piGreatPersonPoints, "GreatPersons", "Belief_GreatPersonPoints", "GreatPersonType", "BeliefType", szBeliefType, "Value"); kUtility.SetYields(m_piCapitalYieldChange, "Belief_CapitalYieldChanges", "BeliefType", szBeliefType); kUtility.SetYields(m_piCoastalCityYieldChange, "Belief_CoastalCityYieldChanges", "BeliefType", szBeliefType); + + //NearbyTerrainYieldChanges + { + kUtility.Initialize2DArray(m_ppiNearbyTerrainYieldChange, "Terrains", "Yields"); + + std::string strKey("Belief_NearbyTerrainYieldChanges"); + Database::Results* pResults = kUtility.GetResults(strKey); + if (pResults == NULL) + { + pResults = kUtility.PrepareResults(strKey, "select Terrains.ID as TerrainID, Yields.ID as YieldID, Yield from Belief_NearbyTerrainYieldChanges inner join Terrains on Terrains.Type = TerrainType inner join Yields on Yields.Type = YieldType where BeliefType = ?"); + } + + pResults->Bind(1, szBeliefType); + + while (pResults->Step()) + { + const int TerrainID = pResults->GetInt(0); + const int YieldID = pResults->GetInt(1); + const int yield = pResults->GetInt(2); + + m_ppiNearbyTerrainYieldChange[TerrainID][YieldID] = yield; + } + } + kUtility.SetYields(m_piGreatWorkYieldChange, "Belief_GreatWorkYieldChanges", "BeliefType", szBeliefType); kUtility.SetYields(m_piYieldFromKills, "Belief_YieldFromKills", "BeliefType", szBeliefType); kUtility.SetYields(m_piYieldFromRemoveHeresy, "Belief_YieldFromRemoveHeresy", "BeliefType", szBeliefType); @@ -2640,7 +2700,7 @@ int CvReligionBeliefs::GetFullyConvertedHappiness(PlayerTypes ePlayer, const CvC return rtnValue; } -int CvReligionBeliefs::GetCombatVersusOtherReligionOwnLands(PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const +int CvReligionBeliefs::GetCombatBonusOwnLands(PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const { CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); int rtnValue = 0; @@ -2648,7 +2708,7 @@ int CvReligionBeliefs::GetCombatVersusOtherReligionOwnLands(PlayerTypes ePlayer, for(BeliefList::const_iterator it = m_ReligionBeliefs.begin(); it != m_ReligionBeliefs.end(); ++it) { - int iValue = pBeliefs->GetEntry(*it)->GetCombatVersusOtherReligionOwnLands(); + int iValue = pBeliefs->GetEntry(*it)->GetCombatBonusOwnLands(); if (iValue != 0 && IsBeliefValid((BeliefTypes)*it, GetReligion(), ePlayer, pCity, bHolyCityOnly)) { rtnValue += iValue; @@ -2657,7 +2717,7 @@ int CvReligionBeliefs::GetCombatVersusOtherReligionOwnLands(PlayerTypes ePlayer, return rtnValue; } -int CvReligionBeliefs::GetCombatVersusOtherReligionTheirLands(PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const +int CvReligionBeliefs::GetCombatBonusVersusOtherReligionOwnLands(PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const { CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); int rtnValue = 0; @@ -2665,7 +2725,41 @@ int CvReligionBeliefs::GetCombatVersusOtherReligionTheirLands(PlayerTypes ePlaye for(BeliefList::const_iterator it = m_ReligionBeliefs.begin(); it != m_ReligionBeliefs.end(); ++it) { - int iValue = pBeliefs->GetEntry(*it)->GetCombatVersusOtherReligionTheirLands(); + int iValue = pBeliefs->GetEntry(*it)->GetCombatBonusVersusOtherReligionOwnLands(); + if (iValue != 0 && IsBeliefValid((BeliefTypes)*it, GetReligion(), ePlayer, pCity, bHolyCityOnly)) + { + rtnValue += iValue; + } + } + + return rtnValue; +} +int CvReligionBeliefs::GetCombatBonusTheirLands(PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const +{ + CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); + int rtnValue = 0; + + + for(BeliefList::const_iterator it = m_ReligionBeliefs.begin(); it != m_ReligionBeliefs.end(); ++it) + { + int iValue = pBeliefs->GetEntry(*it)->GetCombatBonusTheirLands(); + if (iValue != 0 && IsBeliefValid((BeliefTypes)*it, GetReligion(), ePlayer, pCity, bHolyCityOnly)) + { + rtnValue += iValue; + } + } + + return rtnValue; +} +int CvReligionBeliefs::GetCombatBonusVersusOtherReligionTheirLands(PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const +{ + CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); + int rtnValue = 0; + + + for(BeliefList::const_iterator it = m_ReligionBeliefs.begin(); it != m_ReligionBeliefs.end(); ++it) + { + int iValue = pBeliefs->GetEntry(*it)->GetCombatBonusVersusOtherReligionTheirLands(); if (iValue != 0 && IsBeliefValid((BeliefTypes)*it, GetReligion(), ePlayer, pCity, bHolyCityOnly)) { rtnValue += iValue; @@ -3563,6 +3657,56 @@ int CvReligionBeliefs::GetCoastalCityYieldChange(int iPopulation, YieldTypes eYi return rtnValue; } +int CvReligionBeliefs::GetNearbyTerrainYieldChange(int iPopulation, TerrainTypes eTerrain, YieldTypes eYield, PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const +{ + CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); + int rtnValue = 0; + + for (BeliefList::const_iterator it = m_ReligionBeliefs.begin(); it != m_ReligionBeliefs.end(); ++it) + { + int iValue = 0; + if (iPopulation >= pBeliefs->GetEntry(*it)->GetMinPopulation()) + { + iValue = pBeliefs->GetEntry(*it)->GetNearbyTerrainYieldChange((int)eTerrain, (int)eYield); + } + if (iValue != 0 && IsBeliefValid((BeliefTypes)*it, GetReligion(), ePlayer, pCity, bHolyCityOnly)) + { + rtnValue += iValue; + } + } + + return rtnValue; +} + +// For each active belief, take the max yield across all matching terrains, then sum across beliefs. +// This ensures a city adjacent to both tundra and snow gets each belief's bonus once, not stacked, +// but two separate beliefs with different terrains both apply. +int CvReligionBeliefs::GetNearbyTerrainYieldChangeForCity(int iPopulation, const std::vector& abTerrainMatch, YieldTypes eYield, PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const +{ + CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); + int rtnValue = 0; + + for (BeliefList::const_iterator it = m_ReligionBeliefs.begin(); it != m_ReligionBeliefs.end(); ++it) + { + if (iPopulation < pBeliefs->GetEntry(*it)->GetMinPopulation()) + continue; + if (!IsBeliefValid((BeliefTypes)*it, GetReligion(), ePlayer, pCity, bHolyCityOnly)) + continue; + + int iMaxForThisBelief = 0; + for (int iTerrain = 0; iTerrain < (int)abTerrainMatch.size(); iTerrain++) + { + if (abTerrainMatch[iTerrain]) + { + iMaxForThisBelief = max(iMaxForThisBelief, pBeliefs->GetEntry(*it)->GetNearbyTerrainYieldChange(iTerrain, (int)eYield)); + } + } + rtnValue += iMaxForThisBelief; + } + + return rtnValue; +} + int CvReligionBeliefs::GetGreatWorkYieldChange(int iPopulation, YieldTypes eYield, PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const { CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); @@ -4163,7 +4307,7 @@ int CvReligionBeliefs::GetPressureChangeTradeRoute(PlayerTypes ePlayer, const Cv return rtnValue; } -/// Get bonus pressure from trade routes +/// Get bonus happiness from foreign spies int CvReligionBeliefs::GetHappinessFromForeignSpies(PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const { CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); @@ -4654,6 +4798,24 @@ int CvReligionBeliefs::GetCSYieldBonus(PlayerTypes ePlayer, const CvCity* pCity, return rtnValue; } +/// Get bonus work rate for civilians +int CvReligionBeliefs::GetCivilianWorkRate(PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const +{ + CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); + int rtnValue = 0; + + for(BeliefList::const_iterator it = m_ReligionBeliefs.begin(); it != m_ReligionBeliefs.end(); ++it) + { + int iValue = pBeliefs->GetEntry(*it)->GetCivilianWorkRate(); + if (iValue != 0 && IsBeliefValid((BeliefTypes)*it, GetReligion(), ePlayer, pCity, bHolyCityOnly)) + { + rtnValue += iValue; + } + } + + return rtnValue; +} + /// Get votes per improvement (fractional) from belief fraction CvReligionBeliefs::GetVoteFromOwnedImprovement(ImprovementTypes eImprovement, PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const { @@ -4675,6 +4837,23 @@ fraction CvReligionBeliefs::GetVoteFromOwnedImprovement(ImprovementTypes eImprov return fVotes; } +/// Get yield from beliefs from # of followers halved +int CvReligionBeliefs::GetUnitCombatProductionModifiers(UnitCombatTypes eUnitCombat, PlayerTypes ePlayer, const CvCity* pCity, bool bHolyCityOnly) const +{ + CvBeliefXMLEntries* pBeliefs = GC.GetGameBeliefs(); + int rtnValue = 0; + + for (BeliefList::const_iterator it = m_ReligionBeliefs.begin(); it != m_ReligionBeliefs.end(); ++it) + { + int iValue = pBeliefs->GetEntry(*it)->GetUnitCombatProductionModifiers(eUnitCombat); + if (iValue != 0 && IsBeliefValid((BeliefTypes)*it, GetReligion(), ePlayer, pCity, bHolyCityOnly)) + { + rtnValue += iValue; + } + } + + return rtnValue; +} /// Get unique civ CivilizationTypes CvReligionBeliefs::GetUniqueCiv(PlayerTypes ePlayer, bool bHolyCityOnly) const diff --git a/CvGameCoreDLL_Expansion2/CvBeliefClasses.h b/CvGameCoreDLL_Expansion2/CvBeliefClasses.h index 112d78926a..106a21a403 100644 --- a/CvGameCoreDLL_Expansion2/CvBeliefClasses.h +++ b/CvGameCoreDLL_Expansion2/CvBeliefClasses.h @@ -113,8 +113,10 @@ class CvBeliefEntry: public CvBaseInfo int GetYieldFromHost(int i) const; int GetYieldFromFaithPurchase(int i) const; int GetYieldFromKnownPantheons(int i) const; - int GetCombatVersusOtherReligionOwnLands() const; - int GetCombatVersusOtherReligionTheirLands() const; + int GetCombatBonusOwnLands() const; + int GetCombatBonusVersusOtherReligionOwnLands() const; + int GetCombatBonusTheirLands() const; + int GetCombatBonusVersusOtherReligionTheirLands() const; int GetMissionaryInfluenceCS()const; int GetHappinessPerPantheon() const; int GetExtraVotes() const; @@ -127,7 +129,9 @@ class CvBeliefEntry: public CvBaseInfo int GetMaxYieldPerFollowerPercent(int i) const; int GetIgnorePolicyRequirementsAmount() const; int GetCSYieldBonus() const; + int GetCivilianWorkRate() const; int GetImprovementVoteChange(ImprovementTypes eIndex1) const; + int GetUnitCombatProductionModifiers(int i) const; CivilizationTypes GetRequiredCivilization() const; EraTypes GetObsoleteEra() const; @@ -167,6 +171,7 @@ class CvBeliefEntry: public CvBaseInfo int GetGoldenAgeGreatPersonRateModifier(int i) const; int GetCapitalYieldChange(int i) const; int GetCoastalCityYieldChange(int i) const; + int GetNearbyTerrainYieldChange(int i, int j) const; int GetGreatWorkYieldChange(int i) const; int GetYieldFromKills(YieldTypes eYield) const; int GetYieldFromRemoveHeresy(YieldTypes eYield) const; @@ -286,6 +291,7 @@ class CvBeliefEntry: public CvBaseInfo int* m_piGreatPersonPoints; int* m_piCapitalYieldChange; int* m_piCoastalCityYieldChange; + int** m_ppiNearbyTerrainYieldChange; int* m_piGreatWorkYieldChange; int* m_piYieldFromKills; int* m_piYieldFromRemoveHeresy; @@ -337,10 +343,13 @@ class CvBeliefEntry: public CvBaseInfo int* m_piMaxYieldPerFollower; int* m_piMaxYieldPerFollowerPercent; int* m_piImprovementVoteChange; + int* m_piUnitCombatProductionModifiers; int m_iReducePolicyRequirements; int m_iCSYieldBonus; - int m_iCombatVersusOtherReligionOwnLands; - int m_iCombatVersusOtherReligionTheirLands; + int m_iCombatBonusOwnLands; + int m_iCombatBonusVersusOtherReligionOwnLands; + int m_iCombatBonusTheirLands; + int m_iCombatBonusVersusOtherReligionTheirLands; int m_iMissionaryInfluenceCS; int m_iHappinessPerPantheon; int m_iExtraVotes; @@ -348,6 +357,7 @@ class CvBeliefEntry: public CvBaseInfo int m_iFollowerScalerLimiter; int m_iPolicyReductionWonderXFollowerCities; int m_iGreatPeopleFaithCostMod; + int m_iCivilianWorkRate; bool m_bAIGoodStartingPantheon; CivilizationTypes m_eRequiredCivilization; @@ -436,8 +446,10 @@ class CvReligionBeliefs int GetInquisitorPressureRetention(PlayerTypes ePlayer = NO_PLAYER, bool bHolyCityOnly = false) const; int GetFaithBuildingTourism(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetFullyConvertedHappiness(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; - int GetCombatVersusOtherReligionOwnLands(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; - int GetCombatVersusOtherReligionTheirLands(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; + int GetCombatBonusOwnLands(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; + int GetCombatBonusVersusOtherReligionOwnLands(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; + int GetCombatBonusTheirLands(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; + int GetCombatBonusVersusOtherReligionTheirLands(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetMissionaryInfluenceCS(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetHappinessPerPantheon(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetExtraVotes(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; @@ -488,6 +500,8 @@ class CvReligionBeliefs int GetGoldenAgeGreatPersonRateModifier(GreatPersonTypes eGreatPerson, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetCapitalYieldChange(int iPopulation, YieldTypes eYield, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetCoastalCityYieldChange(int iPopulation, YieldTypes eYield, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; + int GetNearbyTerrainYieldChange(int iPopulation, TerrainTypes eTerrain, YieldTypes eYield, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; + int GetNearbyTerrainYieldChangeForCity(int iPopulation, const std::vector& abTerrainMatch, YieldTypes eYield, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetGreatWorkYieldChange(int iPopulation, YieldTypes eYield, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetYieldFromBarbarianKills(YieldTypes eYield, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetYieldFromKills(YieldTypes eYield, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; @@ -551,7 +565,9 @@ class CvReligionBeliefs CivilizationTypes GetUniqueCiv(PlayerTypes ePlayer = NO_PLAYER, bool bHolyCityOnly = false) const; int GetIgnorePolicyRequirementsAmount(EraTypes eEra, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; int GetCSYieldBonus(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; + int GetCivilianWorkRate(PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; fraction GetVoteFromOwnedImprovement(ImprovementTypes eImprovement, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; + int GetUnitCombatProductionModifiers(UnitCombatTypes eUnitCombat, PlayerTypes ePlayer = NO_PLAYER, const CvCity* pCity = NULL, bool bHolyCityOnly = false) const; const BeliefList& GetBeliefList() const { return m_ReligionBeliefs; } diff --git a/CvGameCoreDLL_Expansion2/CvCity.cpp b/CvGameCoreDLL_Expansion2/CvCity.cpp index 4ae05cfbf1..ba10e5e988 100644 --- a/CvGameCoreDLL_Expansion2/CvCity.cpp +++ b/CvGameCoreDLL_Expansion2/CvCity.cpp @@ -494,6 +494,7 @@ CvCity::CvCity() : , m_iResistanceCounter() , m_iPlagueCounter() , m_iPlagueTurns() + , m_iDefenseProcessTurns() , m_iSappedTurns() , m_iBuildingProductionBlockedTurns() , m_iNoTourismTurns() @@ -1647,6 +1648,7 @@ void CvCity::reset(int iID, PlayerTypes eOwner, int iX, int iY, bool bConstructo m_iResistanceCounter = 0; m_iPlagueCounter = 0; m_iPlagueTurns = -1; + m_iDefenseProcessTurns = 0; m_iSappedTurns = 0; m_iBuildingProductionBlockedTurns = 0; m_iNoTourismTurns = 0; @@ -2306,6 +2308,13 @@ void CvCity::doTurn() { VALIDATE_OBJECT(); + bool bRunningDefenseProcess = false; + if (getProductionProcess() != NO_PROCESS) + { + CvProcessInfo* pkProcessInfo = GC.getProcessInfo(getProductionProcess()); + bRunningDefenseProcess = pkProcessInfo && pkProcessInfo->getDefenseValuePerTurn() != 0; + } + ResetGreatWorkYieldCache(); if (getDamage() > 0 && !IsBlockadedWaterAndLand()) @@ -2329,9 +2338,13 @@ void CvCity::doTurn() if (getProductionProcess() != NO_PROCESS) { CvProcessInfo* pkProcessInfo = GC.getProcessInfo(getProductionProcess()); - if (pkProcessInfo && pkProcessInfo->getDefenseValue() != 0) + if (pkProcessInfo && (pkProcessInfo->getDefenseValue() != 0 || pkProcessInfo->getDefenseValuePerTurn() != 0)) { - int iPile = getYieldRateTimes100(YIELD_PRODUCTION) * pkProcessInfo->getDefenseValue(); + int iDefenseValue = pkProcessInfo->getDefenseValue() + GetDefenseProcessTurns() * pkProcessInfo->getDefenseValuePerTurn(); + if (pkProcessInfo->getDefenseValueCap() > 0) + iDefenseValue = min(iDefenseValue, pkProcessInfo->getDefenseValueCap()); + + int iPile = getYieldRateTimes100(YIELD_PRODUCTION) * iDefenseValue; iHitsHealed += iPile / 10000; } } @@ -2339,6 +2352,15 @@ void CvCity::doTurn() changeDamage(-iHitsHealed); } + if (bRunningDefenseProcess) + { + ChangeDefenseProcessTurns(1); + } + else + { + SetDefenseProcessTurns(0); + } + if (getDamage() < 0) setDamage(0); @@ -12723,6 +12745,7 @@ int CvCity::getProductionModifier(UnitTypes eUnit, CvString* toolTipSink, bool b if (eUnitCombatType != NO_UNITCOMBAT) { iTempMod = getUnitCombatProductionModifier(eUnitCombatType); + iMultiplier += iTempMod; if (toolTipSink && iTempMod) { @@ -12827,13 +12850,16 @@ int CvCity::getProductionModifier(UnitTypes eUnit, CvString* toolTipSink, bool b GC.getGame().BuildProdModHelpText(toolTipSink, "TXT_KEY_PRODMOD_SPACE_PLAYER", iTempMod); } } - else + + // Production bonus from the City's Religion + ReligionTypes eMajority = GetCityReligions()->GetReligiousMajority(); + if (eMajority != NO_RELIGION) { - ReligionTypes eMajority = GetCityReligions()->GetReligiousMajority(); - if (eMajority != NO_RELIGION && (pkUnitInfo->GetCombat() > 0 || pkUnitInfo->GetRangedCombat() > 0)) + const CvReligion* pReligion = GetCityReligions()->GetMajorityReligion(); + if (pReligion) { - const CvReligion* pReligion = GetCityReligions()->GetMajorityReligion(); - if (pReligion) + // to all military units? + if (pkUnitInfo->GetCombat() > 0 || pkUnitInfo->GetRangedCombat() > 0) { iTempMod = pReligion->m_Beliefs.GetUnitProductionModifier(); iMultiplier += iTempMod; @@ -12842,6 +12868,18 @@ int CvCity::getProductionModifier(UnitTypes eUnit, CvString* toolTipSink, bool b GC.getGame().BuildProdModHelpText(toolTipSink, "TXT_KEY_PRODMOD_RELIGION_UNIT", iTempMod); } } + + // what about to specific unit combats? + UnitCombatTypes eUnitCombatType = (UnitCombatTypes)(pkUnitInfo->GetUnitCombatType()); + if (eUnitCombatType != NO_UNITCOMBAT) + { + iTempMod = pReligion->m_Beliefs.GetUnitCombatProductionModifiers(eUnitCombatType, getOwner(), this); + iMultiplier += iTempMod; + if (toolTipSink && iTempMod) + { + GC.getGame().BuildProdModHelpText(toolTipSink, "TXT_KEY_PRODMOD_RELIGION_UNIT_COMBAT", iTempMod); + } + } } } @@ -15047,6 +15085,17 @@ void CvCity::UpdateReligion(ReligionTypes eNewMajority, bool bRecalcPlotYields) { iReligionYieldChange += pReligion->m_Beliefs.GetCoastalCityYieldChange(getPopulation(), (YieldTypes)iYield, getOwner(), this); } + { + std::vector abTerrainMatch(GC.getNumTerrainInfos(), false); + for (int iTerrain = 0; iTerrain < GC.getNumTerrainInfos(); iTerrain++) + { + if (plot()->getTerrainType() == (TerrainTypes)iTerrain || IsAdjacentToTerrain((TerrainTypes)iTerrain)) + { + abTerrainMatch[iTerrain] = true; + } + } + iReligionYieldChange += pReligion->m_Beliefs.GetNearbyTerrainYieldChangeForCity(getPopulation(), abTerrainMatch, (YieldTypes)iYield, getOwner(), this); + } BeliefTypes eSecondaryPantheon = GetCityReligions()->GetSecondaryReligionPantheonBelief(); if (eSecondaryPantheon != NO_BELIEF && getPopulation() >= GC.GetGameBeliefs()->GetEntry(eSecondaryPantheon)->GetMinPopulation()) @@ -15070,6 +15119,17 @@ void CvCity::UpdateReligion(ReligionTypes eNewMajority, bool bRecalcPlotYields) { iReligionYieldChange += GC.GetGameBeliefs()->GetEntry(eSecondaryPantheon)->GetCoastalCityYieldChange((YieldTypes)iYield); } + { + int iNearbyTerrainYield = 0; + for (int iTerrain = 0; iTerrain < GC.getNumTerrainInfos(); iTerrain++) + { + if (plot()->getTerrainType() == (TerrainTypes)iTerrain || IsAdjacentToTerrain((TerrainTypes)iTerrain)) + { + iNearbyTerrainYield = max(iNearbyTerrainYield, GC.GetGameBeliefs()->GetEntry(eSecondaryPantheon)->GetNearbyTerrainYieldChange(iTerrain, iYield)); + } + } + iReligionYieldChange += iNearbyTerrainYield; + } } ChangeBaseYieldRateFromReligion((YieldTypes)iYield, iReligionYieldChange); @@ -15151,6 +15211,17 @@ void CvCity::UpdateReligion(ReligionTypes eNewMajority, bool bRecalcPlotYields) { iReligionYieldChange += GC.GetGameBeliefs()->GetEntry(ePantheonBelief)->GetCoastalCityYieldChange((YieldTypes)iYield); } + { + int iNearbyTerrainYield = 0; + for (int iTerrain = 0; iTerrain < GC.getNumTerrainInfos(); iTerrain++) + { + if (plot()->getTerrainType() == (TerrainTypes)iTerrain || IsAdjacentToTerrain((TerrainTypes)iTerrain)) + { + iNearbyTerrainYield = max(iNearbyTerrainYield, GC.GetGameBeliefs()->GetEntry(ePantheonBelief)->GetNearbyTerrainYieldChange(iTerrain, iYield)); + } + } + iReligionYieldChange += iNearbyTerrainYield; + } iReligionYieldChange += GC.GetGameBeliefs()->GetEntry(ePantheonBelief)->GetYieldChangeTradeRoute((YieldTypes)iYield); ChangeBaseYieldRateFromReligion((YieldTypes)iYield, iReligionYieldChange); @@ -16602,10 +16673,11 @@ void CvCity::setPopulation(int iNewValue, bool bReassignPop /* = true */, bool b if (iOldPopulation != iNewValue) { - // If we are reducing population, remove the workers first - if (bReassignPop) + // If we are reducing population + if (iPopChange < 0) { - if (iPopChange < 0) + // remove the workers first + if (bReassignPop) { // Need to Remove Citizens for (int iNewPopLoop = -iPopChange; iNewPopLoop--;) @@ -16620,6 +16692,18 @@ void CvCity::setPopulation(int iNewValue, bool bReassignPop /* = true */, bool b ASSERT(iUnassignedWorkers >= -iPopChange); GetCityCitizens()->ChangeNumUnassignedCitizens(std::max(iPopChange, -iUnassignedWorkers)); } + + // make sure this doesn't bring City HP below 1 + if (MOD_BALANCE_CITY_STRENGTH_SWITCH) + { + int iHPLoss = -1 * iPopChange * /*8*/ GD_INT_GET(CITY_STRENGTH_POPULATION_CHANGE); // absolute value + int iCurrentHP = GetMaxHitPoints() - getDamage(); + if (iCurrentHP < iHPLoss) + { + int iNewMaxHP = GetMaxHitPoints() - iHPLoss; + setDamage(iNewMaxHP - 1); + } + } } m_iPopulation = iNewValue; @@ -22677,7 +22761,6 @@ int CvCity::getBaseYieldRateModifier(YieldTypes eIndex, int iAssumedExtraModifie int iTempMod = kOwner.getCityYieldModFromMonopoly(eIndex); if (iTempMod != 0) { - iTempMod += kOwner.GetMonopolyModPercent(); iModifier += iTempMod; if (toolTipSink) { @@ -27236,9 +27319,12 @@ void CvCity::updateStrengthValue() if (getProductionProcess() != NO_PROCESS) { CvProcessInfo* pkProcessInfo = GC.getProcessInfo(getProductionProcess()); - if (pkProcessInfo && pkProcessInfo->getDefenseValue() != 0) + if (pkProcessInfo && (pkProcessInfo->getDefenseValue() != 0 || pkProcessInfo->getDefenseValuePerTurn() != 0)) { - iStrengthValue += (getYieldRateTimes100(YIELD_PRODUCTION, false, true) * pkProcessInfo->getDefenseValue()) / 100; + int iDefenseValue = pkProcessInfo->getDefenseValue() + GetDefenseProcessTurns() * pkProcessInfo->getDefenseValuePerTurn(); + if (pkProcessInfo->getDefenseValueCap() > 0) + iDefenseValue = min(iDefenseValue, pkProcessInfo->getDefenseValueCap()); + iStrengthValue += (getYieldRateTimes100(YIELD_PRODUCTION, false, true) * iDefenseValue) / 100; } } @@ -27430,9 +27516,13 @@ int CvCity::getStrengthValue(bool bForRangeStrike, bool bIgnoreBuildings, const if (getProductionProcess() != NO_PROCESS) { CvProcessInfo* pkProcessInfo = GC.getProcessInfo(getProductionProcess()); - if (pkProcessInfo && pkProcessInfo->getDefenseValue() != 0) + if (pkProcessInfo && (pkProcessInfo->getDefenseValue() != 0 || pkProcessInfo->getDefenseValuePerTurn() != 0)) { - iValue -= (getYieldRateTimes100(YIELD_PRODUCTION, false, true) * pkProcessInfo->getDefenseValue()) / 100; + int iDefenseValue = pkProcessInfo->getDefenseValue() + GetDefenseProcessTurns() * pkProcessInfo->getDefenseValuePerTurn(); + if (pkProcessInfo->getDefenseValueCap() > 0) + iDefenseValue = min(iDefenseValue, pkProcessInfo->getDefenseValueCap()); + + iValue -= (getYieldRateTimes100(YIELD_PRODUCTION, false, true) * iDefenseValue) / 100; } } @@ -28877,10 +28967,10 @@ void CvCity::UpdateYieldsFromExistingFriendsAndAllies(bool bRemove) //this method should only be called once per city on its first turn or when the capital moves ... int iSign = bRemove ? -1 : +1; - + CvPlayer& kPlayer = GET_PLAYER(getOwner()); + if (isCapital()) { - CvPlayer& kPlayer = GET_PLAYER(getOwner()); int iNumAllies = kPlayer.GetNumCSAllies(); int iNumFriends = kPlayer.GetNumCSFriends(); if (iNumAllies > 0 || iNumFriends > 0) @@ -28895,6 +28985,21 @@ void CvCity::UpdateYieldsFromExistingFriendsAndAllies(bool bRemove) } } + // Annexed CS + if (kPlayer.GetPlayerTraits()->IsAnnexedCityStatesGiveYields()) + { + for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) + { + YieldTypes eYield = (YieldTypes)iI; + int iAnnexedYields = isCapital() ? kPlayer.GetYieldInCapitalPerTurnFromAnnexedMinorsTimes100(eYield) : kPlayer.GetYieldInOtherCitiesPerTurnFromAnnexedMinorsTimes100(eYield); + if (iAnnexedYields != 0) + { + ChangeBaseYieldRateFromCSAllianceTimes100(eYield, iAnnexedYields); + //CUSTOMLOG("adjusted %s in %s by %d/100 for annexation, current value is %d", GC.getYieldInfo(eYield)->getDescription(), getNameKey(), iSign * iAnnexedYields, GetBaseYieldRateFromCSAlliance(eYield)); + } + } + } + for (int iPlayerLoop = MAX_MAJOR_CIVS; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { PlayerTypes eCityOwner = (PlayerTypes)getOwner(); @@ -31972,6 +32077,7 @@ void CvCity::Serialize(City& city, Visitor& visitor) visitor(city.m_iResistanceCounter); visitor(city.m_iPlagueCounter); visitor(city.m_iPlagueTurns); + visitor(city.m_iDefenseProcessTurns); visitor(city.m_iPlagueType); visitor(city.m_iSappedTurns); visitor(city.m_iBuildingProductionBlockedTurns); @@ -32652,10 +32758,38 @@ CvUnit* CvCity::getBestRangedStrikeTarget() const //a bit redundant with the internal of canRangeStrikeAt but that's life CvUnit* pTarget = rangedStrikeTarget(pTargetPlot); int iDamage = rangeCombatDamage(pTarget, false, NULL); - if (iDamage > iBestScore) + int iBonus = 0; + if (iDamage > 0) { - iBestScore = iDamage; - pBestTarget = pTarget; + int iCurrHitPoints = pTarget->GetCurrHitPoints(); + + // It's good to kill units + if (iDamage >= iCurrHitPoints) + iBonus += 15; + + // it's good to damage city bombard units + if (pTarget->getUnitInfo().GetDefaultUnitAIType() == UNITAI_CITY_BOMBARD) + iBonus += 10; + + // It's generally not useful to bombard civilians + else if (pTarget->IsCivilianUnit()) + { + if (pTarget->IsGreatGeneral() || pTarget->IsGreatAdmiral()) + { + // Killing great generals and admirals is good though + if (iDamage >= pTarget->GetCurrHitPoints()) + iBonus = 10; + else + iDamage = 2; + } + else + iDamage = 1; + } + if (iDamage + iBonus > iBestScore) + { + iBestScore = iDamage + iBonus; + pBestTarget = pTarget; + } } } } @@ -34819,6 +34953,25 @@ void CvCity::SetPlagueTurns(int iValue) } } +int CvCity::GetDefenseProcessTurns() const +{ + return m_iDefenseProcessTurns; +} +void CvCity::ChangeDefenseProcessTurns(int iValue) //Set in city::doturn +{ + if (iValue != 0) + { + m_iDefenseProcessTurns += iValue; + } +} +void CvCity::SetDefenseProcessTurns(int iValue) +{ + if (iValue != m_iDefenseProcessTurns) + { + m_iDefenseProcessTurns = iValue; + } +} + int CvCity::GetSappedTurns() const { return m_iSappedTurns; diff --git a/CvGameCoreDLL_Expansion2/CvCity.h b/CvGameCoreDLL_Expansion2/CvCity.h index 1bda38e026..bd0fb7f079 100644 --- a/CvGameCoreDLL_Expansion2/CvCity.h +++ b/CvGameCoreDLL_Expansion2/CvCity.h @@ -1757,6 +1757,10 @@ class CvCity void ChangePlagueTurns(int iValue); //Set in city::doturn void SetPlagueTurns(int iValue); + int GetDefenseProcessTurns() const; + void ChangeDefenseProcessTurns(int iValue); //Set in city::doturn + void SetDefenseProcessTurns(int iValue); + int GetSappedTurns() const; void SetSappedTurns(int iValue); void ChangeSappedTurns(int iValue); @@ -2171,6 +2175,7 @@ class CvCity int m_iPlagueCounter; int m_iPlagueTurns; int m_iPlagueType; + int m_iDefenseProcessTurns; int m_iSappedTurns; int m_iBuildingProductionBlockedTurns; int m_iNoTourismTurns; @@ -2559,6 +2564,7 @@ SYNC_ARCHIVE_VAR(int, m_iResistanceCounter) SYNC_ARCHIVE_VAR(int, m_iPlagueCounter) SYNC_ARCHIVE_VAR(int, m_iPlagueTurns) SYNC_ARCHIVE_VAR(int, m_iPlagueType) +SYNC_ARCHIVE_VAR(int, m_iDefenseProcessTurns) SYNC_ARCHIVE_VAR(int, m_iSappedTurns) SYNC_ARCHIVE_VAR(int, m_iBuildingProductionBlockedTurns) SYNC_ARCHIVE_VAR(int, m_iNoTourismTurns) diff --git a/CvGameCoreDLL_Expansion2/CvDangerPlots.cpp b/CvGameCoreDLL_Expansion2/CvDangerPlots.cpp index 44db9bbfb7..9fe897bca3 100644 --- a/CvGameCoreDLL_Expansion2/CvDangerPlots.cpp +++ b/CvGameCoreDLL_Expansion2/CvDangerPlots.cpp @@ -871,10 +871,6 @@ int CvDangerPlotContents::GetDanger(const CvUnit* pUnit, const SUnitIDValueConta int iEnemyRange = pAttacker->IsCanAttackRanged() ? pAttacker->GetRange() : 1; bool bOutOfRange = plotDistance(*m_pPlot, *pAttacker->plot()) > iEnemyRange; - //assume enemy units will not abandon cities - if (bOutOfRange && pAttacker->plot()->isCity() && !pAttacker->canMoveAfterAttacking()) - continue; - //if the attacker is not out of range, assume they need to move for the attack, so we don't know their plot //todo: consider whether the enemy units would block each other from attacking? int iDamage = TacticalAIHelpers::GetSimulatedDamageFromAttackOnUnit(pUnit, pAttacker, m_pPlot, bOutOfRange ? NULL : pAttacker->plot(), iAttackerDamage, false, iExtraAttackerDamage, iExtraDamage, true); diff --git a/CvGameCoreDLL_Expansion2/CvDealAI.cpp b/CvGameCoreDLL_Expansion2/CvDealAI.cpp index f1607362a1..61044a7922 100644 --- a/CvGameCoreDLL_Expansion2/CvDealAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvDealAI.cpp @@ -425,6 +425,10 @@ DemandResponseTypes CvDealAI::DoHumanDemand(CvDeal* pDeal) /// What is the AI's response to a demand? DemandResponseTypes CvDealAI::GetDemandResponse(CvDeal* pDeal) const { + // AI teammates of humans will never give in to a demand. + if (GetPlayer()->IsAITeammateOfHuman()) + return DEMAND_RESPONSE_REFUSE_PROTECTED_BY_MASTER; + PlayerTypes eFromPlayer = pDeal->GetOtherPlayer(GetPlayer()->GetID()); PlayerTypes eMyPlayer = GetPlayer()->GetID(); TeamTypes eFromTeam = GET_PLAYER(eFromPlayer).getTeam(); @@ -5535,315 +5539,313 @@ bool CvDealAI::IsMakeDemand(PlayerTypes eOtherPlayer, CvDeal* pDeal) return (!pDeal->m_TradedItems.empty() && iTotalValue > 0); } -/// A good time to make an offer for someone's extra Luxury? -bool CvDealAI::IsMakeOfferForLuxuryResource(PlayerTypes eOtherPlayer, CvDeal* pDeal) +/// A good time to make an offer to become their Vassal? +bool CvDealAI::IsMakeOfferToBecomeVassal(PlayerTypes eOtherPlayer, CvDeal* pDeal) { - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); + if (!pDeal->IsPossibleToTradeItem(GetPlayer()->GetID(), eOtherPlayer, TRADE_ITEM_VASSALAGE)) + return false; - ResourceTypes eLuxuryFromThem = NO_RESOURCE; + // Seed the deal with the item we want + pDeal->AddVassalageTrade(GetPlayer()->GetID()); - // Don't ask for a Luxury if we're hostile or planning a war - CivApproachTypes eApproach = GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer); - if (eApproach <= CIV_APPROACH_HOSTILE) - { + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work + + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; +} + +/// A good time to make an offer to make them our Vassal? +bool CvDealAI::IsMakeOfferForVassalage(PlayerTypes eOtherPlayer, CvDeal* pDeal) +{ + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_VASSALAGE)) return false; - } + + // Seed the deal with the item we want + pDeal->AddVassalageTrade(eOtherPlayer); + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - int iBestValue = 20; + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; +} - // See if the other player has a Resource to trade - int iDuration = GC.getGame().GetDealDuration(); - for(int iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) - { - ResourceTypes eResource = (ResourceTypes) iResourceLoop; +/// A good time to make an offer for a Defensive Pact? +bool CvDealAI::IsMakeOfferForDefensivePact(PlayerTypes eOtherPlayer, CvDeal* pDeal) +{ + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_DEFENSIVE_PACT)) + return false; - // Only look at Luxuries - const CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); + // Seed the deal with the item we want + pDeal->AddDefensivePact(GetPlayer()->GetID(), GC.getGame().GetDealDuration()); + pDeal->AddDefensivePact(eOtherPlayer, GC.getGame().GetDealDuration()); - if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESOURCES, eResource)) - continue; + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - if(pkResourceInfo == NULL || pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_LUXURY) - { - continue; - } + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; +} - //one copy is enough - if (m_pPlayer->getNumResourceAvailable(eResource, true) > 0) - { - continue; - } +bool CvDealAI::IsMakeOfferForRevokeVassalage(PlayerTypes eOtherPlayer, CvDeal* pDeal) +{ + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_VASSALAGE_REVOKE)) + return false; - // Don't ask if they have only one copy - if (GET_PLAYER(eOtherPlayer).getNumResourceAvailable(eResource, false) <= 1) - continue; + // Seed the deal with the item we want + pDeal->AddRevokeVassalageTrade(eOtherPlayer); - // Is it possible to trade that item? - if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESOURCES, eResource, 1)) - continue; + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - // Can we strike a deal with the other AI? - if (GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eResource, 1, -1, false, iDuration, /* bIsAIOffer*/ true, /*bEqualize*/ true) == INT_MAX) - continue; + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; +} - // Let's try to get the resource that's most valuable to us (do not do bEqualize here) - int iItemValue = GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eResource, 1, -1, false, iDuration, /* bIsAIOffer*/ true, /*bEqualize*/ false); - if (iItemValue != INT_MAX && iItemValue > iBestValue) +/// A good time to make an offer to trade cities? +bool CvDealAI::IsMakeOfferForCityExchange(PlayerTypes eOtherPlayer, CvDeal* pDeal) +{ + int iCityLoop = 0; + CvCity* pBestBuyCity = NULL; + int iBestBuyCity = 120; // initial value, deal must be good to justify building a courthouse + CvCity* pBestSellCity = NULL; + int iBestSellCity = 150; // initial value, they must overpay a lot + + //check their cities + for (CvCity* pTheirCity = GET_PLAYER(eOtherPlayer).firstCity(&iCityLoop); pTheirCity != NULL; pTheirCity = GET_PLAYER(eOtherPlayer).nextCity(&iCityLoop)) + { + if (pDeal->IsPossibleToTradeItem(eOtherPlayer, m_pPlayer->GetID(), TRADE_ITEM_CITIES, pTheirCity->getX(), pTheirCity->getY())) { - eLuxuryFromThem = eResource; - iBestValue = iItemValue; + int iMyPrice = GetCityValueForDeal(pTheirCity, GetPlayer()->GetID()); + int iTheirPrice = GetCityValueForDeal(pTheirCity, eOtherPlayer); + + // Prevent integer overflow when multiplying by 100 + int iBuyRatio = 0; + if (iMyPrice != INT_MAX && iTheirPrice != INT_MAX && iMyPrice <= INT_MAX/100) + { + iBuyRatio = (iMyPrice*100) / max(1,iTheirPrice); + } + if (iMyPrice != INT_MAX && iTheirPrice != INT_MAX && iBuyRatio > iBestBuyCity) + { + pBestBuyCity = pTheirCity; + iBestBuyCity = iBuyRatio; + } } } - // Extra Luxury found! - if (eLuxuryFromThem != NO_RESOURCE) - { - // Seed the deal with the item we want - pDeal->AddResourceTrade(eOtherPlayer, eLuxuryFromThem, 1, iDuration); + if (pBestBuyCity == NULL) + return false; - // AI evaluation - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, /*bDontChangeMyExistingItems*/ bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work + pDeal->AddCityTrade(eOtherPlayer, pBestBuyCity->GetID()); - return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; + //check my cities + for (CvCity* pMyCity = GetPlayer()->firstCity(&iCityLoop); pMyCity != NULL; pMyCity = GetPlayer()->nextCity(&iCityLoop)) + { + if (pDeal->IsPossibleToTradeItem(m_pPlayer->GetID(), eOtherPlayer, TRADE_ITEM_CITIES, pMyCity->getX(), pMyCity->getY())) + { + int iMyPrice = GetCityValueForDeal(pMyCity, m_pPlayer->GetID()); + int iTheirPrice = GetCityValueForDeal(pMyCity, eOtherPlayer); + + // Prevent integer overflow when multiplying by 100 + int iSellRatio = 0; + if (iMyPrice != INT_MAX && iTheirPrice != INT_MAX && iTheirPrice <= INT_MAX/100) + { + iSellRatio = (iTheirPrice*100)/max(1,iMyPrice); + } + if (iMyPrice != INT_MAX && iTheirPrice != INT_MAX && iSellRatio > iBestSellCity) + { + pBestSellCity = pMyCity; + iBestSellCity = iSellRatio; + } + } } - return false; -} + if (pBestSellCity == NULL) + return false; -/// A good time to make an offer for someone's extra strats? -bool CvDealAI::IsMakeOfferForStrategicResource(PlayerTypes eOtherPlayer, CvDeal* pDeal) -{ - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); + pDeal->AddCityTrade(m_pPlayer->GetID(), pBestSellCity->GetID()); - ResourceTypes eStratFromThem = NO_RESOURCE; + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work + + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; +} - // Don't ask for a resource if we're hostile or planning a war - CivApproachTypes eApproach = GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer); - if (eApproach <= CIV_APPROACH_HOSTILE) +/// A good time to make an offer for technology? +bool CvDealAI::IsMakeOfferForTech(PlayerTypes eOtherPlayer, CvDeal* pDeal) +{ + // No current research? We may have just finished something. + if (GetPlayer()->GetPlayerTechs()->GetCurrentResearch() == NO_TECH) return false; - int iBestValue = 15; + // Don't iterate through all techs if we know tech trading isn't possible + if (GC.getGame().isOption(GAMEOPTION_NO_SCIENCE) || !GC.getGame().isOption(GAMEOPTION_ENABLE_TECH_TRADING)) + return false; - // See if the other player has a Resource to trade - for(int iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) - { - ResourceTypes eResource = (ResourceTypes) iResourceLoop; + TeamTypes eMyTeam = GetPlayer()->getTeam(); + TeamTypes eOtherTeam = GET_PLAYER(eOtherPlayer).getTeam(); + CvTeam* pMyTeam = &GET_TEAM(eMyTeam); + CvTeam* pOtherTeam = &GET_TEAM(eOtherTeam); + if (!pOtherTeam->isTechTrading()) + return false; + if (!pMyTeam->HasEmbassyAtTeam(eOtherTeam) || !pOtherTeam->HasEmbassyAtTeam(eMyTeam)) + return false; - // Only look at strategic resources here - const CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); - if(pkResourceInfo == NULL || pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_STRATEGIC) - continue; + if (GET_PLAYER(eOtherPlayer).IsAITeammateOfHuman()) + return false; - if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESOURCES, eResource)) - continue; + TechTypes eTechWeWant = NO_TECH; + int iBestValue = 0; - // If we have some to spare, don't get more - if (GetPlayer()->getNumResourceAvailable(eResource, true) > 2) - continue; + // See if the other player has a technology to trade + for (int iTechLoop = 0; iTechLoop < GC.getNumTechInfos(); iTechLoop++) + { + TechTypes eTech = (TechTypes) iTechLoop; + int iValue = 0; - int iNum = GET_PLAYER(eOtherPlayer).getNumResourceAvailable(eResource, false); - if (iNum <= 0) + // Let's not ask for the tech we're currently researching + if (eTech == GetPlayer()->GetPlayerTechs()->GetCurrentResearch()) continue; - // can't strike a deal with the other AI? - if (GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eResource, 1, -1, false, GC.getGame().GetDealDuration(), /*bIsAIOffer*/ true, /*bEqualize*/ true) == INT_MAX) + // don't do the expensive turn calculation for techs that can't be traded anyway + if (eTech == GET_PLAYER(eOtherPlayer).GetPlayerTechs()->GetCurrentResearch()) + continue; + if (!GetPlayer()->GetPlayerTechs()->CanResearch(eTech)) continue; - // Let's try to get the resource that's most valuable to us (do not do bEqualize here) - int iItemValue = GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eResource, 1, -1, false, GC.getGame().GetDealDuration(), /*bIsAIOffer*/ true, /*bEqualize*/ false); - if(iItemValue != INT_MAX && iItemValue > iBestValue) + // Can they sell that tech? + if (pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_TECHS, eTech)) { - eStratFromThem = eResource; - iBestValue = iItemValue; + iValue = GetTechValue(eTech, false, eOtherPlayer); + if (iValue > iBestValue) + { + eTechWeWant = eTech; + iBestValue = iValue; + } } } - // Extra Strat found! - if(eStratFromThem != NO_RESOURCE) + // TECH FOUND! + if (eTechWeWant != NO_TECH) { - int iAvailable = GET_PLAYER(eOtherPlayer).getNumResourceAvailable(eStratFromThem, false) - 1; // don't ask for all of their resources - int iDesired = 0; - - int iPrevDealValue = -1; - int iNextDealValue = 0; - - // we try to increase iDesired as long as it's possible and as long as we're willing to pay more for the additional item - while(iPrevDealValue != iNextDealValue) - { - iDesired++; - // max 5 items - if (iDesired > 5 || iDesired > iAvailable) - break; - - if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESOURCES, eStratFromThem, iDesired)) - break; - - // can we strike a deal with the other AI? - if (GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eStratFromThem, iDesired, -1, false, GC.getGame().GetDealDuration(), /*bIsAIOffer*/ true, /*bEqualize*/ true) == INT_MAX) - break; - - iPrevDealValue = iNextDealValue; - // this is our valuation of the item, use bEqualize=false here - iNextDealValue = GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eStratFromThem, iDesired, -1, false, GC.getGame().GetDealDuration(), /*bIsAIOffer*/ true, /*bEqualize*/ false); - } - // the last item added was too much, don't include it in the deal - iDesired--; - - // failsave - if (iDesired == 0) - return false; - // Seed the deal with the item we want - pDeal->AddResourceTrade(eOtherPlayer, eStratFromThem, iDesired, GC.getGame().GetDealDuration()); + pDeal->AddTechTrade(eOtherPlayer, eTechWeWant); // AI evaluation bool bUselessReferenceVariable = false; bool bCantMatchOffer = false; - bool bDealAcceptable = false; - - while (!bDealAcceptable) - { - bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - bDealAcceptable = bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; - if (!bDealAcceptable) - { - // try again with one resource item less - iDesired--; - if (iDesired == 0) - { - return false; - } - else - { - pDeal->ClearItems(); - pDeal->AddResourceTrade(eOtherPlayer, eStratFromThem, iDesired, GC.getGame().GetDealDuration()); - } - } - } + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - return bDealAcceptable; + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; } + return false; } - -/// A good time to make an offer to get an embassy? -bool CvDealAI::IsMakeOfferForEmbassy(PlayerTypes eOtherPlayer, CvDeal* pDeal) +/// A good time to make an offer for a Vote Commitment? +bool CvDealAI::IsMakeOfferForVote(PlayerTypes eOtherPlayer, CvDeal* pDeal) { - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - // Don't ask for an embassy if we're hostile or planning war - CivApproachTypes eApproach = GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer); - if (eApproach <= CIV_APPROACH_HOSTILE || eApproach == CIV_APPROACH_GUARDED) - { + CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); + if (!pLeague) return false; - } - // Can we actually complete this deal? - // Note the order of the player ids: This is because the trade item is "accept embassy" not "send embassy" - if(!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_ALLOW_EMBASSY)) - { + if (!GET_PLAYER(eOtherPlayer).GetLeagueAI()->CanCommitVote(GetPlayer()->GetID())) return false; - } - // AI should not offer embassies for gold - wait until embassy for embassy is possible - // Note that humans can still offer and get an embassy for gold trade, it's not illegal - if(!pDeal->IsPossibleToTradeItem(GetPlayer()->GetID(), eOtherPlayer, TRADE_ITEM_ALLOW_EMBASSY) && !GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).HasEmbassyAtTeam(GetPlayer()->getTeam())) + int iBestValue = 0; + int iBestProposal = -1; + int iProposalID = -1; + int iVoteChoice = -1; + int iNumVotes = -1; + bool bRepeal = false; + CvLeagueAI::VoteCommitmentList vDesiredCommitments = GetPlayer()->GetLeagueAI()->GetDesiredVoteCommitments(eOtherPlayer); + for (CvLeagueAI::VoteCommitmentList::iterator it = vDesiredCommitments.begin(); it != vDesiredCommitments.end(); ++it) { - return false; + iProposalID = it->iResolutionID; + iVoteChoice = it->iVoteChoice; + iNumVotes = it->iNumVotes; + bRepeal = !it->bEnact; + + if (iProposalID != -1 && pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_VOTE_COMMITMENT, iProposalID, iVoteChoice, iNumVotes, bRepeal)) + { + int iItemValue = GetTradeItemValue(TRADE_ITEM_VOTE_COMMITMENT, /*bFromMe*/ false, eOtherPlayer, iProposalID, iVoteChoice, iNumVotes, bRepeal, -1, true); + if (iItemValue != INT_MAX && iItemValue > iBestValue) + { + iBestProposal = iProposalID; + iBestValue = iItemValue; + } + } } - // Do we actually want an embassy with eOtherPlayer? - if(GetPlayer()->GetDiplomacyAI()->WantsEmbassyAtPlayer(eOtherPlayer)) - { - // Seed the deal with the item we want - pDeal->AddAllowEmbassy(eOtherPlayer); + if (iBestProposal == -1) + return false; - // AI evaluation - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work + pDeal->AddVoteCommitment(eOtherPlayer, iProposalID, iVoteChoice, iNumVotes, bRepeal); - return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; - } + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - return false; + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; } -/// A good time to make an offer to get Open Borders? -bool CvDealAI::IsMakeOfferForOpenBorders(PlayerTypes eOtherPlayer, CvDeal* pDeal) +/// A good time to offer for World Map? +bool CvDealAI::IsMakeOfferForMaps(PlayerTypes eOtherPlayer, CvDeal* pDeal) { - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - // Don't ask for Open Borders if we're hostile or planning war - CivApproachTypes eApproach = GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer); - if (eApproach <= CIV_APPROACH_HOSTILE) - { + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_MAPS)) return false; - } - // Can we actually complete this deal? - if(!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_OPEN_BORDERS)) - { - return false; - } - - // Already allowing Open Borders? - if(GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).IsAllowsOpenBordersToTeam(GetPlayer()->getTeam())) - { + // If this is a human, needs to be worth enough to bother them. + if (GET_PLAYER(eOtherPlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && GetMapValue(false, eOtherPlayer) < 750) return false; - } - // Do we actually want OB with eOtherPlayer? - if(GetPlayer()->GetDiplomacyAI()->IsWantsOpenBordersWithPlayer(eOtherPlayer)) - { - // Seed the deal with the item we want - pDeal->AddOpenBorders(eOtherPlayer, GC.getGame().GetDealDuration()); - - // AI evaluation - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work + // Seed the deal with the item we want + pDeal->AddMapTrade(eOtherPlayer); - return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; - } + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - return false; + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; } -/// A good time to make an offer for a Research Agreement? -bool CvDealAI::IsMakeOfferForResearchAgreement(PlayerTypes eOtherPlayer, CvDeal* pDeal) +/// A good time to make an offer to start a war? +bool CvDealAI::IsMakeOfferForThirdPartyWar(PlayerTypes eOtherPlayer, CvDeal* pDeal) { - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - if (GetPlayer()->IsAITeammateOfHuman()) + // Asking a vassal or AI teammate of human? Abort! + if (GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).IsVassalOfSomeone() || GET_PLAYER(eOtherPlayer).IsAITeammateOfHuman()) return false; - if (!GetPlayer()->GetDiplomacyAI()->IsWantsResearchAgreementWithPlayer(eOtherPlayer)) - return false; + int iBestValue = 0; + TeamTypes eBestTeam = NO_TEAM; + for (int iI = 0; iI < MAX_CIV_PLAYERS; iI++) + { + PlayerTypes eAgainstPlayer = (PlayerTypes)iI; - if (!GET_PLAYER(eOtherPlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(eOtherPlayer).GetDiplomacyAI()->IsWantsResearchAgreementWithPlayer(GetPlayer()->GetID())) - return false; + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_THIRD_PARTY_WAR, GET_PLAYER(eAgainstPlayer).getTeam())) + continue; - // Logic for when THIS AI wants to make a RA is in the Diplo AI + int iWarValue = GetThirdPartyWarValue(false, eOtherPlayer, GET_PLAYER(eAgainstPlayer).getTeam()); + if (iWarValue != INT_MAX && iWarValue > iBestValue) + { + eBestTeam = GET_PLAYER(eAgainstPlayer).getTeam(); + iBestValue = iWarValue; + } + } - // Can we actually complete this deal? - if(!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESEARCH_AGREEMENT)) - { + if (eBestTeam == NO_TEAM) return false; - } - // Seed the deal with the item we want - pDeal->AddResearchAgreement(GetPlayer()->GetID(), GC.getGame().GetDealDuration()); - pDeal->AddResearchAgreement(eOtherPlayer, GC.getGame().GetDealDuration()); + pDeal->AddThirdPartyWar(eOtherPlayer, eBestTeam); // AI evaluation bool bUselessReferenceVariable = false; @@ -5853,32 +5855,34 @@ bool CvDealAI::IsMakeOfferForResearchAgreement(PlayerTypes eOtherPlayer, CvDeal* return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; } -/// A good time to make an offer for a Defensive Pact? -bool CvDealAI::IsMakeOfferForDefensivePact(PlayerTypes eOtherPlayer, CvDeal* pDeal) +/// A good time to make an offer for a Peace Deal? +bool CvDealAI::IsMakeOfferForThirdPartyPeace(PlayerTypes eOtherPlayer, CvDeal* pDeal) { - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - // Logic for when THIS AI wants to make a RA is in the Diplo AI - - // Can we actually complete this deal? - if(!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_DEFENSIVE_PACT)) - { + // Asking a vassal or AI teammate of human? Abort! + if (GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).IsVassalOfSomeone() || GET_PLAYER(eOtherPlayer).IsAITeammateOfHuman()) return false; - } - if (!GetPlayer()->GetDiplomacyAI()->IsWantsDefensivePactWithPlayer(eOtherPlayer)) + int iBestValue = 0; + TeamTypes eBestTeam = NO_TEAM; + for (int iI = 0; iI < MAX_CIV_PLAYERS; iI++) { - return false; + PlayerTypes eAgainstPlayer = (PlayerTypes)iI; + + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_THIRD_PARTY_PEACE, GET_PLAYER(eAgainstPlayer).getTeam())) + continue; + + int iPeaceValue = GetThirdPartyPeaceValue(false, eOtherPlayer, GET_PLAYER(eAgainstPlayer).getTeam()); + if (iPeaceValue != INT_MAX && iPeaceValue > iBestValue) + { + eBestTeam = GET_PLAYER(eAgainstPlayer).getTeam(); + iBestValue = iPeaceValue; + } } - if (!GET_PLAYER(eOtherPlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(eOtherPlayer).GetDiplomacyAI()->IsWantsDefensivePactWithPlayer(GetPlayer()->GetID())) - { + + if (eBestTeam == NO_TEAM) return false; - } - // Seed the deal with the item we want - pDeal->AddDefensivePact(GetPlayer()->GetID(), GC.getGame().GetDealDuration()); - pDeal->AddDefensivePact(eOtherPlayer, GC.getGame().GetDealDuration()); + pDeal->AddThirdPartyPeace(eOtherPlayer, eBestTeam, GC.getGame().getGameSpeedInfo().getPeaceDealDuration()); // AI evaluation bool bUselessReferenceVariable = false; @@ -5888,91 +5892,126 @@ bool CvDealAI::IsMakeOfferForDefensivePact(PlayerTypes eOtherPlayer, CvDeal* pDe return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; } -/// A good time to make an offer to buy or sell a city? -bool CvDealAI::IsMakeOfferForCityExchange(PlayerTypes eOtherPlayer, CvDeal* pDeal) +/// A good time to make an offer for someone's extra strats? +bool CvDealAI::IsMakeOfferForStrategicResource(PlayerTypes eOtherPlayer, CvDeal* pDeal) { - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); + ResourceTypes eStratFromThem = NO_RESOURCE; + int iBestValue = 15; - // Don't ask for a city if we're hostile or planning a war - CivApproachTypes eApproach = GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer); - if (eApproach <= CIV_APPROACH_HOSTILE || eApproach == CIV_APPROACH_GUARDED) + // See if the other player has a Resource to trade + for (int iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) { - return false; - } + ResourceTypes eResource = (ResourceTypes) iResourceLoop; - int iCityLoop = 0; - CvCity* pBestBuyCity = NULL; - int iBestBuyCity = 120; //initial value, deal must be good to justify building a courthouse - CvCity* pBestSellCity = NULL; - int iBestSellCity = 150; //initial value, they must overpay a lot + // Only look at strategic resources here + const CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); + if (pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_STRATEGIC) + continue; - //check their cities - for (CvCity* pTheirCity = GET_PLAYER(eOtherPlayer).firstCity(&iCityLoop); pTheirCity != NULL; pTheirCity = GET_PLAYER(eOtherPlayer).nextCity(&iCityLoop)) - { + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESOURCES, eResource)) + continue; - if(pDeal->IsPossibleToTradeItem(eOtherPlayer, m_pPlayer->GetID(), TRADE_ITEM_CITIES, pTheirCity->getX(), pTheirCity->getY())) - { - int iMyPrice = GetCityValueForDeal(pTheirCity, GetPlayer()->GetID()); - int iTheirPrice = GetCityValueForDeal(pTheirCity, eOtherPlayer); + // If we have some to spare, don't get more + if (GetPlayer()->getNumResourceAvailable(eResource, true) > 2) + continue; - // Prevent integer overflow when multiplying by 100 - int iBuyRatio = 0; - if(iMyPrice != INT_MAX && iTheirPrice != INT_MAX && iMyPrice <= INT_MAX/100) - { - iBuyRatio = (iMyPrice*100)/max(1,iTheirPrice); - } - if(iMyPrice!=INT_MAX && iTheirPrice!=INT_MAX && iBuyRatio>iBestBuyCity) - { - pBestBuyCity = pTheirCity; - iBestBuyCity = iBuyRatio; - } + int iNum = GET_PLAYER(eOtherPlayer).getNumResourceAvailable(eResource, false); + if (iNum <= 0) + continue; + + // can't strike a deal with the other AI? + if (GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eResource, 1, -1, false, GC.getGame().GetDealDuration(), /*bIsAIOffer*/ true, /*bEqualize*/ true) == INT_MAX) + continue; + + // Let's try to get the resource that's most valuable to us (do not do bEqualize here) + int iItemValue = GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eResource, 1, -1, false, GC.getGame().GetDealDuration(), /*bIsAIOffer*/ true, /*bEqualize*/ false); + if (iItemValue != INT_MAX && iItemValue > iBestValue) + { + eStratFromThem = eResource; + iBestValue = iItemValue; } } - - if (pBestBuyCity == NULL) - { - pDeal->ClearItems(); - return false; - } - else - { - pDeal->AddCityTrade(eOtherPlayer, pBestBuyCity->GetID()); - } - //check my cities - for(CvCity* pMyCity = GetPlayer()->firstCity(&iCityLoop); pMyCity != NULL; pMyCity = GetPlayer()->nextCity(&iCityLoop)) + // Extra Strat found! + if (eStratFromThem != NO_RESOURCE) { + int iAvailable = GET_PLAYER(eOtherPlayer).getNumResourceAvailable(eStratFromThem, false) - 1; // don't ask for all of their resources + int iDesired = 0; - if(pDeal->IsPossibleToTradeItem(m_pPlayer->GetID(), eOtherPlayer, TRADE_ITEM_CITIES, pMyCity->getX(), pMyCity->getY())) + int iPrevDealValue = -1; + int iNextDealValue = 0; + + // we try to increase iDesired as long as it's possible and as long as we're willing to pay more for the additional item + while (iPrevDealValue != iNextDealValue) { - int iMyPrice = GetCityValueForDeal(pMyCity, m_pPlayer->GetID()); - int iTheirPrice = GetCityValueForDeal(pMyCity, eOtherPlayer); + iDesired++; + // max 5 items + if (iDesired > 5 || iDesired > iAvailable) + break; - // Prevent integer overflow when multiplying by 100 - int iSellRatio = 0; - if(iMyPrice != INT_MAX && iTheirPrice != INT_MAX && iTheirPrice <= INT_MAX/100) - { - iSellRatio = (iTheirPrice*100)/max(1,iMyPrice); - } - if(iMyPrice!=INT_MAX && iTheirPrice!=INT_MAX && iSellRatio>iBestSellCity) + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESOURCES, eStratFromThem, iDesired)) + break; + + // can we strike a deal with the other AI? + if (GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eStratFromThem, iDesired, -1, false, GC.getGame().GetDealDuration(), /*bIsAIOffer*/ true, /*bEqualize*/ true) == INT_MAX) + break; + + iPrevDealValue = iNextDealValue; + // this is our valuation of the item, use bEqualize=false here + iNextDealValue = GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eStratFromThem, iDesired, -1, false, GC.getGame().GetDealDuration(), /*bIsAIOffer*/ true, /*bEqualize*/ false); + } + // the last item added was too much, don't include it in the deal + iDesired--; + + // failsave + if (iDesired == 0) + return false; + + // Seed the deal with the item we want + pDeal->AddResourceTrade(eOtherPlayer, eStratFromThem, iDesired, GC.getGame().GetDealDuration()); + + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = false; + + while (!bDealAcceptable) + { + bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work + bDealAcceptable = bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; + if (!bDealAcceptable) { - pBestSellCity = pMyCity; - iBestSellCity = iSellRatio; + // try again with one resource item less + iDesired--; + if (iDesired == 0) + { + return false; + } + else + { + pDeal->ClearItems(); + pDeal->AddResourceTrade(eOtherPlayer, eStratFromThem, iDesired, GC.getGame().GetDealDuration()); + } } } + + return bDealAcceptable; } + return false; +} - if(pBestSellCity == NULL) - { - pDeal->ClearItems(); +/// A good time to make an offer to exchange embassies in each others' capitals? +bool CvDealAI::IsMakeOfferForEmbassyExchange(PlayerTypes eOtherPlayer, CvDeal* pDeal) +{ + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_ALLOW_EMBASSY)) + return false; + + if (!pDeal->IsPossibleToTradeItem(GetPlayer()->GetID(), eOtherPlayer, TRADE_ITEM_ALLOW_EMBASSY)) return false; - } - else - { - //ok, everything is good - pDeal->AddCityTrade(m_pPlayer->GetID(), pBestSellCity->GetID()); - } + + // Seed the deal with the item we want + pDeal->AddAllowEmbassy(GetPlayer()->GetID()); + pDeal->AddAllowEmbassy(eOtherPlayer); // AI evaluation bool bUselessReferenceVariable = false; @@ -5982,105 +6021,38 @@ bool CvDealAI::IsMakeOfferForCityExchange(PlayerTypes eOtherPlayer, CvDeal* pDea return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; } -/// A good time to make an offer to start a war? -bool CvDealAI::IsMakeOfferForThirdPartyWar(PlayerTypes eOtherPlayer, CvDeal* pDeal) +/// A good time to make an offer to establish an embassy in their capital? +bool CvDealAI::IsMakeOfferForEmbassy(PlayerTypes eOtherPlayer, CvDeal* pDeal) { - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - if(eOtherPlayer == NO_PLAYER) - { - return false; - } - - // Don't ask humans' AI teammates - if (GET_PLAYER(eOtherPlayer).IsAITeammateOfHuman()) - { + // Note the order of the player IDs: This is because the trade item is "accept embassy" not "send embassy" + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_ALLOW_EMBASSY)) return false; - } - // Don't ask for a war if we're hostile or planning a war - CivApproachTypes eApproach = GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer); - if (eApproach <= CIV_APPROACH_HOSTILE || eApproach == CIV_APPROACH_GUARDED) - { + // AI should avoid offering embassies for gold - wait until embassy for embassy is possible + // Note that humans can still offer and get an embassy for gold trade, it's not illegal + if (!pDeal->IsPossibleToTradeItem(GetPlayer()->GetID(), eOtherPlayer, TRADE_ITEM_ALLOW_EMBASSY) && !GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).HasEmbassyAtTeam(GetPlayer()->getTeam())) return false; - } - // If we aren't at war, also buck on deceptive. Otherwise we want allies ASAP. - if (!GetPlayer()->IsAtWarAnyMajor() && eApproach == CIV_APPROACH_DECEPTIVE) - return false; + // Seed the deal with the item we want + pDeal->AddAllowEmbassy(eOtherPlayer); - // Don't ask for war if they are weaker than us - if (GetPlayer()->GetDiplomacyAI()->GetMilitaryStrengthComparedToUs(eOtherPlayer) < STRENGTH_AVERAGE && GetPlayer()->GetDiplomacyAI()->GetEconomicStrengthComparedToUs(eOtherPlayer) < STRENGTH_AVERAGE) - { - return false; - } + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - // Don't ask for war if we are in a DoF (we'll do the 'free' method instead) - if(GetPlayer()->GetDiplomacyAI()->IsDoFAccepted(eOtherPlayer)) - { - return false; - } + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; +} - //Don't offer if we have a DP. - if (GET_TEAM(GetPlayer()->getTeam()).IsHasDefensivePact(GET_PLAYER(eOtherPlayer).getTeam())) - { +/// A good time to make an offer for a Research Agreement? +bool CvDealAI::IsMakeOfferForResearchAgreement(PlayerTypes eOtherPlayer, CvDeal* pDeal) +{ + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESEARCH_AGREEMENT)) return false; - } - //Asking a vassal? Abort! - if(GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).IsVassalOfSomeone()) - { - return false; - } - //Vassals asking? Nope! - if(GET_TEAM(GetPlayer()->getTeam()).IsVassalOfSomeone()) - { - return false; - } - - int iWarValue = 0; - int iBestValue = 0; - TeamTypes eBestTeam = NO_TEAM; - - // find the first player associated with the team - for (int iI = 0; iI < MAX_CIV_PLAYERS; iI++) - { - PlayerTypes eAgainstPlayer = (PlayerTypes)iI; - if (eAgainstPlayer == NO_PLAYER) - { - continue; - } - //Is minor? Don't do it! (Too easy to wiggle out of) - if(GET_PLAYER(eAgainstPlayer).isMinorCiv()) - { - continue; - } - //Is our major approach friendly? Don't do it! - if(GET_PLAYER(eAgainstPlayer).isMajorCiv() && GetPlayer()->GetDiplomacyAI()->GetCivApproach(eAgainstPlayer) > CIV_APPROACH_GUARDED) - { - continue; - } - // Can we actually complete this deal? - if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_THIRD_PARTY_WAR, GET_PLAYER(eAgainstPlayer).getTeam())) - continue; - - iWarValue = GetThirdPartyWarValue(false, eOtherPlayer, GET_PLAYER(eAgainstPlayer).getTeam()); - if(iWarValue != INT_MAX && iWarValue > iBestValue) - { - eBestTeam = GET_PLAYER(eAgainstPlayer).getTeam(); - iBestValue = iWarValue; - } - } - - if(eBestTeam == NO_TEAM) - { - return false; - } - else - { - pDeal->AddThirdPartyWar(eOtherPlayer, eBestTeam); - } + // Seed the deal with the item we want + pDeal->AddResearchAgreement(GetPlayer()->GetID(), GC.getGame().GetDealDuration()); + pDeal->AddResearchAgreement(eOtherPlayer, GC.getGame().GetDealDuration()); // AI evaluation bool bUselessReferenceVariable = false; @@ -6090,86 +6062,35 @@ bool CvDealAI::IsMakeOfferForThirdPartyWar(PlayerTypes eOtherPlayer, CvDeal* pDe return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; } -/// A good time to make an offer for a Peace Deal? -bool CvDealAI::IsMakeOfferForThirdPartyPeace(PlayerTypes eOtherPlayer, CvDeal* pDeal) +/// A good time to make an offer to exchange Open Borders? +bool CvDealAI::IsMakeOfferForOpenBordersExchange(PlayerTypes eOtherPlayer, CvDeal* pDeal) { - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - if(eOtherPlayer == NO_PLAYER) - { - return false; - } - //Friends? We overlook friendly wars. - if(GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer) == CIV_APPROACH_FRIENDLY) - { + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_OPEN_BORDERS)) return false; - } - // Don't ask for peace if we are in a DoF - if(GetPlayer()->GetDiplomacyAI()->IsDoFAccepted(eOtherPlayer)) - { - return false; - } - //Asking a vassal? Abort! - if(GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).IsVassalOfSomeone()) - { - return false; - } - //Vassals asking? Nope! - if(GET_TEAM(GetPlayer()->getTeam()).IsVassalOfSomeone()) - { + if (!pDeal->IsPossibleToTradeItem(GetPlayer()->GetID(), eOtherPlayer, TRADE_ITEM_OPEN_BORDERS)) return false; - } - int iWarValue = 0; - int iBestValue = 0; - TeamTypes eBestTeam = NO_TEAM; + // Seed the deal with the item we want + pDeal->AddOpenBorders(GetPlayer()->GetID(), GC.getGame().GetDealDuration()); + pDeal->AddOpenBorders(eOtherPlayer, GC.getGame().GetDealDuration()); - // find the first player associated with the team - for (int iI = 0; iI < MAX_CIV_PLAYERS; iI++) - { - PlayerTypes eAgainstPlayer = (PlayerTypes)iI; - if (eAgainstPlayer == NO_PLAYER) - { - continue; - } - //Minor Civ? Only if allies or protective - if(GET_PLAYER(eAgainstPlayer).isMinorCiv() && (!GET_PLAYER(eAgainstPlayer).GetMinorCivAI()->IsAllies(GetPlayer()->GetID()))) - { - continue; - } - //Minor civ? Only if we're not out for conquest. - if(GET_PLAYER(eAgainstPlayer).isMinorCiv() && (GetPlayer()->GetDiplomacyAI()->GetCivApproach(eAgainstPlayer) == CIV_APPROACH_WAR)) - { - continue; - } - //Is our approach of the player we're asking about bad? Don't do it! - if(!GET_PLAYER(eAgainstPlayer).isMinorCiv() && GetPlayer()->GetDiplomacyAI()->GetCivApproach(eAgainstPlayer) < CIV_APPROACH_DECEPTIVE) - { - continue; - } - // Can we actually complete this deal? - if(pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_THIRD_PARTY_PEACE, GET_PLAYER(eAgainstPlayer).getTeam())) - { - iWarValue = GetThirdPartyPeaceValue(false, eOtherPlayer, GET_PLAYER(eAgainstPlayer).getTeam()); - if(iWarValue!=INT_MAX && iWarValue > iBestValue) - { - eBestTeam = GET_PLAYER(eAgainstPlayer).getTeam(); - iBestValue = iWarValue; - } - } - } - int iPeaceDuration = GC.getGame().getGameSpeedInfo().getPeaceDealDuration(); - if(eBestTeam == NO_TEAM) - { + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work + + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; +} + +/// A good time to make an offer to get Open Borders? +bool CvDealAI::IsMakeOfferForOpenBorders(PlayerTypes eOtherPlayer, CvDeal* pDeal) +{ + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_OPEN_BORDERS)) return false; - } - else - { - pDeal->AddThirdPartyPeace(eOtherPlayer, eBestTeam, iPeaceDuration); - } + // Seed the deal with the item we want + pDeal->AddOpenBorders(eOtherPlayer, GC.getGame().GetDealDuration()); // AI evaluation bool bUselessReferenceVariable = false; @@ -6179,82 +6100,65 @@ bool CvDealAI::IsMakeOfferForThirdPartyPeace(PlayerTypes eOtherPlayer, CvDeal* p return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; } -/// A good time to make an offer for a Peace Deal? -bool CvDealAI::IsMakeOfferForVote(PlayerTypes eOtherPlayer, CvDeal* pDeal) +/// A good time to make an offer for someone's extra Luxury? +bool CvDealAI::IsMakeOfferForLuxuryResource(PlayerTypes eOtherPlayer, CvDeal* pDeal) { - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); + ResourceTypes eLuxuryFromThem = NO_RESOURCE; + int iBestValue = 20; - CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); - if(!pLeague) - { - return false; - } - - if(eOtherPlayer == NO_PLAYER) - { - return false; - } - //Friends? - if(GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer) < CIV_APPROACH_AFRAID) - { - return false; - } - if (!GET_PLAYER(eOtherPlayer).GetLeagueAI()->CanCommitVote(GetPlayer()->GetID())) - { - return false; - } - //Asking a vassal? Abort! - if(GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).IsVassalOfSomeone()) - { - return false; - } - //Vassals asking? Nope! - if(GET_TEAM(GetPlayer()->getTeam()).IsVassalOfSomeone()) + // See if the other player has a Resource to trade + int iDuration = GC.getGame().GetDealDuration(); + for (int iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) { - return false; - } + ResourceTypes eResource = (ResourceTypes) iResourceLoop; + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESOURCES, eResource)) + continue; - int iBestValue = 0; - int iBestProposal = -1; - int iProposalID = -1; - int iVoteChoice = -1; - int iNumVotes = -1; - bool bRepeal = false; - CvLeagueAI::VoteCommitmentList vDesiredCommitments = GetPlayer()->GetLeagueAI()->GetDesiredVoteCommitments(eOtherPlayer); - for (CvLeagueAI::VoteCommitmentList::iterator it = vDesiredCommitments.begin(); it != vDesiredCommitments.end(); ++it) - { - iProposalID = it->iResolutionID; - iVoteChoice = it->iVoteChoice; - iNumVotes = it->iNumVotes; - bRepeal = !it->bEnact; + // Only look at Luxuries + const CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); + if (pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_LUXURY) + continue; + + // One copy is enough + if (m_pPlayer->getNumResourceAvailable(eResource, true) > 0) + continue; - if (iProposalID != -1 && pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_VOTE_COMMITMENT, iProposalID, iVoteChoice, iNumVotes, bRepeal)) + // Don't ask if they have only one copy + if (GET_PLAYER(eOtherPlayer).getNumResourceAvailable(eResource, false) <= 1) + continue; + + // Is it possible to trade that item? + if (!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_RESOURCES, eResource, 1)) + continue; + + // Can we strike a deal with the other AI? + if (GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eResource, 1, -1, false, iDuration, /* bIsAIOffer*/ true, /*bEqualize*/ true) == INT_MAX) + continue; + + // Let's try to get the resource that's most valuable to us (do not do bEqualize here) + int iItemValue = GetTradeItemValue(TRADE_ITEM_RESOURCES, false, eOtherPlayer, eResource, 1, -1, false, iDuration, /* bIsAIOffer*/ true, /*bEqualize*/ false); + if (iItemValue != INT_MAX && iItemValue > iBestValue) { - int iItemValue = GetTradeItemValue(TRADE_ITEM_VOTE_COMMITMENT, /*bFromMe*/ false, eOtherPlayer, iProposalID, iVoteChoice, iNumVotes, bRepeal, -1, true); - if(iItemValue!=INT_MAX && iItemValue > iBestValue) - { - iBestProposal = iProposalID; - iBestValue = iItemValue; - } + eLuxuryFromThem = eResource; + iBestValue = iItemValue; } } - if(iBestProposal == -1) - { - return false; - } - else + // Extra Luxury found! + if (eLuxuryFromThem != NO_RESOURCE) { - pDeal->AddVoteCommitment(eOtherPlayer, iProposalID, iVoteChoice, iNumVotes, bRepeal); - } + // Seed the deal with the item we want + pDeal->AddResourceTrade(eOtherPlayer, eLuxuryFromThem, 1, iDuration); - // AI evaluation - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work + // AI evaluation + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, /*bDontChangeMyExistingItems*/ bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; + return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; + } + + return false; } /// This function called when human player enters diplomacy @@ -6379,22 +6283,19 @@ DemandResponseTypes CvDealAI::GetRequestForHelpResponse(CvDeal* pDeal) const bool bGiveUpOneTech = false; // Too soon for another help request? - if(pDiploAI->IsHelpRequestTooSoon(eFromPlayer)) + if (pDiploAI->IsHelpRequestTooSoon(eFromPlayer)) return DEMAND_RESPONSE_GIFT_REFUSE_TOO_SOON; // Not too soon for a help request else { // Deal valued impossible? - if (GET_PLAYER(eMyPlayer).GetDealAI()->GetDealValue(pDeal) == INT_MAX) - { + if (GetDealValue(pDeal) == INT_MAX) return DEMAND_RESPONSE_GIFT_REFUSE_TOO_MUCH; - } + // Deceptive AIs won't ever accept a help request if (pDiploAI->GetCivApproach(eFromPlayer) <= CIV_APPROACH_DECEPTIVE) - { return DEMAND_RESPONSE_GIFT_REFUSE_TOO_MUCH; - } int iOddsOfGivingIn = pDiploAI->GetLoyalty() * 10; // initial odds of giving in are loyalty * 10 @@ -7465,284 +7366,6 @@ int CvDealAI::GetRevokeVassalageValue(bool bFromMe, PlayerTypes eOtherPlayer, bo return iItemValue; } -/// A good time to offer for World Map? -bool CvDealAI::IsMakeOfferForMaps(PlayerTypes eOtherPlayer, CvDeal* pDeal) -{ - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - // Don't ask for a map if we're hostile - if (GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer) == CIV_APPROACH_HOSTILE) - { - return false; - } - - // Can we actually complete this deal? - if(!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_MAPS)) - { - return false; - } - - // Do we actually want Maps from this other Player? - if(GetPlayer()->GetDiplomacyAI()->WantsMapsFromPlayer(eOtherPlayer)) - { - // Seed the deal with the item we want - pDeal->AddMapTrade(eOtherPlayer); - - // AI evaluation - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - - return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; - } - return false; -} - -/// A good time to make an offer for technology? -bool CvDealAI::IsMakeOfferForTech(PlayerTypes eOtherPlayer, CvDeal* pDeal) -{ - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - // Don't ask for Technology if we're hostile or planning war - CivApproachTypes eApproach = GetPlayer()->GetDiplomacyAI()->GetCivApproach(eOtherPlayer); - if (eApproach <= CIV_APPROACH_HOSTILE || eApproach == CIV_APPROACH_GUARDED) - { - return false; - } - - //No current research? We may have just finished something. - if(GetPlayer()->GetPlayerTechs()->GetCurrentResearch() == NO_TECH) - { - return false; - } - //No current research? We may have just finished something. - if(GET_PLAYER(eOtherPlayer).GetPlayerTechs()->GetCurrentResearch() == NO_TECH) - { - return false; - } - - int iTechLoop = 0; - TechTypes eTech; - TechTypes eTechWeWant = NO_TECH; - - int iBestValue = 0; - // See if the other player has a technology to trade - for(iTechLoop = 0; iTechLoop < GC.getNumTechInfos(); iTechLoop++) - { - eTech = (TechTypes) iTechLoop; - - int iValue = 0; - // Let's not ask for the tech we're currently researching - if(eTech == GetPlayer()->GetPlayerTechs()->GetCurrentResearch()) - { - continue; - } - - if(!GetPlayer()->GetPlayerTechs()->IsResearch()) - { - continue; - } - if(!GET_PLAYER(eOtherPlayer).GetPlayerTechs()->IsResearch()) - { - continue; - } - if(eTech == GET_PLAYER(eOtherPlayer).GetPlayerTechs()->GetCurrentResearch()) - { - continue; - } - // don't do the expensive turn calculation for techs that can't be traded anyway - if(!GetPlayer()->GetPlayerTechs()->CanResearch(eTech)) - continue; - - // Can they sell that tech? - if(pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_TECHS, eTech)) - { - iValue = GetTechValue(eTech, false, eOtherPlayer); - if(iValue > iBestValue) - { - eTechWeWant = eTech; - iBestValue = iValue; - } - } - } - - // TECH FOUND! - if(eTechWeWant != NO_TECH) - { - // Seed the deal with the item we want - pDeal->AddTechTrade(eOtherPlayer, eTechWeWant); - - // AI evaluation - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - - return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; - } - - return false; -} - -/// A good time to make an offer for Vassalage? -bool CvDealAI::IsMakeOfferForVassalage(PlayerTypes eOtherPlayer, CvDeal* pDeal) -{ - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - // Human teams don't get asked for vassalage - if(GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).isHuman(ISHUMAN_AI_DIPLOMACY)) - { - return false; - } - - if(!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_VASSALAGE)) - { - return false; - } - - if (m_pPlayer->IsAtWarWith(eOtherPlayer)) - { - return false; - } - - // we don't want to do it? - if (!GetPlayer()->GetDiplomacyAI()->IsVassalageAcceptable(eOtherPlayer, true)) - { - return false; - } - - // they don't want to do it? - if (!GET_PLAYER(eOtherPlayer).GetDiplomacyAI()->IsVassalageAcceptable(GetPlayer()->GetID(), false)) - { - return false; - } - - // Seed the deal with the item we want - pDeal->AddVassalageTrade(eOtherPlayer); - - // AI evaluation - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - - return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; -} - -/// A good time to make an offer for Vassalage? -bool CvDealAI::IsMakeOfferToBecomeVassal(PlayerTypes eOtherPlayer, CvDeal* pDeal) -{ - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - if (!pDeal->IsPossibleToTradeItem(GetPlayer()->GetID(), eOtherPlayer, TRADE_ITEM_VASSALAGE)) - { - return false; - } - - if (m_pPlayer->IsAtWarWith(eOtherPlayer)) - { - return false; - } - - // we don't want to do it? - if (!GetPlayer()->GetDiplomacyAI()->IsVassalageAcceptable(eOtherPlayer, false)) - { - return false; - } - - // they don't want to do it? - if (!GET_PLAYER(eOtherPlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(eOtherPlayer).GetDiplomacyAI()->IsVassalageAcceptable(GetPlayer()->GetID(), true)) - { - return false; - } - - // Seed the deal with the item we want - pDeal->AddVassalageTrade(GetPlayer()->GetID()); - - // AI evaluation - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - - return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; -} - -bool CvDealAI::IsMakeOfferForRevokeVassalage(PlayerTypes eOtherPlayer, CvDeal* pDeal) -{ - PRECONDITION(eOtherPlayer >= 0); - PRECONDITION(eOtherPlayer < MAX_MAJOR_CIVS); - - //If the other player has no vassals... - if(GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).GetNumVassals() <= 0) - { - return false; - } - - if(!pDeal->IsPossibleToTradeItem(eOtherPlayer, GetPlayer()->GetID(), TRADE_ITEM_VASSALAGE_REVOKE)) - { - return false; - } - - bool bWorthIt = false; - for(int iTeamLoop= 0; iTeamLoop < MAX_TEAMS; iTeamLoop++) - { - TeamTypes eLoopTeam = (TeamTypes) iTeamLoop; - - // Ignore minors. - if(!GET_TEAM(eLoopTeam).isMinorCiv()) - { - // Is eLoopTeam the vassal of them? - if(GET_TEAM(eLoopTeam).IsVassal(GET_PLAYER(eOtherPlayer).getTeam())) - { - if(GET_TEAM(eLoopTeam).isAlive()) - { - const vector& vVassalTeam = GET_TEAM(eLoopTeam).getPlayers(); - for (size_t iPlayerLoop = 0; iPlayerLoop < vVassalTeam.size(); ++iPlayerLoop) - { - PlayerTypes eVassalPlayer = vVassalTeam[iPlayerLoop]; - - // Check if this player actually wants to be free of their vassalage. - // If not, we never consider freeing the team worth it and exit immediately. - // - // Note: Any one player not wanting to be free is enough for us to disregard the rest of the team. - if (!GET_PLAYER(eVassalPlayer).GetDiplomacyAI()->IsEndVassalageWithPlayerAcceptable(eOtherPlayer)) - { - bWorthIt = false; - break; - } - // Now check if we actually care about this player. - // - // Note: We only need to care for one player on the team to consider freeing the team. - if (!bWorthIt) - { - bWorthIt = (GetPlayer()->GetDiplomacyAI()->GetCivOpinion(eVassalPlayer) >= CIV_OPINION_FRIEND) - || (GetPlayer()->GetDiplomacyAI()->GetCivOpinion(eVassalPlayer) >= CIV_OPINION_FAVORABLE - && GetPlayer()->GetDiplomacyAI()->IsDoFAccepted(eVassalPlayer) - && GetPlayer()->GetDiplomacyAI()->GetCivApproach(eVassalPlayer) == CIV_APPROACH_FRIENDLY) - || (GetPlayer()->GetDiplomacyAI()->WasResurrectedBy(eVassalPlayer) - || GetPlayer()->GetDiplomacyAI()->IsMasterLiberatedMeFromVassalage(eVassalPlayer)); - } - } - } - } - } - } - bool bDealAcceptable = false; - bool bCantMatchOffer = false; - if(bWorthIt) - { - // Seed the deal with the item we want - pDeal->AddRevokeVassalageTrade(eOtherPlayer); - // AI evaluation - bool bUselessReferenceVariable = false; - bDealAcceptable = DoEqualizeDeal(pDeal, eOtherPlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - } - - return bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0; -} - /// See if adding Maps to their side of the deal helps even out pDeal void CvDealAI::DoAddMapsToThem(CvDeal* pDeal, PlayerTypes eThem, int& iTotalValue, int iThresholdValue) { diff --git a/CvGameCoreDLL_Expansion2/CvDealAI.h b/CvGameCoreDLL_Expansion2/CvDealAI.h index 29a328d735..569c99850a 100644 --- a/CvGameCoreDLL_Expansion2/CvDealAI.h +++ b/CvGameCoreDLL_Expansion2/CvDealAI.h @@ -129,21 +129,23 @@ class CvDealAI int GetPotentialDemandValue(PlayerTypes eOtherPlayer, CvDeal* pDeal, int iIdealValue); bool IsMakeDemand(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForLuxuryResource(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForEmbassy(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForOpenBorders(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForResearchAgreement(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForStrategicResource(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferToBecomeVassal(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForVassalage(PlayerTypes eOtherPlayer, CvDeal* pDeal); bool IsMakeOfferForDefensivePact(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForRevokeVassalage(PlayerTypes eOtherPlayer, CvDeal* pDeal); bool IsMakeOfferForCityExchange(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForThirdPartyWar(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForThirdPartyPeace(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForTech(PlayerTypes eOtherPlayer, CvDeal* pDeal); bool IsMakeOfferForVote(PlayerTypes eOtherPlayer, CvDeal* pDeal); bool IsMakeOfferForMaps(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForTech(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForVassalage(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferToBecomeVassal(PlayerTypes eOtherPlayer, CvDeal* pDeal); - bool IsMakeOfferForRevokeVassalage(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForThirdPartyWar(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForThirdPartyPeace(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForStrategicResource(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForEmbassyExchange(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForEmbassy(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForResearchAgreement(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForOpenBordersExchange(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForOpenBorders(PlayerTypes eOtherPlayer, CvDeal* pDeal); + bool IsMakeOfferForLuxuryResource(PlayerTypes eOtherPlayer, CvDeal* pDeal); // Called when the human opens or closes the Trade Screen diff --git a/CvGameCoreDLL_Expansion2/CvDealClasses.cpp b/CvGameCoreDLL_Expansion2/CvDealClasses.cpp index 30048ec205..4cb83b74eb 100644 --- a/CvGameCoreDLL_Expansion2/CvDealClasses.cpp +++ b/CvGameCoreDLL_Expansion2/CvDealClasses.cpp @@ -702,7 +702,7 @@ bool CvDeal::IsPossibleToTradeItem(PlayerTypes ePlayer, PlayerTypes eToPlayer, T return false; // AI teammate of human - if (pFromPlayer->IsAITeammateOfHuman()) + if (pFromPlayer->IsAITeammateOfHuman() || (MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM && pToPlayer->IsAITeammateOfHuman())) return false; // Neither of us yet has the tech for Open Borders @@ -1209,6 +1209,13 @@ bool CvDeal::IsPossibleToTradeItem(PlayerTypes ePlayer, PlayerTypes eToPlayer, T if (GC.getGame().isOption(GAMEOPTION_NO_SCIENCE) || !GC.getGame().isOption(GAMEOPTION_ENABLE_TECH_TRADING)) return false; + // Either side has not founded a city yet? + if (!pFromPlayer->GetPlayerTechs()->IsResearch()) + return false; + + if (!pToPlayer->GetPlayerTechs()->IsResearch()) + return false; + // Mutual embassies are required (unless this is a peace deal) if (!bPeaceDeal) { @@ -1226,7 +1233,7 @@ bool CvDeal::IsPossibleToTradeItem(PlayerTypes ePlayer, PlayerTypes eToPlayer, T return false; CvTechEntry* pkTechInfo = GC.getTechInfo(eTech); - if (!pkTechInfo || pkTechInfo->IsRepeat()) + if (pkTechInfo->IsRepeat()) return false; // We don't own this tech @@ -1254,7 +1261,7 @@ bool CvDeal::IsPossibleToTradeItem(PlayerTypes ePlayer, PlayerTypes eToPlayer, T return false; // AI teammate of human - if (pFromPlayer->IsAITeammateOfHuman()) + if (pFromPlayer->IsAITeammateOfHuman() || pToPlayer->IsAITeammateOfHuman()) return false; // Vassalage is disabled @@ -1297,7 +1304,7 @@ bool CvDeal::IsPossibleToTradeItem(PlayerTypes ePlayer, PlayerTypes eToPlayer, T if (pFromPlayer->IsAITeammateOfHuman()) return false; - // Must be able to end all vassals + // Must be able to end all vassals (this also checks > 0 vassals) if (!pFromTeam->canEndAllVassal()) return false; @@ -1773,7 +1780,7 @@ CvString CvDeal::GetReasonsItemUntradeable(PlayerTypes ePlayer, PlayerTypes eToP } // AI teammate of human - if (pFromPlayer->IsAITeammateOfHuman()) + if (pFromPlayer->IsAITeammateOfHuman() || (MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM && pToPlayer->IsAITeammateOfHuman())) { strTooltip = strDivider; strTooltip += strStartColor; @@ -2325,7 +2332,7 @@ CvString CvDeal::GetReasonsItemUntradeable(PlayerTypes ePlayer, PlayerTypes eToP } // AI teammate of human - if (pFromPlayer->IsAITeammateOfHuman()) + if (pFromPlayer->IsAITeammateOfHuman() || pToPlayer->IsAITeammateOfHuman()) { strTooltip = strDivider; strTooltip += strStartColor; @@ -5735,29 +5742,39 @@ void CvGameDeals::DoEndTradedItem(CvTradedItem* pItem, PlayerTypes eToPlayer, bo if(!GET_TEAM(eFromTeam).isAtWar(eToTeam) && !bCancelled) { - // Beaker boost = ((sum of both players' beakers over term of RA) / 2) / 3) * (median tech percentage rate) + // Beaker boost = (RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT * (average of both players' beakers over term of RA) + (1 - RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT) * (minimum of both players' beakers over term of RA)) / 3) * (median tech percentage rate (50%)) CvTeam& kTeam = GET_TEAM(toPlayer.getTeam()); - int iToPlayerBeakers = toPlayer.GetResearchAgreementCounter(eFromPlayer); - int iFromPlayerBeakers = fromPlayer.GetResearchAgreementCounter(eToPlayer); - int iBeakersBonus = min(iToPlayerBeakers, iFromPlayerBeakers) / /*3*/ GD_INT_GET(RESEARCH_AGREEMENT_BOOST_DIVISOR); //one (third) of minimum contribution - iBeakersBonus = (iBeakersBonus * toPlayer.GetMedianTechPercentage()) / 100; + int iToPlayerBeakers = toPlayer.GetResearchAgreementCounter(eFromPlayer) / 100; + int iFromPlayerBeakers = fromPlayer.GetResearchAgreementCounter(eToPlayer) / 100; + int iBeakersBonus = 0; + + iBeakersBonus = ((100 - GD_INT_GET(RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT)) * min(iToPlayerBeakers, iFromPlayerBeakers) + GD_INT_GET(RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT) * (iToPlayerBeakers + iFromPlayerBeakers) / 2) / (100 * /*3*/ GD_INT_GET(RESEARCH_AGREEMENT_BOOST_DIVISOR)); + + iBeakersBonus *= toPlayer.GetMedianTechPercentage(); + iBeakersBonus/= 100; + + // some of the yields of the RA are given per turn, not as instant yield when the agreement ends + iBeakersBonus *= (100 - GD_INT_GET(RESEARCH_AGREEMENT_PER_TURN_YIELD_PERCENT)); + iBeakersBonus /= 100; TechTypes eCurrentTech = toPlayer.GetPlayerTechs()->GetCurrentResearch(); CvCity* pCapital = toPlayer.getCapitalCity(); if (pCapital) { - toPlayer.doInstantYield(INSTANT_YIELD_TYPE_RESEARCH_AGREMEENT, false, NO_GREATPERSON, NO_BUILDING, iBeakersBonus, false, NO_PLAYER, NULL, false, pCapital, false, false, false, YIELD_SCIENCE); - } - else - if(eCurrentTech == NO_TECH) - { - toPlayer.changeOverflowResearch(iBeakersBonus); - toPlayer.changeInstantYieldValue(YIELD_SCIENCE, iBeakersBonus); + toPlayer.doInstantYield(INSTANT_YIELD_TYPE_RESEARCH_AGREEMENT, false, NO_GREATPERSON, NO_BUILDING, iBeakersBonus, false, NO_PLAYER, NULL, false, pCapital, false, false, false, YIELD_SCIENCE); } else { - kTeam.GetTeamTechs()->ChangeResearchProgress(eCurrentTech, iBeakersBonus, eToPlayer); - toPlayer.changeInstantYieldValue(YIELD_SCIENCE, iBeakersBonus); + if (eCurrentTech == NO_TECH) + { + toPlayer.changeOverflowResearch(iBeakersBonus); + toPlayer.changeInstantYieldValue(YIELD_SCIENCE, iBeakersBonus); + } + else + { + kTeam.GetTeamTechs()->ChangeResearchProgress(eCurrentTech, iBeakersBonus, eToPlayer); + toPlayer.changeInstantYieldValue(YIELD_SCIENCE, iBeakersBonus); + } } pNotifications = toPlayer.GetNotifications(); diff --git a/CvGameCoreDLL_Expansion2/CvDiplomacyAI.cpp b/CvGameCoreDLL_Expansion2/CvDiplomacyAI.cpp index 60228c51e6..f665e389e3 100644 --- a/CvGameCoreDLL_Expansion2/CvDiplomacyAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvDiplomacyAI.cpp @@ -296,8 +296,8 @@ void CvDiplomacyAI::Init(CvPlayer* pPlayer) m_aiNegativeReligiousConversionPoints[iI] = 0; m_aiNumTimesRobbedBy[iI] = 0; m_aiPerformedCoupAgainstUs[iI] = 0; - m_aiLikedTheirProposalValue[iI] = 0; - m_aiSupportedOurProposalValue[iI] = 0; + m_aiProposalAgreementValue[iI] = 0; + m_aiProposalSupportDelta[iI] = 0; m_aiVotingHistoryScore[iI] = 0; m_aiSupportedOurHostingValue[iI] = 0; m_aiNegativeArchaeologyPoints[iI] = 0; @@ -396,6 +396,7 @@ void CvDiplomacyAI::Init(CvPlayer* pPlayer) // Non-serialized memory m_bAvoidDeals = false; + m_bSkipForTeammates = false; m_bIgnoreWarmonger = false; m_eVassalPlayerToLiberate = NO_PLAYER; @@ -630,8 +631,8 @@ void CvDiplomacyAI::Serialize(DiplomacyAI& diplomacyAI, Visitor& visitor) visitor(diplomacyAI.m_aiNegativeReligiousConversionPoints); visitor(diplomacyAI.m_aiNumTimesRobbedBy); visitor(diplomacyAI.m_aiPerformedCoupAgainstUs); - visitor(diplomacyAI.m_aiLikedTheirProposalValue); - visitor(diplomacyAI.m_aiSupportedOurProposalValue); + visitor(diplomacyAI.m_aiProposalAgreementValue); + visitor(diplomacyAI.m_aiProposalSupportDelta); visitor(diplomacyAI.m_aiVotingHistoryScore); visitor(diplomacyAI.m_aiSupportedOurHostingValue); visitor(diplomacyAI.m_aiNegativeArchaeologyPoints); @@ -1915,8 +1916,8 @@ int CvDiplomacyAI::ApplyPercentageModifier(int iValue, int iModifier, bool bDecr /// Returns a personality weight with a small random element int CvDiplomacyAI::RandomizePersonalityFlavor(int iOriginalValue, const CvSeeder& seed) { - // Four flavors can be set to -12 to guarantee that the AI will pursue a specific victory condition, if it is enabled. - // These should be treated as a 10 for all other purposes, and they override Random Personalities. + // Any flavor set to -12 should be treated as a 10 for all other purposes, and they override Random Personalities. + // Four of these flavors guarantee that the AI will pursue a specific victory condition, if it is enabled. if (iOriginalValue == -12) return 10; @@ -2103,8 +2104,7 @@ void CvDiplomacyAI::SelectDefaultVictoryPursuits() VictoryPursuitTypes ePrimaryVictory = NO_VICTORY_PURSUIT; VictoryPursuitTypes eSecondaryVictory = NO_VICTORY_PURSUIT; - // Ignore Random Personalities when the DiploAI option is active or when retrieving hints for highly specialized scenarios - LeaderHeadTypes leader = (iNumValidOptions > 2 && !MOD_DIPLOAI_LIMIT_VICTORY_PURSUIT_RANDOMIZATION) ? GetPlayer()->getPersonalityType() : GetPlayer()->getLeaderType(); + LeaderHeadTypes leader = GetPlayer()->getPersonalityType(); if (leader != NO_LEADER) { CvLeaderHeadInfo* pkLeaderHeadInfo = GC.getLeaderHeadInfo(leader); @@ -2335,14 +2335,19 @@ void CvDiplomacyAI::SelectDefaultVictoryPursuits() SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DOMINATION); return; } + if (bCanUnlockPolicies) + { + SetPrimaryVictoryPursuit(VICTORY_PURSUIT_CULTURE); + return; + } if (bCanUnlockTechs) { SetPrimaryVictoryPursuit(VICTORY_PURSUIT_SCIENCE); return; } - if (bCanUnlockPolicies) + if (bCanUseLeague) { - SetPrimaryVictoryPursuit(VICTORY_PURSUIT_CULTURE); + SetPrimaryVictoryPursuit(VICTORY_PURSUIT_DIPLOMACY); return; } } @@ -2383,21 +2388,6 @@ void CvDiplomacyAI::SelectDefaultVictoryPursuits() VictoryScores[VICTORY_PURSUIT_SCIENCE] += pFlavorMgr->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_SCIENCE")); VictoryScores[VICTORY_PURSUIT_SCIENCE] += pFlavorMgr->GetPersonalityFlavorForDiplomacy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_GROWTH")); - // Add leader bias for default primary pursuit - if (ePrimaryVictory != NO_VICTORY_PURSUIT) - VictoryScores[ePrimaryVictory] += eSecondaryVictory == NO_VICTORY_PURSUIT ? /*16*/ GD_INT_GET(VICTORY_PURSUIT_PRIMARY_ONLY_HINT_WEIGHT) : /*10*/ GD_INT_GET(VICTORY_PURSUIT_PRIMARY_HINT_WEIGHT); - - // Default secondary pursuit adds a smaller amount of bias, if it exists - // Note: If default primary pursuit is NO_VICTORY_PURSUIT, there will also be no default secondary pursuit - if (eSecondaryVictory != NO_VICTORY_PURSUIT) - VictoryScores[eSecondaryVictory] += /*6*/ GD_INT_GET(VICTORY_PURSUIT_SECONDARY_HINT_WEIGHT); - - // Not many City-States? City-States aren't 100% essential but without them a Diplomatic Victory becomes much harder. - int iNumMinorsEver = GC.getGame().GetNumMinorCivsEver(); - int iNumMajorsEver = GC.getGame().GetNumMajorCivsEver(); - if (iNumMinorsEver < iNumMajorsEver) - VictoryScores[VICTORY_PURSUIT_DIPLOMACY] += iNumMinorsEver <= iNumMajorsEver / 2 ? /*-20*/ GD_INT_GET(VICTORY_PURSUIT_FEW_CITY_STATES_PENALTY) : /*-10*/ GD_INT_GET(VICTORY_PURSUIT_FEW_CITY_STATES_PENALTY) / 2; - // Reduce weight significantly if the associated victory condition is disabled. // But not entirely, because this is a civ's ROUTE to victory, not necessarily the victory condition they will achieve. bool bDominationVictoryEnabled = GC.getGame().isVictoryValid((VictoryTypes) GC.getInfoTypeForString("VICTORY_DOMINATION", true)); @@ -2414,6 +2404,25 @@ void CvDiplomacyAI::SelectDefaultVictoryPursuits() if (!bScienceVictoryEnabled) VictoryScores[VICTORY_PURSUIT_SCIENCE] += /*-20*/ GD_INT_GET(VICTORY_PURSUIT_DISABLED_VICTORY_PENALTY); + // Victory pursuit hint weights may be affected by disabled victory conditions as well + bool bPrimaryVictoryDisabled = ((ePrimaryVictory == VICTORY_PURSUIT_DOMINATION && !bDominationVictoryEnabled) || (ePrimaryVictory == VICTORY_PURSUIT_DIPLOMACY && !bDiploVictoryEnabled) || (ePrimaryVictory == VICTORY_PURSUIT_CULTURE && !bCultureVictoryEnabled) || (ePrimaryVictory == VICTORY_PURSUIT_SCIENCE && !bScienceVictoryEnabled)); + bool bSecondaryVictoryEnabled = ((eSecondaryVictory == VICTORY_PURSUIT_DOMINATION && bDominationVictoryEnabled) || (eSecondaryVictory == VICTORY_PURSUIT_DIPLOMACY && bDiploVictoryEnabled) || (eSecondaryVictory == VICTORY_PURSUIT_CULTURE && bCultureVictoryEnabled) || (eSecondaryVictory == VICTORY_PURSUIT_SCIENCE && bScienceVictoryEnabled)); + + // Add leader bias for default primary pursuit + if (ePrimaryVictory != NO_VICTORY_PURSUIT) + VictoryScores[ePrimaryVictory] += ((eSecondaryVictory == NO_VICTORY_PURSUIT || !bSecondaryVictoryEnabled) && !bPrimaryVictoryDisabled) ? /*16*/ GD_INT_GET(VICTORY_PURSUIT_PRIMARY_ONLY_HINT_WEIGHT) : /*10*/ GD_INT_GET(VICTORY_PURSUIT_PRIMARY_HINT_WEIGHT); + + // Default secondary pursuit adds a smaller amount of bias, if it exists + // Note: If default primary pursuit is NO_VICTORY_PURSUIT, there will also be no default secondary pursuit + if (eSecondaryVictory != NO_VICTORY_PURSUIT) + VictoryScores[eSecondaryVictory] += (bPrimaryVictoryDisabled && bSecondaryVictoryEnabled) ? /*10*/ GD_INT_GET(VICTORY_PURSUIT_PRIMARY_HINT_WEIGHT) : /*6*/ GD_INT_GET(VICTORY_PURSUIT_SECONDARY_HINT_WEIGHT); + + // Not many City-States? City-States aren't 100% essential but without them a Diplomatic Victory becomes much harder. + int iNumMinorsEver = GC.getGame().GetNumMinorCivsEver(); + int iNumMajorsEver = GC.getGame().GetNumMajorCivsEver(); + if (iNumMinorsEver < iNumMajorsEver) + VictoryScores[VICTORY_PURSUIT_DIPLOMACY] += iNumMinorsEver <= iNumMajorsEver / 2 ? /*-20*/ GD_INT_GET(VICTORY_PURSUIT_FEW_CITY_STATES_PENALTY) : /*-10*/ GD_INT_GET(VICTORY_PURSUIT_FEW_CITY_STATES_PENALTY) / 2; + // Zero out all invalid scores if (!bCanConquer) VictoryScores[VICTORY_PURSUIT_DOMINATION] = 0; @@ -2453,11 +2462,7 @@ void CvDiplomacyAI::SelectDefaultVictoryPursuits() iScore += GC.getGame().randRangeInclusive(0, iRandomness, CvSeeder::fromRaw(0xf8b11a03).mix(ID).mix(i)); iScore -= GC.getGame().randRangeInclusive(0, iRandomness, CvSeeder::fromRaw(0x9ce36696).mix(ID).mix(i)); - // Protect against modder stupidity - if (iScore < 0) - iScore = 0; - - SortedVictoryScores.SetWeight(i, iScore); + SortedVictoryScores.SetWeight(i, max(iScore, 0)); } SortedVictoryScores.StableSortItems(); @@ -7817,37 +7822,37 @@ void CvDiplomacyAI::ChangeNumTimesPerformedCoupAgainstUs(PlayerTypes ePlayer, in } /// How much support or opposition did we have for their most recent World Congress proposal? -int CvDiplomacyAI::GetLikedTheirProposalValue(PlayerTypes ePlayer) const +int CvDiplomacyAI::GetProposalAgreementValue(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); - return m_aiLikedTheirProposalValue[ePlayer]; + return m_aiProposalAgreementValue[ePlayer]; } -void CvDiplomacyAI::SetLikedTheirProposalValue(PlayerTypes ePlayer, int iValue) +void CvDiplomacyAI::SetProposalAgreementValue(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); - m_aiLikedTheirProposalValue[ePlayer] = range(iValue, CHAR_MIN, CHAR_MAX); + m_aiProposalAgreementValue[ePlayer] = range(iValue, CHAR_MIN, CHAR_MAX); } /// How much support or opposition did they give to our most recent World Congress proposal? -int CvDiplomacyAI::GetSupportedOurProposalValue(PlayerTypes ePlayer) const +int CvDiplomacyAI::GetProposalSupportDelta(PlayerTypes ePlayer) const { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); - return m_aiSupportedOurProposalValue[ePlayer]; + return m_aiProposalSupportDelta[ePlayer]; } -void CvDiplomacyAI::SetSupportedOurProposalValue(PlayerTypes ePlayer, int iValue) +void CvDiplomacyAI::SetProposalSupportDelta(PlayerTypes ePlayer, int iValue) { PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); - ASSERT(iValue >= -100 && iValue <= 100, "Setting SupportedOurProposalValue to an invalid value"); + ASSERT(iValue >= -100 && iValue <= 100, "Setting ProposalSupportDelta to an invalid value"); int iSetValue = iValue; - int iCurrentValue = GetSupportedOurProposalValue(ePlayer); + int iCurrentValue = GetProposalSupportDelta(ePlayer); // The easy cases if (iCurrentValue == 0 || IsTeammate(ePlayer)) { - m_aiSupportedOurProposalValue[ePlayer] = iSetValue; + m_aiProposalSupportDelta[ePlayer] = iSetValue; } // Remember previous support or opposition! This will get a little complicated, but it makes the AI smarter. else if (iSetValue != 0) @@ -7935,19 +7940,19 @@ void CvDiplomacyAI::SetSupportedOurProposalValue(PlayerTypes ePlayer, int iValue // Then add the scaled current value to the new value to get the sum iSetValue += iNewCurrentValue; - m_aiSupportedOurProposalValue[ePlayer] = range(iSetValue, -100, 100); + m_aiProposalSupportDelta[ePlayer] = range(iSetValue, -100, 100); } } /// Helper functions so the AI can remember recent World Congress diplomacy bool CvDiplomacyAI::IsSupportedOurProposalAndThenFoiledUs(PlayerTypes ePlayer) const { - return GetTheyFoiledOurProposalTurn(ePlayer) > -1 && GetSupportedOurProposalValue(ePlayer) < 0; + return GetTheyFoiledOurProposalTurn(ePlayer) > -1 && GetProposalSupportDelta(ePlayer) < 0; } bool CvDiplomacyAI::IsFoiledOurProposalAndThenSupportedUs(PlayerTypes ePlayer) const { - return GetTheySupportedOurProposalTurn(ePlayer) > -1 && GetSupportedOurProposalValue(ePlayer) > 0; + return GetTheySupportedOurProposalTurn(ePlayer) > -1 && GetProposalSupportDelta(ePlayer) > 0; } /// How much do we like or dislike their voting history on proposals in the World Congress? @@ -8507,7 +8512,7 @@ void CvDiplomacyAI::SetWeLikedTheirProposalTurn(PlayerTypes ePlayer, int iTurn) ASSERT(NotMe(ePlayer), "Setting WeLikedTheirProposalTurn for self"); m_aiWeLikedTheirProposalTurn[ePlayer] = iTurn; if (iTurn == -1) - SetLikedTheirProposalValue(ePlayer, 0); + SetProposalAgreementValue(ePlayer, 0); } bool CvDiplomacyAI::WeLikedTheirProposal(PlayerTypes ePlayer) const @@ -8529,7 +8534,7 @@ void CvDiplomacyAI::SetWeDislikedTheirProposalTurn(PlayerTypes ePlayer, int iTur ASSERT(NotMe(ePlayer), "Setting WeDislikedTheirProposalTurn for self"); m_aiWeDislikedTheirProposalTurn[ePlayer] = iTurn; if (iTurn == -1) - SetLikedTheirProposalValue(ePlayer, 0); + SetProposalAgreementValue(ePlayer, 0); } bool CvDiplomacyAI::WeDislikedTheirProposal(PlayerTypes ePlayer) const @@ -8690,6 +8695,18 @@ void CvDiplomacyAI::SetAvoidDeals(bool bValue) m_bAvoidDeals = bValue; } +/// Are we avoiding sending certain messages to a teammate? Temporary non-serialized value, used to avoid constant calls to CvPlayer::getTeam() +/// Also used to skip various diplo statement logic at the start of many functions while maintaining readability +bool CvDiplomacyAI::SkipForTeammates() const +{ + return m_bSkipForTeammates; +} + +void CvDiplomacyAI::SetSkipForTeammates(bool bValue) +{ + m_bSkipForTeammates = bValue; +} + /// Are we ignoring warmongering? Temporary non-serialized value. bool CvDiplomacyAI::IsIgnoreWarmonger() const { @@ -22698,11 +22715,11 @@ void CvDiplomacyAI::DoUpdatePrimeLeagueAlly() continue; // Must have no penalty for defeating our proposals - if (GetSupportedOurProposalValue(ePlayer) > 0) + if (GetProposalSupportDelta(ePlayer) > 0) continue; // Must not have strongly or overwhelmingly disliked their most recent proposal - if (GetLikedTheirProposalValue(ePlayer) > /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)) + if (GetProposalAgreementValue(ePlayer) > /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)) continue; // Can't be below the bad voting history threshold @@ -23025,41 +23042,6 @@ bool CvDiplomacyAI::IsGoodChoiceForResearchAgreement(PlayerTypes ePlayer) return true; } -/// Are we able to make a Research Agreement with ePlayer right now? -bool CvDiplomacyAI::IsCanMakeResearchAgreementRightNow(PlayerTypes ePlayer) -{ - // We don't want a RA with this guy - if (!IsWantsResearchAgreementWithPlayer(ePlayer)) - return false; - - // Already have a RA? - if (IsHasResearchAgreement(ePlayer)) - return false; - - // Either side already has all techs? - if (GET_TEAM(GetTeam()).GetTeamTechs()->HasResearchedAllTechs() || GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->HasResearchedAllTechs()) - return false; - - // We need tech & embassy to make a RA - if (!GET_TEAM(GetTeam()).IsResearchAgreementTradingAllowedWithTeam(GET_PLAYER(ePlayer).getTeam())) - return false; - - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).IsResearchAgreementTradingAllowedWithTeam(GetTeam())) - return false; - - int iGoldAmount = GC.getGame().GetGameDeals().GetTradeItemGoldCost(TRADE_ITEM_RESEARCH_AGREEMENT, GetID(), ePlayer); - - // We don't have enough Gold - if (GetPlayer()->GetTreasury()->GetGold() < iGoldAmount) - return false; - - // They don't have enough Gold - if (GET_PLAYER(ePlayer).GetTreasury()->GetGold() < iGoldAmount) - return false; - - return true; -} - PlayerTypes CvDiplomacyAI::GetHighestScoringDefensivePact(vector& vAcceptableChoices, vector& vPlayersToExclude) { if (vAcceptableChoices.empty()) @@ -25800,6 +25782,7 @@ void CvDiplomacyAI::DoUpdatePeaceTreatyWillingness(bool bMyTurn) // If we're fine and GLOBAL_CS_OVERSEAS_TERRITORY=1, try to capture any allied City-States of theirs we've endangered. if (MOD_GLOBAL_CS_OVERSEAS_TERRITORY && !bInTerribleShape && !bAnySeriousDangerUs) { + bool bButItRefused = false; for (std::vector::iterator iter = vEndangeredCityStates.begin(); iter != vEndangeredCityStates.end(); ++iter) { PlayerTypes eMinor = *iter; @@ -25807,6 +25790,7 @@ void CvDiplomacyAI::DoUpdatePeaceTreatyWillingness(bool bMyTurn) if (eAlly == NO_PLAYER || GET_PLAYER(eAlly).getTeam() != *it) continue; + bButItRefused = true; CvString strLogMessage; if (bLog) strLogMessage.Format("No peace! Making peace will lock us out of capturing their allied City-State's city!"); @@ -25817,6 +25801,8 @@ void CvDiplomacyAI::DoUpdatePeaceTreatyWillingness(bool bMyTurn) RefusePeaceTreaty(vEnemyTeamMembers[i], strLogMessage); } } + if (bButItRefused) + continue; } //----------------------------------// @@ -27392,6 +27378,10 @@ bool CvDiplomacyAI::IsValidDemandTarget(PlayerTypes ePlayer, int& iDemandValueSc if (GetPlayer()->GetProximityToPlayer(ePlayer) < PLAYER_PROXIMITY_CLOSE) return false; + // Don't target an AI teammate of a human, they will never accept + if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) + return false; + // Can't make/accept demands if sanctioned CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (pLeague != NULL && pLeague->IsTradeEmbargoed(GetID(), ePlayer)) @@ -27739,8413 +27729,9142 @@ bool CvDiplomacyAI::DeclareWar(TeamTypes eTeam) // Diplomatic Interactions // ************************************ +/// Possible Contact Statement - We attacked a minor that is protected by someone +void CvDiplomacyAI::DoWeAttackedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + if (SkipForTeammates()) + return; -// ----------------------------------------------------------------------------------------------- - - -// ************************************ -// Counters -// ************************************ - -/// Increment our turn counters -void CvDiplomacyAI::DoCounters() -{ - vector v; + // Don't send this to an AI teammate of a human + if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) + return; - // Loop through all (known) Players - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + for (int iMinorCivLoop = MAX_MAJOR_CIVS; iMinorCivLoop < MAX_CIV_PLAYERS; iMinorCivLoop++) { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + PlayerTypes eMinor = (PlayerTypes) iMinorCivLoop; - if (eLoopPlayer != GetID() && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && IsHasMet(eLoopPlayer, true)) + if (IsAtWar(eMinor) && GET_PLAYER(eMinor).isAlive() && GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(ePlayer)) { - CvNotifications* pNotifications = GET_PLAYER(eLoopPlayer).GetNotifications(); - - // Are we ready to forget our denunciation? - if (IsDenouncedPlayer(eLoopPlayer) && GetTurnsSinceDenouncedPlayer(eLoopPlayer) >= GC.getGame().getGameSpeedInfo().getRelationshipDuration()) - { - SetDenouncedPlayer(eLoopPlayer, false); + // Did we declare war on them recently? + int iTurn = GET_PLAYER(eMinor).GetMinorCivAI()->GetTurnLastAttacked(GetTeam()); + if (iTurn < 0) + return; - // Let's reevaluate this guy if they haven't denounced us and aren't untrustworthy. - // Rewards human appeasement strategies. - if (!IsDenouncedByPlayer(eLoopPlayer) && !IsUntrustworthy(eLoopPlayer)) - v.push_back(eLoopPlayer); + // Has this message already been sent during this war? + if (HasSentAttackProtectedMinorTaunt(ePlayer, eMinor)) + return; - // Notify the target of the denouncement that it has expired. - if (pNotifications) - { - CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_THEIR_DENUNCIATION_EXPIRED_S"); - Localization::String strInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_THEIR_DENUNCIATION_EXPIRED"); - Localization::String strTemp = strInfo; - strTemp << GET_PLAYER(GetID()).getCivilizationShortDescriptionKey(); - pNotifications->Add(NOTIFICATION_DENUNCIATION_EXPIRED, strTemp.toUTF8(), strSummary, -1, -1, GetID(), eLoopPlayer); - } - } + int iTurnDifference = GC.getGame().getGameTurn() - iTurn; + if (iTurnDifference >= 10) + return; - // Has our Friendship expired? - if (IsDoFAccepted(eLoopPlayer) && GetTurnsSinceBefriendedPlayer(eLoopPlayer) >= GC.getGame().getGameSpeedInfo().getRelationshipDuration()) + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WE_ATTACKED_YOUR_MINOR) >= 1) { - SetDoFAccepted(eLoopPlayer, false); - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->SetDoFAccepted(GetID(), false); - - // Notify both parties that our friendship has expired. - if (pNotifications) - { - CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED", GET_PLAYER(GetID()).getCivilizationShortDescriptionKey()); - CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED_S"); - pNotifications->Add(NOTIFICATION_FRIENDSHIP_EXPIRED, strBuffer, strSummary, -1, -1, GetID(), eLoopPlayer); - } - - pNotifications = GET_PLAYER(GetID()).GetNotifications(); - if (pNotifications) - { - CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); - CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED_S"); - pNotifications->Add(NOTIFICATION_FRIENDSHIP_EXPIRED, strBuffer, strSummary, -1, -1, eLoopPlayer, GetID()); - } + eStatement = DIPLO_STATEMENT_WE_ATTACKED_YOUR_MINOR; + iData1 = eMinor; } } } - - DoReevaluatePlayers(v); } -/// Does this AI have a gold quest active with any minor civ? -bool CvDiplomacyAI::IsHasActiveGoldQuest() +/// Possible Contact Statement - We bullied a minor that is protected by someone +void CvDiplomacyAI::DoWeBulliedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { - //antonjs: consider: optimize - for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) - { - PlayerTypes eMinor = (PlayerTypes) iMinorLoop; - if (GET_PLAYER(eMinor).GetMinorCivAI()->IsActiveQuestForPlayer(GetID(), MINOR_CIV_QUEST_GIVE_GOLD)) - return true; - if (GET_PLAYER(eMinor).GetMinorCivAI()->IsActiveQuestForPlayer(GetID(), MINOR_CIV_QUEST_INVEST)) - return true; - } - return false; -} + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; -///////////////////////////////////////////////////////// -// Requests -///////////////////////////////////////////////////////// + if (SkipForTeammates()) + return; + // Don't send this to our master + if (IsVassal(ePlayer)) + return; + // Don't send this to an AI teammate of a human + if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) + return; + for (int iMinorCivLoop = MAX_MAJOR_CIVS; iMinorCivLoop < MAX_CIV_PLAYERS; iMinorCivLoop++) + { + PlayerTypes eMinor = (PlayerTypes) iMinorCivLoop; -/// Is this AI willing to make a request of ePlayer? -bool CvDiplomacyAI::IsMakeRequest(PlayerTypes ePlayer, CvDeal* pDeal, bool& bRandPassed) -{ - bool bFriendly = GetSurfaceApproach(ePlayer) == CIV_APPROACH_FRIENDLY; + // Minor must be alive + if (!GET_PLAYER(eMinor).isAlive()) + continue; - if(bFriendly && IsDoFAccepted(ePlayer)) - { - // Is there something we want? - bool bWantsSomething = false; - // Is there a strong reason why we want something? (added to rand roll) - int iWeightBias = 0; + // Is this minor protected by this player? + if (!GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(ePlayer)) + continue; - // Luxury Request - if(!bWantsSomething) - bWantsSomething = IsLuxuryRequest(ePlayer, pDeal, iWeightBias); - // Gold Request - if(!bWantsSomething) - bWantsSomething = IsGoldRequest(ePlayer, pDeal, iWeightBias); + // Did we bully this minor last turn? + if (!GET_PLAYER(eMinor).GetMinorCivAI()->IsEverBulliedByMajor(GetID())) + continue; - if (bWantsSomething) - { - // Random element - int iRand = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x99eccf9f).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); - iRand += iWeightBias; + if (GET_PLAYER(eMinor).GetMinorCivAI()->GetTurnLastBulliedByMajor(GetID()) != GC.getGame().getGameTurn() - 1) + continue; - if (iRand >= 7) - { - bRandPassed = true; - return true; - } - else - { - bRandPassed = false; - return false; - } + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WE_BULLIED_YOUR_MINOR) >= 1) + { + eStatement = DIPLO_STATEMENT_WE_BULLIED_YOUR_MINOR; + iData1 = eMinor; + break; } } - - return false; } -/// Does this AI want a luxury resource gift from ePlayer? -bool CvDiplomacyAI::IsLuxuryRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias) +/// Possible Contact Statement - We're done being ePlayer's vassal +void CvDiplomacyAI::DoEndVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - iWeightBias = 0; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - ResourceTypes eLuxuryToAskFor = NO_RESOURCE; + if (SkipForTeammates()) + return; - int iResourceLoop = 0; + if (MOD_DIPLOAI_SHUT_UP_INDEPENDENCE_REQUESTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // See if the other player has a Resource to trade - for(iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) - { - const ResourceTypes eResource = static_cast(iResourceLoop); + // Must be vassal of ePlayer + if (!IsVassal(ePlayer)) + return; - CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); - if(pkResourceInfo) - { - // Only look at Luxuries - if(pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_LUXURY) - continue; + // Don't send this to an AI teammate of a human + if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) + return; - // Any extras? - if(GET_PLAYER(ePlayer).getNumResourceAvailable(eResource, /*bIncludeImport*/ false) < 2) - continue; + // Done being this player's vassal? + if (IsEndVassalageAcceptable(ePlayer)) + eStatement = DIPLO_STATEMENT_REVOKE_VASSALAGE; +} - // Can they actually give us this item - if(!pDeal->IsPossibleToTradeItem(ePlayer, GetID(), TRADE_ITEM_RESOURCES, eResource, 1)) - continue; +/// Possible Contact Statement - Attacked a City-State we're protective towards +void CvDiplomacyAI::DoAttackedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - eLuxuryToAskFor = eResource; - break; - } - } + if (SkipForTeammates()) + return; - // Didn't find something they could give us? - if(eLuxuryToAskFor == NO_RESOURCE) - return false; + // Don't send this to AI teammates of humans, otherwise the humans on their team might get a backstabbing penalty for something they weren't aware of + if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) + return; - // See if there's any Luxuries WE can trade (because if there are then we shouldn't be asking for hand outs) - for(iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) + // Hasn't happened yet, or we've already requested that they back off + int iTurn = GetOtherPlayerAttackedProtectedMinorTurn(ePlayer); + if (iTurn == -1 || iTurn <= GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE)) + return; + + // If we've made peace more recently than the City-State was attacked, don't send the statement + int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(ePlayer).getTeam()); + if (iTurn < iPeaceTreatyTurn) + return; + + // Have we contacted you before about this? + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE) != INT_MAX) { - const ResourceTypes eResource = static_cast(iResourceLoop); + // If approach is HOSTILE or WAR, we might declare war + bool bDeclareWar = GetCivApproach(ePlayer) <= CIV_APPROACH_HOSTILE && !GetPlayer()->IsNoNewWars(); + if (bDeclareWar && !IsPotentialWarTarget(ePlayer)) + bDeclareWar = false; - CvResourceInfo* pkResource = GC.getResourceInfo(eResource); - if(pkResource) - { - // Only look at Luxuries - if(pkResource->getResourceUsage() != RESOURCEUSAGE_LUXURY) - continue; + if (bDeclareWar && !GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + bDeclareWar = false; - // Any extras? - if(GetPlayer()->getNumResourceAvailable(eResource, /*bIncludeImport*/ false) < 2) - continue; + // Sanity check - are we backstabbing? + if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(ePlayer, true)) + bDeclareWar = false; - // Can they actually give us this item - if(!pDeal->IsPossibleToTradeItem(GetID(), ePlayer, TRADE_ITEM_RESOURCES, eResource, 1)) - continue; + // Sanity check - avoid going bankrupt + int iMinIncome = 2 + (GetPlayer()->GetCurrentEra() * 2); + if (bDeclareWar && IsWarWouldBankruptUs(ePlayer, iMinIncome)) + bDeclareWar = false; - // Found something we can trade to them, so abort - return false; + // Sanity check - who else would we go to war with? + if (bDeclareWar) + { + vector vDefensiveWarAllies = GetDefensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); + + for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) + { + // Would we be declaring war on a powerful neighbor? + if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) + { + if (GET_PLAYER(*it).isMajorCiv()) + { + if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) + { + bDeclareWar = false; + break; + } + // If we're already planning a war/demand against them, then we don't care. + else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) + { + if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) + { + bDeclareWar = false; + break; + } + } + } + else + { + if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE) + { + if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) + { + bDeclareWar = false; + break; + } + } + } + } + } } - } - // Add a little something extra since we're in dire straits - if(GetPlayer()->IsEmpireUnhappy()) - iWeightBias += 5; + if (bDeclareWar && DeclareWar(ePlayer)) + { + GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer, 1); - // Now seed the deal - pDeal->AddResourceTrade(ePlayer, eLuxuryToAskFor, 1, GC.getGame().GetDealDuration()); + if (GC.getGame().getActivePlayer() == ePlayer) + { + const char* strText = GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_WARMONGER); + gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } - return true; + // Flag the statement as sent, to stop this from firing every turn + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE, GC.getGame().getGameTurn()); + return; + } + } + + // Minor civ must still be alive! + PlayerTypes eMinorCiv = GetOtherPlayerProtectedMinorAttacked(ePlayer); + if (GET_PLAYER(eMinorCiv).isAlive()) + { + eStatement = DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE; + iData1 = eMinorCiv; + } } -/// Does this AI want a gold gift from ePlayer? -bool CvDiplomacyAI::IsGoldRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias) +/// Possible Contact Statement - Bullied a City-State we're protective towards +void CvDiplomacyAI::DoBulliedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { - iWeightBias = 0; - - int iOurGold = GetPlayer()->GetTreasury()->GetGold(); - int iOurGPT = GetPlayer()->calculateGoldRate(); - int iOurExpenses = GetPlayer()->GetTreasury()->CalculateTotalCosts(); - int iOurGrossIncome = iOurGPT + iOurExpenses; - - // If we have no expenses, don't ask (and also don't crash) - if(iOurExpenses == 0) - return false; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // If we already have some gold saved up then don't bother - if(iOurGold > 100) - return false; + if (SkipForTeammates()) + return; - // If we're making 35% more than we're spending then don't ask, we're doing alright - if(iOurGrossIncome * 100 / iOurExpenses > 135) - return false; + // Hasn't happened yet, or we've already requested that they back off + int iTurn = GetOtherPlayerBulliedProtectedMinorTurn(ePlayer); + if (iTurn == -1 || iTurn <= GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE)) + return; - int iTheirGold = GET_PLAYER(ePlayer).GetTreasury()->GetGold(); - int iTheirGPT = GET_PLAYER(ePlayer).calculateGoldRate(); - int iTheirExpenses = GET_PLAYER(ePlayer).GetTreasury()->CalculateTotalCosts(); - int iTheirGrossIncome = iTheirGPT + iTheirExpenses; + // If we've made peace more recently than the City-State was bullied, don't send the statement + int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(ePlayer).getTeam()); + if (iTurn < iPeaceTreatyTurn) + return; - // Don't divide by zero please - if(iTheirExpenses != 0) - { - // If they're making less than 35% more than they're spending then don't ask, they're not in great shape - if(iTheirGrossIncome * 100 / iTheirExpenses < 135) - return false; - } - else if(iTheirGPT <= iOurGPT) + // Have we asked you to make a promise before? + if (GetBullyCityStatePromiseState(ePlayer) > NO_PROMISE_STATE) { - return false; - } + // If approach is HOSTILE or WAR, we might declare war + bool bDeclareWar = GetCivApproach(ePlayer) <= CIV_APPROACH_HOSTILE && !GetPlayer()->IsNoNewWars(); + if (bDeclareWar && !IsPotentialWarTarget(ePlayer)) + bDeclareWar = false; - // Add a little something extra since we're in dire straits - if(iOurGPT < 0) - iWeightBias += 5; + if (bDeclareWar && !GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + bDeclareWar = false; - // If we've made it this far we'd like to ask, so figure out how much we want to ask for - int iGoldToAskFor = iTheirGPT * GC.getGame().GetDealDuration() / 5; - int iGPTToAskFor = 0; + // If it's just bullying, we have to feel protective of City-States and they must not be stronger + if (bDeclareWar && GetMinorCivApproachBias(CIV_APPROACH_FRIENDLY) <= 6) + bDeclareWar = false; - if(iGoldToAskFor > iTheirGold) - { - iGoldToAskFor = 0; - iGPTToAskFor = max(1, iTheirGPT / 6); - } + if (bDeclareWar && GetMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE) + bDeclareWar = false; - // Now seed the deal - if(iGoldToAskFor > 0) - pDeal->AddGoldTrade(ePlayer, iGoldToAskFor, true); - else if(iGPTToAskFor > 0) - pDeal->AddGoldPerTurnTrade(ePlayer, iGPTToAskFor, GC.getGame().GetDealDuration()); + // Sanity check - are we backstabbing? + if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(ePlayer, true)) + bDeclareWar = false; - return true; -} + // Sanity check - avoid going bankrupt + int iMinIncome = 2 + (GetPlayer()->GetCurrentEra() * 2); + if (bDeclareWar && IsWarWouldBankruptUs(ePlayer, iMinIncome)) + bDeclareWar = false; -/// Are we willing to swap embassies with ePlayer? -bool CvDiplomacyAI::IsEmbassyExchangeAcceptable(PlayerTypes ePlayer) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + // Sanity check - who else would we go to war with? + if (bDeclareWar) + { + vector vDefensiveWarAllies = GetDefensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); - CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); + for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) + { + // Would we be declaring war on a powerful neighbor? + if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) + { + if (GET_PLAYER(*it).isMajorCiv()) + { + if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) + { + bDeclareWar = false; + break; + } + // If we're already planning a war/demand against them, then we don't care. + else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) + { + if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) + { + bDeclareWar = false; + break; + } + } + } + else + { + if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE) + { + if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) + { + bDeclareWar = false; + break; + } + } + } + } + } + } - if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) - { - return false; - } + if (bDeclareWar && DeclareWar(ePlayer)) + { + GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer, 1); - if (IsUntrustworthy(ePlayer)) - { - return false; - } + if (GC.getGame().getActivePlayer() == ePlayer) + { + const char* strText = GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_WARMONGER); + gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } - switch (eApproach) + // Flag the statement as sent, to stop this from firing every turn + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE, GC.getGame().getGameTurn()); + } + // Otherwise, ask you to make a promise + else if (GetBullyCityStatePromiseState(ePlayer) == NO_PROMISE_STATE) { - case CIV_APPROACH_WAR: - case CIV_APPROACH_HOSTILE: - case CIV_APPROACH_GUARDED: - return false; - default: - return true; + PlayerTypes eMinorCiv = GetOtherPlayerProtectedMinorBullied(ePlayer); + // Minor civ must still be alive! + if (GET_PLAYER(eMinorCiv).isAlive()) + { + eStatement = DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE; + iData1 = eMinorCiv; + } } } -/// What are our opinions of this player's neigbors? -CivOpinionTypes CvDiplomacyAI::GetNeighborOpinion(PlayerTypes ePlayer) const +/* +/// Possible Contact Statement - Notify human it's time for a coop war they agreed to +void CvDiplomacyAI::DoCoopWarTimeStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { - if (ePlayer == NO_PLAYER) + PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); + PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + + if(eStatement == NO_DIPLO_STATEMENT_TYPE) { - return CIV_OPINION_NEUTRAL; - } + // Don't send this to AI players - coop war timer is automatically handled in DoCounters() + if(!GET_PLAYER(ePlayer).isHuman()) + return; - int iBad = 0; - int iNeutral = 0; - int iGood = 0; + CvTeam* pTeam = &GET_TEAM(GET_PLAYER(ePlayer).getTeam()); - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + PlayerTypes eTargetPlayer; + TeamTypes eTargetTeam; - if (IsPlayerValid(eLoopPlayer, true) && GET_PLAYER(eLoopPlayer).isMajorCiv() && GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) + for(int iTargetLoop = 0; iTargetLoop < MAX_MAJOR_CIVS; iTargetLoop++) { - if (GetCivOpinion(eLoopPlayer) <= CIV_OPINION_COMPETITOR) + eTargetPlayer = (PlayerTypes) iTargetLoop; + + bool bInvalid = false; + + if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GET_PLAYER(eTargetPlayer).getTeam(), ePlayer)) { - iBad++; + bInvalid = true; } - else if (GetCivOpinion(eLoopPlayer) == CIV_OPINION_NEUTRAL) + + if (!IsPlayerValid(eTargetPlayer)) { - iNeutral++; + bInvalid = true; } - else if (GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FAVORABLE) + + if (bInvalid) { - iGood++; - } - } - } - if (iGood > iNeutral && iGood > iBad) - { - return CIV_OPINION_FRIEND; - } - else if (iNeutral > iGood && iNeutral > iBad) - { - return CIV_OPINION_NEUTRAL; - } - else if (iBad > iGood && iBad > iNeutral) - { - return CIV_OPINION_ENEMY; - } - else - { - return CIV_OPINION_NEUTRAL; - } -} + if (GetCoopWarAcceptedState(ePlayer, eTargetPlayer) == COOP_WAR_STATE_SOON) + { + SetCoopWarAcceptedState(ePlayer, eTargetPlayer, NO_COOP_WAR_STATE); + SetCoopWarCounter(ePlayer, eTargetPlayer, -666); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCoopWarAcceptedState(GetID(), eTargetPlayer, NO_COOP_WAR_STATE); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCoopWarCounter(GetID(), eTargetPlayer, -666); + } + continue; + } -bool CvDiplomacyAI::MusteringForNeighborAttack(PlayerTypes ePlayer) const -{ - if (ePlayer == NO_PLAYER) - { - return false; - } + // Agreed to go to war soon... what's the counter at? + if(GetCoopWarAcceptedState(ePlayer, eTargetPlayer) == COOP_WAR_STATE_SOON) + { + if(GetCoopWarCounter(ePlayer, eTargetPlayer) == GD_INT_GET(COOP_WAR_SOON_COUNTER)) + { + eTargetTeam = GET_PLAYER(eTargetPlayer).getTeam(); - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + // If they're already at war, don't bother + if(!pTeam->isAtWar(eTargetTeam) && GET_PLAYER(eTargetPlayer).isAlive()) + { + eStatement = DIPLO_STATEMENT_COOP_WAR_TIME; + iData1 = eTargetPlayer; - if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isMajorCiv() && GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) - { - if (IsAtWar(eLoopPlayer) || GetCivApproach(eLoopPlayer) == CIV_APPROACH_WAR) - { - return true; + // Don't evaluate other players + break; + } + // Human is already at war - process what we would have if he'd agreed at this point + else + { + SetCoopWarAcceptedState(ePlayer, eTargetPlayer, COOP_WAR_STATE_ACCEPTED); + + // AI declaration + if(!IsAtWar(eTargetPlayer) && GET_PLAYER(eTargetPlayer).isAlive()) + { + if (DeclareWar(eTargetPlayer)) + { + GetPlayer()->GetMilitaryAI()->RequestBasicAttack(eTargetPlayer, 1); + } + } + } + } } } } - - return false; -} - -/// Do we want to have an embassy in the player's capital? - this is only used for when to trigger an AI request, not whether or not the AI will accept a deal period -bool CvDiplomacyAI::WantsEmbassyAtPlayer(PlayerTypes ePlayer) const -{ - // May want to make this logic more sophisticated eventually. This will do for now. - return GetCivApproach(ePlayer) > CIV_APPROACH_HOSTILE; } +*/ -/// Are we willing to accept Open Borders from eOtherPlayer? -bool CvDiplomacyAI::IsWantsOpenBordersWithPlayer(PlayerTypes ePlayer) +/// Possible Contact Statement - guy has his military positioned aggressively near us +void CvDiplomacyAI::DoAggressiveMilitaryStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - if (IsVassal(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassal(GetID())) - return true; - - if (IsUntrustworthy(ePlayer)) - return false; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) - { - return false; - } - if (IsArmyInPlaceForAttack(ePlayer)) - { - return false; - } + if (SkipForTeammates()) + return; - if (!MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM) - { - // If going for culture win we always want open borders with civs we need influence on - if (IsGoingForCultureVictory()) - { - InfluenceLevelTypes eInfluenceLevel = m_pPlayer->GetCulture()->GetInfluenceLevel(ePlayer); - InfluenceLevelTrend eInfluenceTrend = m_pPlayer->GetCulture()->GetInfluenceTrend(ePlayer); + // Don't bother if they've already made or broken a military promise to us + if (GetMilitaryPromiseState(ePlayer) > NO_PROMISE_STATE) + return; - if (eInfluenceLevel < INFLUENCE_LEVEL_INFLUENTIAL || eInfluenceTrend <= INFLUENCE_TREND_STATIC) - return true; - } - } - else - { - // If they need influence over us and they aren't our liberator or DP, we don't want their OB, thanks. - if (IsCompetingForVictory() && !IsHasDefensivePact(ePlayer) && GetNumCitiesLiberatedBy(ePlayer) <= 0) - { - DisputeLevelTypes eVictoryDispute = GetVictoryDisputeLevel(ePlayer); - BlockLevelTypes eVictoryBlock = GetVictoryBlockLevel(ePlayer); + // We must be able to declare war on each other - in both directions, since the promise is mutual + TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); + if (!GET_TEAM(eTeam).canDeclareWar(GetTeam(), ePlayer) || !GET_TEAM(GetTeam()).canDeclareWar(eTeam, GetID())) + return; - if (eVictoryDispute > DISPUTE_LEVEL_NONE || eVictoryBlock > BLOCK_LEVEL_NONE) - { - InfluenceLevelTypes eInfluenceLevel = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()); - InfluenceLevelTrend eInfluenceTrend = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(GetID()); + // They're HIGH or INCREDIBLE this turn + if (GetMilitaryAggressivePosture(ePlayer) < AGGRESSIVE_POSTURE_HIGH) + return; - if (eInfluenceLevel == INFLUENCE_LEVEL_FAMILIAR && eInfluenceTrend == INFLUENCE_TREND_RISING) // Familiar and rising? Slow them down! - { - return false; - } - else if (eInfluenceLevel == INFLUENCE_LEVEL_POPULAR) - { - if (eInfluenceTrend != INFLUENCE_TREND_FALLING) // Popular and not falling? Slow them down! - return false; + // AI teammates of humans can't send this, otherwise the humans on our team might get a backstabbing penalty for something they weren't aware of + if (GetPlayer()->IsAITeammateOfHuman()) + return; - if (eVictoryDispute == DISPUTE_LEVEL_FIERCE || eVictoryBlock == BLOCK_LEVEL_FIERCE || GetBiggestCompetitor() == ePlayer) // Popular and fiercely competitive? Slow them down! - return false; - } - else if (eInfluenceLevel == INFLUENCE_LEVEL_INFLUENTIAL && eInfluenceTrend != INFLUENCE_TREND_RISING) // Already Influential, but not rising? Let's try to reverse that! - { - return false; - } - } - } - } + // Don't send this to AI teammates of humans, otherwise the humans on their team might get a backstabbing penalty for something they weren't aware of + if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) + return; - if (GetCivApproach(ePlayer) == CIV_APPROACH_WAR) - { - return false; - } - if (IsHasDefensivePact(ePlayer) || IsDoFAccepted(ePlayer)) - { - return true; - } - if (IsGoingForWorldConquest() || MusteringForNeighborAttack(ePlayer)) - { - return true; - } - if (GetCivApproach(ePlayer) >= CIV_APPROACH_AFRAID) - { - return true; - } + // This promise is mutual, so don't send the statement if we (or our teammates) are planning war + if (AvoidExchangesWithPlayer(ePlayer, /*bWarOnly*/ true)) + return; - if (m_pPlayer->IsCramped() || (GET_PLAYER(ePlayer).getNumCities() * 3) > (m_pPlayer->getNumCities() * 2)) + // Check other player status + for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) { - return true; - } + PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; + if (eThirdParty == GetID() || eThirdParty == ePlayer) + continue; - EconomicAIStrategyTypes eNeedRecon = (EconomicAIStrategyTypes) GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_RECON"); - EconomicAIStrategyTypes eNeedNavalRecon = (EconomicAIStrategyTypes) GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_RECON_SEA"); - if (m_pPlayer->GetEconomicAI()->IsUsingStrategy(eNeedRecon)) - { - return true; - } - if (m_pPlayer->GetEconomicAI()->IsUsingStrategy(eNeedNavalRecon)) - { - int iCityLoop = 0; - for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) - { - if (pLoopCity->isCoastal()) - { - return true; - } - } - } + // Are they at war with anyone we're neighbors with? + if (GetPlayer()->GetProximityToPlayer(eThirdParty) == PLAYER_PROXIMITY_NEIGHBORS && GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isAtWar(GET_PLAYER(eThirdParty).getTeam())) + return; - if (GetPlayer()->GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) - { - AICityStrategyTypes ePocketCity = (AICityStrategyTypes) GC.getInfoTypeForString("AICITYSTRATEGY_POCKET_CITY"); - int iCityLoop = 0; - for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) - { - if (pLoopCity->GetCityStrategyAI()->IsUsingCityStrategy(ePocketCity)) - { - return true; - } - } + // Are they an AI preparing for a coop war against us with a human? Don't send this statement, because being dragged into a war early is unfun for humans + if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eThirdParty).isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eThirdParty).GetDiplomacyAI()->GetCoopWarState(ePlayer, GetID()) == COOP_WAR_STATE_PREPARING) + return; } - return false; + eStatement = DIPLO_STATEMENT_AGGRESSIVE_MILITARY_WARNING; } -/// Are we willing to give Open Borders to eOtherPlayer? -bool CvDiplomacyAI::IsWillingToGiveOpenBordersToPlayer(PlayerTypes ePlayer) +/// Possible Contact Statement - Renew Recently Expired Deal +CvDeal* CvDiplomacyAI::DoRenewExpiredDeal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - if (IsUntrustworthy(ePlayer)) - { - return false; - } + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return NULL; - // Do not let them in if they think our beautiful fields are their living room! - if (GetNumTimesCultureBombed(ePlayer) > 0) - return false; + if (IsAvoidDeals()) + return NULL; - // Are they here to steal our PRICELESS ARCHAEOLOGICAL ARTIFACTS??? - if (GetNumArtifactsEverDugUp(ePlayer) > 0) - { - int iHiddenSites = GetPlayer()->GetEconomicAI()->GetVisibleHiddenAntiquitySitesOwnTerritory(); - int iNormalSites = GetPlayer()->GetEconomicAI()->GetVisibleAntiquitySitesOwnTerritory() - iHiddenSites; - PolicyBranchTypes eHiddenSiteBranch = MOD_BALANCE_VP ? (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_AESTHETICS", true) : (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_EXPLORATION", true); - - if (iNormalSites > 0) - { - return false; - } - // Have they unlocked the branch whose finisher lets them see hidden sites? - if (iHiddenSites > 0 && GET_PLAYER(ePlayer).GetPlayerPolicies()->IsPolicyBranchUnlocked(eHiddenSiteBranch)) - { - return false; - } - } + if (MOD_DIPLOAI_SHUT_UP_TRADE_RENEWALS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return NULL; - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToWorldConquest()) - { - return false; - } - if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) - { - return false; - } - if (IsArmyInPlaceForAttack(ePlayer)) - { - return false; - } - - if (MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM) - { - // If going for culture win we always want to open our borders to civs we need influence on - if (IsGoingForCultureVictory()) - { - InfluenceLevelTypes eInfluenceLevel = m_pPlayer->GetCulture()->GetInfluenceLevel(ePlayer); - InfluenceLevelTrend eInfluenceTrend = m_pPlayer->GetCulture()->GetInfluenceTrend(ePlayer); + // Find all potentially renewable deals: deals which will expire after this turn and don't have any non-renewable items + CvDeal* pBestDeal = NULL; + int iBestDealValue = -INT_MAX; - if (eInfluenceLevel < INFLUENCE_LEVEL_INFLUENTIAL || eInfluenceTrend <= INFLUENCE_TREND_STATIC) - return true; - } - } - else + CvGameDeals& kGameDeals = GC.getGame().GetGameDeals(); + DealList::iterator it; + for (it = kGameDeals.m_CurrentDeals.begin(); it != kGameDeals.m_CurrentDeals.end(); ++it) { - // If they need influence over us and they aren't our liberator or DP, we don't want to give them OB, thanks. - if (IsCompetingForVictory() && !IsHasDefensivePact(ePlayer) && GetNumCitiesLiberatedBy(ePlayer) <= 0) + if (it->m_iFinalTurn == GC.getGame().getGameTurn()) { - DisputeLevelTypes eVictoryDispute = GetVictoryDisputeLevel(ePlayer); - BlockLevelTypes eVictoryBlock = GetVictoryBlockLevel(ePlayer); - - if (eVictoryDispute > DISPUTE_LEVEL_NONE || eVictoryBlock > BLOCK_LEVEL_NONE) + if ((it->m_eToPlayer == ePlayer && it->m_eFromPlayer == GetID()) || + (it->m_eFromPlayer == ePlayer && it->m_eToPlayer == GetID())) { - InfluenceLevelTypes eInfluenceLevel = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()); - InfluenceLevelTrend eInfluenceTrend = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(GetID()); + // if there are non-renewable items in the deal, move on + if (!it->IsPotentiallyRenewable()) + continue; - if (eInfluenceLevel == INFLUENCE_LEVEL_FAMILIAR && eInfluenceTrend == INFLUENCE_TREND_RISING) // Familiar and rising? Slow them down! - { - return false; - } - else if (eInfluenceLevel == INFLUENCE_LEVEL_POPULAR) - { - if (eInfluenceTrend != INFLUENCE_TREND_FALLING) // Popular and not falling? Slow them down! - return false; + // if this deal is a gift or peace deal, move on. + if (it->m_bIsGift || it->IsPeaceTreatyTrade(it->GetFromPlayer()) || it->IsPeaceTreatyTrade(it->GetToPlayer())) + continue; - if (eVictoryDispute == DISPUTE_LEVEL_FIERCE || eVictoryBlock == BLOCK_LEVEL_FIERCE || GetBiggestCompetitor() == ePlayer) // Popular and fiercely competitive? Slow them down! - return false; - } - else if (eInfluenceLevel == INFLUENCE_LEVEL_INFLUENTIAL && eInfluenceTrend != INFLUENCE_TREND_RISING) // Already Influential, but not rising? Let's try to reverse that! + CvDeal* pCurrentDeal = &(*it); + + // Make a copy of the deal because we don't want the items of the original deal to change when trying to equalize + CvDeal dealCopy = *pCurrentDeal; + // set pCurrentDeal as the deal that's being considered for renewal: deal valuations will be calculated as if pCurrentDeal had expired + GC.getGame().GetGameDeals().SetRenewDealID(GetID(), ePlayer, pCurrentDeal->m_iID); + m_pPlayer->GetDealAI()->ClearCachedDealItemValues(); + GET_PLAYER(ePlayer).GetDealAI()->ClearCachedDealItemValues(); + + int iValue = m_pPlayer->GetDealAI()->GetDealValue(&dealCopy); + if (iValue != INT_MAX) { - return false; + bool bAbleToEqualize = false; + if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + { + bool bUselessReferenceVariable = false; + bool bCantMatchOffer = false; + bAbleToEqualize = m_pPlayer->GetDealAI()->DoEqualizeDeal(&dealCopy, ePlayer, bUselessReferenceVariable, bCantMatchOffer); + } + else + bAbleToEqualize = true; + + if (bAbleToEqualize) + { + eStatement = DIPLO_STATEMENT_RENEW_DEAL; + if (dealCopy.m_iToPlayerValue > iBestDealValue) + { + iBestDealValue = dealCopy.m_iToPlayerValue; + pBestDeal = pCurrentDeal; + } + } } } } } - if (GetCivApproach(ePlayer) == CIV_APPROACH_WAR) - { - return false; - } - if (IsHasDefensivePact(ePlayer) || IsDoFAccepted(ePlayer)) - { - return true; - } - if (GetCivApproach(ePlayer) >= CIV_APPROACH_AFRAID) - { - return true; - } - if (GetMilitaryStrengthComparedToUs(ePlayer) <= STRENGTH_AVERAGE) + GC.getGame().GetGameDeals().SetRenewDealID(GetID(), ePlayer, pBestDeal ? pBestDeal->m_iID : -1); + return pBestDeal; +} + +/// Possible Contact Statement - Killed a City-State we're protective towards +void CvDiplomacyAI::DoKilledCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + // They must have broken a promise not to conquer our protected City-States + if (!BrokeAttackCityStatePromise(ePlayer)) + return; + + // Only send this once per game + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_KILLED_PROTECTED_CITY_STATE) == INT_MAX) + return; + + // They must be able to declare war on us + if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) + return; + + PlayerTypes eMinorCiv = GetOtherPlayerProtectedMinorKilled(ePlayer); + if (eMinorCiv != NO_PLAYER) { - return true; + PRECONDITION(eMinorCiv >= MAX_MAJOR_CIVS && eMinorCiv < MAX_CIV_PLAYERS); + eStatement = DIPLO_STATEMENT_KILLED_PROTECTED_CITY_STATE; + iData1 = eMinorCiv; } - - return false; } -/// Are we willing to swap Open Borders with ePlayer? -bool CvDiplomacyAI::IsOpenBordersExchangeAcceptable(PlayerTypes ePlayer) +/// Possible Contact Statement - Tell the player he broke his expansion promise +void CvDiplomacyAI::DoExpansionBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - return IsWillingToGiveOpenBordersToPlayer(ePlayer) && IsWantsOpenBordersWithPlayer(ePlayer); -} + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + if (SkipForTeammates()) + return; -///////////////////////////////////////////////////////// -// Planning Exchanges -///////////////////////////////////////////////////////// + if (!BrokeExpansionPromise(ePlayer)) + return; -/// Is this war not worth caring about? -bool CvDiplomacyAI::IsPhonyWar(PlayerTypes ePlayer, bool bIgnoreCurrentApproach /* = false */) const + int iTurnsBetweenStatements = (/*50*/ GD_INT_GET(EXPANSION_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EXPANSION_BROKEN_PROMISE) >= iTurnsBetweenStatements) + eStatement = DIPLO_STATEMENT_EXPANSION_BROKEN_PROMISE; +} + +/// Possible Contact Statement - Tell the player he broke his Plot Buying promise +void CvDiplomacyAI::DoPlotBuyingBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - if (!IsAtWar(ePlayer)) - return false; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Approach is WAR - if (!bIgnoreCurrentApproach && GetCivApproach(ePlayer) == CIV_APPROACH_WAR) - return false; + if (SkipForTeammates()) + return; - // They captured our cities before, don't dismiss them. - if (GetNumCitiesCapturedBy(ePlayer) > 0) - return false; + if (!BrokeBorderPromise(ePlayer)) + return; - // War state too high or too low - WarStateTypes eWarState = GetWarState(ePlayer); - if (eWarState >= WAR_STATE_OFFENSIVE || eWarState <= WAR_STATE_TROUBLED) - return false; + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_PLOT_BUYING_BROKEN_PROMISE) == INT_MAX) + eStatement = DIPLO_STATEMENT_PLOT_BUYING_BROKEN_PROMISE; +} - // War score is too high - int iWarScore = GetPlayer()->GetDiplomacyAI()->GetWarScore(ePlayer); - if (iWarScore <= GetWarscoreThresholdNegative() || iWarScore >= GetWarscoreThresholdPositive()) - return false; +/// Possible Contact Statement - They dug up one of our artifacts, and we want them to stop that +void CvDiplomacyAI::DoDugUpMyYardStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // We want to conquer them! - if (IsWantsToConquer(ePlayer)) - return false; + if (SkipForTeammates()) + return; - // We have offensive operations ongoing. - if (m_pPlayer->HasAnyOffensiveOperationsAgainstPlayer(ePlayer)) - return false; + if (GetNumArtifactsEverDugUp(ePlayer) <= 0) + return; - // Our cities are threatened by them or vice versa - if (GetPlayer()->GetDiplomacyAI()->GetNumberOfThreatenedCities(ePlayer) > 0 || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumberOfThreatenedCities(GetID()) > 0) - return false; + // Ask them to make a promise if they haven't before + if (GetNoDiggingPromiseState(ePlayer) != NO_PROMISE_STATE || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsPlayerAskedNotToDig(GetID())) + return; - // They're too close or have a lot of soldiers nearby - PlayerProximityTypes eProximity = GetPlayer()->GetProximityToPlayer(ePlayer); - AggressivePostureTypes ePosture = GetMilitaryAggressivePosture(ePlayer); - StrengthTypes eMilitaryStrength = GetMilitaryStrengthComparedToUs(ePlayer); + eStatement = DIPLO_STATEMENT_STOP_DIGGING; +} - if (eProximity == PLAYER_PROXIMITY_NEIGHBORS) - { - return false; - } - else if (eProximity == PLAYER_PROXIMITY_CLOSE) - { - if (ePosture >= AGGRESSIVE_POSTURE_MEDIUM || eMilitaryStrength >= STRENGTH_POWERFUL) - return false; - } - else - { - if (ePosture >= AGGRESSIVE_POSTURE_HIGH || eMilitaryStrength == STRENGTH_IMMENSE) - return false; - } +/// Possible Contact Statement - They converted one of our cities, and we want them to stop that +void CvDiplomacyAI::DoConvertedMyCityStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - return true; + if (SkipForTeammates()) + return; + + if (!HasEverConvertedCity(ePlayer)) + return; + + if (GetNoConvertPromiseState(ePlayer) != NO_PROMISE_STATE || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsPlayerAskedNotToConvert(GetID())) + return; + + eStatement = DIPLO_STATEMENT_STOP_CONVERSIONS; } -/// Does the AI even want to conquer another player if they are at war? -/// Since there is no "defensive war" flag, this seems to be the best way to differentiate -bool CvDiplomacyAI::IsWantsToConquer(PlayerTypes ePlayer) const +/// Possible Contact Statement - Comment on aggressive expansion by this player +void CvDiplomacyAI::DoExpansionWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - if (!IsAtWar(ePlayer)) - return false; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if (IsAlwaysAtWar(ePlayer)) - return true; - - if (GC.getGame().GetNumMajorCivsAlive() == 2) - return true; - - if (GetPlayer()->IsAITeammateOfHuman()) - return true; + if (SkipForTeammates()) + return; - // Doing well already - let's keep it up. - if (GetWarState(ePlayer) >= WAR_STATE_OFFENSIVE) - return true; + if (!IsAngryAboutExpansion(ePlayer)) + return; - // Captured one of our key cities? - if (IsCapitalCapturedBy(ePlayer, true, false) || IsHolyCityCapturedBy(ePlayer, true, false)) - return true; - - // If they're about to win, we have nothing to lose! - if (IsEndgameAggressiveTo(ePlayer)) - return true; + int iTurnsBetweenStatements = (/*50*/ GD_INT_GET(EXPANSION_PROMISE_TURNS_EFFECTIVE) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EXPANSION_WARNING) >= iTurnsBetweenStatements) + eStatement = DIPLO_STATEMENT_EXPANSION_WARNING; +} - // If we're in bad shape for war, retreat! - if (GetPlayer()->IsInTerribleShapeForWar()) - return false; - - TargetValueTypes eTargetValue = GetTargetValue(ePlayer); - bool bWeHaveGoodAttackTarget = GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer); - bool bAggressive = GetPlayer()->GetPlayerTraits()->IsWarmonger() || (IsCompetingForVictory() && IsGoingForWorldConquest()); +/// Possible Contact Statement - Comment on aggressive Plot Buying by this player +void CvDiplomacyAI::DoPlotBuyingWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // They're an easy target, so play offensively! - if (IsEasyTarget(ePlayer) && bWeHaveGoodAttackTarget) - return true; + if (SkipForTeammates()) + return; - // We started or wanted this war - if they're not a bad target we should attack at full force - if (IsAggressor(ePlayer)) - { - // Bad target - if (eTargetValue == TARGET_VALUE_IMPOSSIBLE || (eTargetValue == TARGET_VALUE_BAD && !bWeHaveGoodAttackTarget)) - { - // Aggressive and fiercely competitive players don't give up so easily - if (IsMajorCompetitor(ePlayer) && bAggressive) - { - return true; - } + if (EverMadeBorderPromise(ePlayer) || GetBorderPromiseState(ePlayer) != NO_PROMISE_STATE) + return; - // We're in a coop war with another player against this guy - if (GetGlobalCoopWarAgainstState(ePlayer) >= COOP_WAR_STATE_PREPARING) - { - return true; - } + // We must be at least strongly competitive about territory to send this + if (GetLandDisputeLevel(ePlayer) < DISPUTE_LEVEL_STRONG) + return; - return false; - } - - return true; - } - // Didn't start or want this war - don't care as much - else - { - // Aggressive and nearby players - if (bAggressive && GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE) - { - if (eTargetValue == TARGET_VALUE_IMPOSSIBLE) - { - return false; - } - else if (eTargetValue == TARGET_VALUE_BAD && !bWeHaveGoodAttackTarget) - { - return false; - } - } - // Other players - else if (eTargetValue <= TARGET_VALUE_BAD || (eTargetValue <= TARGET_VALUE_AVERAGE && !bWeHaveGoodAttackTarget)) - { - return false; - } - } + // We must have spotted them buying up plots + if (GetPlotBuyingAggressivePosture(ePlayer) < AGGRESSIVE_POSTURE_LOW) + return; - return true; //by default we conquer everything + int iTurnsBetweenStatements = (/*50*/ GD_INT_GET(BORDER_PROMISE_TURNS_EFFECTIVE) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_PLOT_BUYING_WARNING) >= iTurnsBetweenStatements) + eStatement = DIPLO_STATEMENT_PLOT_BUYING_WARNING; } -/// Is this major civ a potential military target or threat? -bool CvDiplomacyAI::IsPotentialMilitaryTargetOrThreat(PlayerTypes ePlayer, bool bFromApproachSelection) const +/// Possible Contact Statement - This player killed our spy +void CvDiplomacyAI::DoKilledMySpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - if (!GET_PLAYER(ePlayer).isMajorCiv() || !GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).getNumCities() <= 0 || IsTeammate(ePlayer)) - return false; - - if (!IsAtWar(ePlayer) && GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE)) - return false; - - bool bVassal = IsVassal(ePlayer); - if (!bVassal) - { - if (IsNukedBy(ePlayer) || IsCapitalCapturedBy(ePlayer) || IsHolyCityCapturedBy(ePlayer)) - return true; - } - - bool bFriends = IsMaster(ePlayer) || IsFriendOrAlly(ePlayer); - - // Only care if this player is close to us - if (GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE || GET_PLAYER(ePlayer).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE || GetMilitaryAggressivePosture(ePlayer) >= AGGRESSIVE_POSTURE_MEDIUM) - { - bool bStronger = GetRawMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE || GetEconomicStrengthComparedToUs(ePlayer) > STRENGTH_STRONG || GetWarmongerThreat(ePlayer) >= THREAT_SEVERE; - if (!bVassal) - { - if (bStronger && !bFriends) - return true; - - if (GetNumCitiesCapturedBy(ePlayer) > 0) - return true; - - // Going to war? - if (AvoidExchangesWithPlayer(ePlayer, true, bFromApproachSelection)) - return true; - } - - // If they're stronger than us, our master, or if their military strength is at least POOR (one level below), let's check diplomacy - if (bStronger || bVassal || GetRawMilitaryStrengthComparedToUs(ePlayer) >= STRENGTH_POOR) - { - vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); - vector vPlayersToCheck = GetOffensiveWarAllies(ePlayer, /*bIncludeMinors*/ false, /*bReverseMode*/ true); - vPlayersToCheck.push_back(ePlayer); - for (std::vector::iterator it = vPlayersToCheck.begin(); it != vPlayersToCheck.end(); ++it) - { - for (size_t i=0; iGetDiplomacyAI(); - if (!pMyTeamPlayer->isMajorCiv() || pMyTeamPlayer->getNumCities() <= 0) - continue; - - // Backstabber? - // Annoying to check it like this, but because this function is called a lot, we want to optimize where possible. - if (GET_PLAYER(*it).getTeam() == GET_PLAYER(ePlayer).getTeam() && pDiplo->IsUntrustworthyFriend(*it)) - return true; - - if (!pMyTeamPlayer->isAlive()) - continue; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if (!bVassal) - { - bool bIgnoreSelfApproach = pMyTeamPlayer->isHuman(ISHUMAN_AI_DIPLOMACY) || (bFromApproachSelection && pMyTeamPlayer->GetID() == GetID()); + if (SkipForTeammates()) + return; - // Is this a Defensive Pact? - if (GET_PLAYER(*it).GetDiplomacyAI()->IsHasDefensivePact(ePlayer)) - { - // If we're going to war with their DP, they might be a threat or target. - // Otherwise, we don't care. - if (AvoidExchangesWithPlayer(*it, true, bIgnoreSelfApproach)) - return true; + int iTurn = m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyDied[ePlayer]; + if (iTurn <= 0 || iTurn < GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_KILLED_MY_SPY)) + return; - continue; - } + // If we've made peace more recently than the spy was killed, don't send the statement + int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(ePlayer).getTeam()); + if (iTurn < iPeaceTreatyTurn) + return; - // Bad approach towards them? - if (!bIgnoreSelfApproach) - { - CivApproachTypes eApproach = pDiplo->GetCivApproach(*it); - if (eApproach == CIV_APPROACH_AFRAID && !bFriends) - return true; + // Don't send the message if they ignored our request before, or we ignored theirs. + if (GetSpyPromiseState(ePlayer) >= PROMISE_STATE_IGNORED || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetSpyPromiseState(GetID()) >= PROMISE_STATE_IGNORED) + return; - if (eApproach <= CIV_APPROACH_GUARDED) - return true; - } - } + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_KILLED_MY_SPY) >= 40) + eStatement = DIPLO_STATEMENT_KILLED_MY_SPY; +} - // Negative approach towards us? - CivApproachTypes eApproachTowardsUs = pDiplo->GetVisibleApproachTowardsUs(*it); - if (eApproachTowardsUs <= CIV_APPROACH_GUARDED) - return true; +/// Possible Contact Statement - We killed this player's spy +void CvDiplomacyAI::DoKilledYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Denouncement in either direction? - if (pDiplo->IsDenouncedPlayer(*it) || pDiplo->IsDenouncedByPlayer(*it)) - return true; + if (SkipForTeammates()) + return; - // Previous war? - if (!bFriends && !bVassal && pDiplo->GetNumWarsDeclaredOnUs(*it) > 0) - return true; + if (m_pPlayer->GetEspionageAI()->m_aiNumSpiesKilled[ePlayer] <= 0) + return; - // Any other reason for them to be mad at us? - if (!bVassal && GET_PLAYER(*it).GetDiplomacyAI()->GetNumCitiesCapturedBy(vMyTeam[i]) > 0) - return true; - if (GET_PLAYER(*it).GetDiplomacyAI()->IsPlayerCapturedCapital(vMyTeam[i]) || GET_PLAYER(*it).GetDiplomacyAI()->IsPlayerCapturedHolyCity(vMyTeam[i])) - return true; - // Run these checks last because they're more expensive. - if (GET_PLAYER(*it).GetDiplomacyAI()->IsUntrustworthy(vMyTeam[i])) - return true; - if (bVassal && pDiplo->GetVassalTreatmentLevel(*it) <= VASSAL_TREATMENT_MISTREATED) - return true; - } + // Hasn't happened yet, or we've already requested that they back off, or we were caught spying on them more recently + int iTurn = m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyKilled[ePlayer]; + if (iTurn <= 0 || iTurn < GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_KILLED_YOUR_SPY) || + iTurn <= m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyDied[ePlayer]) + return; - if (GET_PLAYER(*it).GetNumOurCitiesOwnedBy(GetID()) > 0) - return true; - } - } - } + // If we've made peace more recently than the spy was killed, don't send the statement + int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(ePlayer).getTeam()); + if (iTurn < iPeaceTreatyTurn) + return; - return false; -} + // Ask them to make a promise if they haven't before + if (GetSpyPromiseState(ePlayer) != NO_PROMISE_STATE) + return; -/// Returns if this player has been nuked by ePlayer -bool CvDiplomacyAI::IsNukedBy(PlayerTypes ePlayer) const -{ - return (GetNumTimesNuked(ePlayer) > 0); + eStatement = DIPLO_STATEMENT_KILLED_YOUR_SPY; } -/// Is this player a friend or ally in any way? Quick heuristic check that only checks for good things. -bool CvDiplomacyAI::IsFriendOrAlly(PlayerTypes ePlayer) const +/// Possible Contact Statement - We caught this player spying on us +void CvDiplomacyAI::DoCaughtYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - if (IsTeammate(ePlayer)) - return true; - - if (!GET_PLAYER(ePlayer).isAlive() || ePlayer == BARBARIAN_PLAYER) - return false; - - if (IsDoFAccepted(ePlayer)) - return true; - - if (IsHasDefensivePact(ePlayer)) - return true; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if (GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) - return true; + if (SkipForTeammates()) + return; - if (GetDoFType(ePlayer) >= DOF_TYPE_ALLIES) - return true; + // Hasn't happened yet, or we've already requested that they back off, or we were caught spying on them more recently + int iTurn = m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyCaught[ePlayer]; + if (iTurn <= 0 || iTurn < GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_CAUGHT_YOUR_SPY) || + iTurn < GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_KILLED_YOUR_SPY) || + iTurn <= m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyDied[ePlayer]) + return; - if (WasResurrectedBy(ePlayer)) - return true; + // If we've made peace more recently than the spy was caught, don't send the statement + int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(ePlayer).getTeam()); + if (iTurn < iPeaceTreatyTurn) + return; - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->WasResurrectedBy(GetID())) - return true; + // Ask them to make a promise if they haven't before + if (GetSpyPromiseState(ePlayer) != NO_PROMISE_STATE) + return; - return false; + eStatement = DIPLO_STATEMENT_CAUGHT_YOUR_SPY; } -/// Is this player causing us problems in the early game? -bool CvDiplomacyAI::IsEarlyGameCompetitor(PlayerTypes ePlayer) +/// Possible Contact Statement - "Now Influential", or either the AI or human ideology is exerting significant pressure on the other +void CvDiplomacyAI::DoIdeologicalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - // Not the early game - if (GetPlayer()->GetCurrentEra() > 2) - return false; - - if (GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY) - return true; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if (GetLandDisputeLevel(ePlayer) > DISPUTE_LEVEL_WEAK) - return true; + if (SkipForTeammates()) + return; - if (GetNumWondersBeatenTo(ePlayer) > 0) - return true; + // Are we influential over them for the first time? + if (m_pPlayer->GetCulture()->GetInfluenceLevel(ePlayer) >= INFLUENCE_LEVEL_INFLUENTIAL) + { + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_OUR_CULTURE_INFLUENTIAL) == INT_MAX) + { + eStatement = DIPLO_STATEMENT_OUR_CULTURE_INFLUENTIAL; + return; + } + } - if (GetNumDemandsMade(ePlayer) > 0) - return true; + // Are they influential over us for the first time? + CvPlayer &kTheirPlayer = GET_PLAYER(ePlayer); + if (kTheirPlayer.GetCulture()->GetInfluenceLevel(GetID()) >= INFLUENCE_LEVEL_INFLUENTIAL) + { + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_YOUR_CULTURE_INFLUENTIAL) == INT_MAX) + { + eStatement = DIPLO_STATEMENT_YOUR_CULTURE_INFLUENTIAL; + return; + } + } - if (GetNegativeReligiousConversionPoints(ePlayer) > 0) - return true; + // Everything below are "insult" type messages + if (MOD_DIPLOAI_SHUT_UP_INSULTS && kTheirPlayer.isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - if (GetNumTimesTheyPlottedAgainstUs(ePlayer) > 0) - return true; + // Don't care if either player is a vassal + if (GetPlayer()->IsVassalOfSomeone() || GET_PLAYER(ePlayer).IsVassalOfSomeone()) + return; - if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) - return true; + // Don't send if we're acting afraid + if (GetSurfaceApproach(ePlayer) == CIV_APPROACH_AFRAID) + return; - // Major early lead in techs/policies? Let's slow that down. - if (GetPolicyBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG || GetTechBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) - return true; + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; - // Special check for England / Statecraft - if (GetNumTimesRobbedBy(ePlayer) > 0 || GetNumTimesPerformedCoupAgainstUs(ePlayer) > 0) - return true; + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(kTheirPlayer.getTeam(), GetID())) + return; - if (IgnoredBullyCityStatePromise(ePlayer) || BrokeBullyCityStatePromise(ePlayer) || IsAngryAboutProtectedMinorAttacked(ePlayer) || IsAngryAboutProtectedMinorKilled(ePlayer)) - return true; + PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); + PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); + if (eMyBranch == NO_POLICY_BRANCH_TYPE || eTheirBranch == NO_POLICY_BRANCH_TYPE) + return; - if (IsUntrustworthy(ePlayer)) - return true; + DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; + PolicyBranchTypes eAutocracy = (PolicyBranchTypes)GD_INT_GET(POLICY_BRANCH_AUTOCRACY); + PolicyBranchTypes eFreedom = (PolicyBranchTypes)GD_INT_GET(POLICY_BRANCH_FREEDOM); + PolicyBranchTypes eOrder = (PolicyBranchTypes)GD_INT_GET(POLICY_BRANCH_ORDER); - // Are they a juicy target? - if (IsEasyTarget(ePlayer) || GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer)) + // Did this player recently switch ideology due to our pressure? + int iIdeologySwitchTurn = kTheirPlayer.GetCulture()->GetTurnIdeologySwitch(); + if (iIdeologySwitchTurn > 0 && iIdeologySwitchTurn + 10 > GC.getGame().getGameTurn()) { - PlayerProximityTypes eProximity = GetPlayer()->GetProximityToPlayer(ePlayer); - bool bRecklessExpander = IsRecklessExpander(ePlayer); - bool bWonderSpammer = IsWonderSpammer(ePlayer); - - if (eProximity == PLAYER_PROXIMITY_NEIGHBORS) + if (eTheirBranch == eAutocracy && eMyBranch == eAutocracy) { - if (bRecklessExpander || bWonderSpammer) - return true; + eTempStatement = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY; } - else if (eProximity == PLAYER_PROXIMITY_CLOSE) + else if (eTheirBranch == eFreedom && eMyBranch == eFreedom) { - if (IsConqueror() || IsSecondaryConqueror()) + eTempStatement = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM; + } + else if (eTheirBranch == eOrder && eMyBranch == eOrder) + { + eTempStatement = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER; + } + if (eTempStatement != NO_DIPLO_STATEMENT_TYPE) + { + //don't spam the message + bool bNeverSent = true; + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - if (bRecklessExpander || bWonderSpammer) - return true; + PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; + if (eLoopPlayer == ePlayer) + continue; + + if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) != INT_MAX) + { + bNeverSent = false; + break; + } } - else if (IsCultural() || IsSecondaryCultural()) + // This triggers the insult throttle, but isn't blocked by it since it's sent once per game + if (bNeverSent) { - if (bWonderSpammer) - return true; + eStatement = eTempStatement; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); + return; } } } - return false; -} - -/// Should we ignore Social Policy differences with ePlayer? -bool CvDiplomacyAI::IsIgnorePolicyDifferences(PlayerTypes ePlayer) const -{ - if (!GetPlayer()->isMajorCiv() || !GET_PLAYER(ePlayer).isMajorCiv()) - return true; - - if (IsTeammate(ePlayer) || IsVassal(ePlayer) || IsMaster(ePlayer)) - return true; - - if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) - return false; + PublicOpinionTypes eOpinionInMyCiv = m_pPlayer->GetCulture()->GetPublicOpinionType(); + PlayerTypes eMyGreatestInfluence = m_pPlayer->GetCulture()->GetPublicOpinionBiggestInfluence(); + PublicOpinionTypes eOpinionInTheirCiv = kTheirPlayer.GetCulture()->GetPublicOpinionType(); + PlayerTypes eTheirGreatestInfluence = kTheirPlayer.GetCulture()->GetPublicOpinionBiggestInfluence(); - // Capital or Holy City captured? Exception if they resurrected us: only test to see if they captured it. - if (WasResurrectedBy(ePlayer)) + // Is their ideology causing civil unrest in our nation? + if (eOpinionInMyCiv >= PUBLIC_OPINION_CIVIL_RESISTANCE && eMyGreatestInfluence == ePlayer) { - vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); - for (size_t i=0; iGetNumTurnsSinceStatementSent(ePlayer, eTempStatement) != INT_MAX) + { + bNeverSent = false; + break; + } + } + // If someone else has sent this message before, need to pass a Chattiness roll in order to send it + // This triggers the insult throttle, but isn't blocked by it since it's sent once per game + if (bNeverSent || GC.getGame().randRangeInclusive(1, 10, CvSeeder::fromRaw(0x245b83a7).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= GetChattiness()) + { + eStatement = eTempStatement; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); + return; + } } } - else + // Is my ideology causing civil unrest in their nation? + if (eOpinionInTheirCiv >= PUBLIC_OPINION_CIVIL_RESISTANCE && eTheirGreatestInfluence == GetID()) { - if (IsCapitalCapturedBy(ePlayer)) - return false; - } + if (eTheirBranch == eAutocracy) + { + eTempStatement = DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY; + } + else if (eTheirBranch == eFreedom) + { + eTempStatement = DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM; + } + else if (eTheirBranch == eOrder) + { + eTempStatement = DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER; + } + if (eTempStatement != NO_DIPLO_STATEMENT_TYPE) + { + // Only send this message once per game + if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) != INT_MAX) + return; - if (IsPlayerLiberatedCapital(ePlayer) || IsPlayerLiberatedHolyCity(ePlayer) || IsPlayerReturnedCapital(ePlayer) || IsPlayerReturnedHolyCity(ePlayer) || WasResurrectedBy(ePlayer)) - return true; + bool bNeverSent = true; + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + { + PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; + if (eLoopPlayer == ePlayer || eLoopPlayer == GetID()) + continue; - if (IsDoFAccepted(ePlayer) || GetDoFType(ePlayer) >= DOF_TYPE_ALLIES) - return true; + if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) != INT_MAX) + { + bNeverSent = false; + break; + } + } + // If someone else has sent this message before, need to pass a Chattiness roll in order to send it + // This triggers the insult throttle, but isn't blocked by it since it's sent once per game + if (bNeverSent || GC.getGame().randRangeInclusive(1, 10, CvSeeder::fromRaw(0xcb41fed6).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= GetChattiness()) + { + eStatement = eTempStatement; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); + return; + } + } + } +} - // Ideological or religious buddies tolerate policy differences. - if (IsPlayerSameIdeology(ePlayer) || IsPlayerSameReligion(ePlayer)) - return true; +/// Possible Contact Statement - We're ending our Declaration of Friendship with them +void CvDiplomacyAI::DoEndDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if (IsCityRecentlyLiberatedBy(ePlayer)) - return true; + if (SkipForTeammates()) + return; - // The presence of a Diplomat smoothes over cultural (but not religious/ideological) differences. - if (GetPlayer()->GetEspionage()->IsMyDiplomatVisitingThem(ePlayer) || GetPlayer()->GetEspionage()->IsOtherDiplomatVisitingMe(ePlayer)) - return true; + // Must currently be friends + if (!IsDoFAccepted(ePlayer)) + return; - // If they're helping us go to war, we'll set aside our differences for now. - if (GetCoopWarAgreementScore(ePlayer) > 0 || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) - return true; + // Must want to end the friendship. + if (!IsWantsToEndDoFWithPlayer(ePlayer)) + return; - return false; + if (IsDenounceFriendAcceptable(ePlayer)) + eStatement = DIPLO_STATEMENT_DENOUNCE_FRIEND; // AI is denouncing one of its friends (backstabbing) + else + eStatement = DIPLO_STATEMENT_END_WORK_WITH_US; // Just ending the friendship normally } -/// Should we ignore religious differences with ePlayer? -bool CvDiplomacyAI::IsIgnoreReligionDifferences(PlayerTypes ePlayer) const +/// Possible Contact Statement - We're denouncing a player +void CvDiplomacyAI::DoDenounceStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) { - if (!GetPlayer()->isMajorCiv() || !GET_PLAYER(ePlayer).isMajorCiv()) - return true; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if (IsTeammate(ePlayer) || IsMaster(ePlayer)) - return true; + if (SkipForTeammates()) + return; - if (GetNegativeReligiousConversionPoints(ePlayer) > 0) - return false; + // Can't have already denounced them + if (IsDenouncedPlayer(ePlayer)) + return; - if (IsHolyCityCapturedBy(ePlayer)) - return false; + // Must want to denounce them + if (!IsDenounceAcceptable(ePlayer, /*bBias*/ false)) + return; - // Capital captured? Exception if they resurrected us: only test to see if they captured it. - if (WasResurrectedBy(ePlayer)) - { - vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); - for (size_t i=0; i= 0, "DIPLOMACY_AI: Invalid Player Index."); + PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - // Special diplomatic behavior for Celts - no natural religion spread - if (!HasEverConvertedCity(ePlayer) && !GET_PLAYER(ePlayer).GetDiplomacyAI()->HasEverConvertedCity(GetID())) + if(eStatement == NO_DIPLO_STATEMENT_TYPE) { - if (GetPlayer()->GetPlayerTraits()->IsNoNaturalReligionSpread()) - return true; + bool bRandFailed = false; - if (GET_PLAYER(ePlayer).GetPlayerTraits()->IsNoNaturalReligionSpread()) - return true; + PlayerTypes eTarget = GetRequestFriendToDenounce(ePlayer, bRandFailed); + if(eTarget != NO_PLAYER) + { + DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE; + + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50) + { + if(!bRandFailed) + { + eStatement = eTempStatement; + iData1 = eTarget; + } + } + } } +} - if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) - return false; +/// Possible Contact Statement - Vassal has been liberated +void CvDiplomacyAI::DoLiberateMyVassalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Ideological buddies tolerate religious differences. - if (IsPlayerSameIdeology(ePlayer)) - return true; + if (GetVassalPlayerToLiberate() == ePlayer) + eStatement = DIPLO_STATEMENT_LIBERATE_VASSAL; +} - if (IsPlayerLiberatedCapital(ePlayer) || IsPlayerLiberatedHolyCity(ePlayer) || IsPlayerReturnedCapital(ePlayer) || IsPlayerReturnedHolyCity(ePlayer) || WasResurrectedBy(ePlayer) || GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) - return true; +/// Possible Contact Statement - Share intrigue with this player +void CvDiplomacyAI::DoShareIntrigueStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if (IsCityRecentlyLiberatedBy(ePlayer)) - return true; + // Does this player know of an untold plot against ePlayer? + IntrigueNotificationMessage* pNotificationMessage = m_pPlayer->GetEspionage()->GetRecentIntrigueInfo(ePlayer); + if (!pNotificationMessage || pNotificationMessage->m_eSourcePlayer == NO_PLAYER) + return; - // If they're helping us go to war, we'll set aside our differences for now. - if (GetCoopWarAgreementScore(ePlayer) > 1 || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) - return true; + bool bIsNewIntrigue = true; + // has any other player told the player about this plot? + for (uint ui = 0; ui < MAX_MAJOR_CIVS; ui++) + { + PlayerTypes eOtherPlayer = (PlayerTypes)ui; + // don't evaluate the plotting player + if (eOtherPlayer == pNotificationMessage->m_eSourcePlayer) + continue; - return false; -} + if (GET_PLAYER(eOtherPlayer).GetEspionage()->HasSharedIntrigue(ePlayer, pNotificationMessage->m_eSourcePlayer, pNotificationMessage->m_eDiplomacyPlayer, (CvIntrigueType)(pNotificationMessage->m_iIntrigueType))) + { + bIsNewIntrigue = false; + break; + } + } -/// Should we ignore ideological differences with ePlayer? -bool CvDiplomacyAI::IsIgnoreIdeologyDifferences(PlayerTypes ePlayer) const -{ - if (!GetPlayer()->isMajorCiv() || !GET_PLAYER(ePlayer).isMajorCiv()) - return true; + if (bIsNewIntrigue) + { + // Do not share intrigue if we hate them, are planning war, etc. + if (IsAvoidDeals()) + return; - if (IsTeammate(ePlayer) || IsVassal(ePlayer) || IsMaster(ePlayer)) - return true; + if (IsActHostileTowardsHuman(ePlayer)) + return; - if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) - return false; + if (GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY || IsUntrustworthy(ePlayer)) + return; - // Capital or Holy City captured? Exception if they resurrected us: only test to see if they captured it. - if (WasResurrectedBy(ePlayer)) - { - vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); - for (size_t i=0; i GetCachedOpinionWeight(pNotificationMessage->m_eSourcePlayer) && + (!IsDoFAccepted(ePlayer) || GetWorkAgainstWillingness() > 6)) { - if (!GET_PLAYER(vTheirTeam[i]).isMajorCiv()) - continue; - - if (IsPlayerCapturedCapital(vTheirTeam[i]) || IsPlayerCapturedHolyCity(vTheirTeam[i])) - return false; + return; } + + eStatement = DIPLO_STATEMENT_SHARE_INTRIGUE; } else { - if (IsCapitalCapturedBy(ePlayer)) - return false; + // mark this as shared so we don't reevaluate this message + m_pPlayer->GetEspionage()->MarkRecentIntrigueAsShared(ePlayer, pNotificationMessage->m_eSourcePlayer, pNotificationMessage->m_eDiplomacyPlayer, (CvIntrigueType)(pNotificationMessage->m_iIntrigueType)); } +} - if (IsPlayerLiberatedCapital(ePlayer) || WasResurrectedBy(ePlayer) || GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) - return true; +/// Possible Contact Statement - We want to make a Declaration of Friendship with them +void CvDiplomacyAI::DoDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // If they're helping us go to war, we'll set aside our differences for now. - if (GetCoopWarAgreementScore(ePlayer) > 2 || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) - return true; + if (SkipForTeammates()) + return; - // Cold War resolution active? - CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); - if (pLeague != NULL) - { - // Loop through all (known) Players - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + if (MOD_DIPLOAI_SHUT_UP_FRIENDSHIP_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - if (IsPlayerValid(eLoopPlayer)) - { - if (GC.getGame().GetGameLeagues()->IsIdeologyEmbargoed(GetID(), eLoopPlayer)) - { - return false; - } - } - } - } + // Already friends? + if (IsDoFAccepted(ePlayer)) + return; - if (IsPlayerLiberatedHolyCity(ePlayer) || IsCityRecentlyLiberatedBy(ePlayer)) - return true; + // Not if we ended a friendship with someone this turn - let's take our time and reevaluate first. + if (HasEndedFriendshipThisTurn()) + return; - return false; -} + // Do we actually want a DoF with ePlayer? + if (!IsWantsDoFWithPlayer(ePlayer)) + return; -/// Is this player causing trouble around our Minor Civs? -bool CvDiplomacyAI::IsMinorCivTroublemaker(PlayerTypes ePlayer, bool bIgnoreBullying /* = false */) const -{ - if (GetMinorCivDisputeLevel(ePlayer) > DISPUTE_LEVEL_WEAK) - return true; - - if (GetNumTimesPerformedCoupAgainstUs(ePlayer) > 0) - return true; - - if (IsAngryAboutProtectedMinorKilled(ePlayer) || IsAngryAboutProtectedMinorAttacked(ePlayer) || BrokeAttackCityStatePromise(ePlayer) || IgnoredAttackCityStatePromise(ePlayer)) - return true; - - if (!bIgnoreBullying) + DiploStatementTypes eTempStatement = DIPLO_STATEMENT_WORK_WITH_US; + + if (GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) { - if (IsAngryAboutProtectedMinorBullied(ePlayer) || BrokeBullyCityStatePromise(ePlayer) || IgnoredBullyCityStatePromise(ePlayer)) - return true; + eTempStatement = DIPLO_STATEMENT_DOF_BB; } - - return false; + else if (GetDoFType(ePlayer) == DOF_TYPE_ALLIES) + { + eTempStatement = DIPLO_STATEMENT_DOF_ALLIES; + } + else if (GetDoFType(ePlayer) == DOF_TYPE_FRIENDS) + { + eTempStatement = DIPLO_STATEMENT_DOF_FRIENDS; + } + else if (GetDoFType(ePlayer) == DOF_TYPE_UNTRUSTWORTHY) + { + eTempStatement = DIPLO_STATEMENT_DOF_UNTRUSTWORTHY; + } + + if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 30) + eStatement = eTempStatement; } -/// Diplomacy AI Options -/// Should we specifically hide dispute-related modifiers towards ePlayer? -bool CvDiplomacyAI::ShouldHideDisputeMods(PlayerTypes ePlayer) const +/// Possible Contact Statement - AI wants to become the player's vassal +void CvDiplomacyAI::DoBecomeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { - // Game options forbid hiding. - if (MOD_DIPLOAI_SHOW_HIDDEN_OPINION_MODIFIERS || GC.getGame().isOption(GAMEOPTION_TRANSPARENT_DIPLOMACY)) - return false; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // If we're at war, don't bother. - if (IsAtWar(ePlayer)) - return false; + if (SkipForTeammates()) + return; - // If we've declared war on them previously, let's be honest about disputes. - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumWarsDeclaredOnUs(GetID()) > 0) - return false; + if (IsAvoidDeals()) + return; - // If we're their vassal, don't bother. - if (IsVassal(ePlayer)) - return false; + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // If we've denounced them, don't bother. - if (IsDenouncedPlayer(ePlayer)) - return false; + // We don't want to do it? + if (!IsVassalageAcceptable(ePlayer, /*bMasterEvaluation*/ false)) + return; - // If they've liberated us, let's be honest. - if (IsLiberator(ePlayer, false, false)) - return false; + // They don't want to do it? + if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassalageAcceptable(GetID(), /*bMasterEvaluation*/ true)) + return; - // If they're untrustworthy, don't bother hiding anything. - if (IsUntrustworthy(ePlayer)) - return false; + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ACCEPT_VASSALAGE) >= 50) + { + // Can we make an offer for vassalage? + if (GetPlayer()->GetDealAI()->IsMakeOfferToBecomeVassal(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_ACCEPT_VASSALAGE; + else + pDeal->ClearItems(); + } +} - CivApproachTypes eSurfaceApproach = GetSurfaceApproach(ePlayer); +/// Possible Contact Statement - AI wants the player to become their vassal +void CvDiplomacyAI::DoMakeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Only hide if our surface approach is FRIENDLY or AFRAID - return eSurfaceApproach == CIV_APPROACH_FRIENDLY || eSurfaceApproach == CIV_APPROACH_AFRAID; + if (SkipForTeammates()) + return; + + if (IsAvoidDeals()) + return; + + // Only send this to AI players + if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // We don't want to do it? + if (!IsVassalageAcceptable(ePlayer, /*bMasterEvaluation*/ true)) + return; + + // They don't want to do it? + if (!GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassalageAcceptable(GetID(), /*bMasterEvaluation*/ false)) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_BECOME_MY_VASSAL) >= 50) + { + if (GetPlayer()->GetDealAI()->IsMakeOfferForVassalage(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_BECOME_MY_VASSAL; + else + pDeal->ClearItems(); + } } -/// Should we hide certain negative opinion modifiers towards ePlayer? (stuff like competition, warmongering etc) -/// NOTE: If both hiding dispute mods and hiding negative mods, *and* DIPLOAI_HONEST_OPINION_MODIFIERS=0, dispute mods will show up as if the DisputeLevel was NONE. Otherwise, dispute mods are simply hidden. -bool CvDiplomacyAI::ShouldHideNegativeMods(PlayerTypes ePlayer) const +/// Possible Contact Statement - Defensive Pact Offer +void CvDiplomacyAI::DoDefensivePactOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { - // Game options forbid hiding. - if (MOD_DIPLOAI_SHOW_HIDDEN_OPINION_MODIFIERS || GC.getGame().isOption(GAMEOPTION_TRANSPARENT_DIPLOMACY)) - return false; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // If we're at war, don't bother. - if (IsAtWar(ePlayer)) - return false; + if (SkipForTeammates()) + return; - // If we're their vassal, don't bother. - if (IsVassal(ePlayer)) - return false; + if (IsAvoidDeals()) + return; - // If we've denounced them, don't bother. - if (IsDenouncedPlayer(ePlayer)) - return false; + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // If they've liberated us, let's be honest. - if (IsLiberator(ePlayer, false, false)) - return false; + // We don't want to do it? + if (!IsWantsDefensivePactWithPlayer(ePlayer)) + return; - // If they're untrustworthy, don't bother hiding anything. - if (IsUntrustworthy(ePlayer)) - return false; + // They don't want to do it? + if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsWantsDefensivePactWithPlayer(GetID())) + return; - CivApproachTypes eSurfaceApproach = GetSurfaceApproach(ePlayer); + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_DEFENSIVE_PACT_REQUEST) >= 20) + { + if (GetPlayer()->GetDealAI()->IsMakeOfferForDefensivePact(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_DEFENSIVE_PACT_REQUEST; + else + pDeal->ClearItems(); + } +} - // Always hide if our surface approach is FRIENDLY or AFRAID - if (eSurfaceApproach == CIV_APPROACH_FRIENDLY || eSurfaceApproach == CIV_APPROACH_AFRAID) - return true; +/// Possible Contact Statement - Coop War Request +void CvDiplomacyAI::DoCoopWarStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Never hide if our surface approach is HOSTILE - if (eSurfaceApproach == CIV_APPROACH_HOSTILE) - return false; + if (IsAvoidDeals()) + return; - // If we're acting hostile, don't hide anything. - if (IsActHostileTowardsHuman(ePlayer)) - return false; + if (MOD_DIPLOAI_SHUT_UP_COOP_WAR_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // If they're a favorable target, let's not bother hiding things. - if (GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_POWERFUL) + PlayerTypes eTargetPlayer = NO_PLAYER; + if (DoTestCoopWarDesire(ePlayer, /*passed by address*/ eTargetPlayer)) { - if (IsEasyTarget(ePlayer) || GetTargetValue(ePlayer) >= TARGET_VALUE_FAVORABLE) - return false; - } + if (eTargetPlayer == NO_PLAYER) + return; - return true; // Let's conceal our negative thoughts! + int iTurnsBetweenStatements = 10; + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_COOP_WAR_REQUEST) >= iTurnsBetweenStatements) + { + eStatement = DIPLO_STATEMENT_COOP_WAR_REQUEST; + iData1 = eTargetPlayer; + } + } } +/// Possible Contact Statement - Vassal taxes have been raised +void CvDiplomacyAI::DoVassalTaxesRaisedStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; -// ************************************ -// Evaluation of Other Players' Tendencies -// ************************************ + if (SkipForTeammates()) + return; + PlayerTypes eVassal = NO_PLAYER; + if (IsVassal(ePlayer) && IsVassalTaxRaised(ePlayer)) + { + // If we are the vassal, this is an insult/whining + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + { + SetVassalTaxRaised(ePlayer, false); + return; + } -/// ePlayer made peace with someone, so figure out what that means -void CvDiplomacyAI::DoWeMadePeaceWithSomeone(TeamTypes eOtherTeam) -{ - if (eOtherTeam < 0 || eOtherTeam >= MAX_CIV_TEAMS) return; + eVassal = GetID(); + } + else if (IsMaster(ePlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassalTaxRaised(GetID())) + eVassal = ePlayer; - vector vPlayersToReevaluate; - vector vOtherTeam = GET_TEAM(eOtherTeam).getPlayers(); - for (size_t i=0; igetFirstOffensiveAIOperation(ePeacePlayer) != NULL) + // Turn off the statement for all teammates + vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); + for (size_t i=0; iStopAllLandOffensiveOperationsAgainstPlayer(ePeacePlayer,AI_ABORT_WAR_STATE_CHANGE); - GetPlayer()->StopAllSeaOffensiveOperationsAgainstPlayer(ePeacePlayer,AI_ABORT_WAR_STATE_CHANGE); + GET_PLAYER(vMyTeam[i]).GetDiplomacyAI()->SetVassalTaxRaised(ePlayer, false); } + } +} - // Reset values specific to this war - SetAggressor(ePeacePlayer, false); - SetWarProgressScore(ePeacePlayer, 0); +/// Possible Contact Statement - Vassal taxes have been lowered +void CvDiplomacyAI::DoVassalTaxesLoweredStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Reset number of turns locked into war - GET_TEAM(GetTeam()).SetNumTurnsLockedIntoWar(GET_PLAYER(ePeacePlayer).getTeam(), 0); + if (SkipForTeammates()) + return; - if (GET_PLAYER(ePeacePlayer).isMajorCiv()) + PlayerTypes eVassal = NO_PLAYER; + if (IsVassal(ePlayer) && IsVassalTaxLowered(ePlayer)) + { + // If we are the vassal, this is a compliment + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) { - CancelCoopWarsAgainstPlayer(ePeacePlayer, false); + SetVassalTaxLowered(ePlayer, false); + return; + } - // Halve war weariness - GetPlayer()->SetWarWeariness(ePeacePlayer, GetPlayer()->GetWarWeariness(ePeacePlayer) / 2); + eVassal = GetID(); + } + else if (IsMaster(ePlayer) && GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassalTaxLowered(GetID())) + eVassal = ePlayer; - // Clear penalties for stealing territory and refusing to go on coop wars now that we made peace - if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) - { - SetNumTimesCultureBombed(ePeacePlayer, 0); - if (GetCoopWarAgreementScore(ePeacePlayer) < 0) - SetCoopWarAgreementScore(ePeacePlayer, 0); + // We are ePlayer's vassal + if (eVassal == GetID()) + { + eStatement = DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_HUMAN_MASTER; + SetVassalTaxLowered(ePlayer, false); + } + // ePlayer is our vassal + else if (eVassal == ePlayer) + { + eStatement = DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_AI_MASTER; - vPlayersToReevaluate.push_back(ePeacePlayer); - } - } - else if (GET_PLAYER(ePeacePlayer).isMinorCiv()) + // Turn off the statement for all teammates + vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); + for (size_t i=0; iSetVassalTaxLowered(ePlayer, false); } } - - // Update other diplomacy stuff! - DoReevaluatePlayers(vPlayersToReevaluate, false, false); } -/// ePlayer declared war on someone, so figure out what that means -void CvDiplomacyAI::DoPlayerDeclaredWarOnSomeone(PlayerTypes ePlayer, TeamTypes eOtherTeam, bool bDefensivePact) +/// Possible Contact Statement - We befriended one of the human's enemies and we're letting them know +void CvDiplomacyAI::DoFYIBefriendedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { - if (ePlayer < 0 || ePlayer >= MAX_CIV_PLAYERS || GET_PLAYER(ePlayer).isBarbarian()) return; - if (eOtherTeam < 0 || eOtherTeam >= MAX_CIV_TEAMS || GET_TEAM(eOtherTeam).isBarbarian()) return; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - PlayerTypes eMyPlayer = GetID(); - vector vAttackedTeam = GET_TEAM(eOtherTeam).getPlayers(); - for (size_t i=0; i= 50 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE) >= 10 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_TAUNT_THROTTLE) >= 20) { - PlayerTypes eAttackedPlayer = GET_PLAYER(vAttackedTeam[i]).GetID(); + CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + PlayerTypes eBestPlayer = NO_PLAYER; + int iLowestTurns = INT_MAX; + int iOpinionTiebreaker = 0; - // We were attacked! Change appropriate diplomacy stuff! - if (eAttackedPlayer == eMyPlayer) + // Loop through all players until we find one that we just made friends with, that ePlayer denounced or is at war with + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - ChangeNumWarsFought(ePlayer, 1); + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - if (!bDefensivePact) - { - ChangeNumWarsDeclaredOnUs(ePlayer, 1); - } - - // Only stuff for major civs - if (!GET_PLAYER(ePlayer).isMajorCiv()) + if (!GET_PLAYER(eLoopPlayer).isAlive()) continue; - if (!bDefensivePact) - { - bool bBackstabTimer = IsDoFBroken(ePlayer) && GetTurnsSinceDoFBroken(ePlayer) < /*10*/ GD_INT_GET(DOF_BROKEN_BACKSTAB_TIMER); - - // WAS working with this player - if (IsDoFAccepted(ePlayer) || bBackstabTimer) - { - ChangeRecentAssistValue(ePlayer, -300); - SetDoFBroken(ePlayer, true, true); - SetFriendDeclaredWarOnUs(ePlayer, true); - } + if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) + continue; - // HAD been resurrected by this player - if (WasResurrectedBy(ePlayer)) - { - ChangeRecentAssistValue(ePlayer, -300); - SetResurrectorAttackedUs(ePlayer, true); - } + // We must have befriended this guy, and they must have either denounced them or be at war + if (!IsDoFAccepted(eLoopPlayer) || (!pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer) && !pTheirDiploAI->IsAtWar(eLoopPlayer))) + continue; - if (GetCoopWarAgreementScore(ePlayer) > 0) - { - SetCoopWarAgreementScore(ePlayer, 0); - } - } + int iTurnsSinceTrigger = GetTurnsSinceBefriendedPlayer(eLoopPlayer); - // Reset DoF values - SetDoFAccepted(ePlayer, false); - SetDoFType(ePlayer, DOF_TYPE_UNTRUSTWORTHY); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, false); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(eMyPlayer, DOF_TYPE_UNTRUSTWORTHY); + if (GC.getGame().getElapsedGameTurns() <= iTurnsSinceTrigger) + continue; - if (GetShareApproachResponse(ePlayer) != SHARE_APPROACH_RESPONSE_REFUSED) - SetShareApproachResponse(ePlayer, NO_SHARE_APPROACH_RESPONSE); - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetShareApproachResponse(eMyPlayer) != SHARE_APPROACH_RESPONSE_REFUSED) - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetShareApproachResponse(eMyPlayer, NO_SHARE_APPROACH_RESPONSE); + // Too much time has passed + if (iTurnsSinceTrigger >= 3) + continue; - // End all coop war agreements with this player - CancelCoopWarsWithPlayer(ePlayer, !bDefensivePact); + // We must have befriended them after their adversarial state started + int iTheirTurns = max(pTheirDiploAI->GetTurnsSinceDenouncedPlayer(eLoopPlayer), GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetNumTurnsAtWar(GET_PLAYER(eLoopPlayer).getTeam())); + if (iTheirTurns < iTurnsSinceTrigger || (iTheirTurns == iTurnsSinceTrigger && GetID() < ePlayer)) + continue; - // Player broke a promise that he wasn't going to attack us? - if (MadeMilitaryPromise(ePlayer)) + // We want the most recent candidate + if (iTurnsSinceTrigger < iLowestTurns) { - if (!bDefensivePact) - SetMilitaryPromiseState(ePlayer, PROMISE_STATE_BROKEN); - else - SetMilitaryPromiseState(ePlayer, NO_PROMISE_STATE); + iLowestTurns = iTurnsSinceTrigger; + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } - - // Reset various promises for both of us...all is fair in war! - SetExpansionPromiseState(ePlayer, NO_PROMISE_STATE); - SetBorderPromiseState(ePlayer, NO_PROMISE_STATE); - SetSpyPromiseState(ePlayer, NO_PROMISE_STATE); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetExpansionPromiseState(eMyPlayer, NO_PROMISE_STATE); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetBorderPromiseState(eMyPlayer, NO_PROMISE_STATE); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetSpyPromiseState(eMyPlayer, NO_PROMISE_STATE); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetMilitaryPromiseState(eMyPlayer, NO_PROMISE_STATE); - - // We're no longer trade partners - SetRecentTradeValue(ePlayer, 0); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetRecentTradeValue(eMyPlayer, 0); - - if (!bDefensivePact) + else if (iTurnsSinceTrigger == iLowestTurns && GetCachedOpinionWeight(eLoopPlayer) < iOpinionTiebreaker) { - // Forget any of that liberation crud! - SetResurrectedBy(ePlayer, false); - SetPlayerLiberatedCapital(ePlayer, false); - SetPlayerLiberatedHolyCity(ePlayer, false); - SetNumCitiesLiberatedBy(ePlayer, 0); - SetNumCitiesEverLiberatedBy(ePlayer, 0); - SetPlayerReturnedCapital(ePlayer, false); - SetPlayerReturnedHolyCity(ePlayer, false); - SetMasterLiberatedMeFromVassalage(ePlayer, false); - SetVassalagePeacefullyRevokedTurn(ePlayer, -1); - - // Forget civilians returned, landmarks built, and intrigue shared so they don't affect relations any more - SetNumCiviliansReturnedToMe(ePlayer, 0); - SetNumLandmarksBuiltForMe(ePlayer, 0); - SetNumTimesIntrigueSharedBy(ePlayer, 0); - - // Clear positive diplomatic values - SetCommonFoeValue(ePlayer, 0); - SetVassalProtectValue(ePlayer, 0); - if (GetRecentAssistValue(ePlayer) < 0) - SetRecentAssistValue(ePlayer, 0); + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } } - - // If it's us OR we know the attacked player, change appropriate values - if (!bDefensivePact && IsPlayerValid(eAttackedPlayer, true) && GET_PLAYER(ePlayer).isMajorCiv()) + if (eBestPlayer != NO_PLAYER) { - if (GET_PLAYER(eAttackedPlayer).isMajorCiv()) - { - ChangeOtherPlayerNumMajorsAttacked(ePlayer, 1, eOtherTeam); + int iChance = 4 + (2 * GetChattiness()) + (GetChattiness() / 3); // ranges from 6-27% - if (GetTeam() != eOtherTeam && !IsAtWar(ePlayer) && !IsUntrustworthy(ePlayer)) - { - // If we view the target as a backstabber, apply a large diplo bonus. - if (IsUntrustworthy(eAttackedPlayer)) - { - ChangeRecentAssistValue(ePlayer, 300); - } - // Did they declare war on someone we're at war with? - else if (IsAtWar(eAttackedPlayer)) - { - // If we're doing badly in the war, we appreciate the assistance. - switch (GetWarState(eAttackedPlayer)) - { - // No war state is fine here since they may have just gone to war. - case NO_WAR_STATE_TYPE: - case WAR_STATE_NEARLY_WON: - case WAR_STATE_OFFENSIVE: - break; - case WAR_STATE_CALM: - ChangeRecentAssistValue(ePlayer, 50); - break; - case WAR_STATE_STALEMATE: - ChangeRecentAssistValue(ePlayer, 100); - break; - case WAR_STATE_TROUBLED: - case WAR_STATE_DEFENSIVE: - ChangeRecentAssistValue(ePlayer, 200); - break; - case WAR_STATE_NEARLY_DEFEATED: - ChangeRecentAssistValue(ePlayer, 300); - break; - } - } - } - } - else if (GET_PLAYER(eAttackedPlayer).isMinorCiv()) + // Add a modest bonus if we dislike them or they're weaker + int iBonus = 0; + CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); + switch (eOpinion) { - // Did they attack a Minor we're protecting? - bool bProtected = GET_PLAYER(eAttackedPlayer).GetMinorCivAI()->IsProtectedByMajor(eMyPlayer) || GET_PLAYER(eAttackedPlayer).GetMinorCivAI()->IsAllies(eMyPlayer); + case CIV_OPINION_UNFORGIVABLE: + iBonus += 6; + break; + case CIV_OPINION_ENEMY: + iBonus += 4; + break; + case CIV_OPINION_COMPETITOR: + iBonus += 2; + break; + default: + break; + } - if (bProtected) - { - SetOtherPlayerAttackedProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); - SetOtherPlayerProtectedMinorAttacked(ePlayer, eAttackedPlayer); - } + if (GetBiggestCompetitor() == ePlayer || GetPrimeLeagueCompetitor() == ePlayer) + iBonus += 2; - // To prevent infinite warmongering exploits, only apply further penalties if they haven't attacked this minor within the last 50 turns. - int iTurn = GET_PLAYER(eAttackedPlayer).GetMinorCivAI()->GetTurnLastAttacked(GET_PLAYER(ePlayer).getTeam()); - int iTurnDifference = GC.getGame().getGameTurn() - iTurn; - if (iTurn > -1 && iTurnDifference < 50) - return; + if (GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_AVERAGE) + iBonus += 2; - if (bProtected) - ChangeOtherPlayerNumProtectedMinorsAttacked(ePlayer, 1); + iChance += iBonus; + if (iChance > 30) + iChance = 30; - ChangeOtherPlayerNumMinorsAttacked(ePlayer, 1, eOtherTeam); + if (iLowestTurns == 0) + { + iChance *= 2; } - } - } -} - -/// ePlayer bullied eOtherPlayer (minor civ), so figure out what that means -void CvDiplomacyAI::DoPlayerBulliedSomeone(PlayerTypes ePlayer, PlayerTypes eOtherPlayer) -{ - if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return; - if (eOtherPlayer < MAX_MAJOR_CIVS || eOtherPlayer >= MAX_CIV_PLAYERS) return; - - // The bully was someone else - if (IsPlayerValid(ePlayer)) - { - // Did they bully a Minor we're protecting? - if (GET_PLAYER(eOtherPlayer).isMinorCiv() && GET_PLAYER(eOtherPlayer).GetMinorCivAI()->IsProtectedByMajor(GetID())) - { - // Only apply this penalty if they haven't bullied them already - if (GET_PLAYER(eOtherPlayer).GetMinorCivAI()->GetTurnLastBulliedByMajor(ePlayer) == -1) + else if (iLowestTurns > 1) { - ChangeOtherPlayerNumProtectedMinorsBullied(ePlayer, 1); + iChance += 1; + iChance /= 2; } - SetOtherPlayerProtectedMinorBullied(ePlayer, eOtherPlayer); - SetOtherPlayerBulliedProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); - - // You broke the promise you made! - if (MadeBullyCityStatePromise(ePlayer)) + // We're chatty enough to say something! + if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x85433256).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChance) { - SetBullyCityStatePromiseState(ePlayer, PROMISE_STATE_BROKEN); + eStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY; + iData1 = eBestPlayer; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_TAUNT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_TAUNT_THROTTLE, GC.getGame().getGameTurn()); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); } } } } -/// Return the value of the warmonger amount adjusted by how much this player hates warmongers -int CvDiplomacyAI::GetOtherPlayerWarmongerScore(PlayerTypes ePlayer) const +/// Possible Contact Statement - We denounced one of the human's friends and we're letting them know +void CvDiplomacyAI::DoFYIDenouncedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { - if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - int iReturnValue = GetOtherPlayerWarmongerAmount(ePlayer); + if (SkipForTeammates()) + return; - iReturnValue *= GetWarmongerHate(); // ranges from 1 to 10 - iReturnValue /= 20; + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - return iReturnValue; -} + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; -///////////////////////////////////////////////////////// -// Contact -///////////////////////////////////////////////////////// + // Don't send taunts unless we're acting hostile + if (!IsActHostileTowardsHuman(ePlayer)) + return; -/// First contact between this player and another -void CvDiplomacyAI::DoFirstContact(PlayerTypes ePlayer) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_CIV_PLAYERS, "DIPLOMACY_AI: Invalid Player Index."); + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; - if (ePlayer != GetID()) + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND) >= 50 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE) >= 10 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_TAUNT_THROTTLE) >= 20) { - DoFirstContactInitRelationship(ePlayer); + CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + PlayerTypes eBestPlayer = NO_PLAYER; + int iLowestTurns = INT_MAX; + int iOpinionTiebreaker = 0; - // Humans don't say hi to one another through the shadow diplo AI and, uh, don't show up in MP please - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + // Loop through all players until we find one that we just denoucned, that ePlayer has befriended + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - // JdH: notifications do get send in MP + updated to new - if (GC.getGame().isFinalInitialized()) + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (!GET_PLAYER(eLoopPlayer).isAlive()) + continue; + + if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) + continue; + + // We must have denounced them, and they must have befriended them + if (!IsDenouncedPlayer(eLoopPlayer) || !pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) + continue; + + int iTurnsSinceTrigger = GetTurnsSinceDenouncedPlayer(eLoopPlayer); + + if (GC.getGame().getElapsedGameTurns() <= iTurnsSinceTrigger) + continue; + + // Too much time has passed + if (iTurnsSinceTrigger >= 3) + continue; + + // We must have denounced them after they befriended them + int iTheirTurns = pTheirDiploAI->GetTurnsSinceBefriendedPlayer(eLoopPlayer); + if (iTheirTurns < iTurnsSinceTrigger || (iTheirTurns == iTurnsSinceTrigger && GetID() < ePlayer)) + continue; + + // We want the most recent candidate + if (iTurnsSinceTrigger < iLowestTurns) { - if (!IsAtWar(ePlayer) && CvPreGame::isHuman(ePlayer)) - { - // Human to Human will just send a notification - if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) - { - CvPlayer& kTargetPlayer = GET_PLAYER(ePlayer); - CvNotifications* pNotifications = kTargetPlayer.GetNotifications(); - if (pNotifications) - { - CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_SUMMARY_MET_MINOR_CIV", GetPlayer()->getNameKey()); - pNotifications->Add(NOTIFICATION_GENERIC, strBuffer, strBuffer, -1, -1, GetID()); - } - } - else - { - // AI to Human: during auto-moves, send a notification instead of a request - // to avoid creating pending requests after turn end - if (GET_PLAYER(ePlayer).isAutoMoves()) - { - CvPlayer& kTargetPlayer = GET_PLAYER(ePlayer); - CvNotifications* pNotifications = kTargetPlayer.GetNotifications(); - if (pNotifications) - { - CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_SUMMARY_MET_MINOR_CIV", GetPlayer()->getNameKey()); - pNotifications->Add(NOTIFICATION_GENERIC, strBuffer, strBuffer, -1, -1, GetID()); - } - } - else - { - const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_INTRO); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DEFAULT_ROOT, szText, LEADERHEAD_ANIM_INTRO); - } - } - } + iLowestTurns = iTurnsSinceTrigger; + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); + } + else if (iTurnsSinceTrigger == iLowestTurns && GetCachedOpinionWeight(eLoopPlayer) > iOpinionTiebreaker) + { + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } } - else + if (eBestPlayer != NO_PLAYER) { - if(!GC.getGame().isNetworkMultiPlayer()) // KWG: Candidate for !GC.getGame().IsOption(GAMEOPTION_SIMULTANEOUS_TURNS) + int iChance = 4 + (2 * GetChattiness()) + (GetChattiness() / 3); // ranges from 6-27% + + // Add a modest bonus if we dislike them or they're weaker + int iBonus = 0; + CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); + switch (eOpinion) { - if(!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) - { - // Should fire off a diplo message when we meet a human - if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - { - if(!IsAtWar(ePlayer)) - { - if(GC.getGame().isFinalInitialized()) - { - if(std::find(m_aGreetPlayers.begin(), m_aGreetPlayers.end(), ePlayer) == m_aGreetPlayers.end()) - { - // Put in the list of people to greet when their turn comes up. - m_aGreetPlayers.push_back(ePlayer); - } - } - } - } - } - else - { - // Human to Human will just send a notification - CvPlayer& kTargetPlayer = GET_PLAYER(ePlayer); - if(kTargetPlayer.isHuman(ISHUMAN_AI_DIPLOMACY)) - { - if(!IsAtWar(ePlayer)) - { - if(GC.getGame().isFinalInitialized()) - { - CvNotifications* pNotifications = kTargetPlayer.GetNotifications(); - if(pNotifications) - { - CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_SUMMARY_MET_MINOR_CIV", GetPlayer()->getNameKey()); - pNotifications->Add(NOTIFICATION_GENERIC, strBuffer, strBuffer, -1, -1, GetID()); - } - } - } - } - } + case CIV_OPINION_UNFORGIVABLE: + iBonus += 6; + break; + case CIV_OPINION_ENEMY: + iBonus += 4; + break; + case CIV_OPINION_COMPETITOR: + iBonus += 2; + break; + default: + break; + } + + if (GetBiggestCompetitor() == ePlayer || GetPrimeLeagueCompetitor() == ePlayer) + iBonus += 2; + + if (GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_AVERAGE) + iBonus += 2; + + iChance += iBonus; + if (iChance > 30) + iChance = 30; + + if (iLowestTurns == 0) + { + iChance *= 2; + } + else if (iLowestTurns > 1) + { + iChance += 1; + iChance /= 2; + } + + // We're chatty enough to say something! + if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x58262340).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChance) + { + eStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND; + iData1 = eBestPlayer; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_TAUNT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_TAUNT_THROTTLE, GC.getGame().getGameTurn()); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); } } } } -/// Initiate relationship values towards a new player on first contact -void CvDiplomacyAI::DoFirstContactInitRelationship(PlayerTypes ePlayer) +/// Possible Contact Statement - We're angry that they denounced one of our friends +void CvDiplomacyAI::DoAngryDenouncedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { - if (GC.getGame().isFinalInitialized()) - { - DoUpdateConquestStats(); - DoUpdateMilitaryAggressivePostures(); - } + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Major Civ - if (GET_PLAYER(ePlayer).isMajorCiv() && NotTeam(ePlayer)) - { - for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) - { - CivApproachTypes eApproach = (CivApproachTypes) iApproachLoop; - SetPlayerApproachValue(ePlayer, eApproach, 0); - } + if (SkipForTeammates()) + return; - DoReevaluatePlayer(ePlayer); - } - // Minor civ - else if (GET_PLAYER(ePlayer).isMinorCiv()) - { - SetCivApproach(ePlayer, CIV_APPROACH_NEUTRAL); - } -} -// ----------------------------------------------------------------------------------------------- -/// Player killed us -void CvDiplomacyAI::DoKilledByPlayer(PlayerTypes ePlayer) -{ - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; + + // Don't send this if we're acting nice + if (GetSurfaceApproach(ePlayer) == CIV_APPROACH_FRIENDLY || GetSurfaceApproach(ePlayer) == CIV_APPROACH_AFRAID) + return; + + // If we've already denounced them, no use whining at this point + if (IsDenouncedPlayer(ePlayer)) + return; + + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND) >= 50 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE) >= 10) { - if(ePlayer != NO_PLAYER && CvPreGame::isHuman(ePlayer)) + CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + PlayerTypes eBestPlayer = NO_PLAYER; + int iLowestTurns = INT_MAX; + int iOpinionTiebreaker = 0; + + // Loop through all players until we find one that is our friend, that ePlayer denounced + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEFEATED); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_DEFEATED); + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (!GET_PLAYER(eLoopPlayer).isAlive()) + continue; + + if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) + continue; + + // We must have befriended this guy, and they must have denounced them + if (!IsDoFAccepted(eLoopPlayer) || !pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) + continue; + + int iTurnsSinceTrigger = pTheirDiploAI->GetTurnsSinceDenouncedPlayer(eLoopPlayer); + + if (GC.getGame().getElapsedGameTurns() <= iTurnsSinceTrigger) + continue; + + // Too much time has passed + if (iTurnsSinceTrigger >= 3) + continue; + + // They must have denounced them after we befriended them + int iOurTurns = GetTurnsSinceBefriendedPlayer(eLoopPlayer); + if (iOurTurns < iTurnsSinceTrigger || (iOurTurns == iTurnsSinceTrigger && GetID() > ePlayer)) + continue; + + // We want the most recent candidate + if (iTurnsSinceTrigger < iLowestTurns) + { + iLowestTurns = iTurnsSinceTrigger; + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); + } + else if (iTurnsSinceTrigger == iLowestTurns && GetCachedOpinionWeight(eLoopPlayer) < iOpinionTiebreaker) + { + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); + } } - } - else - { - if(ePlayer == GC.getGame().getActivePlayer() && !GC.getGame().isNetworkMultiPlayer()) + if (eBestPlayer != NO_PLAYER) { - const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEFEATED); - gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_DEFEATED); - } + int iChance = 4 + (2 * GetChattiness()) + (GetChattiness() / 3); // ranges from 6-27% + if (iLowestTurns == 0) + { + iChance *= 2; + } + else if (iLowestTurns > 1) + { + iChance += 1; + iChance /= 2; + } - if (MOD_ENABLE_ACHIEVEMENTS && !GC.getGame().isGameMultiPlayer()) - { - gDLL->UnlockAchievement(ACHIEVEMENT_DESTROY_CIV); - CvAchievementUnlocker::AlexanderConquest(ePlayer); + // We're chatty enough to say something! + if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x2207e072).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChance) + { + eStatement = DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND; + iData1 = eBestPlayer; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); + } } } } -// ------------------------------------------------------------------------------------------------------------------- -/// Send a statement to another player -void CvDiplomacyAI::DoSendStatementToPlayer(PlayerTypes ePlayer, DiploStatementTypes eStatement, int iData1, CvDeal* pDeal) + +/// Possible Contact Statement - We're angry that they befriended a player we denounced or are at war with +void CvDiplomacyAI::DoAngryBefriendedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) { - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - ASSERT(eStatement >= 0, "DIPLOMACY_AI: Invalid DiploStatementType."); - PRECONDITION(eStatement < NUM_DIPLO_STATEMENT_TYPES, "DIPLOMACY_AI: Invalid DiploStatementType."); + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - ASSERT(!pDeal || (pDeal->m_eFromPlayer == m_eID && pDeal->m_eToPlayer == ePlayer) || (pDeal->m_eFromPlayer == ePlayer && pDeal->m_eToPlayer == m_eID), "Sending deal statement with incorrect players in the deal."); + if (SkipForTeammates()) + return; - const char* szText = NULL; - bool bHuman = GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY); + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // Aggressive Military warning - if(eStatement == DIPLO_STATEMENT_AGGRESSIVE_MILITARY_WARNING) + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; + + // Don't send this if we're acting nice + if (GetSurfaceApproach(ePlayer) == CIV_APPROACH_FRIENDLY || GetSurfaceApproach(ePlayer) == CIV_APPROACH_AFRAID) + return; + + // If we've already denounced them, no use whining at this point + if (IsDenouncedPlayer(ePlayer)) + return; + + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY) >= 50 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE) >= 10) { - if(bHuman) - { - if(IsActHostileTowardsHuman(ePlayer)) - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_AGGRESSIVE_MILITARY_WARNING); - else - szText = GetDiploStringForMessage(DIPLO_MESSAGE_AGGRESSIVE_MILITARY_WARNING); + CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + PlayerTypes eBestPlayer = NO_PLAYER; + int iLowestTurns = INT_MAX; + int iOpinionTiebreaker = 0; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_AGGRESSIVE_MILITARY_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE); - } - // AI resolution - else + // Loop through all players until we find one that is our enemy, that ePlayer befriended + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam())) - { - // Make promises between all members of both teams - for (int iI = 0; iI < MAX_MAJOR_CIVS; iI++) - { - PlayerTypes eMyTeammate = (PlayerTypes)iI; - if (!GET_PLAYER(eMyTeammate).isAlive()) - continue; - if (GET_PLAYER(eMyTeammate).getTeam() != GetTeam()) - continue; + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - for (int iJ = 0; iJ < MAX_MAJOR_CIVS; iJ++) - { - PlayerTypes eTheirTeammate = (PlayerTypes)iJ; - if (!GET_PLAYER(eTheirTeammate).isAlive()) - continue; - if (GET_PLAYER(eTheirTeammate).getTeam() != GET_PLAYER(ePlayer).getTeam()) - continue; + if (!GET_PLAYER(eLoopPlayer).isAlive()) + continue; - GET_PLAYER(eMyTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eTheirTeammate, PROMISE_STATE_MADE); - GET_PLAYER(eTheirTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eMyTeammate, PROMISE_STATE_MADE); - } - } - } - else - { - MoveTroopsResponseTypes eResponse = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMoveTroopsRequestResponse(GetID(), /*bJustChecking*/ false); - if (eResponse == MOVE_TROOPS_RESPONSE_REFUSE) - { - if (!GET_PLAYER(ePlayer).GetDiplomacyAI()->DeclareWar(GetTeam())) - { - // Make promises between all members of both teams - for (int iI = 0; iI < MAX_MAJOR_CIVS; iI++) - { - PlayerTypes eMyTeammate = (PlayerTypes)iI; - if (!GET_PLAYER(eMyTeammate).isAlive()) - continue; - if (GET_PLAYER(eMyTeammate).getTeam() != GetTeam()) - continue; + if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) + continue; - for (int iJ = 0; iJ < MAX_MAJOR_CIVS; iJ++) - { - PlayerTypes eTheirTeammate = (PlayerTypes)iJ; - if (!GET_PLAYER(eTheirTeammate).isAlive()) - continue; - if (GET_PLAYER(eTheirTeammate).getTeam() != GET_PLAYER(ePlayer).getTeam()) - continue; + // They must have befriended this guy, and we must have either denounced them or be at war with them + if (!pTheirDiploAI->IsDoFAccepted(eLoopPlayer) || (!IsDenouncedPlayer(eLoopPlayer) && !IsAtWar(eLoopPlayer))) + continue; - GET_PLAYER(eMyTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eTheirTeammate, PROMISE_STATE_MADE); - GET_PLAYER(eTheirTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eMyTeammate, PROMISE_STATE_MADE); - } - } - } - else - { - SetMilitaryPromiseState(ePlayer, PROMISE_STATE_IGNORED); - } - } - else - { - // Make promises between all members of both teams - for (int iI = 0; iI < MAX_MAJOR_CIVS; iI++) - { - PlayerTypes eMyTeammate = (PlayerTypes)iI; - if (!GET_PLAYER(eMyTeammate).isAlive()) - continue; - if (GET_PLAYER(eMyTeammate).getTeam() != GetTeam()) - continue; + int iTurnsSinceTrigger = pTheirDiploAI->GetTurnsSinceBefriendedPlayer(eLoopPlayer); - for (int iJ = 0; iJ < MAX_MAJOR_CIVS; iJ++) - { - PlayerTypes eTheirTeammate = (PlayerTypes)iJ; - if (!GET_PLAYER(eTheirTeammate).isAlive()) - continue; - if (GET_PLAYER(eTheirTeammate).getTeam() != GET_PLAYER(ePlayer).getTeam()) - continue; + if (GC.getGame().getElapsedGameTurns() <= iTurnsSinceTrigger) + continue; - GET_PLAYER(eMyTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eTheirTeammate, PROMISE_STATE_MADE); - GET_PLAYER(eTheirTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eMyTeammate, PROMISE_STATE_MADE); - } - } - } + // Too much time has passed + if (iTurnsSinceTrigger >= 3) + continue; + + // They must have befriended them after we did + int iOurTurns = max(GetTurnsSinceDenouncedPlayer(eLoopPlayer), GET_TEAM(GetTeam()).GetNumTurnsAtWar(GET_PLAYER(eLoopPlayer).getTeam())); + if (iOurTurns < iTurnsSinceTrigger || (iOurTurns == iTurnsSinceTrigger && GetID() > ePlayer)) + continue; + + // We want the most recent candidate + if (iTurnsSinceTrigger < iLowestTurns) + { + iLowestTurns = iTurnsSinceTrigger; + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); + } + else if (iTurnsSinceTrigger == iLowestTurns && GetCachedOpinionWeight(eLoopPlayer) > iOpinionTiebreaker) + { + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } } - } - - // Player killed a City-State we were protecting - else if (eStatement == DIPLO_STATEMENT_KILLED_PROTECTED_CITY_STATE) - { - if (bHuman) + if (eBestPlayer != NO_PLAYER) { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - ASSERT(eMinorCiv != NO_PLAYER); - if(eMinorCiv != NO_PLAYER) + int iChance = 4 + (2 * GetChattiness()) + (GetChattiness() / 3); // ranges from 6-27% + if (iLowestTurns == 0) { - const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); + iChance *= 2; + } + else if (iLowestTurns > 1) + { + iChance += 1; + iChance /= 2; + } - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_KILLED_PROTECTED_CITY_STATE, NO_PLAYER, strMinorCivKey); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + // We're chatty enough to say something! + if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x7e74ea9f).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChance) + { + eStatement = DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY; + iData1 = eBestPlayer; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); } } } +} - // Player attacked a City-State we're protecting - else if (eStatement == DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE) +/// Possible Contact Statement - We're happy that they denounced a player we denounced +void CvDiplomacyAI::DoHappyDenouncedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // No compliments if we're acting hostile towards them + if (IsActHostileTowardsHuman(ePlayer)) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY) >= 50 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE) >= 10) { - if (bHuman) + CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + PlayerTypes eBestPlayer = NO_PLAYER; + int iLowestTurns = INT_MAX; + int iOpinionTiebreaker = 0; + + // Loop through all players until we find one that is our enemy, that ePlayer denounced + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - ASSERT(eMinorCiv != NO_PLAYER); - if (eMinorCiv != NO_PLAYER) - { - const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + if (!GET_PLAYER(eLoopPlayer).isAlive()) + continue; - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_ATTACKED_PROTECTED_CITY_STATE, NO_PLAYER, strMinorCivKey); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_ATTACKED_MINOR_CIV, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) + continue; + + // We must have both denounced this guy + if (!IsDenouncedPlayer(eLoopPlayer) || !pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) + continue; + + int iTurnsSinceTrigger = pTheirDiploAI->GetTurnsSinceDenouncedPlayer(eLoopPlayer); + + if (GC.getGame().getElapsedGameTurns() <= iTurnsSinceTrigger) + continue; + + // Too much time has passed + if (iTurnsSinceTrigger >= 3) + continue; + + // They must have denounced them after we did + int iOurTurns = GetTurnsSinceDenouncedPlayer(eLoopPlayer); + if (iOurTurns < iTurnsSinceTrigger || (iOurTurns == iTurnsSinceTrigger && GetID() > ePlayer)) + continue; + + // We want the most recent candidate + if (iTurnsSinceTrigger < iLowestTurns) + { + iLowestTurns = iTurnsSinceTrigger; + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } - } - else - { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - ASSERT(eMinorCiv != NO_PLAYER); - if (eMinorCiv != NO_PLAYER) + else if (iTurnsSinceTrigger == iLowestTurns && GetCachedOpinionWeight(eLoopPlayer) > iOpinionTiebreaker) { - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_WAR) - { - SetAttackCityStatePromiseState(ePlayer, PROMISE_STATE_IGNORED); - } - else - { - SetAttackCityStatePromiseState(ePlayer, PROMISE_STATE_MADE); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCivApproach(eMinorCiv, CIV_APPROACH_NEUTRAL); - } + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } } - } - - // Player bullied a City-State we're protecting - else if (eStatement == DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE) - { - if (bHuman) + if (eBestPlayer != NO_PLAYER) { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - ASSERT(eMinorCiv != NO_PLAYER); - if (eMinorCiv != NO_PLAYER) + int iChance = 4 + (2 * GetChattiness()) + (GetChattiness() / 3); // ranges from 6-27% + if (iLowestTurns == 0) { - const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); - - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_BULLIED_PROTECTED_CITY_STATE, NO_PLAYER, strMinorCivKey); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_BULLIED_MINOR_CIV, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + iChance *= 2; } - } - else - { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - ASSERT(eMinorCiv != NO_PLAYER); - if (eMinorCiv != NO_PLAYER) + else if (iLowestTurns > 1) { - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_HOSTILE) - { - SetBullyCityStatePromiseState(ePlayer, PROMISE_STATE_IGNORED); - } - else - { - SetBullyCityStatePromiseState(ePlayer, PROMISE_STATE_MADE); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCivApproach(eMinorCiv, CIV_APPROACH_NEUTRAL); - } + iChance += 1; + iChance /= 2; } - } - } - // Serious Expansion warning - else if(eStatement == DIPLO_STATEMENT_EXPANSION_SERIOUS_WARNING) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXPANSION_SERIOUS_WARNING); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_EXPANSION_SERIOUS_WARNING, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + // We're chatty enough to say something! + if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0xd55f1785).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChance) + { + eStatement = DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY; + iData1 = eBestPlayer; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE, GC.getGame().getGameTurn()); + } } } +} - // Expansion warning - else if (eStatement == DIPLO_STATEMENT_EXPANSION_WARNING) - { - if (bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXPANSION_WARNING); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_EXPANSION_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE); - } - else - { - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDontSettleAcceptable(GetID())) - SetExpansionPromiseState(ePlayer, PROMISE_STATE_MADE); - else - SetExpansionPromiseState(ePlayer, PROMISE_STATE_IGNORED); - } +/// Possible Contact Statement - We denounced someone the human has denounced and we're letting them know +void CvDiplomacyAI::DoFYIDenouncedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - SetAngryAboutExpansion(ePlayer, false); - SetEverRequestedExpansionPromise(ePlayer, true); + if (SkipForTeammates()) + return; - // Flag all of their cities as ignored for future bickering - int iCityLoop = 0; - for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) - { - pLoopCity->SetIgnoredForExpansionBickering(GetID(), true); - } - } + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // Broken Expansion Promise - else if(eStatement == DIPLO_STATEMENT_EXPANSION_BROKEN_PROMISE) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXPANSION_BROKEN_PROMISE); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } + // No compliments if we're acting hostile towards them + if (IsActHostileTowardsHuman(ePlayer)) + return; - // Serious Plot Buying warning - else if(eStatement == DIPLO_STATEMENT_PLOT_BUYING_SERIOUS_WARNING) + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY) >= 50 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE) >= 10) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_SERIOUS_WARNING); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_SERIOUS_WARNING, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } + CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + PlayerTypes eBestPlayer = NO_PLAYER; + int iLowestTurns = INT_MAX; + int iOpinionTiebreaker = 0; - // Plot Buying warning - else if(eStatement == DIPLO_STATEMENT_PLOT_BUYING_WARNING) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_WARNING); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE); - } - else + // Loop through all players until we find one that we just denounced, that ePlayer has denounced + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetBoldness() > 8) + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (!GET_PLAYER(eLoopPlayer).isAlive()) + continue; + + if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) + continue; + + // We must have both denounced them + if (!IsDenouncedPlayer(eLoopPlayer) || !pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) + continue; + + int iTurnsSinceTrigger = GetTurnsSinceDenouncedPlayer(eLoopPlayer); + + if (GC.getGame().getElapsedGameTurns() <= iTurnsSinceTrigger) + continue; + + // Too much time has passed + if (iTurnsSinceTrigger >= 3) + continue; + + // We must have denounced them after they did + int iTheirTurns = pTheirDiploAI->GetTurnsSinceDenouncedPlayer(eLoopPlayer); + if (iTheirTurns < iTurnsSinceTrigger || (iTheirTurns == iTurnsSinceTrigger && GetID() < ePlayer)) + continue; + + // We want the most recent candidate + if (iTurnsSinceTrigger < iLowestTurns) { - SetBorderPromiseState(ePlayer, PROMISE_STATE_IGNORED); + iLowestTurns = iTurnsSinceTrigger; + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } - else + else if (iTurnsSinceTrigger == iLowestTurns && GetCachedOpinionWeight(eLoopPlayer) > iOpinionTiebreaker) { - SetBorderPromiseState(ePlayer, PROMISE_STATE_MADE); + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } } - } + if (eBestPlayer != NO_PLAYER) + { + int iChance = 4 + (2 * GetChattiness()) + (GetChattiness() / 3); // ranges from 6-27% - // Broken Plot Buying Promise - else if(eStatement == DIPLO_STATEMENT_PLOT_BUYING_BROKEN_PROMISE) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_BROKEN_PROMISE); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } - - // We attacked a Minor someone has a PtP with - else if(eStatement == DIPLO_STATEMENT_WE_ATTACKED_YOUR_MINOR) - { - if(bHuman) - { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - ASSERT(eMinorCiv != NO_PLAYER); - if(eMinorCiv != NO_PLAYER) + // Add a modest bonus if we like them or they're stronger + int iBonus = 0; + CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); + switch (eOpinion) { - const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); - if(IsActHostileTowardsHuman(ePlayer)) - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_WE_ATTACKED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); - else - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_ATTACKED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); + case CIV_OPINION_ALLY: + iBonus += 6; + break; + case CIV_OPINION_FRIEND: + iBonus += 4; + break; + case CIV_OPINION_FAVORABLE: + iBonus += 2; + break; + default: + break; + } - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_I_ATTACKED_YOUR_MINOR_CIV, szText, LEADERHEAD_ANIM_POSITIVE, eMinorCiv); + if (IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer) || GetCivApproach(ePlayer) == CIV_APPROACH_FRIENDLY) + iBonus += 2; - // Extra flag, since diplo log does not save which minor civ the message was about - SetSentAttackProtectedMinorTaunt(ePlayer, eMinorCiv, true); - } - } - else - { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - ASSERT(eMinorCiv != NO_PLAYER); - if(eMinorCiv != NO_PLAYER) - { - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_FRIENDLY || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMinorCivCompetitiveness() > 6) - { - SetOtherPlayerSidedWithProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); + if (GetMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE) + iBonus += 2; - bool bValid = false; - if (GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - { - if (GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x15c58904).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) < GetWarmongerHate()) - { - bValid = true; - } - } - if (bValid && (GetBoldness() > 6 || GetMeanness() > 6)) - { - if (m_pPlayer->GetDiplomacyAI()->DeclareWar(ePlayer)) - { - pDeal->ClearItems(); - bool bCareful = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true) > 0 && GetGlobalCoopWarAgainstState(ePlayer) < COOP_WAR_STATE_PREPARING; - GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer, 3, bCareful); - } - } - } - else - { - GC.getGame().DoMinorPledgeProtection(ePlayer, eMinorCiv, false, true); // Pledge is broken! - } - } - } - } + iChance += iBonus; + if (iChance > 30) + iChance = 30; - // We bullied a Minor someone has a PtP with - else if (eStatement == DIPLO_STATEMENT_WE_BULLIED_YOUR_MINOR) - { - if(bHuman) - { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - ASSERT(eMinorCiv != NO_PLAYER); - if (eMinorCiv != NO_PLAYER) + if (iLowestTurns == 0) { - const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); - if(IsActHostileTowardsHuman(ePlayer)) - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_WE_BULLIED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); - else - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_BULLIED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); - - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_I_BULLIED_YOUR_MINOR_CIV, szText, LEADERHEAD_ANIM_POSITIVE, eMinorCiv); + iChance *= 2; } - } - else - { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - ASSERT(eMinorCiv != NO_PLAYER); - if(eMinorCiv != NO_PLAYER) + else if (iLowestTurns > 1) { - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_FRIENDLY || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMinorCivCompetitiveness() > 6) - { - SetOtherPlayerSidedWithProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); - - bool bValid = false; - if (GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - { - if (GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x1da72213).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) < GetWarmongerHate()) - { - bValid = true; - } - } - if (bValid && (GetBoldness() > 7 || GetMeanness() > 7)) - { - if (m_pPlayer->GetDiplomacyAI()->DeclareWar(ePlayer)) - { - pDeal->ClearItems(); - GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer,3); - } - } - } - else - { - GC.getGame().DoMinorPledgeProtection(ePlayer, eMinorCiv, false, true); // Pledge is broken! - } + iChance += 1; + iChance /= 2; } - } - } - // We'd like a defense pact - else if(eStatement == DIPLO_STATEMENT_DEFENSIVE_PACT_REQUEST) - { - // Active human - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEFENSE_PACT_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player - else - { - if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else + // We're chatty enough to say something! + if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0xea12329a).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChance) { - CvDeal kDeal = *pDeal; - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + eStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY; + iData1 = eBestPlayer; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE, GC.getGame().getGameTurn()); } } } - // We'd like a 3rd party war - else if(eStatement == DIPLO_STATEMENT_THIRDPARTY_WAR_REQUEST) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_THIRDPARTY_WAR_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player - else - { - if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else - { - CvDeal kDeal = *pDeal; +} - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } - } - } - // We'd like a peace trade - else if(eStatement == DIPLO_STATEMENT_THIRDPARTY_PEACE_REQUEST) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_THIRDPARTY_PEACE_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player - else - { - if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else - { - CvDeal kDeal = *pDeal; +/// Possible Contact Statement - We're happy they befriended one of our friends +void CvDiplomacyAI::DoHappyBefriendedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } - } - } - // We'd like a vote trade - else if(eStatement == DIPLO_STATEMENT_VOTE_REQUEST) + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // No compliments if we're acting hostile towards them + if (IsActHostileTowardsHuman(ePlayer)) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND) >= 50 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE) >= 10) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VOTE_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player - else + CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + PlayerTypes eBestPlayer = NO_PLAYER; + int iLowestTurns = INT_MAX; + int iOpinionTiebreaker = 0; + + // Loop through all players until we find one that is our friend, that ePlayer DoFed + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + if (!GET_PLAYER(eLoopPlayer).isAlive()) + continue; + + if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) + continue; + + // We must have both befriended this guy + if (!IsDoFAccepted(eLoopPlayer) || !pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) + continue; + + // If we've both been friends with this guy multiple times with no issues in the interim, let's not repeat ourselves + // ...unless we've never sent the statement before + if (GetDoFType(eLoopPlayer) > DOF_TYPE_FRIENDS && pTheirDiploAI->GetDoFType(eLoopPlayer) > DOF_TYPE_FRIENDS && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND) < INT_MAX) + continue; + + int iTurnsSinceTrigger = pTheirDiploAI->GetTurnsSinceBefriendedPlayer(eLoopPlayer); + + if (GC.getGame().getElapsedGameTurns() <= iTurnsSinceTrigger) + continue; + + // Too much time has passed + if (iTurnsSinceTrigger >= 3) + continue; + + // They must have befriended them after we did + int iOurTurns = GetTurnsSinceBefriendedPlayer(eLoopPlayer); + if (iOurTurns < iTurnsSinceTrigger || (iOurTurns == iTurnsSinceTrigger && GetID() > ePlayer)) + continue; + + // We want the most recent candidate + if (iTurnsSinceTrigger < iLowestTurns) { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + iLowestTurns = iTurnsSinceTrigger; + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } - else + else if (iTurnsSinceTrigger == iLowestTurns && GetCachedOpinionWeight(eLoopPlayer) < iOpinionTiebreaker) { - CvDeal kDeal = *pDeal; - - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } } - } - - // We'd like to trade cities - else if(eStatement == DIPLO_STATEMENT_TRADE_CITIES_REQUEST) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_TRADE_CITIES_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player - else + if (eBestPlayer != NO_PLAYER) { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + int iChance = 4 + (2 * GetChattiness()) + (GetChattiness() / 3); // ranges from 6-27% + if (iLowestTurns == 0) { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + iChance *= 2; } - else + else if (iLowestTurns > 1) { - CvDeal kDeal = *pDeal; + iChance += 1; + iChance /= 2; + } - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + // We're chatty enough to say something! + if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x2b7c6537).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChance) + { + eStatement = DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND; + iData1 = eBestPlayer; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE, GC.getGame().getGameTurn()); } } } - // We'd like to exchange cities - else if(eStatement == DIPLO_STATEMENT_EXCHANGE_CITIES) +} + +/// Possible Contact Statement - We befriended one of the human's friends, and we're letting them know +void CvDiplomacyAI::DoFYIBefriendedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // No compliments if we're acting hostile towards them + if (IsActHostileTowardsHuman(ePlayer)) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND) >= 50 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE) >= 10) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXCHANGE_CITIES_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player - else + CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + PlayerTypes eBestPlayer = NO_PLAYER; + int iLowestTurns = INT_MAX; + int iOpinionTiebreaker = 0; + + // Loop through all players until we find one that we just befriended, that ePlayer has befriended + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (!GET_PLAYER(eLoopPlayer).isAlive()) + continue; + + if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) + continue; + + // We must have both befriended this guy + if (!IsDoFAccepted(eLoopPlayer) || !pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) + continue; + + // If we've both been friends with this guy multiple times with no issues in the interim, let's not repeat ourselves + // ...unless we've never sent the statement before + if (GetDoFType(eLoopPlayer) > DOF_TYPE_FRIENDS && pTheirDiploAI->GetDoFType(eLoopPlayer) > DOF_TYPE_FRIENDS && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND) < INT_MAX) + continue; + + int iTurnsSinceTrigger = GetTurnsSinceBefriendedPlayer(eLoopPlayer); + + if (GC.getGame().getElapsedGameTurns() <= iTurnsSinceTrigger) + continue; + + // Too much time has passed + if (iTurnsSinceTrigger >= 3) + continue; + + // We must have befriended them after they did + int iTheirTurns = pTheirDiploAI->GetTurnsSinceBefriendedPlayer(eLoopPlayer); + if (iTheirTurns < iTurnsSinceTrigger || (iTheirTurns == iTurnsSinceTrigger && GetID() < ePlayer)) + continue; + + // We want the most recent candidate + if (iTurnsSinceTrigger < iLowestTurns) { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + iLowestTurns = iTurnsSinceTrigger; + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } - else + else if (iTurnsSinceTrigger == iLowestTurns && GetCachedOpinionWeight(eLoopPlayer) < iOpinionTiebreaker) { - CvDeal kDeal = *pDeal; - - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + eBestPlayer = eLoopPlayer; + iOpinionTiebreaker = GetCachedOpinionWeight(eLoopPlayer); } } - } - - // We'd like to work with a player - else if (eStatement == DIPLO_STATEMENT_WORK_WITH_US || eStatement == DIPLO_STATEMENT_DOF_BB || eStatement == DIPLO_STATEMENT_DOF_ALLIES || eStatement == DIPLO_STATEMENT_DOF_FRIENDS || eStatement == DIPLO_STATEMENT_DOF_UNTRUSTWORTHY) - { - // Send message to human - if (bHuman) + if (eBestPlayer != NO_PLAYER) { - switch (eStatement) + int iChance = 4 + (2 * GetChattiness()) + (GetChattiness() / 3); // ranges from 6-27% + + // Add a modest bonus if we like them + int iBonus = 0; + CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); + switch (eOpinion) { - case DIPLO_STATEMENT_DOF_BB: - szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_BATTLE_BROTHERS); - break; - case DIPLO_STATEMENT_DOF_ALLIES: - szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_OLD_FRIENDS); + case CIV_OPINION_ALLY: + iBonus += 6; break; - case DIPLO_STATEMENT_DOF_FRIENDS: - szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_FRIENDS); + case CIV_OPINION_FRIEND: + iBonus += 4; break; - case DIPLO_STATEMENT_DOF_UNTRUSTWORTHY: - szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_UNTRUSTWORTHY); + case CIV_OPINION_FAVORABLE: + iBonus += 2; break; default: - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_WITH_US); break; } - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_WORK_WITH_US, szText, LEADERHEAD_ANIM_REQUEST); - } - // AI resolution - // Accept - reject is assumed from the counter - else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAcceptable(GetID())) - { - SetDoFAccepted(ePlayer, true); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(GetID(), true); - - // Update diplomacy stuff - DoReevaluatePlayer(ePlayer, false, false); - GET_PLAYER(ePlayer).GetDiplomacyAI()->DoReevaluatePlayer(GetID(), false, false); + if (IsDoFAccepted(ePlayer) || IsHasDefensivePact(ePlayer) || GetCivApproach(ePlayer) == CIV_APPROACH_FRIENDLY) + iBonus += 2; - LogDoF(ePlayer); - } - } + iChance += iBonus; + if (iChance > 30) + iChance = 30; - // We no longer want to work with a player - else if(eStatement == DIPLO_STATEMENT_END_WORK_WITH_US) - { - PlayerTypes eMyPlayer = GetID(); - - SetDoFAccepted(ePlayer, false); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, false); - - // End all coop war agreements with this player - GET_PLAYER(ePlayer).GetDiplomacyAI()->CancelCoopWarsWithPlayer(eMyPlayer, true); - - // End any Defensive Pact - GET_TEAM(GET_PLAYER(eMyPlayer).getTeam()).SetHasDefensivePact(GET_PLAYER(ePlayer).getTeam(), false); - GET_TEAM(GET_PLAYER(ePlayer).getTeam()).SetHasDefensivePact(GET_PLAYER(eMyPlayer).getTeam(), false); - - SetDoFType(ePlayer, DOF_TYPE_UNTRUSTWORTHY); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(eMyPlayer, DOF_TYPE_UNTRUSTWORTHY); - GET_PLAYER(ePlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -300); - - // Other players' reactions - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + if (iLowestTurns == 0) + { + iChance *= 2; + } + else if (iLowestTurns > 1) + { + iChance += 1; + iChance /= 2; + } - if (IsPlayerValid(eLoopPlayer, true) && !GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && eLoopPlayer != ePlayer && eLoopPlayer != eMyPlayer) + // We're chatty enough to say something! + if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x5eec20f2).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChance) { - // Our teammates - if (IsTeammate(eLoopPlayer)) - { - if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer) || GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(ePlayer)) - { - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 300); - } - } - else if (GET_PLAYER(eLoopPlayer).getTeam() != GET_PLAYER(ePlayer).getTeam()) - { - // Player might apply a diplo bonus if they don't hate us - if (!GET_TEAM(GetTeam()).isAtWar(GET_PLAYER(eLoopPlayer).getTeam()) && !IsDenouncedPlayer(eLoopPlayer) && - !GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDenouncedPlayer(eMyPlayer) && !GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(eMyPlayer) && - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(eMyPlayer) != CIV_OPINION_UNFORGIVABLE) - { - // Bonus for ending a DoF with a backstabber - if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(ePlayer)) - { - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 75); - } - // Bonus for ending a DoF with a player they're at war with - else if (GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam())) - { - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 50); - - // Extra bonus if they're doing badly in the war - if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetWarState(ePlayer) <= WAR_STATE_TROUBLED) - { - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 25); - } - } - // Bonus for ending a DoF with a player who denounced them - else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer)) - { - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 50); - } - // Penalty for ending a DoF with a friend, DP or ally - else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(ePlayer) || - GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).IsHasDefensivePact(GET_PLAYER(ePlayer).getTeam()) || - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) - { - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -75); - } - } - // Penalty for ending a DoF with a friend, DP or ally (they hate us) - else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(ePlayer) || - GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).IsHasDefensivePact(GET_PLAYER(ePlayer).getTeam()) || - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) - { - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -150); - } - } - // Their teammates - else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsTeammate(ePlayer)) - { - GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -300); - } + eStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND; + iData1 = eBestPlayer; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE, GC.getGame().getGameTurn()); } } + } +} - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFBroken(eMyPlayer, true, false); - LogBrokenDoF(ePlayer); +/// Possible Contact Statement - The human helped our proposal fail in the World Congress +void CvDiplomacyAI::DoTheyFoiledOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Send message to human - if (bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_END_WORK_WITH_US, ePlayer); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } + if (SkipForTeammates()) + return; - // Denounce - else if(eStatement == DIPLO_STATEMENT_DENOUNCE) - { - DoDenouncePlayer(ePlayer); - LogDenounce(ePlayer); + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // Send message to human - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_AGAINST_SOMEONE, ePlayer); + // Haven't foiled a proposal recently? + int iTurn = GetTheyFoiledOurProposalTurn(ePlayer); + if (iTurn == -1 || (GC.getGame().getGameTurn() - iTurn) > 10) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; - // Denounce Friend (backstab) - else if(eStatement == DIPLO_STATEMENT_DENOUNCE_FRIEND) - { - DoDenouncePlayer(ePlayer); - LogDenounce(ePlayer, /*bBackstab*/ true); + // Don't send this if we're acting friendly or afraid towards them + if (GetSurfaceApproach(ePlayer) == CIV_APPROACH_FRIENDLY || GetSurfaceApproach(ePlayer) == CIV_APPROACH_AFRAID) + return; - // Send message to human - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_AI_DOF_BACKSTAB, ePlayer); + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_THEY_FOILED_OUR_PROPOSAL) >= 50) + { + eStatement = DIPLO_STATEMENT_THEY_FOILED_OUR_PROPOSAL; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); // triggers the throttle, but isn't blocked by it + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); } +} - // Request Friend Denounce Someone - else if(eStatement == DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE) - { - PlayerTypes eTarget = (PlayerTypes) iData1; - ASSERT(eTarget != NO_PLAYER); - if(eTarget != NO_PLAYER) - { - const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); +/// Possible Contact Statement - The human helped relocate the World Congress to our lands +void CvDiplomacyAI::DoTheySupportedOurHosting(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Send message to human - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_AI_DENOUNCE_REQUEST, ePlayer, strTargetCivKey); + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_AI_REQUEST_DENOUNCE, szText, LEADERHEAD_ANIM_POSITIVE, eTarget); - } - else - { - bool bAgree = IsDenounceAcceptable(eTarget, /*bBias*/ true); + // Haven't supported our hosting recently? + int iTurn = GetTheySupportedOurHostingTurn(ePlayer); + if (iTurn == -1 || (GC.getGame().getGameTurn() - iTurn) > 20) + return; - LogFriendRequestDenounce(ePlayer, eTarget, bAgree); + // It'd be strange to send this if sanctions have been proposed between us + if (HasEverSanctionedUs(ePlayer) || CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer, true)) + return; - if(bAgree) - { - GET_PLAYER(ePlayer).GetDiplomacyAI()->DoDenouncePlayer(eTarget); - GET_PLAYER(ePlayer).GetDiplomacyAI()->LogDenounce(eTarget); + // No compliments if we're acting hostile towards them + if (IsActHostileTowardsHuman(ePlayer)) + return; - // Denounced a human? - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - if(GET_PLAYER(eTarget).isHuman(ISHUMAN_AI_DIPLOMACY)) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_AGAINST_SOMEONE, eTarget); - CvDiplomacyRequests::SendRequest(ePlayer, eTarget, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } - else - { - if(eTarget == GC.getGame().getActivePlayer()) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_AGAINST_SOMEONE, eTarget); - CvDiplomacyRequests::SendRequest(ePlayer, eTarget, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } - } - else - { - // Oh, you're gonna say no, are you? - if(IsFriendDenounceRefusalUnacceptable(ePlayer, eTarget)) - { - DoDenouncePlayer(ePlayer); - LogDenounce(ePlayer, /*bBackstab*/ false, /*bRefusal*/ true); - } - } - } - } + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_THEY_SUPPORTED_OUR_HOSTING) >= 50) + { + eStatement = DIPLO_STATEMENT_THEY_SUPPORTED_OUR_HOSTING; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE); // triggers the throttle, but isn't blocked by it + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE, GC.getGame().getGameTurn()); } +} - // We'd like to declare war on someone - else if (eStatement == DIPLO_STATEMENT_COOP_WAR_REQUEST) +/// Possible Contact Statement - The human helped our proposal pass in the World Congress +void CvDiplomacyAI::DoTheySupportedOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Haven't passed a proposal recently? + int iTurn = GetTheySupportedOurProposalTurn(ePlayer); + if (iTurn == -1 || (GC.getGame().getGameTurn() - iTurn) > 10) + return; + + // It'd be strange to send this if sanctions have been proposed between us + if (HasEverSanctionedUs(ePlayer) || CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer, true)) + return; + + // No compliments if we're acting hostile towards them + if (IsActHostileTowardsHuman(ePlayer)) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_THEY_SUPPORTED_OUR_PROPOSAL) >= 50) { - PlayerTypes eAgainstPlayer = (PlayerTypes) iData1; - ASSERT(IsPlayerValid(eAgainstPlayer)); - if (IsPlayerValid(eAgainstPlayer)) - { - // Send message to human - if (bHuman) - { - const char* strAgainstPlayerKey = GET_PLAYER(eAgainstPlayer).getNameKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_REQUEST, ePlayer, strAgainstPlayerKey); + eStatement = DIPLO_STATEMENT_THEY_SUPPORTED_OUR_PROPOSAL; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE); // triggers the throttle, but isn't blocked by it + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE, GC.getGame().getGameTurn()); + } +} - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_COOP_WAR, szText, LEADERHEAD_ANIM_POSITIVE, eAgainstPlayer); - } - // AI resolution - else - { - CoopWarStates eResponse = GET_PLAYER(ePlayer).GetDiplomacyAI()->RespondToCoopWarRequest(GetID(), eAgainstPlayer); +/// Possible Contact Statement - We liked the human's proposal to the World Congress +void CvDiplomacyAI::DoWeLikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if (eResponse == COOP_WAR_STATE_REJECTED) - { - int iAssistPenalty = AdjustConditionalModifier(-100, GetNeediness()); - ChangeRecentAssistValue(ePlayer, iAssistPenalty); - ChangeCoopWarAgreementScore(ePlayer, -1); - } - } + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Must have liked their most recent proposal + if (!WeLikedTheirProposal(ePlayer)) + return; + + // It'd be strange to send this if sanctions have been proposed between us + if (HasEverSanctionedUs(ePlayer) || CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(GetID(), ePlayer, true)) + return; + + bool bUnsanctioningUsNow = false; + CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); + ASSERT(pLeague, "AI likes someone's recent proposal but the World Congress isn't active?"); + if (!pLeague) + return; + + for (RepealProposalList::iterator it = pLeague->m_vRepealProposals.begin(); it != pLeague->m_vRepealProposals.end(); ++it) + { + PlayerTypes eProposer = it->GetProposalPlayer(); + if (eProposer == ePlayer && GetPlayer()->GetLeagueAI()->IsSanctionProposal(&(*it), GetID())) + { + bUnsanctioningUsNow = true; + break; } } - /* - // We'd like to declare war on someone - else if(eStatement == DIPLO_STATEMENT_COOP_WAR_TIME) + if (!bUnsanctioningUsNow) { - PlayerTypes eAgainstPlayer = (PlayerTypes) iData1; - ASSERT(eAgainstPlayer != NO_PLAYER); - if(eAgainstPlayer != NO_PLAYER) + // Don't speak up if it's only a weak like + if (GetProposalAgreementValue(ePlayer) > /*-30*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL)) + return; + + // Sent another compliment too recently? + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE) < 10) + return; + + // No compliments if we're acting hostile towards them + if (IsActHostileTowardsHuman(ePlayer)) + return; + + // We don't want to spam the player with this statement from everyone at once, so check to see if anyone else has sent this very recently + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - // Send message to human - if(bHuman) + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + if (eLoopPlayer != GetID() && eLoopPlayer != ePlayer) { - const char* strAgainstPlayerKey = GET_PLAYER(eAgainstPlayer).getNameKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_TIME, ePlayer, strAgainstPlayerKey); - - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_COOP_WAR_TIME, szText, LEADERHEAD_ANIM_POSITIVE, eAgainstPlayer); + int iTurnsSinceSent = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL); + if (iTurnsSinceSent < 3) + return; } } - - // No AI resolution! This is handled automatically in DoCounters() - no need for diplo exchange } - */ - // We're making a demand of this player - else if (eStatement == DIPLO_STATEMENT_DEMAND) - { - // Active human - if (bHuman) - { - // Assume they accepted - if they refuse, we'll reset this to 0 in FROM_UI_DIPLO_EVENT_DEMAND_HUMAN_REFUSAL - ChangeNumConsecutiveDemandsTheyAccepted(ePlayer, 1); + // We want this message to only pop up within the first x% of a session window after the proposal was made. + // But session windows will decrease as time goes on (or a modder could make them increase). So we need to calculate how long it's been since the last session. + int iLikedTheirProposalTurn = GetWeLikedTheirProposalTurn(ePlayer); + int iTurnLastSessionEnded = pLeague->GetLastSessionEndedTurn(); + int iEligibleWindow = max(pLeague->GetSessionTurnInterval() * 40 / 100, 1); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEMAND); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_DEMAND, szText, LEADERHEAD_ANIM_DEMAND); - } - // AI player - else - { - // Process diplomacy consequences from the AI on the receiving end - DemandResponseTypes eResponse = GET_PLAYER(ePlayer).GetDealAI()->GetDemandResponse(pDeal); - GET_PLAYER(ePlayer).GetDiplomacyAI()->DoDemandMade(GetID(), eResponse); + // Don't carry over from old sessions + if (iLikedTheirProposalTurn < iTurnLastSessionEnded) + return; - // Did they accept? - if (eResponse == DEMAND_RESPONSE_ACCEPT) - { - if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else - { - CvDeal kDeal = *pDeal; + // Too much time has passed? + int iTurnsSinceTrigger = GC.getGame().getGameTurn() - iLikedTheirProposalTurn; + if (iTurnsSinceTrigger > iEligibleWindow) + return; - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } - } - // Demand rebuffed - else - { - pDeal->ClearItems(); + // Have we commented on this specific proposal already? + int iTurnsSinceStatementSent = GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL); + if (iTurnsSinceStatementSent <= iTurnsSinceTrigger) + return; - // Does the AI declare war? - bool bDeclareWar = GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID()) && !GetPlayer()->IsNoNewWars(); + // If they proposed to unsanction us, 100% chance we send the statement. + if (bUnsanctioningUsNow) + { + eStatement = DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE, GC.getGame().getGameTurn()); + return; + } - // Must be a potential war target - if (bDeclareWar && !IsPotentialWarTarget(ePlayer)) - bDeclareWar = false; + // Commented on any proposal we liked too recently? + if (iTurnsSinceStatementSent <= 50) + return; - // Sanity check - who else would we go to war with? - if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(ePlayer, true)) - bDeclareWar = false; - - // Sanity check - avoid going bankrupt - int iMinIncome = 2 + (GetPlayer()->GetCurrentEra() * 2); - if (bDeclareWar && IsWarWouldBankruptUs(ePlayer, iMinIncome)) - bDeclareWar = false; + // Are we chatty enough? + int iChance = CvDiplomacyAIHelpers::GetProposalAgreementTalkativeness(GetID(), ePlayer, true, iTurnsSinceTrigger, iEligibleWindow); + if (GC.getGame().randRangeInclusive(1, 1000, CvSeeder::fromRaw(0x4ce615ad).mix(GetID()).mix(ePlayer)) <= iChance) + { + eStatement = DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE, GC.getGame().getGameTurn()); + } +} - // Sanity check - who else would we go to war with? - if (bDeclareWar) - { - vector vDefensiveWarAllies = GetDefensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); +/// Possible Contact Statement - We disliked the human's proposal to the World Congress +void CvDiplomacyAI::DoWeDislikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) - { - // Would we be declaring war on a powerful neighbor? - if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) - { - if (GET_PLAYER(*it).isMajorCiv()) - { - if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) - { - bDeclareWar = false; - break; - } - // If we're already planning a war/demand against them, then we don't care. - else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) - { - if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) - { - bDeclareWar = false; - break; - } - } - } - else - { - if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE) - { - if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) - { - bDeclareWar = false; - break; - } - } - } - } - } - } + if (SkipForTeammates()) + return; - if (bDeclareWar) - { - // If already at war or non-HOSTILE and not decisively stronger, AI has a chance to bluff - int iCurrentWars = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true); - bool bCanBluff = iCurrentWars > 0 || (GetCivApproach(ePlayer) > CIV_APPROACH_HOSTILE && GetTargetValue(ePlayer) <= TARGET_VALUE_AVERAGE); - if (bCanBluff) - { - if (GetCivOpinion(ePlayer) == CIV_OPINION_UNFORGIVABLE || IsEndgameAggressiveTo(ePlayer) || IsCapitalCapturedBy(ePlayer, true, true) || IsHolyCityCapturedBy(ePlayer, true, true)) - { - bCanBluff = false; - } - } - if (bCanBluff) - { - int iBluffChance = 20 + 20 * min(iCurrentWars, 3); - if (GetBiggestCompetitor() == ePlayer) - iBluffChance /= 2; + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x40f558f0).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) > iBluffChance) - bCanBluff = false; - } - // Not bluffing - declare war! - if (!bCanBluff && DeclareWar(ePlayer)) - { - bool bCareful = iCurrentWars > 0 && GetGlobalCoopWarAgainstState(ePlayer) < COOP_WAR_STATE_PREPARING; - GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer, 3, bCareful); - } - } - } - } - } + // Must have disliked their most recent proposal + if (!WeDislikedTheirProposal(ePlayer)) + return; - // We're making a request of this player - else if(eStatement == DIPLO_STATEMENT_REQUEST) + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; + + bool bSanctioningUsNow = false; + CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); + ASSERT(pLeague, "AI dislikes someone's recent proposal but the World Congress isn't active?"); + if (!pLeague) + return; + + for (EnactProposalList::iterator it = pLeague->m_vEnactProposals.begin(); it != pLeague->m_vEnactProposals.end(); ++it) { - // Active human - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_REQUEST); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_REQUEST, szText, LEADERHEAD_ANIM_REQUEST); - } - // AI player - else + PlayerTypes eProposer = it->GetProposalPlayer(); + if (eProposer == ePlayer && GetPlayer()->GetLeagueAI()->IsSanctionProposal(&(*it), GetID())) { - // For now the AI will always give in - may eventually write additional logic here - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else - { - CvDeal kDeal = *pDeal; - - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } + bSanctioningUsNow = true; + break; } } - // Player has a Luxury Resource we'd like to trade for - else if(eStatement == DIPLO_STATEMENT_LUXURY_TRADE) + if (!bSanctioningUsNow) { - // Active human - if(bHuman) + // Don't speak up if it's only a weak dislike + if (GetProposalAgreementValue(ePlayer) < /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)) + return; + + // Also don't speak up for a regular dislike if they've ever unsanctioned us, or have recently tried to + if (GetProposalAgreementValue(ePlayer) < /*45*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_STRONG) && + (HasEverUnsanctionedUs(ePlayer) || HasTriedToUnsanctionUs(ePlayer))) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_LUXURY_TRADE); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + return; } - // Offer to an AI player - else + + // Sent another insult too recently? + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE) < 10) + return; + + // Don't send this if we're acting friendly or afraid towards them + if (GetSurfaceApproach(ePlayer) == CIV_APPROACH_FRIENDLY || GetSurfaceApproach(ePlayer) == CIV_APPROACH_AFRAID) + return; + + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; + + // We don't want to spam the player with this statement from everyone at once, so check to see if anyone else has sent this very recently + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + if (eLoopPlayer != GetID() && eLoopPlayer != ePlayer) { - CvDeal kDeal = *pDeal; - - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + int iTurnsSinceSent = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL); + if (iTurnsSinceSent < 3) + return; } } } - // We'd like to exchange embassies with this player - else if(eStatement == DIPLO_STATEMENT_EMBASSY_EXCHANGE) + // We want this message to only pop up within the first x% of a session window after the proposal was made. + // But session windows will decrease as time goes on (or a modder could make them increase). So we need to calculate how long it's been since the last session. + int iDislikedTheirProposalTurn = GetWeDislikedTheirProposalTurn(ePlayer); + int iTurnLastSessionEnded = pLeague->GetLastSessionEndedTurn(); + int iEligibleWindow = max(pLeague->GetSessionTurnInterval() * 40 / 100, 1); + + // Don't carry over from old sessions + if (iDislikedTheirProposalTurn < iTurnLastSessionEnded) + return; + + // Too much time has passed? + int iTurnsSinceTrigger = GC.getGame().getGameTurn() - iDislikedTheirProposalTurn; + if (iTurnsSinceTrigger > iEligibleWindow) + return; + + // Have we commented on this specific proposal already? + int iTurnsSinceStatementSent = GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL); + if (iTurnsSinceStatementSent <= iTurnsSinceTrigger) + return; + + // If they proposed to sanction us, 100% chance we send the statement. + if (bSanctioningUsNow) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_EMBASSY_EXCHANGE); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - else - { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else - { - CvDeal kDeal = *pDeal; + eStatement = DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); + return; + } - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } - } + // Commented on any proposal we disliked too recently? + if (iTurnsSinceStatementSent <= 50) + return; + + // Are we chatty enough? + int iChance = CvDiplomacyAIHelpers::GetProposalAgreementTalkativeness(GetID(), ePlayer, false, iTurnsSinceTrigger, iEligibleWindow); + if (GC.getGame().randRangeInclusive(1, 1000, CvSeeder::fromRaw(0x9b0e7357).mix(GetID()).mix(ePlayer)) <= iChance) + { + eStatement = DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); } +} - // We want an embassy in this player's capital - else if(eStatement == DIPLO_STATEMENT_EMBASSY_OFFER) +/// Possible Contact Statement - Request Help +void CvDiplomacyAI::DoRequest(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_HELP_REQUESTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // If we just sent out a generous offer, don't ask for a request until some time has passed + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER) < 25) + return; + + // If a request was accepted or rejected, wait 60 turns. If we rolled for rand and failed, wait 15 turns before we try again + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST) >= 60 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST_RANDFAILED) >= 15) { - if(bHuman) + bool bRandPassed = false; // This is used to see if we WOULD have made a request, but the rand roll failed (so add an entry to the log) + bool bMakeRequest = IsMakeRequest(ePlayer, pDeal, bRandPassed); + + // Want to make a request of ePlayer? Pass pDeal in to see if there's actually anything we want + if (bMakeRequest) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_EMBASSY_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + eStatement = DIPLO_STATEMENT_REQUEST; + pDeal->SetRequestingPlayer(GetID()); + pDeal->m_bIsGift = true; } + // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at else - { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else - { - CvDeal kDeal = *pDeal; + pDeal->ClearItems(); - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } + // Add this statement to the log so we don't evaluate it again until 15 turns has come back around + if (!bRandPassed) + { + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_REQUEST_RANDFAILED, GC.getGame().getGameTurn()); + pDeal->ClearItems(); } } +} - // We'd like mutual Open Borders with this player - else if(eStatement == DIPLO_STATEMENT_OPEN_BORDERS_EXCHANGE) +/// Possible Contact Statement - Third-party offer for ePlayer to liberate their vassals +void CvDiplomacyAI::DoRevokeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (IsAvoidDeals()) + return; + + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Performance improvement - only do the below iteration if ePlayer actually has vassals + if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetNumVassals() <= 0) + return; + + bool bWorthIt = false; + TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); + for (int iTeamLoop = 0; iTeamLoop < MAX_TEAMS; iTeamLoop++) { - // Active human - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_OPEN_BORDERS_EXCHANGE); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player - else + TeamTypes eLoopTeam = (TeamTypes) iTeamLoop; + if (!GET_TEAM(eLoopTeam).isAlive() || GET_TEAM(eLoopTeam).isMinorCiv() || GET_TEAM(eLoopTeam).isBarbarian()) + continue; + + if (!GET_TEAM(eLoopTeam).IsVassal(eTeam)) + continue; + + const vector& vVassalTeam = GET_TEAM(eLoopTeam).getPlayers(); + for (size_t iPlayerLoop = 0; iPlayerLoop < vVassalTeam.size(); ++iPlayerLoop) { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + PlayerTypes eVassalPlayer = vVassalTeam[iPlayerLoop]; + + // Do we hate someone on this team? Then we won't free any teams! + if (GetCivOpinion(eVassalPlayer) <= CIV_OPINION_ENEMY || IsDenouncedPlayer(eVassalPlayer) || IsDenouncedByPlayer(eVassalPlayer)) + return; + + // Now check if we actually care about this player. + // Note: We only need to care for one player on the team to consider freeing the team. + bool bFriends = IsDoFAccepted(eVassalPlayer); + bool bCare = GetCivOpinion(eVassalPlayer) >= CIV_OPINION_FRIEND; + bCare |= WasResurrectedBy(eVassalPlayer); + bCare |= IsMasterLiberatedMeFromVassalage(eVassalPlayer); + bCare |= (GetCivOpinion(eVassalPlayer) >= CIV_OPINION_FAVORABLE && bFriends && GetCivApproach(eVassalPlayer) == CIV_APPROACH_FRIENDLY); + + // If we care about, or are friends with, any one player and their vassalage is voluntary, then we may want to abort. + if ((bCare || bFriends) && GET_PLAYER(eVassalPlayer).GetDiplomacyAI()->IsVoluntaryVassalage(ePlayer)) { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + // If they are being treated OK enough, then abort. + if (GET_PLAYER(eVassalPlayer).GetDiplomacyAI()->GetVassalTreatmentLevel(ePlayer) <= VASSAL_TREATMENT_DISAGREE) + return; } - else - { - CvDeal kDeal = *pDeal; - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } + bWorthIt = bCare; } } + if (!bWorthIt) + return; - // We'd like this player to open their borders to us - else if(eStatement == DIPLO_STATEMENT_OPEN_BORDERS_OFFER) + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REVOKE_VASSALAGE_THIRD_PARTY) >= 50) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_OPEN_BORDERS_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player + if (GetPlayer()->GetDealAI()->IsMakeOfferForRevokeVassalage(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_REVOKE_VASSALAGE_THIRD_PARTY; else - { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else - { - CvDeal kDeal = *pDeal; - - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } - } + pDeal->ClearItems(); } +} - // Offer a Research Agreement - else if(eStatement == DIPLO_STATEMENT_RESEARCH_AGREEMENT_OFFER) +/// Possible Contact Statement - City Exchange +void CvDiplomacyAI::DoCityExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (IsAvoidDeals()) + return; + + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Don't ask to trade cities if approach is worse than NEUTRAL + if (GetCivApproach(ePlayer) < CIV_APPROACH_NEUTRAL) + return; + + if (GetPlayer()->IsAITeammateOfHuman()) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EXCHANGE_CITIES) >= 30) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_RESEARCH_AGREEMENT_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player + if (GetPlayer()->GetDealAI()->IsMakeOfferForCityExchange(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_EXCHANGE_CITIES; else - { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else - { - CvDeal kDeal = *pDeal; - - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } - } + pDeal->ClearItems(); } +} - // Offer to renew an existing trade deal - else if (eStatement == DIPLO_STATEMENT_RENEW_DEAL) - { - if(bHuman) - { - int iDealValueToMe = 0; - DiploMessageTypes eMessageType = NUM_DIPLO_MESSAGE_TYPES; - bool bCantMatchOffer = false; - bool bDealAcceptable = m_pPlayer->GetDealAI()->IsDealWithHumanAcceptable(pDeal, ePlayer, iDealValueToMe, &bCantMatchOffer, false); +/// Possible Contact Statement - Purchase technology +void CvDiplomacyAI::DoTechOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if(bDealAcceptable) - { - eMessageType = DIPLO_MESSAGE_RENEW_DEAL; - } - // We want more from this deal - else - { - eMessageType = DIPLO_MESSAGE_WANT_MORE_RENEW_DEAL; - } + if (SkipForTeammates()) + return; - CvDeal kDeal = *pDeal; - szText = GetDiploStringForMessage(eMessageType); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, &kDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST, /* bRenew */ true); - } - // Offer to an AI player - else - { - CvDeal kDeal = *pDeal; + if (IsAvoidDeals()) + return; - if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(kDeal, true); - } - else - { - // Don't need to call DoOffer because we have already checked that the deal is acceptable for both sides - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - bool bFinalized = GC.getGame().GetGameDeals().FinalizeDeal(kDeal.GetFromPlayer(), kDeal.GetToPlayer(), true); - ASSERT(bFinalized, "Renewal Deal can't be finalized, even though it was checked that the deal is acceptable for both sides.") - } - } - } + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // Our Opinion of them is now Unforgivable - else if(eStatement == DIPLO_STATEMENT_NOW_UNFORGIVABLE) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_NOW_UNFORGIVABLE); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } + if (GetPlayer()->IsAITeammateOfHuman()) + return; - // Our Opinion of them is now Enemy - else if(eStatement == DIPLO_STATEMENT_NOW_ENEMY) + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_TECH_OFFER) >= 30) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_NOW_ENEMY); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } + if (GetPlayer()->GetDealAI()->IsMakeOfferForTech(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_TECH_OFFER; + else + pDeal->ClearItems(); } +} - // They caught one of our spies - else if(eStatement == DIPLO_STATEMENT_CAUGHT_YOUR_SPY) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_CAUGHT_YOUR_SPY); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_CAUGHT_YOUR_SPY, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } +/// Possible Contact Statement - Vote Trade +void CvDiplomacyAI::DoVoteTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // They killed one of our spies - else if(eStatement == DIPLO_STATEMENT_KILLED_YOUR_SPY) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_KILLED_YOUR_SPY); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_KILLED_YOUR_SPY, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } + if (SkipForTeammates()) + return; - // We killed one of their spies - else if(eStatement == DIPLO_STATEMENT_KILLED_MY_SPY) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_KILLED_MY_SPY); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_KILLED_MY_SPY, szText, LEADERHEAD_ANIM_DEFEATED); - } - } + if (IsAvoidDeals()) + return; - // We (the AI) have intrigue information to share with them - else if(eStatement == DIPLO_STATEMENT_SHARE_INTRIGUE) + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_VOTE_REQUEST) >= 10) { - IntrigueNotificationMessage* pNotificationMessage = GetPlayer()->GetEspionage()->GetRecentIntrigueInfo(ePlayer); - ASSERT(pNotificationMessage, "pNotificationMessage is null. Whut?"); - if (pNotificationMessage) - { - PRECONDITION(pNotificationMessage->m_eSourcePlayer != NO_PLAYER, "There is no plotter! What's going on"); - PlayerTypes ePlotterPlayer = pNotificationMessage->m_eSourcePlayer; - CvIntrigueType eIntrigueType = (CvIntrigueType)pNotificationMessage->m_iIntrigueType; - // don't share intrigue about two parties if they are already at war, except for the information that someone has been bribed to go to war - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isAtWar(GET_PLAYER(ePlotterPlayer).getTeam()) || eIntrigueType == INTRIGUE_TYPE_BRIBE_WAR) - { - CvCity* pCity = NULL; - if(pNotificationMessage->m_iCityX != -1 && pNotificationMessage->m_iCityY != -1) - { - CvPlot* pPlot = GC.getMap().plot(pNotificationMessage->m_iCityX, pNotificationMessage->m_iCityY); - if(pPlot) - { - pCity = pPlot->getPlotCity(); - } - } + if (GetPlayer()->GetDealAI()->IsMakeOfferForVote(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_VOTE_REQUEST; + else + pDeal->ClearItems(); + } +} - // add the notification to the player - GET_PLAYER(ePlayer).GetEspionage()->AddIntrigueMessage(GetID(), ePlotterPlayer, ePlayer, pNotificationMessage->m_eDiplomacyPlayer, NO_BUILDING, NO_PROJECT, NO_UNIT, eIntrigueType, 0, pCity, false); +/// Possible Contact Statement - World Map +void CvDiplomacyAI::DoMapsOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - if(bHuman) - { - const char* szPlayerName = NULL; - if(GC.getGame().isGameMultiPlayer() && GET_PLAYER(ePlotterPlayer).isHuman(ISHUMAN_UI)) - { - szPlayerName = GET_PLAYER(ePlotterPlayer).getNickName(); - } - else - { - szPlayerName = GET_PLAYER(ePlotterPlayer).getNameKey(); - } + if (SkipForTeammates()) + return; - const char* szBribedPlayerName = NULL; - szText = ""; + if (IsAvoidDeals()) + return; - switch(eIntrigueType) - { - case INTRIGUE_TYPE_ARMY_SNEAK_ATTACK: - if(pCity) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_KNOWN_CITY, NO_PLAYER, szPlayerName, pCity->getNameKey()); - } - else - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_UNKNOWN_CITY, NO_PLAYER, szPlayerName); - } - break; - case INTRIGUE_TYPE_AMPHIBIOUS_SNEAK_ATTACK: - if(pCity) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_KNOWN_CITY, NO_PLAYER, szPlayerName, pCity->getNameKey()); - } - else - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_UNKNOWN_CITY, NO_PLAYER, szPlayerName); - } - break; - case INTRIGUE_TYPE_DECEPTION: - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE, NO_PLAYER, szPlayerName); - break; - case INTRIGUE_TYPE_BRIBE_WAR: - if (GC.getGame().isGameMultiPlayer() && GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).isHuman(ISHUMAN_UI)) - { - szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNickName(); - } - else - { - szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNameKey(); - } - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_BRIBE_WAR, NO_PLAYER, szPlayerName, szBribedPlayerName); - break; - case INTRIGUE_TYPE_COOP_WAR: - if (GC.getGame().isGameMultiPlayer() && GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).isHuman(ISHUMAN_UI)) - { - szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNickName(); - } - else - { - szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNameKey(); - } - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_COOP_WAR, NO_PLAYER, szPlayerName, szBribedPlayerName); - break; - default: - ASSERT(false, "Unknown intrigue type"); - break; - } + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } - else - { - GET_PLAYER(ePlayer).GetDiplomacyAI()->ChangeNumTimesIntrigueSharedBy(GetID(), 1); - } - } + //problem: on larger maps evaluating the map value every turn for every player is significant performance overhead + //solution: we could cache the value for the active player, saving half the effort, but it's simpler to just not even contemplate the offer every turn + if ((GC.getGame().getGameTurn() + GetID()) % 5 != 0) + return; - // mark the messages as shared so the player isn't told the same thing repeatedly - for(uint ui = 0; ui < MAX_MAJOR_CIVS; ui++) - { - PlayerTypes eLoopPlayer = (PlayerTypes)ui; - GET_PLAYER(eLoopPlayer).GetEspionage()->MarkRecentIntrigueAsShared(ePlayer, ePlotterPlayer, pNotificationMessage->m_eDiplomacyPlayer, eIntrigueType); - } - } - } + if (GetPlayer()->IsAITeammateOfHuman()) + return; - // Stop converting our cities - else if(eStatement == DIPLO_STATEMENT_STOP_CONVERSIONS) + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_MAPS_OFFER) >= 30) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_CONVERSIONS); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_STOP_CONVERSIONS, szText, LEADERHEAD_ANIM_NEGATIVE); - } + if (GetPlayer()->GetDealAI()->IsMakeOfferForMaps(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_MAPS_OFFER; + else + pDeal->ClearItems(); } +} - // Stop digging up our yard - else if(eStatement == DIPLO_STATEMENT_STOP_DIGGING) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_DIGGING); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_STOP_DIGGING, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } +/// Possible Contact Statement - Third Party War Trade +void CvDiplomacyAI::DoThirdPartyWarTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - // Insult - else if(eStatement == DIPLO_STATEMENT_INSULT) - { - // Change other players' guess as to our Approach (right now it falls in line exactly with the Approach...) - //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuess(GetID(), CIV_APPROACH_HOSTILE); - //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuessCounter(GetID(), 0); + if (SkipForTeammates()) + return; - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_INSULT_ROOT); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } + if (IsAvoidDeals()) + return; - // Compliment - else if(eStatement == DIPLO_STATEMENT_COMPLIMENT) - { - // Change other players' guess as to our Approach (right now it falls in line exactly with the Approach...) - //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuess(GetID(), CIV_APPROACH_FRIENDLY); - //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuessCounter(GetID(), 0); + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_COMPLIMENT); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } - } + if (GetPlayer()->IsAITeammateOfHuman()) + return; - // Boot-kissing of a stronger power - else if(eStatement == DIPLO_STATEMENT_BOOT_KISSING) - { - // Change other players' guess as to our Approach (right now it falls in line exactly with the Approach...) - //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuess(GetID(), CIV_APPROACH_AFRAID); - //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuessCounter(GetID(), 0); + // Don't ask for war if we have a Declaration of Friendship - ask for a coop war instead + if (IsDoFAccepted(ePlayer)) + return; - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_BOOT_KISSING); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } - } + // Don't offer if we have a Defensive Pact either - might entangle us in things. + if (IsHasDefensivePact(ePlayer)) + return; - // We're warning a player that his warmongering behavior is attracting attention - else if(eStatement == DIPLO_STATEMENT_WARMONGER) + // Don't ask for war if they are weaker than us + if (GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_AVERAGE && GetEconomicStrengthComparedToUs(ePlayer) < STRENGTH_AVERAGE) + return; + + if (GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_POOR) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_THIRDPARTY_WAR_REQUEST) >= 40) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WARMONGER); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } + if (GetPlayer()->GetDealAI()->IsMakeOfferForThirdPartyWar(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_THIRDPARTY_WAR_REQUEST; + else + pDeal->ClearItems(); } +} - // We're warning a player that his interactions with City-States are not to our liking - else if(eStatement == DIPLO_STATEMENT_MINOR_CIV_COMPETITION) - { - if(bHuman) - { - PlayerTypes eMinorCiv = (PlayerTypes) iData1; - const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); +/// Possible Contact Statement - Peace Trade +void CvDiplomacyAI::DoThirdPartyPeaceTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - szText = GetDiploStringForMessage(DIPLO_MESSAGE_MINOR_CIV_COMPETITION, NO_PLAYER, strMinorCivKey); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } + if (SkipForTeammates()) + return; - // Human befriended an enemy of this AI! - else if(eStatement == DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY) - { - if(bHuman) - { - PlayerTypes eTarget = (PlayerTypes) iData1; - const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOFED_ENEMY, ePlayer, strTargetCivKey); + if (IsAvoidDeals()) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // Human denounced a friend of this AI! - else if(eStatement == DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND) - { - if(bHuman) - { - PlayerTypes eTarget = (PlayerTypes) iData1; - const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCED_FRIEND, ePlayer, strTargetCivKey); + if (GetPlayer()->IsAITeammateOfHuman()) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } + // Don't ask for peace if we have a Declaration of Friendship - we don't want to meddle in their affairs + if (IsDoFAccepted(ePlayer)) + return; - // Human denounced an enemy of this AI! - else if(eStatement == DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY) - { - if(bHuman) - { - PlayerTypes eTarget = (PlayerTypes) iData1; - const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCED_ENEMY, ePlayer, strTargetCivKey); + // Don't ask for peace if we have a Defensive Pact - makes us seem unreliable as an ally + if (IsHasDefensivePact(ePlayer)) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } - } + // Don't spend our money brokering peace if we're a vassal - we have greater priorities and no leverage if they renege. + if (GetPlayer()->IsVassalOfSomeone()) + return; - // Human befriended a friend of this AI! - else if(eStatement == DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND) + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_THIRDPARTY_PEACE_REQUEST) >= 30) { - if(bHuman) - { - PlayerTypes eTarget = (PlayerTypes) iData1; - const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOFED_FRIEND, ePlayer, strTargetCivKey); - - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } + if (GetPlayer()->GetDealAI()->IsMakeOfferForThirdPartyPeace(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_THIRDPARTY_PEACE_REQUEST; + else + pDeal->ClearItems(); } +} - // AI befriended an enemy of the human! - else if(eStatement == DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY) - { - if(bHuman) - { - PlayerTypes eTarget = (PlayerTypes) iData1; - const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCE_SO_AI_DOF, ePlayer, strTargetCivKey); +/// Possible Contact Statement - Strategic Resource Offer +void CvDiplomacyAI::DoStrategicTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } + if (IsAvoidDeals()) + return; - // AI denounced a friend of the human! - else if(eStatement == DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND) - { - if(bHuman) - { - PlayerTypes eTarget = (PlayerTypes) iData1; - const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOF_SO_AI_DENOUNCE, ePlayer, strTargetCivKey); + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_STRATEGIC_TRADE) >= 20) + { + if (GetPlayer()->GetDealAI()->IsMakeOfferForStrategicResource(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_STRATEGIC_TRADE; + else + pDeal->ClearItems(); } +} - // AI denounced an enemy of the human! - else if(eStatement == DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY) - { - if(bHuman) - { - PlayerTypes eTarget = (PlayerTypes) iData1; - const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCE_SO_AI_DENOUNCE, ePlayer, strTargetCivKey); +/// Possibile Contact Statement - Embassy Exchange +void CvDiplomacyAI::DoEmbassyExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } - } + if (SkipForTeammates()) + return; - // AI befriended a friend of the human! - else if(eStatement == DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND) - { - if(bHuman) - { - PlayerTypes eTarget = (PlayerTypes) iData1; - const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOF_SO_AI_DOF, ePlayer, strTargetCivKey); + if (IsAvoidDeals()) + return; - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } - } + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; - // AI chose same late game policy tree as the human! - else if(eStatement == DIPLO_STATEMENT_SAME_POLICIES_FREEDOM) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SAME_POLICIES_FREEDOM); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } - } + // We don't want to do it? + if (!IsEmbassyExchangeAcceptable(ePlayer)) + return; - else if(eStatement == DIPLO_STATEMENT_SAME_POLICIES_ORDER) + // They don't want to do it? + if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsEmbassyExchangeAcceptable(GetID())) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EMBASSY_EXCHANGE) >= 20 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EMBASSY_OFFER) >= 20) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SAME_POLICIES_ORDER); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } + if (GetPlayer()->GetDealAI()->IsMakeOfferForEmbassyExchange(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_EMBASSY_EXCHANGE; + else + pDeal->ClearItems(); } +} - else if(eStatement == DIPLO_STATEMENT_SAME_POLICIES_AUTOCRACY) +/// Possible Contact Statement - Embassy +void CvDiplomacyAI::DoEmbassyOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (IsAvoidDeals()) + return; + + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + if (GetPlayer()->IsAITeammateOfHuman()) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EMBASSY_OFFER) >= 20 && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EMBASSY_EXCHANGE) >= 20) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SAME_POLICIES_AUTOCRACY); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } + if (GetPlayer()->GetDealAI()->IsMakeOfferForEmbassy(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_EMBASSY_OFFER; + else + pDeal->ClearItems(); } +} - else if(eStatement == DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL) - { - if(bHuman) - { - Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); - CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); - if (pLeague != NULL) - { - sLeagueName = pLeague->GetName(); - } - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_LIKED_THEIR_PROPOSAL, ePlayer, sLeagueName); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } +/// Possible Contact Statement - Research Agreement Offer +void CvDiplomacyAI::DoResearchAgreementOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (IsAvoidDeals()) + return; + + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // We don't want to do it? + if (!IsWantsResearchAgreementWithPlayer(ePlayer)) + return; + + // They don't want to do it? + if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsWantsResearchAgreementWithPlayer(GetID())) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_RESEARCH_AGREEMENT_OFFER) >= 20) + { + if (GetPlayer()->GetDealAI()->IsMakeOfferForResearchAgreement(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_RESEARCH_AGREEMENT_OFFER; + else + pDeal->ClearItems(); } +} - else if(eStatement == DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL) +/// Possible Contact Statement - Open Borders Exchange +void CvDiplomacyAI::DoOpenBordersExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (IsAvoidDeals()) + return; + + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // We don't want to do it? + if (!IsOpenBordersExchangeAcceptable(ePlayer)) + return; + + // They don't want to do it? + if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsOpenBordersExchangeAcceptable(GetID())) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_OPEN_BORDERS_EXCHANGE) >= 25 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_OPEN_BORDERS_OFFER) >= 25) + { + if (GetPlayer()->GetDealAI()->IsMakeOfferForOpenBordersExchange(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_OPEN_BORDERS_EXCHANGE; + else + pDeal->ClearItems(); + } +} + +/// Possible Contact Statement - Open Borders +void CvDiplomacyAI::DoOpenBordersOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (IsAvoidDeals()) + return; + + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + if (GetPlayer()->IsAITeammateOfHuman()) + return; + + // We don't want to do it? + if (!IsWantsOpenBordersWithPlayer(ePlayer)) + return; + + // They don't want to do it? + if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsWillingToGiveOpenBordersToPlayer(GetID())) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_OPEN_BORDERS_OFFER) >= 25 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_OPEN_BORDERS_EXCHANGE) >= 25) { - if(bHuman) - { - Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); - CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); - if (pLeague != NULL) - { - sLeagueName = pLeague->GetName(); - } - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_DISLIKED_THEIR_PROPOSAL, ePlayer, sLeagueName); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } + if (GetPlayer()->GetDealAI()->IsMakeOfferForOpenBorders(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_OPEN_BORDERS_OFFER; + else + pDeal->ClearItems(); } +} - else if(eStatement == DIPLO_STATEMENT_THEY_SUPPORTED_OUR_PROPOSAL) +/// Possible Contact Statement - Luxury Trade +void CvDiplomacyAI::DoLuxuryTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (IsAvoidDeals()) + return; + + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_LUXURY_TRADE) >= 20) { - if(bHuman) - { - Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_THEY_SUPPORTED_OUR_PROPOSAL, ePlayer, sLeagueName); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } + if (GetPlayer()->GetDealAI()->IsMakeOfferForLuxuryResource(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + eStatement = DIPLO_STATEMENT_LUXURY_TRADE; + else + pDeal->ClearItems(); } +} - else if(eStatement == DIPLO_STATEMENT_THEY_FOILED_OUR_PROPOSAL) +/// Possible Contact Statement - Generous Offer +void CvDiplomacyAI::DoGenerousOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (IsAvoidDeals()) + return; + + if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Check if we have a bonus on exporting luxuries + bool bWantExport = false; + for (int iYieldLoop = 0; iYieldLoop < GC.getNUM_YIELD_TYPES(); iYieldLoop++) { - if(bHuman) + YieldTypes eYield = static_cast(iYieldLoop); + if (GetPlayer()->GetPlayerTraits()->GetYieldFromExport(eYield) > 0) { - Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); - szText = GetDiploStringForMessage(DIPLO_MESSAGE_THEY_FOILED_OUR_PROPOSAL, ePlayer, sLeagueName); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); + bWantExport = true; + break; } } - else if(eStatement == DIPLO_STATEMENT_THEY_SUPPORTED_OUR_HOSTING) + bool bOffCooldown = GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER) >= 60 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER_RANDFAILED) >= 15 && + // Don't send a generous offer request if we recently sent a Request to this player + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST) >= 25; + + // Shorter cooldown if we want to export + if ((bWantExport && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER) >= 10) || bOffCooldown) { - if(bHuman) + bool bRandPassed = false; + bool bMakeGenerousOffer = false; + if (bWantExport && IsMakeLuxuryOffer(ePlayer, pDeal)) + bMakeGenerousOffer = true; + + if (!bMakeGenerousOffer && bOffCooldown) + bMakeGenerousOffer = IsMakeGenerousOffer(ePlayer, pDeal, bRandPassed); + + if (bMakeGenerousOffer && pDeal->GetNumItems() > 0) { - Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); - CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); - if (pLeague != NULL) - { - sLeagueName = pLeague->GetName(); - } - szText = GetDiploStringForMessage(DIPLO_MESSAGE_THEY_SUPPORTED_OUR_HOSTING, ePlayer, sLeagueName); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + eStatement = DIPLO_STATEMENT_GENEROUS_OFFER; + SetOfferingGift(ePlayer, true); + bRandPassed = true; + pDeal->m_bIsGift = true; } + else + pDeal->ClearItems(); + + // Add this statement to the log so we don't evaluate it again until 15 turns has come back around + if (!bRandPassed) + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER_RANDFAILED, GC.getGame().getGameTurn()); } +} - // Ideological statements - else if(eStatement == DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM) +/// Possible Contact Statement +//void CvDiplomacyAI::DoNowUnforgivableStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement) +//{ +// ASSERT(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); +// ASSERT(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); +// +// if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) +// return; +// +// if (eStatement == NO_DIPLO_STATEMENT_TYPE) +// { +// bool bSendStatement = false; +// +// // Unforgivable! +// if (GetCivOpinion(ePlayer) == CIV_OPINION_UNFORGIVABLE) +// { +// // Our approach (real or fake) can't be Friendly +// if (GetSurfaceApproach(ePlayer) != CIV_APPROACH_FRIENDLY) +// { +// bSendStatement = true; +// } +// } +// +// if (bSendStatement) +// { +// DiploStatementTypes eTempStatement = DIPLO_STATEMENT_NOW_UNFORGIVABLE; +// int iTurnsBetweenStatements = 9999; +// +// if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) +// { +// eStatement = eTempStatement; +// } +// } +// } +//} + +/// Possible Contact Statement +//void CvDiplomacyAI::DoNowEnemyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement) +//{ +// ASSERT(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); +// ASSERT(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); +// +// if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) +// return; +// +// if (eStatement == NO_DIPLO_STATEMENT_TYPE) +// { +// bool bSendStatement = false; +// +// // Don't show this message if we've already given a more severe one +// if (GetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_NOW_UNFORGIVABLE) > -1) +// { +// // An enemy +// if (GetCivOpinion(ePlayer) == CIV_OPINION_ENEMY) +// { +// // Our approach (real or fake) can't be Friendly +// if (GetSurfaceApproach(ePlayer) != CIV_APPROACH_FRIENDLY) +// { +// bSendStatement = true; +// } +// } +// +// if (bSendStatement) +// { +// DiploStatementTypes eTempStatement = DIPLO_STATEMENT_NOW_ENEMY; +// int iTurnsBetweenStatements = 9999; +// +// if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) +// { +// eStatement = eTempStatement; +// } +// } +// } +// } +//} + +/// Possible Contact Statement - Approach towards player is now HOSTILE +void CvDiplomacyAI::DoHostileStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; + + // Don't send taunts unless we're acting hostile + if (!IsActHostileTowardsHuman(ePlayer)) + return; + + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; + + // Requires Approach of HOSTILE, Meanness > 6, and not too early to befriend them + if (GetSurfaceApproach(ePlayer) != CIV_APPROACH_HOSTILE || GetMeanness() <= 6 || IsTooEarlyForDoF(ePlayer)) + return; + + // If we've made peace recently, don't go mouthing off right away + int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(GET_PLAYER(ePlayer).getTeam()); + if (iPeaceTreatyTurn != -1) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } - else if(eStatement == DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER) + int iTurnsSincePeace = GC.getGame().getElapsedGameTurns() - iPeaceTreatyTurn; + if (iTurnsSincePeace < GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns()) + return; + } + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_INSULT) >= 75 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE) >= 10 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_TAUNT_THROTTLE) >= 20) { - if(bHuman) + eStatement = DIPLO_STATEMENT_INSULT; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_TAUNT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_TAUNT_THROTTLE, GC.getGame().getGameTurn()); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); + } +} + +/// Possible Contact Statement - Approach towards player is now AFRAID +void CvDiplomacyAI::DoAfraidStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Surface approach must be AFRAID + if (GetSurfaceApproach(ePlayer) != CIV_APPROACH_AFRAID) + return; + + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; + + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_BOOT_KISSING) >= 50) + eStatement = DIPLO_STATEMENT_BOOT_KISSING; +} + +/// Possible Contact Statement - Warning the player about their warmongering +void CvDiplomacyAI::DoWarmongerStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Only send this message once per game + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WARMONGER) != INT_MAX) + return; + + // Don't send this if we're acting friendly or afraid towards them + if (GetSurfaceApproach(ePlayer) == CIV_APPROACH_FRIENDLY || GetSurfaceApproach(ePlayer) == CIV_APPROACH_AFRAID) + return; + + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; + + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; + + // Warmonger Threat must be at least SEVERE + if (GetWarmongerThreat(ePlayer) < THREAT_SEVERE) + return; + + // We must not be going for conquest ourselves + if (IsGoingForWorldConquest() || IsCloseToWorldConquest()) + return; + + // Recently sent an insult + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE) < 10) + return; + + // 1 in 2 chance we don't send the message (don't want to bombard the player from all sides) + if (GC.getGame().randRangeInclusive(1, 2, CvSeeder::fromRaw(0x4a229eef).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) < 2) + return; + + // Check if anyone has sent the message this game. If so, second roll based on Chattiness flavor. + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + { + PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; + if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) + continue; + + if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WARMONGER) != INT_MAX) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); + if (GC.getGame().randRangeInclusive(1, 10, CvSeeder::fromRaw(0x751f8d72).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) > GetChattiness()) + return; + + break; } } - else if(eStatement == DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY) + + eStatement = DIPLO_STATEMENT_WARMONGER; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); +} + +/// Possible Contact Statement - We're happy we're following the same ideology as the human +void CvDiplomacyAI::DoHappySamePolicyTree(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // No compliments if we're acting hostile towards them + if (IsActHostileTowardsHuman(ePlayer)) + return; + + // Don't care if we're a vassal, unless voluntary + if (IsVassal(ePlayer) && !IsVoluntaryVassalage(ePlayer)) + return; + + if (eStatement == NO_DIPLO_STATEMENT_TYPE) { - if(bHuman) + DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; + + PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); + PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); + if (eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE && eMyBranch == eTheirBranch && GET_PLAYER(ePlayer).GetCulture()->GetTurnIdeologySwitch() < 0) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); + // Check chattiness to see if we send the message this turn + if (GetChattiness() >= GC.getGame().randRangeInclusive(1, 10, CvSeeder::fromRaw(0xf47d6bbc).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID()))) + { + DiploStatementTypes eOtherStatementToCheck = NO_DIPLO_STATEMENT_TYPE; + + if (eMyBranch == GD_INT_GET(POLICY_BRANCH_FREEDOM)) + { + eTempStatement = DIPLO_STATEMENT_SAME_POLICIES_FREEDOM; + eOtherStatementToCheck = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM; + } + else if (eMyBranch == GD_INT_GET(POLICY_BRANCH_ORDER)) + { + eTempStatement = DIPLO_STATEMENT_SAME_POLICIES_ORDER; + eOtherStatementToCheck = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER; + } + else if (eMyBranch == GD_INT_GET(POLICY_BRANCH_AUTOCRACY)) + { + eTempStatement = DIPLO_STATEMENT_SAME_POLICIES_AUTOCRACY; + eOtherStatementToCheck = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY; + } + + // Custom ideology with no programmed dialogue? Abort! + if (eTempStatement == NO_DIPLO_STATEMENT_TYPE) + return; + + // Send it if we've never sent either statement before + // This triggers the compliment throttle, but isn't blocked by it since it's sent once per game + if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) != INT_MAX && GetNumTurnsSinceStatementSent(ePlayer, eOtherStatementToCheck) != INT_MAX) + { + eStatement = eTempStatement; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_COMPLIMENT_THROTTLE, GC.getGame().getGameTurn()); + } + } } } - else if(eStatement == DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM) +} + +/// Possible Contact Statement - Warning the player that we don't like their interactions with "our" City-States +void CvDiplomacyAI::DoMinorCivCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1, bool bFromAdvisor /* = false */) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (!bFromAdvisor) { - if(bHuman) + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // Only send this message once per game + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_MINOR_CIV_COMPETITION) != INT_MAX) + return; + + // Don't send this if we're acting afraid towards them + if (GetSurfaceApproach(ePlayer) == CIV_APPROACH_AFRAID) + return; + + // Dispute Level must be at least STRONG + if (GetMinorCivDisputeLevel(ePlayer) < DISPUTE_LEVEL_STRONG) + return; + + // If we've already denounced them, no using whining at this point + if (IsDenouncedPlayer(ePlayer)) + return; + + // Don't send this if they resurrected us, liberated our key cities, or recently liberated us + if (IsLiberator(ePlayer, true, true) || IsCityRecentlyLiberatedBy(ePlayer)) + return; + + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; + + // This is silly to send if we've sent some other things recently (and also check the insult throttle) + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WE_ATTACKED_YOUR_MINOR) < 50 || + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_WE_BULLIED_YOUR_MINOR) < 30 || + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE) < 50 || + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_KILLED_PROTECTED_CITY_STATE) != INT_MAX || + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE) < 10) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + return; } - } - else if(eStatement == DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER) + } + + // Find a City-State we're upset over + PlayerTypes eBestCityState = NO_PLAYER; + int iMyInfluence = 0; + bool bPlayerIsAllied = false; + for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) { - if(bHuman) + PlayerTypes eMinor = (PlayerTypes) iMinorLoop; + + // Don't evaluate City-States that are unmet/dead + if (!IsPlayerValid(eMinor)) + continue; + + // Ignore if League resolutions make it irrelevant + if (GET_PLAYER(eMinor).GetMinorCivAI()->IsNoAlly() || GET_PLAYER(eMinor).GetMinorCivAI()->GetPermanentAlly() == GetID()) + continue; + + // Must be a City-State we aren't attacking/bullying + if (GetCivApproach(eMinor) <= CIV_APPROACH_HOSTILE) + continue; + + // Must have a PTP with this minor + if (!GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(GetID())) + continue; + + if (GET_PLAYER(eMinor).GetMinorCivAI()->IsAllies(ePlayer)) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + int iInfluence = GET_PLAYER(eMinor).GetMinorCivAI()->GetBaseFriendshipWithMajorTimes100(GetID()); + if (iInfluence > iMyInfluence || !bPlayerIsAllied) + { + eBestCityState = eMinor; + iMyInfluence = iInfluence; + bPlayerIsAllied = true; + } } - } - else if(eStatement == DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY) - { - if(bHuman) + else if (!bPlayerIsAllied && GET_PLAYER(eMinor).GetMinorCivAI()->IsFriends(ePlayer)) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + int iInfluence = GET_PLAYER(eMinor).GetMinorCivAI()->GetBaseFriendshipWithMajorTimes100(GetID()); + if (iInfluence > iMyInfluence) + { + eBestCityState = eMinor; + iMyInfluence = iInfluence; + } } } - else if(eStatement == DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM) + + iData1 = eBestCityState; + + if (!bFromAdvisor) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_FREEDOM); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } + if (eBestCityState == NO_PLAYER) + return; + + // Need to pass a Chattiness roll to send the statement + if (GC.getGame().randRangeInclusive(1, 10, CvSeeder::fromRaw(0xa53b3184).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) > GetChattiness()) + return; + + eStatement = DIPLO_STATEMENT_MINOR_CIV_COMPETITION; + LogStatementToPlayer(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_INSULT_THROTTLE, GC.getGame().getGameTurn()); } - else if(eStatement == DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER) +} + +/// Possible Contact Statement - Message to human if the AI thinks they are getting close to the victory they're also going for. +void CvDiplomacyAI::DoVictoryCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (!IsCompetingForVictory()) + return; + + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; + + int iTurnsBetweenStatements = 50; + AIGrandStrategyTypes eMyGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetActiveGrandStrategy(); + if(eMyGrandStrategy == NO_AIGRANDSTRATEGY) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_ORDER); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } + return; } - else if(eStatement == DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY) + + EraTypes eModern = (EraTypes) GC.getInfoTypeForString("ERA_MODERN", true); + DisputeLevelTypes eDispute = GetVictoryDisputeLevel(ePlayer); + if(eDispute < DISPUTE_LEVEL_STRONG) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_AUTOCRACY); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } + return; } - else if(eStatement == DIPLO_STATEMENT_YOUR_CULTURE_INFLUENTIAL) + CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); + if(eOpinion >= CIV_OPINION_FAVORABLE) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_CULTURE_INFLUENTIAL); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } + return; } - else if(eStatement == DIPLO_STATEMENT_OUR_CULTURE_INFLUENTIAL) + bool bLeagueCompetitor = false; + bool bSpaceRace = false; + bool bCulture = false; + bool bWar = false; + int iVotes = 0; + int iNeededVotes = 0; + CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); + if(pLeague != NULL) { - if(bHuman) + iVotes = pLeague->CalculateStartingVotesForMember(ePlayer); + iNeededVotes = GC.getGame().GetVotesNeededForDiploVictory(); + if(iNeededVotes > 0) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_CULTURE_INFLUENTIAL); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + // 33% there? Close! + if(iVotes >= (iNeededVotes / 3)) + { + bLeagueCompetitor = true; + } } } - // Player has a Strategic Resource we'd like - else if(eStatement == DIPLO_STATEMENT_STRATEGIC_TRADE) + int iProjectCount = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetSSProjectCount(); + if (iProjectCount > 1) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_STRATEGIC_TRADE); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - // Offer to an AI player - else + bSpaceRace = true; + } + else + { + int iTheirTechNum = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); + + int iNumOtherPlayers = 0; + int iNumPlayersAheadInTech = 0; + for(uint ui = 0; ui < MAX_MAJOR_CIVS; ui++) { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + PlayerTypes eOtherPlayer = (PlayerTypes)ui; + if(!GET_PLAYER(eOtherPlayer).isAlive()) { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + continue; } - else + + if (eOtherPlayer == ePlayer) { - CvDeal kDeal = *pDeal; + continue; + } - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + iNumOtherPlayers++; + int iNumTechs = GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); + if (iTheirTechNum > iNumTechs ) + { + iNumPlayersAheadInTech++; } } - } - - // Announce to the human that this AI is competing with them for the same victory condition - else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } - else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } - else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CULTURE) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CULTURE); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } - else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } - else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CONFUSED) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CONFUSED); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } - - // Announce to the human that this AI wants to block them from achieving victory - else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } - } - else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS) - { - if(bHuman) + if(iNumPlayersAheadInTech >= iNumOtherPlayers) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); + bSpaceRace = true; } } - else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_CULTURE) + if(GetWarmongerThreat(ePlayer) >= THREAT_SEVERE && GET_PLAYER(ePlayer).GetNumCapitalCities() > 1 && GetPlayer()->GetNumCapitalCities() > 1) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_CULTURE); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } + bWar = true; } - else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_SPACESHIP) + //More than double our influence, and we both have some? + if(GET_PLAYER(ePlayer).GetCulture()->GetNumCivsInfluentialOn() > 1 && GetPlayer()->GetCulture()->GetNumCivsInfluentialOn() > 0 && (GET_PLAYER(ePlayer).GetCulture()->GetNumCivsInfluentialOn() > (GetPlayer()->GetCulture()->GetNumCivsInfluentialOn() * 2))) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_SPACESHIP); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); - } + bCulture = true; } + DiploStatementTypes eTempStatement; - // We'd like to purchase this player's World Map - else if(eStatement == DIPLO_STATEMENT_MAPS_OFFER) + if(GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategy(ePlayer) == eMyGrandStrategy) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_MAPS_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - else - { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + if(GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategyConfidence(ePlayer) >= GUESS_CONFIDENCE_LIKELY) + { + //Conquered a capital? You are in our way! + if(IsGoingForWorldConquest() && bWar) + { + eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST; + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + { + eStatement = eTempStatement; + return; + } + } + else if(IsGoingForDiploVictory() && bLeagueCompetitor) + { + eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS; + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + { + eStatement = eTempStatement; + return; + } + } + else if(IsGoingForCultureVictory() && bCulture) { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + //We've both influenced someone? Competitor! + eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CULTURE; + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + { + eStatement = eTempStatement; + return; + } } - else + else if(IsGoingForSpaceshipVictory() && bSpaceRace) { - CvDeal kDeal = *pDeal; - - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP; + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + { + eStatement = eTempStatement; + return; + } + } + //Don't have it figured out, but we're competitive? Grr! + else if(GetPlayer()->GetCurrentEra() > eModern) + { + eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CONFUSED; + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + { + eStatement = eTempStatement; + return; + } } } } - // We'd like to purchase a technology from this player - else if(eStatement == DIPLO_STATEMENT_TECH_OFFER) +} + +/// Possible Contact Statement - Message to human if the AI thinks they are getting close to a victory that they're not going for. +void CvDiplomacyAI::DoVictoryBlockStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (!IsCompetingForVictory()) + return; + + if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + // We must be able to declare war on them + if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + return; + + int iTurnsBetweenStatements = 50; + AIGrandStrategyTypes eConquestGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CONQUEST"); + AIGrandStrategyTypes eCultureGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CULTURE"); + AIGrandStrategyTypes eUNGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_UNITED_NATIONS"); + AIGrandStrategyTypes eSpaceshipGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_SPACESHIP"); + EraTypes eAtomic = (EraTypes) GC.getInfoTypeForString("ERA_POSTMODERN", true); + + CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); + if (eOpinion >= CIV_OPINION_FAVORABLE) + return; + + // Let's not send this before the Atomic Era, okay? + if (GetPlayer()->GetCurrentEra() < eAtomic) + return; + + DiploStatementTypes eTempStatement; + + if(GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_TECH_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); - } - else + AIGrandStrategyTypes eGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategy(ePlayer); + if(eGrandStrategy == eConquestGrandStrategy) { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else + eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST; + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { - CvDeal kDeal = *pDeal; - - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + eStatement = eTempStatement; + return; } } - } - // We're making a generous offer to this player - else if(eStatement == DIPLO_STATEMENT_GENEROUS_OFFER) - { - if(bHuman) + else if(eGrandStrategy == eUNGrandStrategy) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_GENEROUS_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_GENEROUS_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS; + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + { + eStatement = eTempStatement; + return; + } } - else + else if(eGrandStrategy == eCultureGrandStrategy) { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_CULTURE; + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + eStatement = eTempStatement; + return; } - else + } + else if(eGrandStrategy == eSpaceshipGrandStrategy) + { + eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_SPACESHIP; + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) { - // For now the AI will always accept - may eventually write additional logic here - CvDeal kDeal = *pDeal; - - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + eStatement = eTempStatement; + return; } } } - //We want to declare independence from our master - else if(eStatement == DIPLO_STATEMENT_REVOKE_VASSALAGE) - { - if (bHuman) - { - if (IsActHostileTowardsHuman(ePlayer)) - szText = GetDiploStringForMessage(DIPLO_MESSAGE_REVOKE_VASSALAGE_HOSTILE); - else - szText = GetDiploStringForMessage(DIPLO_MESSAGE_REVOKE_VASSALAGE); +} - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_AI_REVOKE_VASSALAGE, szText, LEADERHEAD_ANIM_NEGATIVE); - } - // AI resolution - else - { - CvPlayer& kVassalPlayer = GET_PLAYER(GetID()); - CvPlayer& kMasterPlayer = GET_PLAYER(ePlayer); +/// Possible Contact Statement +//void CvDiplomacyAI::DoFriendlyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement) +//{ +// ASSERT(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); +// ASSERT(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); +// +// if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) +// return; +// +// CivApproachTypes eApproach = GetCivApproach(ePlayer); +// +// if (eStatement == NO_DIPLO_STATEMENT_TYPE) +// { +// if (eApproach == CIV_APPROACH_FRIENDLY) +// { +// DiploStatementTypes eTempStatement = DIPLO_STATEMENT_COMPLIMENT; +// int iTurnsBetweenStatements = 35; +// +// if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) +// { +// eStatement = eTempStatement; +// } +// } +// } +//} - bool bPeaceful = kMasterPlayer.GetDiplomacyAI()->IsEndVassalageAcceptable(kVassalPlayer.GetID()); - GET_TEAM(kVassalPlayer.getTeam()).DoEndVassal(kMasterPlayer.getTeam(), bPeaceful, false); - } - } - //We want this player to liberate their vassals - else if(eStatement == DIPLO_STATEMENT_REVOKE_VASSALAGE_THIRD_PARTY) +/// Possible Contact Statement - Peace +void CvDiplomacyAI::DoPeaceOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +{ + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + return; + + if (SkipForTeammates()) + return; + + if (MOD_DIPLOAI_SHUT_UP_PEACE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + return; + + if (!IsAtWar(ePlayer)) + return; + + // Have to have been at war for at least a little while + GetPlayer()->SetCachedValueOfPeaceWithHuman(0); + + // Only offer peace if we actually want peace + if (!IsWantsPeaceWithPlayer(ePlayer)) + return; + + // cities can be added to a peace deal if they are in danger of falling, so that check needs to be done more frequently + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST_PEACE) >= 2) { - if(bHuman) + if (GetPlayer()->GetDealAI()->IsOfferPeace(ePlayer, /*pDeal can be modified in this function*/ pDeal, false /*bEqualizingDeals*/) && pDeal->GetNumItems() > 0 && pDeal->ContainsItemType(TRADE_ITEM_CITIES, ePlayer)) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_REVOKE_VASSAL_THIRD_OFFER); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_POSITIVE); + eStatement = DIPLO_STATEMENT_REQUEST_PEACE; } - // Offer to an AI player else { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else + // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at + pDeal->ClearItems(); + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST_PEACE) >= 5) { - CvDeal kDeal = *pDeal; - - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + if (GetPlayer()->GetDealAI()->IsOfferPeace(ePlayer, /*pDeal can be modified in this function*/ pDeal, false /*bEqualizingDeals*/) && pDeal->GetNumItems() > 0) + { + eStatement = DIPLO_STATEMENT_REQUEST_PEACE; + } + else + { + // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at + pDeal->ClearItems(); + } } } } - // AI offers to make ePlayer his voluntary vassal - else if(eStatement == DIPLO_STATEMENT_BECOME_MY_VASSAL) +} + +// ----------------------------------------------------------------------------------------------- + + +// ************************************ +// Counters +// ************************************ + +/// Increment our turn counters +void CvDiplomacyAI::DoCounters() +{ + vector v; + + // Loop through all (known) Players + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - if(bHuman) - { - ASSERT(false, "Don't send vassalage statement to human!"); - } - else + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (eLoopPlayer != GetID() && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && IsHasMet(eLoopPlayer, true)) { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + CvNotifications* pNotifications = GET_PLAYER(eLoopPlayer).GetNotifications(); + + // Are we ready to forget our denunciation? + if (IsDenouncedPlayer(eLoopPlayer) && GetTurnsSinceDenouncedPlayer(eLoopPlayer) >= GC.getGame().getGameSpeedInfo().getRelationshipDuration()) { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + SetDenouncedPlayer(eLoopPlayer, false); + + // Let's reevaluate this guy if they haven't denounced us and aren't untrustworthy. + // Rewards human appeasement strategies. + if (!IsDenouncedByPlayer(eLoopPlayer) && !IsUntrustworthy(eLoopPlayer)) + v.push_back(eLoopPlayer); + + // Notify the target of the denouncement that it has expired. + if (pNotifications) + { + CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_THEIR_DENUNCIATION_EXPIRED_S"); + Localization::String strInfo = Localization::Lookup("TXT_KEY_NOTIFICATION_THEIR_DENUNCIATION_EXPIRED"); + Localization::String strTemp = strInfo; + strTemp << GET_PLAYER(GetID()).getCivilizationShortDescriptionKey(); + pNotifications->Add(NOTIFICATION_DENUNCIATION_EXPIRED, strTemp.toUTF8(), strSummary, -1, -1, GetID(), eLoopPlayer); + } } - else + + // Has our Friendship expired? + if (IsDoFAccepted(eLoopPlayer) && GetTurnsSinceBefriendedPlayer(eLoopPlayer) >= GC.getGame().getGameSpeedInfo().getRelationshipDuration()) { - CvDeal kDeal = *pDeal; + SetDoFAccepted(eLoopPlayer, false); + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->SetDoFAccepted(GetID(), false); - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + // Notify both parties that our friendship has expired. + if (pNotifications) + { + CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED", GET_PLAYER(GetID()).getCivilizationShortDescriptionKey()); + CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED_S"); + pNotifications->Add(NOTIFICATION_FRIENDSHIP_EXPIRED, strBuffer, strSummary, -1, -1, GetID(), eLoopPlayer); + } + + pNotifications = GET_PLAYER(GetID()).GetNotifications(); + if (pNotifications) + { + CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED", GET_PLAYER(eLoopPlayer).getCivilizationShortDescriptionKey()); + CvString strSummary = GetLocalizedText("TXT_KEY_NOTIFICATION_FRIENDSHIP_EXPIRED_S"); + pNotifications->Add(NOTIFICATION_FRIENDSHIP_EXPIRED, strBuffer, strSummary, -1, -1, eLoopPlayer, GetID()); + } } } } - // AI offers to become voluntary vassal of ePlayer - else if (eStatement == DIPLO_STATEMENT_ACCEPT_VASSALAGE) + + DoReevaluatePlayers(v); +} + +/// Does this AI have a gold quest active with any minor civ? +bool CvDiplomacyAI::IsHasActiveGoldQuest() +{ + //antonjs: consider: optimize + for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) { - if (bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_BECOME_VASSAL); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_POSITIVE); - } - else + PlayerTypes eMinor = (PlayerTypes) iMinorLoop; + if (GET_PLAYER(eMinor).GetMinorCivAI()->IsActiveQuestForPlayer(GetID(), MINOR_CIV_QUEST_GIVE_GOLD)) + return true; + if (GET_PLAYER(eMinor).GetMinorCivAI()->IsActiveQuestForPlayer(GetID(), MINOR_CIV_QUEST_INVEST)) + return true; + } + return false; +} + +///////////////////////////////////////////////////////// +// Requests +///////////////////////////////////////////////////////// + + + + +/// Is this AI willing to make a request of ePlayer? +bool CvDiplomacyAI::IsMakeRequest(PlayerTypes ePlayer, CvDeal* pDeal, bool& bRandPassed) +{ + bool bFriendly = GetSurfaceApproach(ePlayer) == CIV_APPROACH_FRIENDLY; + + if(bFriendly && IsDoFAccepted(ePlayer)) + { + // Is there something we want? + bool bWantsSomething = false; + // Is there a strong reason why we want something? (added to rand roll) + int iWeightBias = 0; + + // Luxury Request + if(!bWantsSomething) + bWantsSomething = IsLuxuryRequest(ePlayer, pDeal, iWeightBias); + // Gold Request + if(!bWantsSomething) + bWantsSomething = IsGoldRequest(ePlayer, pDeal, iWeightBias); + + if (bWantsSomething) { - if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + // Random element + int iRand = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x99eccf9f).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); + iRand += iWeightBias; + + if (iRand >= 7) { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + bRandPassed = true; + return true; } else { - CvDeal kDeal = *pDeal; - - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + bRandPassed = false; + return false; } } } - // AI is happy that they were liberated from vassalage - else if(eStatement == DIPLO_STATEMENT_LIBERATE_VASSAL) + + return false; +} + +/// Does this AI want a luxury resource gift from ePlayer? +bool CvDiplomacyAI::IsLuxuryRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias) +{ + iWeightBias = 0; + + ResourceTypes eLuxuryToAskFor = NO_RESOURCE; + + int iResourceLoop = 0; + + // See if the other player has a Resource to trade + for(iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) { - if(bHuman) + const ResourceTypes eResource = static_cast(iResourceLoop); + + CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); + if(pkResourceInfo) { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_LIBERATE_VASSAL); - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } + // Only look at Luxuries + if(pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_LUXURY) + continue; - // Liberate this vassal - GET_TEAM(GetTeam()).DoLiberateVassal(GET_PLAYER(ePlayer).getTeam()); + // Any extras? + if(GET_PLAYER(ePlayer).getNumResourceAvailable(eResource, /*bIncludeImport*/ false) < 2) + continue; + + // Can they actually give us this item + if(!pDeal->IsPossibleToTradeItem(ePlayer, GetID(), TRADE_ITEM_RESOURCES, eResource, 1)) + continue; + + eLuxuryToAskFor = eResource; + break; + } } - // AI is upset that their taxes were raised - else if(eStatement == DIPLO_STATEMENT_VASSAL_TAXES_RAISED_HUMAN_MASTER) + + // Didn't find something they could give us? + if(eLuxuryToAskFor == NO_RESOURCE) + return false; + + // See if there's any Luxuries WE can trade (because if there are then we shouldn't be asking for hand outs) + for(iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_RAISED_HUMAN_MASTER, ePlayer); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_NEGATIVE); - } - else + const ResourceTypes eResource = static_cast(iResourceLoop); + + CvResourceInfo* pkResource = GC.getResourceInfo(eResource); + if(pkResource) { - ASSERT(false, "Don't send this message to AI!"); + // Only look at Luxuries + if(pkResource->getResourceUsage() != RESOURCEUSAGE_LUXURY) + continue; + + // Any extras? + if(GetPlayer()->getNumResourceAvailable(eResource, /*bIncludeImport*/ false) < 2) + continue; + + // Can they actually give us this item + if(!pDeal->IsPossibleToTradeItem(GetID(), ePlayer, TRADE_ITEM_RESOURCES, eResource, 1)) + continue; + + // Found something we can trade to them, so abort + return false; } } - // AI is happy that their taxes were lowered - else if(eStatement == DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_HUMAN_MASTER) - { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_LOWERED_HUMAN_MASTER, ePlayer); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } - else - { - ASSERT(false, "Don't send this message to AI!"); - } - } - // AI notifies human that their taxes were RAISED - else if(eStatement == DIPLO_STATEMENT_VASSAL_TAXES_RAISED_AI_MASTER) + + // Add a little something extra since we're in dire straits + if(GetPlayer()->IsEmpireUnhappy()) + iWeightBias += 5; + + // Now seed the deal + pDeal->AddResourceTrade(ePlayer, eLuxuryToAskFor, 1, GC.getGame().GetDealDuration()); + + return true; +} + +/// Does this AI want a gold gift from ePlayer? +bool CvDiplomacyAI::IsGoldRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias) +{ + iWeightBias = 0; + + int iOurGold = GetPlayer()->GetTreasury()->GetGold(); + int iOurGPT = GetPlayer()->calculateGoldRate(); + int iOurExpenses = GetPlayer()->GetTreasury()->CalculateTotalCosts(); + int iOurGrossIncome = iOurGPT + iOurExpenses; + + // If we have no expenses, don't ask (and also don't crash) + if(iOurExpenses == 0) + return false; + + // If we already have some gold saved up then don't bother + if(iOurGold > 100) + return false; + + // If we're making 35% more than we're spending then don't ask, we're doing alright + if(iOurGrossIncome * 100 / iOurExpenses > 135) + return false; + + int iTheirGold = GET_PLAYER(ePlayer).GetTreasury()->GetGold(); + int iTheirGPT = GET_PLAYER(ePlayer).calculateGoldRate(); + int iTheirExpenses = GET_PLAYER(ePlayer).GetTreasury()->CalculateTotalCosts(); + int iTheirGrossIncome = iTheirGPT + iTheirExpenses; + + // Don't divide by zero please + if(iTheirExpenses != 0) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_RAISED_AI_MASTER, ePlayer); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_NEGATIVE); - } - else - { - ASSERT(false, "Don't send this message to AI!"); - } + // If they're making less than 35% more than they're spending then don't ask, they're not in great shape + if(iTheirGrossIncome * 100 / iTheirExpenses < 135) + return false; } - // AI notifies human that their taxes were LOWERED - else if(eStatement == DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_AI_MASTER) + else if(iTheirGPT <= iOurGPT) { - if(bHuman) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_LOWERED_AI_MASTER, ePlayer); - CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); - } - else - { - ASSERT(false, "Don't send this message to AI!"); - } + return false; } - // Do we want peace with ePlayer? - else if (eStatement == DIPLO_STATEMENT_REQUEST_PEACE) + // Add a little something extra since we're in dire straits + if(iOurGPT < 0) + iWeightBias += 5; + + // If we've made it this far we'd like to ask, so figure out how much we want to ask for + int iGoldToAskFor = iTheirGPT * GC.getGame().GetDealDuration() / 5; + int iGPTToAskFor = 0; + + if(iGoldToAskFor > iTheirGold) { - if (bHuman) - { - int iOurWarScore = GetWarScore(ePlayer); - if (iOurWarScore >= 10) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_WINNER_PEACE_OFFER); - } - else if (iOurWarScore <= -10) - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_PEACE_MADE_BY_HUMAN_GRACIOUS); - } - else - { - szText = GetDiploStringForMessage(DIPLO_MESSAGE_PEACE_OFFER); - } + iGoldToAskFor = 0; + iGPTToAskFor = max(1, iTheirGPT / 6); + } - CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_POSITIVE); - } - // Offer to an AI player - else - { - if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) - { - GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); - } - else - { - CvDeal kDeal = *pDeal; + // Now seed the deal + if(iGoldToAskFor > 0) + pDeal->AddGoldTrade(ePlayer, iGoldToAskFor, true); + else if(iGPTToAskFor > 0) + pDeal->AddGoldPerTurnTrade(ePlayer, iGPTToAskFor, GC.getGame().GetDealDuration()); - // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending - GC.getGame().GetGameDeals().AddProposedDeal(kDeal); - GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); - } + return true; +} - LogPeaceMade(ePlayer); - } +/// Are we willing to swap embassies with ePlayer? +bool CvDiplomacyAI::IsEmbassyExchangeAcceptable(PlayerTypes ePlayer) +{ + CivApproachTypes eApproach = GetCivApproach(ePlayer); + switch (eApproach) + { + case CIV_APPROACH_WAR: + case CIV_APPROACH_HOSTILE: + case CIV_APPROACH_GUARDED: + return false; + default: + break; } + + return !IsUntrustworthy(ePlayer); } -/// Any Major Civs we want to chat with? -void CvDiplomacyAI::DoContactMajorCivs() +/// What are our opinions of this player's neigbors? +CivOpinionTypes CvDiplomacyAI::GetNeighborOpinion(PlayerTypes ePlayer) const { - DetermineVassalToLiberate(); + if (ePlayer == NO_PLAYER) + { + return CIV_OPINION_NEUTRAL; + } - // NOTE: This function is broken up into two sections: AI contact opportunities, and then human contact opportunities - // This is to prevent a nasty bug where the AI will continue making decisions as the diplo screen is firing up. Making humans - // handled at the end prevents the Diplo AI from having this problem + int iBad = 0; + int iNeutral = 0; + int iGood = 0; - // Loop through AI Players - if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) { - if (m_eDiploMode == DIPLO_SPECIFIC_PLAYER) - { - DoContactPlayer(m_eTargetPlayer); - } - else if (m_eDiploMode == DIPLO_ALL_PLAYERS || m_eDiploMode == DIPLO_AI_PLAYERS) + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (IsPlayerValid(eLoopPlayer, true) && GET_PLAYER(eLoopPlayer).isMajorCiv() && GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) { - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + if (GetCivOpinion(eLoopPlayer) <= CIV_OPINION_COMPETITOR) { - PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; - - if (!IsPlayerValid(eLoopPlayer)) - continue; - - // No humans - if (GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - continue; - - DoContactPlayer(eLoopPlayer); + iBad++; } - } - if (m_eDiploMode == DIPLO_ALL_PLAYERS || m_eDiploMode == DIPLO_HUMAN_PLAYERS) - { - // JdH => contact humans by priority, but use a notification system instead of pop up the diplo screen - // every AI can only talk to one human a time (as a human can only talk to one human a time - // TODO: the one to one restriction should be removed in favor of a trade resource pool allocation - if (!CvDiplomacyRequests::HasActiveDiploRequestWithHuman(GetID())) + else if (GetCivOpinion(eLoopPlayer) == CIV_OPINION_NEUTRAL) { - vector aeHumansByPriority; - vector::const_iterator priorityIter, humanIter; - // bring players in priority order - for (humanIter = CvDiplomacyRequests::s_aDiploHumans.begin(); humanIter != CvDiplomacyRequests::s_aDiploHumans.end(); ++humanIter) - { - PlayerTypes eLoopPlayer = *humanIter; - - ASSERT(CvPreGame::isHuman(eLoopPlayer)); - ASSERT(GET_PLAYER(eLoopPlayer).isTurnActive()); - - if (!IsPlayerValid(eLoopPlayer)) - continue; - - // No AI - if (!GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - continue; - - // Only active Players - if (!GET_PLAYER(eLoopPlayer).isTurnActive()) - continue; - - for (priorityIter = aeHumansByPriority.begin(); priorityIter != aeHumansByPriority.end(); ++priorityIter) - { - if (m_aTradePriority[*priorityIter] < m_aTradePriority[eLoopPlayer]) - { - aeHumansByPriority.insert(priorityIter, eLoopPlayer); - break; - } - } - if (priorityIter == aeHumansByPriority.end()) - { - aeHumansByPriority.push_back(eLoopPlayer); - } - } - - for (humanIter = aeHumansByPriority.begin(); humanIter != aeHumansByPriority.end(); ++humanIter) - { - DoContactPlayer(*humanIter); - if (GET_PLAYER(*humanIter).GetDiplomacyRequests()->HasActiveRequestFrom(GetID())) - { - // we actually found someone worth talking with, the others must wait... - break; - } - } + iNeutral++; + } + else if (GetCivOpinion(eLoopPlayer) >= CIV_OPINION_FAVORABLE) + { + iGood++; } } } + if (iGood > iNeutral && iGood > iBad) + { + return CIV_OPINION_FRIEND; + } + else if (iNeutral > iGood && iNeutral > iBad) + { + return CIV_OPINION_NEUTRAL; + } + else if (iBad > iGood && iBad > iNeutral) + { + return CIV_OPINION_ENEMY; + } else { - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - - if (!IsPlayerValid(eLoopPlayer)) - continue; + return CIV_OPINION_NEUTRAL; + } +} - // No humans - if (GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - continue; +bool CvDiplomacyAI::MusteringForNeighborAttack(PlayerTypes ePlayer) const +{ + if (ePlayer == NO_PLAYER) + { + return false; + } - DoContactPlayer(eLoopPlayer); - } + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + { + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - // Loop through HUMAN Players - if we're not in MP - if (!CvPreGame::isNetworkMultiplayerGame()) + if (IsPlayerValid(eLoopPlayer) && GET_PLAYER(eLoopPlayer).isMajorCiv() && GET_PLAYER(eLoopPlayer).GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) { - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + if (IsAtWar(eLoopPlayer) || GetCivApproach(eLoopPlayer) == CIV_APPROACH_WAR) { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - - if (!IsPlayerValid(eLoopPlayer)) - continue; - - // No AI - if (!GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - continue; - - DoContactPlayer(eLoopPlayer); + return true; } } } + + return false; } -/// Individual contact opportunity -void CvDiplomacyAI::DoContactPlayer(PlayerTypes ePlayer) +/// Are we willing to accept Open Borders from eOtherPlayer? +bool CvDiplomacyAI::IsWantsOpenBordersWithPlayer(PlayerTypes ePlayer) { - // reset these - SetCurrentDealOfferChanged(false); - SetCurrentDealOfferGenerous(false); - - if (!IsValidUIDiplomacyTarget(ePlayer)) - return; // Can't contact this player at the moment. - - // Can't contact this player because of game options - if (MOD_DIPLOAI_SHUT_UP && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - DiploStatementTypes eStatement = NO_DIPLO_STATEMENT_TYPE; - - // We can use this deal pointer to form a trade offer - CvDeal* pDeal = GC.getGame().GetGameDeals().GetTempDeal(); - - // These can be used for info about deal items, e.g. what Minor Civ we're telling the guy to stay away from, etc. - int iData1 = 0; - - // If this is the same turn we've met a player, don't send anything his way quite yet - wait until we've said hello at least - if (GET_TEAM(GetTeam()).GetTurnsSinceMeetingTeam(GET_PLAYER(ePlayer).getTeam()) == 0) - return; - - //End the gift exchange at the start of each round. - GetPlayer()->GetDiplomacyAI()->SetOfferingGift(ePlayer, false); - GetPlayer()->GetDiplomacyAI()->SetOfferedGift(ePlayer, false); - - pDeal->SetRequestingPlayer(NO_PLAYER); - - //Clear this data out before any deals are offered. - SetCantMatchDeal(ePlayer, false); - - iData1 = -1; + if (IsVassal(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassal(GetID())) + return true; - pDeal->ClearItems(); - pDeal->SetFromPlayer(GetID()); - pDeal->SetToPlayer(ePlayer); - pDeal->SetDuration(GC.getGame().getGameSpeedInfo().GetDealDuration()); + if (IsUntrustworthy(ePlayer)) + return false; - bool bSanctioned = false; - if (MOD_BALANCE_VP) + if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) { - CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); - if (pLeague && pLeague->IsTradeEmbargoed(m_pPlayer->GetID(), ePlayer)) - { - bSanctioned = true; - } + return false; + } + if (IsArmyInPlaceForAttack(ePlayer)) + { + return false; } - // JON: Add in some randomization here? - // How predictable do we want the AI to be with regards to what state they're in? - - // Note that the order in which the following functions are called is very important to how the AI behaves - first come, first served - - CvDeal* pRenewDeal = NULL; - // AT PEACE - if (!IsAtWar(ePlayer)) + if (!MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM) { - // Avoiding exchanges? - if (AvoidExchangesWithPlayer(ePlayer)) + // If going for culture win we always want open borders with civs we need influence on + if (IsGoingForCultureVictory()) { - SetAvoidDeals(true); - } - - //DoCoopWarTimeStatement(ePlayer, eStatement, iData1); - DoCoopWarStatement(ePlayer, eStatement, iData1); - pRenewDeal = DoRenewExpiredDeal(ePlayer, eStatement); + InfluenceLevelTypes eInfluenceLevel = m_pPlayer->GetCulture()->GetInfluenceLevel(ePlayer); + InfluenceLevelTrend eInfluenceTrend = m_pPlayer->GetCulture()->GetInfluenceTrend(ePlayer); - // Some things we don't say to teammates - if (GetTeam() != GET_PLAYER(ePlayer).getTeam()) + if (eInfluenceLevel < INFLUENCE_LEVEL_INFLUENTIAL || eInfluenceTrend <= INFLUENCE_TREND_STATIC) + return true; + } + } + else + { + // If they need influence over us and they aren't our liberator or DP, we don't want their OB, thanks. + if (IsCompetingForVictory() && !IsHasDefensivePact(ePlayer) && GetNumCitiesLiberatedBy(ePlayer) <= 0) { - // STATEMENTS - all members but ePlayer passed by address + DisputeLevelTypes eVictoryDispute = GetVictoryDisputeLevel(ePlayer); + BlockLevelTypes eVictoryBlock = GetVictoryBlockLevel(ePlayer); - // Some things we only say to our masters - if (IsVassal(ePlayer)) + if (eVictoryDispute > DISPUTE_LEVEL_NONE || eVictoryBlock > BLOCK_LEVEL_NONE) { - DoEndVassalageStatement(ePlayer, eStatement); - } - - DoAggressiveMilitaryStatement(ePlayer, eStatement); - DoKilledCityStateStatement(ePlayer, eStatement, iData1); - DoAttackedCityStateStatement(ePlayer, eStatement, iData1); - DoBulliedCityStateStatement(ePlayer, eStatement, iData1); - //DoSeriousExpansionWarningStatement(ePlayer, eStatement); - DoExpansionWarningStatement(ePlayer, eStatement); - DoExpansionBrokenPromiseStatement(ePlayer, eStatement); - //DoSeriousPlotBuyingWarningStatement(ePlayer, eStatement); - DoPlotBuyingWarningStatement(ePlayer, eStatement); - DoPlotBuyingBrokenPromiseStatement(ePlayer, eStatement); - - DoWeAttackedYourMinorStatement(ePlayer, eStatement, iData1); - DoWeBulliedYourMinorStatement(ePlayer, eStatement, iData1); - - DoKilledYourSpyStatement(ePlayer, eStatement); - DoKilledMySpyStatement(ePlayer, eStatement); - DoCaughtYourSpyStatement(ePlayer, eStatement); + InfluenceLevelTypes eInfluenceLevel = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()); + InfluenceLevelTrend eInfluenceTrend = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(GetID()); - DoTheySupportedOurHosting(ePlayer, eStatement); - DoWeLikedTheirProposal(ePlayer, eStatement); - DoWeDislikedTheirProposal(ePlayer, eStatement); - DoTheySupportedOurProposal(ePlayer, eStatement); - DoTheyFoiledOurProposal(ePlayer, eStatement); + if (eInfluenceLevel == INFLUENCE_LEVEL_FAMILIAR && eInfluenceTrend == INFLUENCE_TREND_RISING) // Familiar and rising? Slow them down! + { + return false; + } + else if (eInfluenceLevel == INFLUENCE_LEVEL_POPULAR) + { + if (eInfluenceTrend != INFLUENCE_TREND_FALLING) // Popular and not falling? Slow them down! + return false; - DoConvertedMyCityStatement(ePlayer, eStatement); + if (eVictoryDispute == DISPUTE_LEVEL_FIERCE || eVictoryBlock == BLOCK_LEVEL_FIERCE || GetBiggestCompetitor() == ePlayer) // Popular and fiercely competitive? Slow them down! + return false; + } + else if (eInfluenceLevel == INFLUENCE_LEVEL_INFLUENTIAL && eInfluenceTrend != INFLUENCE_TREND_RISING) // Already Influential, but not rising? Let's try to reverse that! + { + return false; + } + } + } + } - DoDugUpMyYardStatement(ePlayer, eStatement); + if (GetCivApproach(ePlayer) == CIV_APPROACH_WAR) + { + return false; + } + if (IsHasDefensivePact(ePlayer) || IsDoFAccepted(ePlayer)) + { + return true; + } + if (IsGoingForWorldConquest() || MusteringForNeighborAttack(ePlayer)) + { + return true; + } + if (GetCivApproach(ePlayer) >= CIV_APPROACH_AFRAID) + { + return true; + } - DoDoFStatement(ePlayer, eStatement); - DoDenounceFriendStatement(ePlayer, eStatement); - DoDenounceStatement(ePlayer, eStatement); - DoEndDoFStatement(ePlayer, eStatement); - //DoRequestFriendDenounceStatement(ePlayer, eStatement, iData1); + if (m_pPlayer->IsCramped() || (GET_PLAYER(ePlayer).getNumCities() * 3) > (m_pPlayer->getNumCities() * 2)) + { + return true; + } - if (!bSanctioned) + EconomicAIStrategyTypes eNeedRecon = (EconomicAIStrategyTypes) GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_RECON"); + EconomicAIStrategyTypes eNeedNavalRecon = (EconomicAIStrategyTypes) GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_RECON_SEA"); + if (m_pPlayer->GetEconomicAI()->IsUsingStrategy(eNeedRecon)) + { + return true; + } + if (m_pPlayer->GetEconomicAI()->IsUsingStrategy(eNeedNavalRecon)) + { + int iCityLoop = 0; + for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) + { + if (pLoopCity->isCoastal()) { - DoMapsOffer(ePlayer, eStatement, pDeal); - DoTechOffer(ePlayer, eStatement, pDeal); + return true; } - - DoRevokeVassalageStatement(ePlayer, eStatement, pDeal); - DoMakeVassalageStatement(ePlayer, eStatement, pDeal); - - DoLiberateMyVassalStatement(ePlayer, eStatement); } + } - // OFFERS - all members but ePlayer passed by address - if (!bSanctioned) + if (GetPlayer()->GetProximityToPlayer(ePlayer) == PLAYER_PROXIMITY_NEIGHBORS) + { + AICityStrategyTypes ePocketCity = (AICityStrategyTypes) GC.getInfoTypeForString("AICITYSTRATEGY_POCKET_CITY"); + int iCityLoop = 0; + for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) { - DoLuxuryTrade(ePlayer, eStatement, pDeal); - DoEmbassyExchange(ePlayer, eStatement, pDeal); - DoEmbassyOffer(ePlayer, eStatement, pDeal); - DoOpenBordersExchange(ePlayer, eStatement, pDeal); - DoOpenBordersOffer(ePlayer, eStatement, pDeal); - DoResearchAgreementOffer(ePlayer, eStatement, pDeal); - DoStrategicTrade(ePlayer, eStatement, pDeal); - DoDefensivePactOffer(ePlayer, eStatement, pDeal); - DoCityExchange(ePlayer, eStatement, pDeal); - DoThirdPartyWarTrade(ePlayer, eStatement, pDeal); - DoThirdPartyPeaceTrade(ePlayer, eStatement, pDeal); - DoVoteTrade(ePlayer, eStatement, pDeal); + if (pLoopCity->GetCityStrategyAI()->IsUsingCityStrategy(ePocketCity)) + { + return true; + } } + } - DoBecomeVassalageStatement(ePlayer, eStatement, pDeal); + return false; +} - DoShareIntrigueStatement(ePlayer, eStatement); +/// Are we willing to give Open Borders to eOtherPlayer? +bool CvDiplomacyAI::IsWillingToGiveOpenBordersToPlayer(PlayerTypes ePlayer) +{ + if (IsUntrustworthy(ePlayer)) + { + return false; + } - if (!bSanctioned) - { - DoRequest(ePlayer, eStatement, pDeal); + // Do not let them in if they think our beautiful fields are their living room! + if (GetNumTimesCultureBombed(ePlayer) > 0) + return false; - DoGenerousOffer(ePlayer, eStatement, pDeal); + // Are they here to steal our PRICELESS ARCHAEOLOGICAL ARTIFACTS??? + if (GetNumArtifactsEverDugUp(ePlayer) > 0) + { + int iHiddenSites = GetPlayer()->GetEconomicAI()->GetVisibleHiddenAntiquitySitesOwnTerritory(); + int iNormalSites = GetPlayer()->GetEconomicAI()->GetVisibleAntiquitySitesOwnTerritory() - iHiddenSites; + PolicyBranchTypes eHiddenSiteBranch = MOD_BALANCE_VP ? (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_AESTHETICS", true) : (PolicyBranchTypes)GC.getInfoTypeForString("POLICY_BRANCH_EXPLORATION", true); + + if (iNormalSites > 0) + { + return false; } - - // Second set of things we don't say to teammates - if (GetTeam() != GET_PLAYER(ePlayer).getTeam()) + // Have they unlocked the branch whose finisher lets them see hidden sites? + if (iHiddenSites > 0 && GET_PLAYER(ePlayer).GetPlayerPolicies()->IsPolicyBranchUnlocked(eHiddenSiteBranch)) { - //DoNowUnforgivableStatement(ePlayer, eStatement); - //DoNowEnemyStatement(ePlayer, eStatement); - //DoFriendlyStatement(ePlayer, eStatement); - - DoAfraidStatement(ePlayer, eStatement); - DoHostileStatement(ePlayer, eStatement); - DoWarmongerStatement(ePlayer, eStatement); - DoMinorCivCompetitionStatement(ePlayer, eStatement, iData1); - - // Don't bother with this fluff stuff it's just AI on AI stuff - if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - { - DoAngryBefriendedEnemy(ePlayer, eStatement, iData1); - DoAngryDenouncedFriend(ePlayer, eStatement, iData1); - - DoHappyDenouncedEnemy(ePlayer, eStatement, iData1); - DoHappyBefriendedFriend(ePlayer, eStatement, iData1); - - DoFYIBefriendedHumanEnemy(ePlayer, eStatement, iData1); - DoFYIDenouncedHumanFriend(ePlayer, eStatement, iData1); - - DoFYIDenouncedHumanEnemy(ePlayer, eStatement, iData1); - DoFYIBefriendedHumanFriend(ePlayer, eStatement, iData1); - DoHappySamePolicyTree(ePlayer, eStatement); - DoIdeologicalStatement(ePlayer, eStatement); - DoVictoryCompetitionStatement(ePlayer, eStatement); - - DoVassalTaxesRaisedStatement(ePlayer, eStatement); - DoVassalTaxesLoweredStatement(ePlayer, eStatement); - } + return false; } } - // AT WAR - else if (!IsAlwaysAtWar(ePlayer)) + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsCloseToWorldConquest()) { - // OFFERS - all members but ePlayer passed by address - DoPeaceOffer(ePlayer, eStatement, pDeal); - - // If not offering peace, can still denounce while at war! - DoDenounceStatement(ePlayer, eStatement); + return false; } - - // Reset avoiding deals value - SetAvoidDeals(false); - -#if !defined(FINAL_RELEASE) || defined(VPDEBUG) - // Check for an optional message injection from the Tuner - if(eStatement == NO_DIPLO_STATEMENT_TYPE && m_eTestStatement != NO_DIPLO_STATEMENT_TYPE && ePlayer == m_eTestToPlayer) + if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) { - eStatement = m_eTestStatement; - iData1 = m_iTestStatementArg1; - - m_eTestStatement = NO_DIPLO_STATEMENT_TYPE; + return false; } -#endif - - // Now send the message - if (eStatement != NO_DIPLO_STATEMENT_TYPE) + if (IsArmyInPlaceForAttack(ePlayer)) { - LogStatementToPlayer(ePlayer, eStatement); - SetTurnStatementLastSent(ePlayer, eStatement, GC.getGame().getGameTurn()); - DoSendStatementToPlayer(ePlayer, eStatement, iData1, pRenewDeal ? pRenewDeal : pDeal); + return false; } -} - -/// Any Minor Civs we want to chat with? -void CvDiplomacyAI::DoContactMinorCivs() -{ - if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) - return; + + if (MOD_BALANCE_FLIPPED_OPEN_BORDERS_TOURISM) + { + // If going for culture win we always want to open our borders to civs we need influence on + if (IsGoingForCultureVictory()) + { + InfluenceLevelTypes eInfluenceLevel = m_pPlayer->GetCulture()->GetInfluenceLevel(ePlayer); + InfluenceLevelTrend eInfluenceTrend = m_pPlayer->GetCulture()->GetInfluenceTrend(ePlayer); - int iDiplomacyFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_DIPLOMACY")); - int iGoldFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_GOLD")); - int iTileImprovementFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_TILE_IMPROVEMENT")); + if (eInfluenceLevel < INFLUENCE_LEVEL_INFLUENTIAL || eInfluenceTrend <= INFLUENCE_TREND_STATIC) + return true; + } + } + else + { + // If they need influence over us and they aren't our liberator or DP, we don't want to give them OB, thanks. + if (IsCompetingForVictory() && !IsHasDefensivePact(ePlayer) && GetNumCitiesLiberatedBy(ePlayer) <= 0) + { + DisputeLevelTypes eVictoryDispute = GetVictoryDisputeLevel(ePlayer); + BlockLevelTypes eVictoryBlock = GetVictoryBlockLevel(ePlayer); - bool bExpandToOtherContinents = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_EXPAND_TO_OTHER_CONTINENTS")); - bool bNeedHappiness = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_HAPPINESS")); - bool bNeedHappinessCritical = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_HAPPINESS_CRITICAL")); - bool bLosingMoney = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_LOSING_MONEY")); + if (eVictoryDispute > DISPUTE_LEVEL_NONE || eVictoryBlock > BLOCK_LEVEL_NONE) + { + InfluenceLevelTypes eInfluenceLevel = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(GetID()); + InfluenceLevelTrend eInfluenceTrend = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceTrend(GetID()); - // ************************** - // Would we like to buyout a minor this turn? (Austria UA) - // ************************** + if (eInfluenceLevel == INFLUENCE_LEVEL_FAMILIAR && eInfluenceTrend == INFLUENCE_TREND_RISING) // Familiar and rising? Slow them down! + { + return false; + } + else if (eInfluenceLevel == INFLUENCE_LEVEL_POPULAR) + { + if (eInfluenceTrend != INFLUENCE_TREND_FALLING) // Popular and not falling? Slow them down! + return false; - bool bWantsToBuyout = GetPlayer()->IsAbleToAnnexCityStates(); - bool bWantsToMarry = GetPlayer()->GetPlayerTraits()->IsDiplomaticMarriage(); + if (eVictoryDispute == DISPUTE_LEVEL_FIERCE || eVictoryBlock == BLOCK_LEVEL_FIERCE || GetBiggestCompetitor() == ePlayer) // Popular and fiercely competitive? Slow them down! + return false; + } + else if (eInfluenceLevel == INFLUENCE_LEVEL_INFLUENTIAL && eInfluenceTrend != INFLUENCE_TREND_RISING) // Already Influential, but not rising? Let's try to reverse that! + { + return false; + } + } + } + } - // ************************** - // Would we like to forcefully annex a minor this turn? - // ************************** + if (GetCivApproach(ePlayer) == CIV_APPROACH_WAR) + { + return false; + } + if (IsHasDefensivePact(ePlayer) || IsDoFAccepted(ePlayer)) + { + return true; + } + if (GetCivApproach(ePlayer) >= CIV_APPROACH_AFRAID) + { + return true; + } + if (GetMilitaryStrengthComparedToUs(ePlayer) <= STRENGTH_AVERAGE) + { + return true; + } - bool bWantsToBullyAnnex = GetPlayer()->GetPlayerTraits()->IsBullyAnnex(); + return false; +} - // ************************** - // Would we like to give a gold gift this turn? - // ************************** +/// Are we willing to swap Open Borders with ePlayer? +bool CvDiplomacyAI::IsOpenBordersExchangeAcceptable(PlayerTypes ePlayer) +{ + return IsWillingToGiveOpenBordersToPlayer(ePlayer) && IsWantsOpenBordersWithPlayer(ePlayer); +} - bool bWantsToMakeGoldGift = false; - // If we're a highly diplomatic leader, then always look for an opportunity - if(iDiplomacyFlavor >= /*4*/ GD_INT_GET(MC_ALWAYS_GIFT_DIPLO_THRESHOLD) || - IsGoingForDiploVictory() || - IsGoingForCultureVictory() || - GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT) || - IsHasActiveGoldQuest() || - m_pPlayer->calculateGoldRate() > 100) // if we are very wealthy always do this +///////////////////////////////////////////////////////// +// Planning Exchanges +///////////////////////////////////////////////////////// + +/// Is this war not worth caring about? +bool CvDiplomacyAI::IsPhonyWar(PlayerTypes ePlayer, bool bIgnoreCurrentApproach /* = false */) const +{ + if (!IsAtWar(ePlayer)) + return false; + + // Approach is WAR + if (!bIgnoreCurrentApproach && GetCivApproach(ePlayer) == CIV_APPROACH_WAR) + return false; + + // They captured our cities before, don't dismiss them. + if (GetNumCitiesCapturedBy(ePlayer) > 0) + return false; + + // War state too high or too low + WarStateTypes eWarState = GetWarState(ePlayer); + if (eWarState >= WAR_STATE_OFFENSIVE || eWarState <= WAR_STATE_TROUBLED) + return false; + + // War score is too high + int iWarScore = GetPlayer()->GetDiplomacyAI()->GetWarScore(ePlayer); + if (iWarScore <= GetWarscoreThresholdNegative() || iWarScore >= GetWarscoreThresholdPositive()) + return false; + + // We want to conquer them! + if (IsWantsToConquer(ePlayer)) + return false; + + // We have offensive operations ongoing. + if (m_pPlayer->HasAnyOffensiveOperationsAgainstPlayer(ePlayer)) + return false; + + // Our cities are threatened by them or vice versa + if (GetPlayer()->GetDiplomacyAI()->GetNumberOfThreatenedCities(ePlayer) > 0 || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumberOfThreatenedCities(GetID()) > 0) + return false; + + // They're too close or have a lot of soldiers nearby + PlayerProximityTypes eProximity = GetPlayer()->GetProximityToPlayer(ePlayer); + AggressivePostureTypes ePosture = GetMilitaryAggressivePosture(ePlayer); + StrengthTypes eMilitaryStrength = GetMilitaryStrengthComparedToUs(ePlayer); + + if (eProximity == PLAYER_PROXIMITY_NEIGHBORS) { - bWantsToMakeGoldGift = true; + return false; + } + else if (eProximity == PLAYER_PROXIMITY_CLOSE) + { + if (ePosture >= AGGRESSIVE_POSTURE_MEDIUM || eMilitaryStrength >= STRENGTH_POWERFUL) + return false; } - // Otherwise, do a random roll else { - int iThreshold = iDiplomacyFlavor; - int iRandRoll = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xbe42edf0).mix(GetID())); + if (ePosture >= AGGRESSIVE_POSTURE_HIGH || eMilitaryStrength == STRENGTH_IMMENSE) + return false; + } - // Threshold will be 15 for a player (3 flavor * 5) - // Threshold will be 5 for non-diplomatic player (2 flavor * 5) + return true; +} - if(iRandRoll < iThreshold) - bWantsToMakeGoldGift = true; - } +/// Does the AI even want to conquer another player if they are at war? +/// Since there is no "defensive war" flag, this seems to be the best way to differentiate +bool CvDiplomacyAI::IsWantsToConquer(PlayerTypes ePlayer) const +{ + if (!IsAtWar(ePlayer)) + return false; - // ************************** - // Would we like to get a unit by bullying this turn? - // ************************** + if (IsAlwaysAtWar(ePlayer)) + return true; + + if (GC.getGame().GetNumMajorCivsAlive() == 2) + return true; + + if (GetPlayer()->IsAITeammateOfHuman()) + return true; - bool bWantsToBullyUnit = false; + // Doing well already - let's keep it up. + if (GetWarState(ePlayer) >= WAR_STATE_OFFENSIVE) + return true; - // Would we like to get Heavy Tribute by bullying this turn? - // Loop through all (known) Minors - for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) - { - PlayerTypes eMinor = (PlayerTypes) iMinorLoop; + // Captured one of our key cities? + if (IsCapitalCapturedBy(ePlayer, true, false) || IsHolyCityCapturedBy(ePlayer, true, false)) + return true; + + // If they're about to win, we have nothing to lose! + if (IsEndgameAggressiveTo(ePlayer)) + return true; - if (!GET_PLAYER(eMinor).isMinorCiv()) - continue; + // If we're in bad shape for war, retreat! + if (GetPlayer()->IsInTerribleShapeForWar()) + return false; + + TargetValueTypes eTargetValue = GetTargetValue(ePlayer); + bool bWeHaveGoodAttackTarget = GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer); + bool bAggressive = GetPlayer()->GetPlayerTraits()->IsWarmonger() || (IsCompetingForVictory() && IsGoingForWorldConquest()); - if (GetCivApproach(eMinor) > CIV_APPROACH_HOSTILE) - continue; + // They're an easy target, so play offensively! + if (IsEasyTarget(ePlayer) && bWeHaveGoodAttackTarget) + return true; - if (MOD_BALANCE_HEAVY_TRIBUTE) + // We started or wanted this war - if they're not a bad target we should attack at full force + if (IsAggressor(ePlayer)) + { + // Bad target + if (eTargetValue == TARGET_VALUE_IMPOSSIBLE || (eTargetValue == TARGET_VALUE_BAD && !bWeHaveGoodAttackTarget)) { - if (GET_PLAYER(eMinor).GetMinorCivAI()->CalculateBullyScore(GetID(), false) >= 50) + // Aggressive and fiercely competitive players don't give up so easily + if (IsMajorCompetitor(ePlayer) && bAggressive) { - bWantsToBullyUnit = true; + return true; } - } - else - { - if (GetPlayer()->GetEconomicAI()->GetWorkersToCitiesRatio() < 0.25 && GetPlayer()->GetEconomicAI()->GetImprovedToImprovablePlotsRatio() < 0.50) + + // We're in a coop war with another player against this guy + if (GetGlobalCoopWarAgainstState(ePlayer) >= COOP_WAR_STATE_PREPARING) { - bWantsToBullyUnit = true; + return true; } - // Otherwise, do a random roll - else - { - int iThreshold = iTileImprovementFlavor; //antonjs: todo: XML - int iRandRoll = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xc87e263f).mix(GetID())); - if (iRandRoll < iThreshold) - bWantsToBullyUnit = true; - } + return false; } - } - - // ************************** - // Would we like to get some gold by bullying this turn? - // ************************** - bool bWantsToBullyGold = false; - - if(iGoldFlavor >= 6 || //antonjs: todo: GD_INT_GET(MC_ALWAYS_BULLY_GOLD_THRESHOLD) - IsGoingForWorldConquest() || - GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_UNIT) || - GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_BUILDING) || - bLosingMoney || - m_pPlayer->calculateGoldRate() < 0) // if we are losing gold per turn - { - bWantsToBullyGold = true; + return true; } - // Otherwise, do a random roll + // Didn't start or want this war - don't care as much else { - int iThreshold = iGoldFlavor; //antonjs: todo: XML - int iRandRoll = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x8ee31b26).mix(GetID())); - - if(iRandRoll < iThreshold) - bWantsToBullyGold = true; + // Aggressive and nearby players + if (bAggressive && GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE) + { + if (eTargetValue == TARGET_VALUE_IMPOSSIBLE) + { + return false; + } + else if (eTargetValue == TARGET_VALUE_BAD && !bWeHaveGoodAttackTarget) + { + return false; + } + } + // Other players + else if (eTargetValue <= TARGET_VALUE_BAD || (eTargetValue <= TARGET_VALUE_AVERAGE && !bWeHaveGoodAttackTarget)) + { + return false; + } } - CvWeightedVector veMinorsToBuyout; // BNW Austria UA - CvWeightedVector veMinorsToMarry; // VP Austria UA - CvWeightedVector veMinorsToBullyAnnex; //Rome UA - CvWeightedVector veMinorsToGiveGold; - CvWeightedVector veMinorsToBullyGold; - CvWeightedVector veMinorsToBullyUnit; - - int iLargeGift = /*1000*/ GD_INT_GET(MINOR_GOLD_GIFT_LARGE); - int iMediumGift = /*500*/ GD_INT_GET(MINOR_GOLD_GIFT_MEDIUM); - int iSmallGift = /*250*/ GD_INT_GET(MINOR_GOLD_GIFT_SMALL); + return true; //by default we conquer everything +} - PlayerTypes eID = GetID(); +/// Is this major civ a potential military target or threat? +bool CvDiplomacyAI::IsPotentialMilitaryTargetOrThreat(PlayerTypes ePlayer, bool bFromApproachSelection) const +{ + if (!GET_PLAYER(ePlayer).isMajorCiv() || !GET_PLAYER(ePlayer).isAlive() || GET_PLAYER(ePlayer).getNumCities() <= 0 || IsTeammate(ePlayer)) + return false; - int iGrowthFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_GROWTH")); - int iScienceFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_SCIENCE")); - int iCultureFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_CULTURE")); - int iFaithFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_RELIGION")); - int iOffenseFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_OFFENSE")); - int iHappinessFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_HAPPINESS")); - int iProductionFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_PRODUCTION")) / 2; + if (!IsAtWar(ePlayer) && GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE)) + return false; - if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0) + bool bVassal = IsVassal(ePlayer); + if (!bVassal) { - if (!GetPlayer()->IsEmpireUnhappy()) - { - bWantsToBullyUnit = true; - bWantsToBullyGold = true; - bWantsToMakeGoldGift = false; - } + if (IsNukedBy(ePlayer) || IsCapitalCapturedBy(ePlayer) || IsHolyCityCapturedBy(ePlayer)) + return true; } - if (GetPlayer()->IsCanBullyFriendlyCS()) - { - bWantsToBullyUnit = true; - bWantsToBullyGold = true; - bWantsToMakeGoldGift = false; - } - else if (GetPlayer()->GetBullyGlobalCSReduction()) - { - bWantsToBullyUnit = true; - bWantsToBullyGold = true; - bWantsToMakeGoldGift = false; - } + bool bFriends = IsMaster(ePlayer) || IsFriendOrAlly(ePlayer); - // Loop through all (known) Minors - for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) + // Only care if this player is close to us + if (GetPlayer()->GetProximityToPlayer(ePlayer) >= PLAYER_PROXIMITY_CLOSE || GET_PLAYER(ePlayer).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE || GetMilitaryAggressivePosture(ePlayer) >= AGGRESSIVE_POSTURE_MEDIUM) { - PlayerTypes eMinor = (PlayerTypes) iMinorLoop; - - CvPlayer* pMinor = &GET_PLAYER(eMinor); - CvMinorCivAI* pMinorCivAI = pMinor->GetMinorCivAI(); - - bool bWantsToConnect = false; - bool bWantsToGiveGoldToThisMinor = false; - bool bWantsToBullyUnitFromThisMinor = false; - bool bWantsToBuyoutThisMinor = false; - bool bWantsToMarryThisMinor = false; - bool bWantsToBullyAnnexThisMinor = false; - - if(IsPlayerValid(eMinor)) + bool bStronger = GetRawMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE || GetEconomicStrengthComparedToUs(ePlayer) > STRENGTH_STRONG || GetWarmongerThreat(ePlayer) >= THREAT_SEVERE; + if (!bVassal) { - // Can't do anything with minors we're at war with, besides make peace (which isn't done here, but in DoMakePeaceWithMinors()) - if(IsAtWar(eMinor)) - continue; - - CivApproachTypes eApproach = GetCivApproach(eMinor); + if (bStronger && !bFriends) + return true; - // Do we want to change our protection of this minor? - DoUpdateMinorCivProtection(eMinor); + if (GetNumCitiesCapturedBy(ePlayer) > 0) + return true; - // Do we want to connect to this player? - int iEra = GetPlayer()->GetCurrentEra(); - if (iEra <= 0) - iEra = 1; + // Going to war? + if (AvoidExchangesWithPlayer(ePlayer, true, bFromApproachSelection)) + return true; + } - if (eApproach == CIV_APPROACH_FRIENDLY && GetPlayer()->getAvgGoldRate() > min(20 * iEra,50)) + // If they're stronger than us, our master, or if their military strength is at least POOR (one level below), let's check diplomacy + if (bStronger || bVassal || GetRawMilitaryStrengthComparedToUs(ePlayer) >= STRENGTH_POOR) + { + vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); + vector vPlayersToCheck = GetOffensiveWarAllies(ePlayer, /*bIncludeMinors*/ false, /*bReverseMode*/ true); + vPlayersToCheck.push_back(ePlayer); + for (std::vector::iterator it = vPlayersToCheck.begin(); it != vPlayersToCheck.end(); ++it) { - if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) + for (size_t i=0; iIsAllies(eID)) - { - bWantsToConnect = true; - } - else if (pMinorCivAI->IsActiveQuestForPlayer(eID, MINOR_CIV_QUEST_ROUTE)) - { - bWantsToConnect = true; - } - } - } + CvPlayer* pMyTeamPlayer = &GET_PLAYER(vMyTeam[i]); + CvDiplomacyAI* pDiplo = pMyTeamPlayer->GetDiplomacyAI(); + if (!pMyTeamPlayer->isMajorCiv() || pMyTeamPlayer->getNumCities() <= 0) + continue; - // Calculate desirability to forcefully annex this minor - if (bWantsToBullyAnnex) - { - int iValue = 100; //antonjs: todo: xml - // Only bother if we actually can annex - CvCity* pMinorCapital = pMinor->getCapitalCity(); - if (pMinor->GetMinorCivAI()->CanMajorBullyUnit(eID) && pMinorCapital != NULL) - { - // Determine presence of player cities on this continent - CvArea* pMinorArea = pMinorCapital->plot()->area(); - bool bPresenceInArea = false; - int iMajorCapitalsInArea = 0; - if (pMinorArea) - { - // Do we have a city here? - if (pMinorArea->getCitiesPerPlayer(eID) > 0) - bPresenceInArea = true; + // Backstabber? + // Annoying to check it like this, but because this function is called a lot, we want to optimize where possible. + if (GET_PLAYER(*it).getTeam() == GET_PLAYER(ePlayer).getTeam() && pDiplo->IsUntrustworthyFriend(*it)) + return true; - // Does another major civ have their capital here? (must be visible) - for (int iMajorRivalLoop = 0; iMajorRivalLoop < MAX_MAJOR_CIVS; iMajorRivalLoop++) - { - PlayerTypes eMajorRivalLoop = (PlayerTypes)iMajorRivalLoop; - if (eMajorRivalLoop == eID) - continue; + if (!pMyTeamPlayer->isAlive()) + continue; - if (GET_PLAYER(eMajorRivalLoop).isAlive()) - { - CvCity* pCapital = GET_PLAYER(eMajorRivalLoop).getCapitalCity(); - if (pCapital && pCapital->plot()) - { - CvPlot* pPlot = pCapital->plot(); - if (pPlot->isVisible(GetTeam())) - iMajorCapitalsInArea++; - } - } - } - } - else + if (!bVassal) { - ASSERT(false, "Could not lookup minor civ's area!"); - } + bool bIgnoreSelfApproach = pMyTeamPlayer->isHuman(ISHUMAN_AI_DIPLOMACY) || (bFromApproachSelection && pMyTeamPlayer->GetID() == GetID()); - // How many units does the city-state have? - int iMinorMilitaryUnits = 0; - int iMinorUnits = 0; - int iLoop = 0; - for (CvUnit* pLoopUnit = pMinor->firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = pMinor->nextUnit(&iLoop)) - { - if (pLoopUnit->IsCanAttack() && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE_SEA) + // Is this a Defensive Pact? + if (GET_PLAYER(*it).GetDiplomacyAI()->IsHasDefensivePact(ePlayer)) { - iMinorMilitaryUnits++; - } - iMinorUnits++; - } + // If we're going to war with their DP, they might be a threat or target. + // Otherwise, we don't care. + if (AvoidExchangesWithPlayer(*it, true, bIgnoreSelfApproach)) + return true; - // Foreign continent - if (!bPresenceInArea) - { - // Military foothold to attack other majors - if (IsGoingForWorldConquest() && iMajorCapitalsInArea > 0) - { - iValue += 100; //antonjs: todo: xml - } - // Expansion - else if (bExpandToOtherContinents) - { - iValue += 60; //antonjs: todo: xml - } - else - { - iValue += -50; //antonjs: todo: xml - } - } - // Continent we have presence on - else - { - // Proximity plays a large factor, since we don't want a remote, isolated city - if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) - { - iValue += 100; //antonjs: todo: xml - // Military units could come to our rescue quickly - if (GetStateAllWars() == STATE_ALL_WARS_LOSING) - { - if (iMinorMilitaryUnits > 0) //antonjs: todo: xml - { - iValue += (iMinorMilitaryUnits) * 10; //antonjs: todo: xml - } - else - { - iValue -= 50; //antonjs: todo: xml - } - } - } - else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) - { - iValue += 10; //antonjs: todo: xml - } - else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) - { - iValue += -50; //antonjs: todo: xml + continue; } - else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_DISTANT) + + // Bad approach towards them? + if (!bIgnoreSelfApproach) { - iValue += -100; //antonjs: todo: xml + CivApproachTypes eApproach = pDiplo->GetCivApproach(*it); + if (eApproach == CIV_APPROACH_AFRAID && !bFriends) + return true; + + if (eApproach <= CIV_APPROACH_GUARDED) + return true; } } - // Military units - How many, and can we support them? - if (GetPlayer()->GetNumUnitsSupplied() >= GetPlayer()->getNumUnits() + iMinorUnits) - { - iValue += (iMinorMilitaryUnits) * 5; + // Negative approach towards us? + CivApproachTypes eApproachTowardsUs = pDiplo->GetVisibleApproachTowardsUs(*it); + if (eApproachTowardsUs <= CIV_APPROACH_GUARDED) + return true; + + // Denouncement in either direction? + if (pDiplo->IsDenouncedPlayer(*it) || pDiplo->IsDenouncedByPlayer(*it)) + return true; + + // Previous war? + if (!bFriends && !bVassal && pDiplo->GetNumWarsDeclaredOnUs(*it) > 0) + return true; + + // Any other reason for them to be mad at us? + if (!bVassal && GET_PLAYER(*it).GetDiplomacyAI()->GetNumCitiesCapturedBy(vMyTeam[i]) > 0) + return true; + if (GET_PLAYER(*it).GetDiplomacyAI()->IsPlayerCapturedCapital(vMyTeam[i]) || GET_PLAYER(*it).GetDiplomacyAI()->IsPlayerCapturedHolyCity(vMyTeam[i])) + return true; + // Run these checks last because they're more expensive. + if (GET_PLAYER(*it).GetDiplomacyAI()->IsUntrustworthy(vMyTeam[i])) + return true; + if (bVassal && pDiplo->GetVassalTreatmentLevel(*it) <= VASSAL_TREATMENT_MISTREATED) + return true; + } + + if (GET_PLAYER(*it).GetNumOurCitiesOwnedBy(GetID()) > 0) + return true; + } + } + } + + return false; +} + +/// Returns if this player has been nuked by ePlayer +bool CvDiplomacyAI::IsNukedBy(PlayerTypes ePlayer) const +{ + return (GetNumTimesNuked(ePlayer) > 0); +} + +/// Is this player a friend or ally in any way? Quick heuristic check that only checks for good things. +bool CvDiplomacyAI::IsFriendOrAlly(PlayerTypes ePlayer) const +{ + if (IsTeammate(ePlayer)) + return true; + + if (!GET_PLAYER(ePlayer).isAlive() || ePlayer == BARBARIAN_PLAYER) + return false; + + if (IsDoFAccepted(ePlayer)) + return true; + + if (IsHasDefensivePact(ePlayer)) + return true; + + if (GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) + return true; + + if (GetDoFType(ePlayer) >= DOF_TYPE_ALLIES) + return true; + + if (WasResurrectedBy(ePlayer)) + return true; + + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->WasResurrectedBy(GetID())) + return true; + + return false; +} + +/// Is this player causing us problems in the early game? +bool CvDiplomacyAI::IsEarlyGameCompetitor(PlayerTypes ePlayer) +{ + // Not the early game + if (GetPlayer()->GetCurrentEra() > 2) + return false; + + if (GetCivOpinion(ePlayer) <= CIV_OPINION_ENEMY) + return true; + + if (GetLandDisputeLevel(ePlayer) > DISPUTE_LEVEL_WEAK) + return true; + + if (GetNumWondersBeatenTo(ePlayer) > 0) + return true; + + if (GetNumDemandsMade(ePlayer) > 0) + return true; + + if (GetNegativeReligiousConversionPoints(ePlayer) > 0) + return true; + + if (GetNumTimesTheyPlottedAgainstUs(ePlayer) > 0) + return true; + + if (IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetID())) + return true; + + // Major early lead in techs/policies? Let's slow that down. + if (GetPolicyBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG || GetTechBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) + return true; + + // Special check for England / Statecraft + if (GetNumTimesRobbedBy(ePlayer) > 0 || GetNumTimesPerformedCoupAgainstUs(ePlayer) > 0) + return true; + + if (IgnoredBullyCityStatePromise(ePlayer) || BrokeBullyCityStatePromise(ePlayer) || IsAngryAboutProtectedMinorAttacked(ePlayer) || IsAngryAboutProtectedMinorKilled(ePlayer)) + return true; + + if (IsUntrustworthy(ePlayer)) + return true; + + // Are they a juicy target? + if (IsEasyTarget(ePlayer) || GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(ePlayer)) + { + PlayerProximityTypes eProximity = GetPlayer()->GetProximityToPlayer(ePlayer); + bool bRecklessExpander = IsRecklessExpander(ePlayer); + bool bWonderSpammer = IsWonderSpammer(ePlayer); + + if (eProximity == PLAYER_PROXIMITY_NEIGHBORS) + { + if (bRecklessExpander || bWonderSpammer) + return true; + } + else if (eProximity == PLAYER_PROXIMITY_CLOSE) + { + if (IsConqueror() || IsSecondaryConqueror()) + { + if (bRecklessExpander || bWonderSpammer) + return true; + } + else if (IsCultural() || IsSecondaryCultural()) + { + if (bWonderSpammer) + return true; + } + } + } + + return false; +} + +/// Should we ignore Social Policy differences with ePlayer? +bool CvDiplomacyAI::IsIgnorePolicyDifferences(PlayerTypes ePlayer) const +{ + if (!GetPlayer()->isMajorCiv() || !GET_PLAYER(ePlayer).isMajorCiv()) + return true; + + if (IsTeammate(ePlayer) || IsVassal(ePlayer) || IsMaster(ePlayer)) + return true; + + if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) + return false; + + // Capital or Holy City captured? Exception if they resurrected us: only test to see if they captured it. + if (WasResurrectedBy(ePlayer)) + { + vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); + for (size_t i=0; i= DOF_TYPE_ALLIES) + return true; + + // Ideological or religious buddies tolerate policy differences. + if (IsPlayerSameIdeology(ePlayer) || IsPlayerSameReligion(ePlayer)) + return true; + + if (IsCityRecentlyLiberatedBy(ePlayer)) + return true; + + // The presence of a Diplomat smoothes over cultural (but not religious/ideological) differences. + if (GetPlayer()->GetEspionage()->IsMyDiplomatVisitingThem(ePlayer) || GetPlayer()->GetEspionage()->IsOtherDiplomatVisitingMe(ePlayer)) + return true; + + // If they're helping us go to war, we'll set aside our differences for now. + if (GetCoopWarAgreementScore(ePlayer) > 0 || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) + return true; + + return false; +} + +/// Should we ignore religious differences with ePlayer? +bool CvDiplomacyAI::IsIgnoreReligionDifferences(PlayerTypes ePlayer) const +{ + if (!GetPlayer()->isMajorCiv() || !GET_PLAYER(ePlayer).isMajorCiv()) + return true; + + if (IsTeammate(ePlayer) || IsMaster(ePlayer)) + return true; + + if (GetNegativeReligiousConversionPoints(ePlayer) > 0) + return false; + + if (IsHolyCityCapturedBy(ePlayer)) + return false; + + // Capital captured? Exception if they resurrected us: only test to see if they captured it. + if (WasResurrectedBy(ePlayer)) + { + vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); + for (size_t i=0; iHasEverConvertedCity(GetID())) + { + if (GetPlayer()->GetPlayerTraits()->IsNoNaturalReligionSpread()) + return true; + + if (GET_PLAYER(ePlayer).GetPlayerTraits()->IsNoNaturalReligionSpread()) + return true; + } + + if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) + return false; + + // Ideological buddies tolerate religious differences. + if (IsPlayerSameIdeology(ePlayer)) + return true; + + if (IsPlayerLiberatedCapital(ePlayer) || IsPlayerLiberatedHolyCity(ePlayer) || IsPlayerReturnedCapital(ePlayer) || IsPlayerReturnedHolyCity(ePlayer) || WasResurrectedBy(ePlayer) || GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) + return true; + + if (IsCityRecentlyLiberatedBy(ePlayer)) + return true; + + // If they're helping us go to war, we'll set aside our differences for now. + if (GetCoopWarAgreementScore(ePlayer) > 1 || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) + return true; + + return false; +} + +/// Should we ignore ideological differences with ePlayer? +bool CvDiplomacyAI::IsIgnoreIdeologyDifferences(PlayerTypes ePlayer) const +{ + if (!GetPlayer()->isMajorCiv() || !GET_PLAYER(ePlayer).isMajorCiv()) + return true; + + if (IsTeammate(ePlayer) || IsVassal(ePlayer) || IsMaster(ePlayer)) + return true; + + if (IsAtWar(ePlayer) || IsUntrustworthy(ePlayer)) + return false; + + // Capital or Holy City captured? Exception if they resurrected us: only test to see if they captured it. + if (WasResurrectedBy(ePlayer)) + { + vector vTheirTeam = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPlayers(); + for (size_t i=0; i 2 || GetGlobalCoopWarWithState(ePlayer) >= COOP_WAR_STATE_PREPARING) + return true; + + // Cold War resolution active? + CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); + if (pLeague != NULL) + { + // Loop through all (known) Players + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + { + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (IsPlayerValid(eLoopPlayer)) + { + if (GC.getGame().GetGameLeagues()->IsIdeologyEmbargoed(GetID(), eLoopPlayer)) + { + return false; + } + } + } + } + + if (IsPlayerLiberatedHolyCity(ePlayer) || IsCityRecentlyLiberatedBy(ePlayer)) + return true; + + return false; +} + +/// Is this player causing trouble around our Minor Civs? +bool CvDiplomacyAI::IsMinorCivTroublemaker(PlayerTypes ePlayer, bool bIgnoreBullying /* = false */) const +{ + if (GetMinorCivDisputeLevel(ePlayer) > DISPUTE_LEVEL_WEAK) + return true; + + if (GetNumTimesPerformedCoupAgainstUs(ePlayer) > 0) + return true; + + if (IsAngryAboutProtectedMinorKilled(ePlayer) || IsAngryAboutProtectedMinorAttacked(ePlayer) || BrokeAttackCityStatePromise(ePlayer) || IgnoredAttackCityStatePromise(ePlayer)) + return true; + + if (!bIgnoreBullying) + { + if (IsAngryAboutProtectedMinorBullied(ePlayer) || BrokeBullyCityStatePromise(ePlayer) || IgnoredBullyCityStatePromise(ePlayer)) + return true; + } + + return false; +} + +/// Diplomacy AI Options +/// Should we specifically hide dispute-related modifiers towards ePlayer? +bool CvDiplomacyAI::ShouldHideDisputeMods(PlayerTypes ePlayer) const +{ + // Game options forbid hiding. + if (MOD_DIPLOAI_SHOW_HIDDEN_OPINION_MODIFIERS || GC.getGame().isOption(GAMEOPTION_TRANSPARENT_DIPLOMACY)) + return false; + + // If we're at war, don't bother. + if (IsAtWar(ePlayer)) + return false; + + // If we've declared war on them previously, let's be honest about disputes. + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetNumWarsDeclaredOnUs(GetID()) > 0) + return false; + + // If we're their vassal, don't bother. + if (IsVassal(ePlayer)) + return false; + + // If we've denounced them, don't bother. + if (IsDenouncedPlayer(ePlayer)) + return false; + + // If they've liberated us, let's be honest. + if (IsLiberator(ePlayer, false, false)) + return false; + + // If they're untrustworthy, don't bother hiding anything. + if (IsUntrustworthy(ePlayer)) + return false; + + CivApproachTypes eSurfaceApproach = GetSurfaceApproach(ePlayer); + + // Only hide if our surface approach is FRIENDLY or AFRAID + return eSurfaceApproach == CIV_APPROACH_FRIENDLY || eSurfaceApproach == CIV_APPROACH_AFRAID; +} + +/// Should we hide certain negative opinion modifiers towards ePlayer? (stuff like competition, warmongering etc) +/// NOTE: If both hiding dispute mods and hiding negative mods, *and* DIPLOAI_HONEST_OPINION_MODIFIERS=0, dispute mods will show up as if the DisputeLevel was NONE. Otherwise, dispute mods are simply hidden. +bool CvDiplomacyAI::ShouldHideNegativeMods(PlayerTypes ePlayer) const +{ + // Game options forbid hiding. + if (MOD_DIPLOAI_SHOW_HIDDEN_OPINION_MODIFIERS || GC.getGame().isOption(GAMEOPTION_TRANSPARENT_DIPLOMACY)) + return false; + + // If we're at war, don't bother. + if (IsAtWar(ePlayer)) + return false; + + // If we're their vassal, don't bother. + if (IsVassal(ePlayer)) + return false; + + // If we've denounced them, don't bother. + if (IsDenouncedPlayer(ePlayer)) + return false; + + // If they've liberated us, let's be honest. + if (IsLiberator(ePlayer, false, false)) + return false; + + // If they're untrustworthy, don't bother hiding anything. + if (IsUntrustworthy(ePlayer)) + return false; + + CivApproachTypes eSurfaceApproach = GetSurfaceApproach(ePlayer); + + // Always hide if our surface approach is FRIENDLY or AFRAID + if (eSurfaceApproach == CIV_APPROACH_FRIENDLY || eSurfaceApproach == CIV_APPROACH_AFRAID) + return true; + + // Never hide if our surface approach is HOSTILE + if (eSurfaceApproach == CIV_APPROACH_HOSTILE) + return false; + + // If we're acting hostile, don't hide anything. + if (IsActHostileTowardsHuman(ePlayer)) + return false; + + // If they're a favorable target, let's not bother hiding things. + if (GetMilitaryStrengthComparedToUs(ePlayer) < STRENGTH_POWERFUL) + { + if (IsEasyTarget(ePlayer) || GetTargetValue(ePlayer) >= TARGET_VALUE_FAVORABLE) + return false; + } + + return true; // Let's conceal our negative thoughts! +} + + +// ************************************ +// Evaluation of Other Players' Tendencies +// ************************************ + + +/// ePlayer made peace with someone, so figure out what that means +void CvDiplomacyAI::DoWeMadePeaceWithSomeone(TeamTypes eOtherTeam) +{ + if (eOtherTeam < 0 || eOtherTeam >= MAX_CIV_TEAMS) return; + + vector vPlayersToReevaluate; + vector vOtherTeam = GET_TEAM(eOtherTeam).getPlayers(); + for (size_t i=0; igetFirstOffensiveAIOperation(ePeacePlayer) != NULL) + { + GetPlayer()->StopAllLandOffensiveOperationsAgainstPlayer(ePeacePlayer,AI_ABORT_WAR_STATE_CHANGE); + GetPlayer()->StopAllSeaOffensiveOperationsAgainstPlayer(ePeacePlayer,AI_ABORT_WAR_STATE_CHANGE); + } + + // Reset values specific to this war + SetAggressor(ePeacePlayer, false); + SetWarProgressScore(ePeacePlayer, 0); + + // Reset number of turns locked into war + GET_TEAM(GetTeam()).SetNumTurnsLockedIntoWar(GET_PLAYER(ePeacePlayer).getTeam(), 0); + + if (GET_PLAYER(ePeacePlayer).isMajorCiv()) + { + CancelCoopWarsAgainstPlayer(ePeacePlayer, false); + + // Halve war weariness + GetPlayer()->SetWarWeariness(ePeacePlayer, GetPlayer()->GetWarWeariness(ePeacePlayer) / 2); + + // Clear penalties for stealing territory and refusing to go on coop wars now that we made peace + if (!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) + { + SetNumTimesCultureBombed(ePeacePlayer, 0); + if (GetCoopWarAgreementScore(ePeacePlayer) < 0) + SetCoopWarAgreementScore(ePeacePlayer, 0); + + vPlayersToReevaluate.push_back(ePeacePlayer); + } + } + else if (GET_PLAYER(ePeacePlayer).isMinorCiv()) + { + // Reset flags for taunt messages we sent to other civs about attacking this minor + ResetSentAttackProtectedMinorTaunts(ePeacePlayer); + } + } + + // Update other diplomacy stuff! + DoReevaluatePlayers(vPlayersToReevaluate, false, false); +} + +/// ePlayer declared war on someone, so figure out what that means +void CvDiplomacyAI::DoPlayerDeclaredWarOnSomeone(PlayerTypes ePlayer, TeamTypes eOtherTeam, bool bDefensivePact) +{ + if (ePlayer < 0 || ePlayer >= MAX_CIV_PLAYERS || GET_PLAYER(ePlayer).isBarbarian()) return; + if (eOtherTeam < 0 || eOtherTeam >= MAX_CIV_TEAMS || GET_TEAM(eOtherTeam).isBarbarian()) return; + + PlayerTypes eMyPlayer = GetID(); + vector vAttackedTeam = GET_TEAM(eOtherTeam).getPlayers(); + for (size_t i=0; i 0) + { + SetCoopWarAgreementScore(ePlayer, 0); + } + } + + // Reset DoF values + SetDoFAccepted(ePlayer, false); + SetDoFType(ePlayer, DOF_TYPE_UNTRUSTWORTHY); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, false); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(eMyPlayer, DOF_TYPE_UNTRUSTWORTHY); + + if (GetShareApproachResponse(ePlayer) != SHARE_APPROACH_RESPONSE_REFUSED) + SetShareApproachResponse(ePlayer, NO_SHARE_APPROACH_RESPONSE); + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetShareApproachResponse(eMyPlayer) != SHARE_APPROACH_RESPONSE_REFUSED) + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetShareApproachResponse(eMyPlayer, NO_SHARE_APPROACH_RESPONSE); + + // End all coop war agreements with this player + CancelCoopWarsWithPlayer(ePlayer, !bDefensivePact); + + // Player broke a promise that he wasn't going to attack us? + if (MadeMilitaryPromise(ePlayer)) + { + if (!bDefensivePact) + SetMilitaryPromiseState(ePlayer, PROMISE_STATE_BROKEN); + else + SetMilitaryPromiseState(ePlayer, NO_PROMISE_STATE); + } + + // Reset various promises for both of us...all is fair in war! + SetExpansionPromiseState(ePlayer, NO_PROMISE_STATE); + SetBorderPromiseState(ePlayer, NO_PROMISE_STATE); + SetSpyPromiseState(ePlayer, NO_PROMISE_STATE); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetExpansionPromiseState(eMyPlayer, NO_PROMISE_STATE); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetBorderPromiseState(eMyPlayer, NO_PROMISE_STATE); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetSpyPromiseState(eMyPlayer, NO_PROMISE_STATE); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetMilitaryPromiseState(eMyPlayer, NO_PROMISE_STATE); + + // We're no longer trade partners + SetRecentTradeValue(ePlayer, 0); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetRecentTradeValue(eMyPlayer, 0); + + if (!bDefensivePact) + { + // Forget any of that liberation crud! + SetResurrectedBy(ePlayer, false); + SetPlayerLiberatedCapital(ePlayer, false); + SetPlayerLiberatedHolyCity(ePlayer, false); + SetNumCitiesLiberatedBy(ePlayer, 0); + SetNumCitiesEverLiberatedBy(ePlayer, 0); + SetPlayerReturnedCapital(ePlayer, false); + SetPlayerReturnedHolyCity(ePlayer, false); + SetMasterLiberatedMeFromVassalage(ePlayer, false); + SetVassalagePeacefullyRevokedTurn(ePlayer, -1); + + // Forget civilians returned, landmarks built, and intrigue shared so they don't affect relations any more + SetNumCiviliansReturnedToMe(ePlayer, 0); + SetNumLandmarksBuiltForMe(ePlayer, 0); + SetNumTimesIntrigueSharedBy(ePlayer, 0); + + // Clear positive diplomatic values + SetCommonFoeValue(ePlayer, 0); + SetVassalProtectValue(ePlayer, 0); + if (GetRecentAssistValue(ePlayer) < 0) + SetRecentAssistValue(ePlayer, 0); + } + } + + // If it's us OR we know the attacked player, change appropriate values + if (!bDefensivePact && IsPlayerValid(eAttackedPlayer, true) && GET_PLAYER(ePlayer).isMajorCiv()) + { + if (GET_PLAYER(eAttackedPlayer).isMajorCiv()) + { + ChangeOtherPlayerNumMajorsAttacked(ePlayer, 1, eOtherTeam); + + if (GetTeam() != eOtherTeam && !IsAtWar(ePlayer) && !IsUntrustworthy(ePlayer)) + { + // If we view the target as a backstabber, apply a large diplo bonus. + if (IsUntrustworthy(eAttackedPlayer)) + { + ChangeRecentAssistValue(ePlayer, 300); + } + // Did they declare war on someone we're at war with? + else if (IsAtWar(eAttackedPlayer)) + { + // If we're doing badly in the war, we appreciate the assistance. + switch (GetWarState(eAttackedPlayer)) + { + // No war state is fine here since they may have just gone to war. + case NO_WAR_STATE_TYPE: + case WAR_STATE_NEARLY_WON: + case WAR_STATE_OFFENSIVE: + break; + case WAR_STATE_CALM: + ChangeRecentAssistValue(ePlayer, 50); + break; + case WAR_STATE_STALEMATE: + ChangeRecentAssistValue(ePlayer, 100); + break; + case WAR_STATE_TROUBLED: + case WAR_STATE_DEFENSIVE: + ChangeRecentAssistValue(ePlayer, 200); + break; + case WAR_STATE_NEARLY_DEFEATED: + ChangeRecentAssistValue(ePlayer, 300); + break; + } + } + } + } + else if (GET_PLAYER(eAttackedPlayer).isMinorCiv()) + { + // Did they attack a Minor we're protecting? + bool bProtected = GET_PLAYER(eAttackedPlayer).GetMinorCivAI()->IsProtectedByMajor(eMyPlayer) || GET_PLAYER(eAttackedPlayer).GetMinorCivAI()->IsAllies(eMyPlayer); + + if (bProtected) + { + SetOtherPlayerAttackedProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); + SetOtherPlayerProtectedMinorAttacked(ePlayer, eAttackedPlayer); + } + + // To prevent infinite warmongering exploits, only apply further penalties if they haven't attacked this minor within the last 50 turns. + int iTurn = GET_PLAYER(eAttackedPlayer).GetMinorCivAI()->GetTurnLastAttacked(GET_PLAYER(ePlayer).getTeam()); + int iTurnDifference = GC.getGame().getGameTurn() - iTurn; + if (iTurn > -1 && iTurnDifference < 50) + return; + + if (bProtected) + ChangeOtherPlayerNumProtectedMinorsAttacked(ePlayer, 1); + + ChangeOtherPlayerNumMinorsAttacked(ePlayer, 1, eOtherTeam); + } + } + } +} + +/// ePlayer bullied eOtherPlayer (minor civ), so figure out what that means +void CvDiplomacyAI::DoPlayerBulliedSomeone(PlayerTypes ePlayer, PlayerTypes eOtherPlayer) +{ + if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return; + if (eOtherPlayer < MAX_MAJOR_CIVS || eOtherPlayer >= MAX_CIV_PLAYERS) return; + + // The bully was someone else + if (IsPlayerValid(ePlayer)) + { + // Did they bully a Minor we're protecting? + if (GET_PLAYER(eOtherPlayer).isMinorCiv() && GET_PLAYER(eOtherPlayer).GetMinorCivAI()->IsProtectedByMajor(GetID())) + { + // Only apply this penalty if they haven't bullied them already + if (GET_PLAYER(eOtherPlayer).GetMinorCivAI()->GetTurnLastBulliedByMajor(ePlayer) == -1) + { + ChangeOtherPlayerNumProtectedMinorsBullied(ePlayer, 1); + } + + SetOtherPlayerProtectedMinorBullied(ePlayer, eOtherPlayer); + SetOtherPlayerBulliedProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); + + // You broke the promise you made! + if (MadeBullyCityStatePromise(ePlayer)) + { + SetBullyCityStatePromiseState(ePlayer, PROMISE_STATE_BROKEN); + } + } + } +} + +/// Return the value of the warmonger amount adjusted by how much this player hates warmongers +int CvDiplomacyAI::GetOtherPlayerWarmongerScore(PlayerTypes ePlayer) const +{ + if (ePlayer < 0 || ePlayer >= MAX_MAJOR_CIVS) return 0; + + int iReturnValue = GetOtherPlayerWarmongerAmount(ePlayer); + + iReturnValue *= GetWarmongerHate(); // ranges from 1 to 10 + iReturnValue /= 20; + + return iReturnValue; +} + +///////////////////////////////////////////////////////// +// Contact +///////////////////////////////////////////////////////// + +/// First contact between this player and another +void CvDiplomacyAI::DoFirstContact(PlayerTypes ePlayer) +{ + PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); + PRECONDITION(ePlayer < MAX_CIV_PLAYERS, "DIPLOMACY_AI: Invalid Player Index."); + + if (ePlayer != GetID()) + { + DoFirstContactInitRelationship(ePlayer); + + // Humans don't say hi to one another through the shadow diplo AI and, uh, don't show up in MP please + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + // JdH: notifications do get send in MP + updated to new + if (GC.getGame().isFinalInitialized()) + { + if (!IsAtWar(ePlayer) && CvPreGame::isHuman(ePlayer)) + { + // Human to Human will just send a notification + if (GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) + { + CvPlayer& kTargetPlayer = GET_PLAYER(ePlayer); + CvNotifications* pNotifications = kTargetPlayer.GetNotifications(); + if (pNotifications) + { + CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_SUMMARY_MET_MINOR_CIV", GetPlayer()->getNameKey()); + pNotifications->Add(NOTIFICATION_GENERIC, strBuffer, strBuffer, -1, -1, GetID()); + } + } + else + { + // AI to Human: during auto-moves, send a notification instead of a request + // to avoid creating pending requests after turn end + if (GET_PLAYER(ePlayer).isAutoMoves()) + { + CvPlayer& kTargetPlayer = GET_PLAYER(ePlayer); + CvNotifications* pNotifications = kTargetPlayer.GetNotifications(); + if (pNotifications) + { + CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_SUMMARY_MET_MINOR_CIV", GetPlayer()->getNameKey()); + pNotifications->Add(NOTIFICATION_GENERIC, strBuffer, strBuffer, -1, -1, GetID()); + } + } + else + { + const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_INTRO); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DEFAULT_ROOT, szText, LEADERHEAD_ANIM_INTRO); + } + } + } + } + } + else + { + if(!GC.getGame().isNetworkMultiPlayer()) // KWG: Candidate for !GC.getGame().IsOption(GAMEOPTION_SIMULTANEOUS_TURNS) + { + if(!GetPlayer()->isHuman(ISHUMAN_AI_DIPLOMACY)) + { + // Should fire off a diplo message when we meet a human + if (GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + { + if(!IsAtWar(ePlayer)) + { + if(GC.getGame().isFinalInitialized()) + { + if(std::find(m_aGreetPlayers.begin(), m_aGreetPlayers.end(), ePlayer) == m_aGreetPlayers.end()) + { + // Put in the list of people to greet when their turn comes up. + m_aGreetPlayers.push_back(ePlayer); + } + } + } + } + } + else + { + // Human to Human will just send a notification + CvPlayer& kTargetPlayer = GET_PLAYER(ePlayer); + if(kTargetPlayer.isHuman(ISHUMAN_AI_DIPLOMACY)) + { + if(!IsAtWar(ePlayer)) + { + if(GC.getGame().isFinalInitialized()) + { + CvNotifications* pNotifications = kTargetPlayer.GetNotifications(); + if(pNotifications) + { + CvString strBuffer = GetLocalizedText("TXT_KEY_NOTIFICATION_SUMMARY_MET_MINOR_CIV", GetPlayer()->getNameKey()); + pNotifications->Add(NOTIFICATION_GENERIC, strBuffer, strBuffer, -1, -1, GetID()); + } + } + } + } + } + } + } + } +} + +/// Initiate relationship values towards a new player on first contact +void CvDiplomacyAI::DoFirstContactInitRelationship(PlayerTypes ePlayer) +{ + if (GC.getGame().isFinalInitialized()) + { + DoUpdateConquestStats(); + DoUpdateMilitaryAggressivePostures(); + } + + // Major Civ + if (GET_PLAYER(ePlayer).isMajorCiv() && NotTeam(ePlayer)) + { + for (int iApproachLoop = 0; iApproachLoop < NUM_CIV_APPROACHES; iApproachLoop++) + { + CivApproachTypes eApproach = (CivApproachTypes) iApproachLoop; + SetPlayerApproachValue(ePlayer, eApproach, 0); + } + + DoReevaluatePlayer(ePlayer); + } + // Minor civ + else if (GET_PLAYER(ePlayer).isMinorCiv()) + { + SetCivApproach(ePlayer, CIV_APPROACH_NEUTRAL); + } +} +// ----------------------------------------------------------------------------------------------- +/// Player killed us +void CvDiplomacyAI::DoKilledByPlayer(PlayerTypes ePlayer) +{ + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + if(ePlayer != NO_PLAYER && CvPreGame::isHuman(ePlayer)) + { + const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEFEATED); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_DEFEATED); + } + } + else + { + if(ePlayer == GC.getGame().getActivePlayer() && !GC.getGame().isNetworkMultiPlayer()) + { + const char* szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEFEATED); + gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_DEFEATED); + } + + if (MOD_ENABLE_ACHIEVEMENTS && !GC.getGame().isGameMultiPlayer()) + { + gDLL->UnlockAchievement(ACHIEVEMENT_DESTROY_CIV); + CvAchievementUnlocker::AlexanderConquest(ePlayer); + } + } +} +// ------------------------------------------------------------------------------------------------------------------- +/// Send a statement to another player +void CvDiplomacyAI::DoSendStatementToPlayer(PlayerTypes ePlayer, DiploStatementTypes eStatement, int iData1, CvDeal* pDeal) +{ + PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + PRECONDITION(eStatement >= 0 && eStatement < NUM_DIPLO_STATEMENT_TYPES, "DIPLOMACY_AI: Invalid DiploStatementType."); + + ASSERT(!pDeal || (pDeal->m_eFromPlayer == m_eID && pDeal->m_eToPlayer == ePlayer) || (pDeal->m_eFromPlayer == ePlayer && pDeal->m_eToPlayer == m_eID), "Sending deal statement with incorrect players in the deal."); + + const char* szText = NULL; + bool bHuman = GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY); + + // Aggressive Military warning + if(eStatement == DIPLO_STATEMENT_AGGRESSIVE_MILITARY_WARNING) + { + if(bHuman) + { + if(IsActHostileTowardsHuman(ePlayer)) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_AGGRESSIVE_MILITARY_WARNING); + else + szText = GetDiploStringForMessage(DIPLO_MESSAGE_AGGRESSIVE_MILITARY_WARNING); + + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_AGGRESSIVE_MILITARY_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE); + } + // AI resolution + else + { + if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam())) + { + // Make promises between all members of both teams + for (int iI = 0; iI < MAX_MAJOR_CIVS; iI++) + { + PlayerTypes eMyTeammate = (PlayerTypes)iI; + if (!GET_PLAYER(eMyTeammate).isAlive()) + continue; + if (GET_PLAYER(eMyTeammate).getTeam() != GetTeam()) + continue; + + for (int iJ = 0; iJ < MAX_MAJOR_CIVS; iJ++) + { + PlayerTypes eTheirTeammate = (PlayerTypes)iJ; + if (!GET_PLAYER(eTheirTeammate).isAlive()) + continue; + if (GET_PLAYER(eTheirTeammate).getTeam() != GET_PLAYER(ePlayer).getTeam()) + continue; + + GET_PLAYER(eMyTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eTheirTeammate, PROMISE_STATE_MADE); + GET_PLAYER(eTheirTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eMyTeammate, PROMISE_STATE_MADE); } + } + } + else + { + MoveTroopsResponseTypes eResponse = GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMoveTroopsRequestResponse(GetID(), /*bJustChecking*/ false); + if (eResponse == MOVE_TROOPS_RESPONSE_REFUSE) + { + if (!GET_PLAYER(ePlayer).GetDiplomacyAI()->DeclareWar(GetTeam())) + { + // Make promises between all members of both teams + for (int iI = 0; iI < MAX_MAJOR_CIVS; iI++) + { + PlayerTypes eMyTeammate = (PlayerTypes)iI; + if (!GET_PLAYER(eMyTeammate).isAlive()) + continue; + if (GET_PLAYER(eMyTeammate).getTeam() != GetTeam()) + continue; - // Happiness - if (bNeedHappiness) - iValue += -50; //antonjs: todo: xml - if (bNeedHappinessCritical) - iValue += -150; //antonjs: todo: xml + for (int iJ = 0; iJ < MAX_MAJOR_CIVS; iJ++) + { + PlayerTypes eTheirTeammate = (PlayerTypes)iJ; + if (!GET_PLAYER(eTheirTeammate).isAlive()) + continue; + if (GET_PLAYER(eTheirTeammate).getTeam() != GET_PLAYER(ePlayer).getTeam()) + continue; - // Bonuses from Annexed City-States - if (GetPlayer()->GetPlayerTraits()->IsAnnexedCityStatesGiveYields()) { - MinorCivTraitTypes eTrait = pMinorCivAI->GetTrait(); - if (eTrait == MINOR_CIV_TRAIT_MILITARISTIC) + GET_PLAYER(eMyTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eTheirTeammate, PROMISE_STATE_MADE); + GET_PLAYER(eTheirTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eMyTeammate, PROMISE_STATE_MADE); + } + } + } + else + { + SetMilitaryPromiseState(ePlayer, PROMISE_STATE_IGNORED); + } + } + else + { + // Make promises between all members of both teams + for (int iI = 0; iI < MAX_MAJOR_CIVS; iI++) + { + PlayerTypes eMyTeammate = (PlayerTypes)iI; + if (!GET_PLAYER(eMyTeammate).isAlive()) + continue; + if (GET_PLAYER(eMyTeammate).getTeam() != GetTeam()) + continue; + + for (int iJ = 0; iJ < MAX_MAJOR_CIVS; iJ++) { - iValue += (iScienceFlavor + iOffenseFlavor); + PlayerTypes eTheirTeammate = (PlayerTypes)iJ; + if (!GET_PLAYER(eTheirTeammate).isAlive()) + continue; + if (GET_PLAYER(eTheirTeammate).getTeam() != GET_PLAYER(ePlayer).getTeam()) + continue; + + GET_PLAYER(eMyTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eTheirTeammate, PROMISE_STATE_MADE); + GET_PLAYER(eTheirTeammate).GetDiplomacyAI()->SetMilitaryPromiseState(eMyTeammate, PROMISE_STATE_MADE); } - else if (eTrait == MINOR_CIV_TRAIT_MERCANTILE) + } + } + } + } + } + + // Player killed a City-State we were protecting + else if (eStatement == DIPLO_STATEMENT_KILLED_PROTECTED_CITY_STATE) + { + if (bHuman) + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + ASSERT(eMinorCiv != NO_PLAYER); + if(eMinorCiv != NO_PLAYER) + { + const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); + + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_KILLED_PROTECTED_CITY_STATE, NO_PLAYER, strMinorCivKey); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } + } + + // Player attacked a City-State we're protecting + else if (eStatement == DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE) + { + if (bHuman) + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + ASSERT(eMinorCiv != NO_PLAYER); + if (eMinorCiv != NO_PLAYER) + { + const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); + + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_ATTACKED_PROTECTED_CITY_STATE, NO_PLAYER, strMinorCivKey); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_ATTACKED_MINOR_CIV, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } + else + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + ASSERT(eMinorCiv != NO_PLAYER); + if (eMinorCiv != NO_PLAYER) + { + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_WAR) + { + SetAttackCityStatePromiseState(ePlayer, PROMISE_STATE_IGNORED); + } + else + { + SetAttackCityStatePromiseState(ePlayer, PROMISE_STATE_MADE); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCivApproach(eMinorCiv, CIV_APPROACH_NEUTRAL); + } + } + } + } + + // Player bullied a City-State we're protecting + else if (eStatement == DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE) + { + if (bHuman) + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + ASSERT(eMinorCiv != NO_PLAYER); + if (eMinorCiv != NO_PLAYER) + { + const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); + + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_BULLIED_PROTECTED_CITY_STATE, NO_PLAYER, strMinorCivKey); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_BULLIED_MINOR_CIV, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } + else + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + ASSERT(eMinorCiv != NO_PLAYER); + if (eMinorCiv != NO_PLAYER) + { + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_HOSTILE) + { + SetBullyCityStatePromiseState(ePlayer, PROMISE_STATE_IGNORED); + } + else + { + SetBullyCityStatePromiseState(ePlayer, PROMISE_STATE_MADE); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCivApproach(eMinorCiv, CIV_APPROACH_NEUTRAL); + } + } + } + } + + // Serious Expansion warning + else if(eStatement == DIPLO_STATEMENT_EXPANSION_SERIOUS_WARNING) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXPANSION_SERIOUS_WARNING); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_EXPANSION_SERIOUS_WARNING, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } + + // Expansion warning + else if (eStatement == DIPLO_STATEMENT_EXPANSION_WARNING) + { + if (bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXPANSION_WARNING); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_EXPANSION_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE); + } + else + { + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDontSettleAcceptable(GetID())) + SetExpansionPromiseState(ePlayer, PROMISE_STATE_MADE); + else + SetExpansionPromiseState(ePlayer, PROMISE_STATE_IGNORED); + } + + SetAngryAboutExpansion(ePlayer, false); + SetEverRequestedExpansionPromise(ePlayer, true); + + // Flag all of their cities as ignored for future bickering + int iCityLoop = 0; + for (CvCity* pLoopCity = GET_PLAYER(ePlayer).firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iCityLoop)) + { + pLoopCity->SetIgnoredForExpansionBickering(GetID(), true); + } + } + + // Broken Expansion Promise + else if(eStatement == DIPLO_STATEMENT_EXPANSION_BROKEN_PROMISE) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXPANSION_BROKEN_PROMISE); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } + + // Serious Plot Buying warning + else if(eStatement == DIPLO_STATEMENT_PLOT_BUYING_SERIOUS_WARNING) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_SERIOUS_WARNING); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_SERIOUS_WARNING, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } + + // Plot Buying warning + else if(eStatement == DIPLO_STATEMENT_PLOT_BUYING_WARNING) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_WARNING); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING, szText, LEADERHEAD_ANIM_NEGATIVE); + } + else + { + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetBoldness() > 8) + { + SetBorderPromiseState(ePlayer, PROMISE_STATE_IGNORED); + } + else + { + SetBorderPromiseState(ePlayer, PROMISE_STATE_MADE); + } + } + } + + // Broken Plot Buying Promise + else if(eStatement == DIPLO_STATEMENT_PLOT_BUYING_BROKEN_PROMISE) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_PLOT_BUYING_BROKEN_PROMISE); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } + + // We attacked a Minor someone has a PtP with + else if(eStatement == DIPLO_STATEMENT_WE_ATTACKED_YOUR_MINOR) + { + if(bHuman) + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + ASSERT(eMinorCiv != NO_PLAYER); + if(eMinorCiv != NO_PLAYER) + { + const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); + if(IsActHostileTowardsHuman(ePlayer)) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_WE_ATTACKED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); + else + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_ATTACKED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); + + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_I_ATTACKED_YOUR_MINOR_CIV, szText, LEADERHEAD_ANIM_POSITIVE, eMinorCiv); + + // Extra flag, since diplo log does not save which minor civ the message was about + SetSentAttackProtectedMinorTaunt(ePlayer, eMinorCiv, true); + } + } + else + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + ASSERT(eMinorCiv != NO_PLAYER); + if(eMinorCiv != NO_PLAYER) + { + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_FRIENDLY || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMinorCivCompetitiveness() > 6) + { + SetOtherPlayerSidedWithProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); + + bool bValid = false; + if (GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + { + if (GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x15c58904).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) < GetWarmongerHate()) { - iValue += iHappinessFlavor * 2; + bValid = true; } - else if (eTrait == MINOR_CIV_TRAIT_CULTURED) + } + if (bValid && (GetBoldness() > 6 || GetMeanness() > 6)) + { + if (m_pPlayer->GetDiplomacyAI()->DeclareWar(ePlayer)) { - iValue += iCultureFlavor * 2; + pDeal->ClearItems(); + bool bCareful = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true) > 0 && GetGlobalCoopWarAgainstState(ePlayer) < COOP_WAR_STATE_PREPARING; + GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer, 3, bCareful); } - else if (eTrait == MINOR_CIV_TRAIT_RELIGIOUS) + } + } + else + { + GC.getGame().DoMinorPledgeProtection(ePlayer, eMinorCiv, false, true); // Pledge is broken! + } + } + } + } + + // We bullied a Minor someone has a PtP with + else if (eStatement == DIPLO_STATEMENT_WE_BULLIED_YOUR_MINOR) + { + if(bHuman) + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + ASSERT(eMinorCiv != NO_PLAYER); + if (eMinorCiv != NO_PLAYER) + { + const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); + if(IsActHostileTowardsHuman(ePlayer)) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HOSTILE_WE_BULLIED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); + else + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_BULLIED_YOUR_MINOR, NO_PLAYER, strMinorCivKey); + + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_I_BULLIED_YOUR_MINOR_CIV, szText, LEADERHEAD_ANIM_POSITIVE, eMinorCiv); + } + } + else + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + ASSERT(eMinorCiv != NO_PLAYER); + if(eMinorCiv != NO_PLAYER) + { + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->GetCivApproach(eMinorCiv) == CIV_APPROACH_FRIENDLY || GET_PLAYER(ePlayer).GetDiplomacyAI()->GetMinorCivCompetitiveness() > 6) + { + SetOtherPlayerSidedWithProtectedMinorTurn(ePlayer, GC.getGame().getGameTurn()); + + bool bValid = false; + if (GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + { + if (GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x1da72213).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) < GetWarmongerHate()) { - iValue += iFaithFlavor * 2; + bValid = true; } - else if (eTrait == MINOR_CIV_TRAIT_MARITIME) + } + if (bValid && (GetBoldness() > 7 || GetMeanness() > 7)) + { + if (m_pPlayer->GetDiplomacyAI()->DeclareWar(ePlayer)) { - iValue += iGrowthFlavor * 2; + pDeal->ClearItems(); + GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer,3); } } + } + else + { + GC.getGame().DoMinorPledgeProtection(ePlayer, eMinorCiv, false, true); // Pledge is broken! + } + } + } + } + + // We'd like a defense pact + else if(eStatement == DIPLO_STATEMENT_DEFENSIVE_PACT_REQUEST) + { + // Active human + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEFENSE_PACT_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + // Offer to an AI player + else + { + if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } + } + } + // We'd like a 3rd party war + else if(eStatement == DIPLO_STATEMENT_THIRDPARTY_WAR_REQUEST) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_THIRDPARTY_WAR_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + // Offer to an AI player + else + { + if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } + } + } + // We'd like a peace trade + else if(eStatement == DIPLO_STATEMENT_THIRDPARTY_PEACE_REQUEST) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_THIRDPARTY_PEACE_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + // Offer to an AI player + else + { + if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } + } + } + // We'd like a vote trade + else if(eStatement == DIPLO_STATEMENT_VOTE_REQUEST) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VOTE_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + // Offer to an AI player + else + { + if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } + } + } + + // We'd like to trade cities + else if(eStatement == DIPLO_STATEMENT_TRADE_CITIES_REQUEST) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_TRADE_CITIES_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + // Offer to an AI player + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; - // Annexing the Ally of another player? - PlayerTypes eAlly = pMinorCivAI->GetAlly(); - if (eAlly != NO_PLAYER) - { - iValue -= (GetPlayer()->GetDiplomacyAI()->GetCivOpinion(eAlly) - CIV_OPINION_NEUTRAL) * 10; - } - // Time to decide - Do we want it enough? - if (iValue > 100) //antonjs: todo: xml - { - veMinorsToBullyAnnex.push_back(eMinor, iValue); - bWantsToBullyAnnexThisMinor = true; - } - } + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } + } + } + // We'd like to exchange cities + else if(eStatement == DIPLO_STATEMENT_EXCHANGE_CITIES) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_EXCHANGE_CITIES_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + // Offer to an AI player + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; - // Calculate desirability to buyout this minor - if (bWantsToBuyout) + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } + } + } + + // We'd like to work with a player + else if (eStatement == DIPLO_STATEMENT_WORK_WITH_US || eStatement == DIPLO_STATEMENT_DOF_BB || eStatement == DIPLO_STATEMENT_DOF_ALLIES || eStatement == DIPLO_STATEMENT_DOF_FRIENDS || eStatement == DIPLO_STATEMENT_DOF_UNTRUSTWORTHY) + { + // Send message to human + if (bHuman) + { + switch (eStatement) { - int iValue = 100; //antonjs: todo: xml - // Only bother if we actually can buyout - CvCity* pMinorCapital = pMinor->getCapitalCity(); - if(GetPlayer()->IsAbleToAnnexCityStates() && pMinorCivAI->CanMajorBuyout(eID) && pMinorCapital != NULL) - { - // Determine presence of player cities on this continent - CvArea* pMinorArea = pMinorCapital->plot()->area(); - bool bPresenceInArea = false; - int iMajorCapitalsInArea = 0; - if(pMinorArea) - { - // Do we have a city here? - if(pMinorArea->getCitiesPerPlayer(eID) > 0) - bPresenceInArea = true; + case DIPLO_STATEMENT_DOF_BB: + szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_BATTLE_BROTHERS); + break; + case DIPLO_STATEMENT_DOF_ALLIES: + szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_OLD_FRIENDS); + break; + case DIPLO_STATEMENT_DOF_FRIENDS: + szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_FRIENDS); + break; + case DIPLO_STATEMENT_DOF_UNTRUSTWORTHY: + szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_UNTRUSTWORTHY); + break; + default: + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_WITH_US); + break; + } - // Does another major civ have their capital here? (must be visible) - for(int iMajorRivalLoop = 0; iMajorRivalLoop < MAX_MAJOR_CIVS; iMajorRivalLoop++) - { - PlayerTypes eMajorRivalLoop = (PlayerTypes) iMajorRivalLoop; - if(eMajorRivalLoop == eID) - continue; + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_WORK_WITH_US, szText, LEADERHEAD_ANIM_REQUEST); + } + // AI resolution + // Accept - reject is assumed from the counter + else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDoFAcceptable(GetID())) + { + SetDoFAccepted(ePlayer, true); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(GetID(), true); - if(GET_PLAYER(eMajorRivalLoop).isAlive()) - { - CvCity* pCapital = GET_PLAYER(eMajorRivalLoop).getCapitalCity(); - if(pCapital && pCapital->plot()) - { - CvPlot* pPlot = pCapital->plot(); - if(pPlot->isVisible(GetTeam())) - iMajorCapitalsInArea++; - } - } - } - } - else - { - ASSERT(false, "Could not lookup minor civ's area!"); - } + // Update diplomacy stuff + DoReevaluatePlayer(ePlayer, false, false); + GET_PLAYER(ePlayer).GetDiplomacyAI()->DoReevaluatePlayer(GetID(), false, false); - // How many units does the city-state have? - int iMinorMilitaryUnits = 0; - int iMinorUnits = 0; - int iLoop = 0; - for (CvUnit* pLoopUnit = pMinor->firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = pMinor->nextUnit(&iLoop)) + LogDoF(ePlayer); + } + } + + // We no longer want to work with a player + else if(eStatement == DIPLO_STATEMENT_END_WORK_WITH_US) + { + PlayerTypes eMyPlayer = GetID(); + + SetDoFAccepted(ePlayer, false); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFAccepted(eMyPlayer, false); + + // End all coop war agreements with this player + GET_PLAYER(ePlayer).GetDiplomacyAI()->CancelCoopWarsWithPlayer(eMyPlayer, true); + + // End any Defensive Pact + GET_TEAM(GET_PLAYER(eMyPlayer).getTeam()).SetHasDefensivePact(GET_PLAYER(ePlayer).getTeam(), false); + GET_TEAM(GET_PLAYER(ePlayer).getTeam()).SetHasDefensivePact(GET_PLAYER(eMyPlayer).getTeam(), false); + + SetDoFType(ePlayer, DOF_TYPE_UNTRUSTWORTHY); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFType(eMyPlayer, DOF_TYPE_UNTRUSTWORTHY); + GET_PLAYER(ePlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -300); + + // Other players' reactions + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + { + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (IsPlayerValid(eLoopPlayer, true) && !GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && eLoopPlayer != ePlayer && eLoopPlayer != eMyPlayer) + { + // Our teammates + if (IsTeammate(eLoopPlayer)) + { + if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer) || GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(ePlayer)) { - if(pLoopUnit->IsCanAttack() && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE_SEA) - { - iMinorMilitaryUnits++; - } - iMinorUnits++; + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 300); } - - // Foreign continent - if(!bPresenceInArea) + } + else if (GET_PLAYER(eLoopPlayer).getTeam() != GET_PLAYER(ePlayer).getTeam()) + { + // Player might apply a diplo bonus if they don't hate us + if (!GET_TEAM(GetTeam()).isAtWar(GET_PLAYER(eLoopPlayer).getTeam()) && !IsDenouncedPlayer(eLoopPlayer) && + !GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDenouncedPlayer(eMyPlayer) && !GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(eMyPlayer) && + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(eMyPlayer) != CIV_OPINION_UNFORGIVABLE) { - // Military foothold to attack other majors - if(IsGoingForWorldConquest() && iMajorCapitalsInArea > 0) - { - iValue += 100; //antonjs: todo: xml - } - // Expansion - else if(bExpandToOtherContinents) - { - iValue += 60; //antonjs: todo: xml - } - else + // Bonus for ending a DoF with a backstabber + if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsUntrustworthy(ePlayer)) { - iValue += -50; //antonjs: todo: xml + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 75); } - } - // Continent we have presence on - else - { - // Proximity plays a large factor, since we don't want a remote, isolated city - if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) + // Bonus for ending a DoF with a player they're at war with + else if (GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam())) { - iValue += 100; //antonjs: todo: xml - // Military units could come to our rescue quickly - if(GetStateAllWars() == STATE_ALL_WARS_LOSING) + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 50); + + // Extra bonus if they're doing badly in the war + if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetWarState(ePlayer) <= WAR_STATE_TROUBLED) { - if(iMinorMilitaryUnits > 0) //antonjs: todo: xml - { - iValue += (iMinorMilitaryUnits) * 10; //antonjs: todo: xml - } - else - { - iValue -= 50; //antonjs: todo: xml - } + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 25); } } - else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) - { - iValue += 10; //antonjs: todo: xml - } - else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) + // Bonus for ending a DoF with a player who denounced them + else if (GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(eLoopPlayer)) { - iValue += -50; //antonjs: todo: xml + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, 50); } - else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_DISTANT) + // Penalty for ending a DoF with a friend, DP or ally + else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(ePlayer) || + GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).IsHasDefensivePact(GET_PLAYER(ePlayer).getTeam()) || + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) { - iValue += -100; //antonjs: todo: xml - } - } - - // Military units - How many, and can we support them? - if(GetPlayer()->GetNumUnitsSupplied() >= GetPlayer()->getNumUnits() + iMinorUnits) - { - iValue += (iMinorMilitaryUnits) * 5; - } - - // Happiness - if(bNeedHappiness) - iValue += -50; //antonjs: todo: xml - if(bNeedHappinessCritical) - iValue += -150; //antonjs: todo: xml - - // Potential bonuses lost - MinorCivTraitTypes eTrait = pMinorCivAI->GetTrait(); - if(eTrait == MINOR_CIV_TRAIT_CULTURED && IsGoingForCultureVictory()) - { - iValue += -70; //antonjs: todo: xml - } - else if(eTrait == MINOR_CIV_TRAIT_MERCANTILE) - { - if(bNeedHappiness) - iValue += -100; //antonjs: todo: xml - if(bNeedHappinessCritical) - iValue += -150; //antonjs: todo: xml + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -75); + } } - - // Time to decide - Do we want it enough? - if(iValue > 100) //antonjs: todo: xml + // Penalty for ending a DoF with a friend, DP or ally (they hate us) + else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsDoFAccepted(ePlayer) || + GET_TEAM(GET_PLAYER(eLoopPlayer).getTeam()).IsHasDefensivePact(GET_PLAYER(ePlayer).getTeam()) || + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetCivOpinion(ePlayer) == CIV_OPINION_ALLY) { - veMinorsToBuyout.push_back(eMinor, iValue); - bWantsToBuyoutThisMinor = true; + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -150); } } - } - - if (bWantsToMarry) - { - // No reason not to, as long as we have the gold (checked below) - if (pMinorCivAI->CanMajorDiploMarriage(eID)) + // Their teammates + else if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->IsTeammate(ePlayer)) { - // Random weight to shuffle the list - veMinorsToMarry.push_back(eMinor, GC.getGame().urandLimitExclusive(1000, CvSeeder::fromRaw(0x977c30aa).mix(GC.getGame().getGameTurn()).mix(eID).mix(eMinor))); - bWantsToMarryThisMinor = true; + GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eMyPlayer, -300); } } + } - // Calculate desirability to give this minor gold - if (bWantsToMakeGoldGift && !bWantsToBuyoutThisMinor && !bWantsToBullyAnnexThisMinor) - { - if (GET_PLAYER(eMinor).GetMinorCivAI()->IsNoAlly()) - { - continue; - } - if (GET_PLAYER(eMinor).GetMinorCivAI()->GetPermanentAlly() == GetID()) - { - continue; - } - - int iValue = /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_THRESHOLD); - // If we're not protective, then don't bother with minor diplo - if (eApproach == CIV_APPROACH_FRIENDLY) - { - MinorGoldGiftInfo sGiftInfo; - sGiftInfo.eMinor = eMinor; - sGiftInfo.eMajorRival = NO_PLAYER; - sGiftInfo.bQuickBoost = false; - sGiftInfo.iGoldAmount = 0; - - iValue += /*10*/ GD_INT_GET(MC_GIFT_WEIGHT_PROTECTIVE); // some base value - - // if we are rich we are more likely to, conversely if we are poor... - iValue += min(max(0, m_pPlayer->calculateGoldRate() - 50),100); - - CvMinorCivInfo* pMinorInfo = GC.getMinorCivInfo(pMinorCivAI->GetMinorCivType()); - - // Diplo victory makes us more likely to spend gold - if (IsGoingForDiploVictory()) - iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_DIPLO_VICTORY); - // double up if this is the home stretch - if (GC.getGame().IsUnitedNationsActive()) - { - iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_DIPLO_VICTORY); - } - // Going for Culture victory, focus on Cultural city states - else if (IsGoingForCultureVictory()) - { - if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_CULTURED) - iValue += /*200*/ GD_INT_GET(MC_GIFT_WEIGHT_CULTURE_VICTORY); - } - // Going for Conquest victory, focus on Militaristic city states - else if (IsGoingForWorldConquest()) - { - if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_MILITARISTIC) - iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_CONQUEST_VICTORY); - } - - //antonjs: todo: work extra gold quest INF potential into the friends/allies/passing logic as well - // Gold gift quest is active, so we would get more bang for our bucks - if (pMinorCivAI->IsActiveQuestForPlayer(eID, MINOR_CIV_QUEST_GIVE_GOLD)) - { - iValue += 150; //antonjs: todo: constant/XML - } - - // Invest quest is active, so we would get more bang for our bucks - if (pMinorCivAI->IsActiveQuestForPlayer(eID, MINOR_CIV_QUEST_INVEST)) - { - iValue += 100; //antonjs: todo: constant/XML - } - - // having traits that give us bonuses also make us want to spend gold - if (m_pPlayer->GetPlayerTraits()->GetCityStateFriendshipModifier() > 0 || m_pPlayer->GetPlayerTraits()->GetCityStateBonusModifier()) - { - iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_DIPLO_VICTORY); - } - - // Nearly everyone likes to grow - if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_MARITIME && !GetPlayer()->IsEmpireUnhappy()) - { - iValue += iGrowthFlavor * max(1, GetPlayer()->getNumCities() / 3) * /*20*/ GD_INT_GET(MC_GIFT_WEIGHT_MARITIME_GROWTH); - } - - // Slight negative weight towards militaristic - if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_MILITARISTIC && !IsGoingForWorldConquest()) - iValue += /*-50*/ GD_INT_GET(MC_GIFT_WEIGHT_MILITARISTIC); + GET_PLAYER(ePlayer).GetDiplomacyAI()->SetDoFBroken(eMyPlayer, true, false); + LogBrokenDoF(ePlayer); - // If they have a resource we don't have, add extra weight - int iResourcesWeLack = pMinorCivAI->GetNumResourcesMajorLacks(eID); - if (iResourcesWeLack > 0) - iValue += iResourcesWeLack * /*80*/ GD_INT_GET(MC_GIFT_WEIGHT_RESOURCE_WE_NEED); + // Send message to human + if (bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_END_WORK_WITH_US, ePlayer); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_NEGATIVE); + } + } - // If the minor is hostile, then reduce the weighting - if (pMinorCivAI->GetPersonality() == MINOR_CIV_PERSONALITY_HOSTILE) - iValue += /*-20*/ GD_INT_GET(MC_GIFT_WEIGHT_HOSTILE); + // Denounce + else if(eStatement == DIPLO_STATEMENT_DENOUNCE) + { + DoDenouncePlayer(ePlayer); + LogDenounce(ePlayer); - // The closer we are the better - if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) - iValue += /*5*/ GD_INT_GET(MC_GIFT_WEIGHT_NEIGHBORS); - else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) - iValue += /*4*/ GD_INT_GET(MC_GIFT_WEIGHT_CLOSE); - else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) - iValue += /*3*/ GD_INT_GET(MC_GIFT_WEIGHT_FAR); + // Send message to human + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_AGAINST_SOMEONE, ePlayer); - int iMediumGiftFriendship = pMinorCivAI->GetFriendshipFromGoldGift(eID, iMediumGift); - int iSmallGiftFriendship = pMinorCivAI->GetFriendshipFromGoldGift(eID, iSmallGift); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } - int iFriendshipWithMinor = pMinorCivAI->GetEffectiveFriendshipWithMajor(eID); + // Denounce Friend (backstab) + else if(eStatement == DIPLO_STATEMENT_DENOUNCE_FRIEND) + { + DoDenouncePlayer(ePlayer); + LogDenounce(ePlayer, /*bBackstab*/ true); - // Only care if we'll actually be Allies or better - bool bMediumGiftAllies = iFriendshipWithMinor + iMediumGiftFriendship >= pMinorCivAI->GetAlliesThreshold(eID); - bool bSmallGiftAllies = iFriendshipWithMinor + iSmallGiftFriendship >= pMinorCivAI->GetAlliesThreshold(eID); + // Send message to human + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_AI_DOF_BACKSTAB, ePlayer); - // Loop through other players to see if we can pass them - for (int iOtherMajorLoop = 0; iOtherMajorLoop < MAX_MAJOR_CIVS; iOtherMajorLoop++) - { - PlayerTypes eOtherMajor = (PlayerTypes) iOtherMajorLoop; + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } + } - // Player must be alive - if (!GET_PLAYER(eOtherMajor).isAlive()) - continue; + // Request Friend Denounce Someone + else if(eStatement == DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE) + { + PlayerTypes eTarget = (PlayerTypes) iData1; + ASSERT(eTarget != NO_PLAYER); + if(eTarget != NO_PLAYER) + { + const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); - int iOtherPlayerFriendshipWithMinor = pMinorCivAI->GetEffectiveFriendshipWithMajor(eOtherMajor); + // Send message to human + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_DOF_AI_DENOUNCE_REQUEST, ePlayer, strTargetCivKey); - // Player must have friendship with this major - if (iOtherPlayerFriendshipWithMinor <= 0) - continue; + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_AI_REQUEST_DENOUNCE, szText, LEADERHEAD_ANIM_POSITIVE, eTarget); + } + else + { + bool bAgree = IsDenounceAcceptable(eTarget, /*bBias*/ true); - // They must have more friendship with this guy than us - if (iFriendshipWithMinor <= iOtherPlayerFriendshipWithMinor) - continue; + LogFriendRequestDenounce(ePlayer, eTarget, bAgree); - // If we can pass them with a small gift, great - if (bSmallGiftAllies && iOtherPlayerFriendshipWithMinor - iFriendshipWithMinor < iSmallGiftFriendship) - { - iValue += /*30*/ GD_INT_GET(MC_SMALL_GIFT_WEIGHT_PASS_OTHER_PLAYER); - sGiftInfo.bQuickBoost = true; - sGiftInfo.eMajorRival = eOtherMajor; - } - // If a medium gift passes them up, that's good too - else if (bMediumGiftAllies && iOtherPlayerFriendshipWithMinor - iFriendshipWithMinor < iMediumGiftFriendship) - { - iValue += /*15*/ GD_INT_GET(MC_GIFT_WEIGHT_PASS_OTHER_PLAYER); - sGiftInfo.eMajorRival = eOtherMajor; - } - // We're behind and we can't catch up right now, so zero-out the value - else - iValue = 0; - } + if(bAgree) + { + GET_PLAYER(ePlayer).GetDiplomacyAI()->DoDenouncePlayer(eTarget); + GET_PLAYER(ePlayer).GetDiplomacyAI()->LogDenounce(eTarget); - // Are we already allies? - if (pMinorCivAI->IsAllies(eID)) + // Denounced a human? + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - // Are we close to losing our status? - if (pMinorCivAI->IsCloseToNotBeingAllies(eID)) + if(GET_PLAYER(eTarget).isHuman(ISHUMAN_AI_DIPLOMACY)) { - iValue += /*250*/ GD_INT_GET(MC_GIFT_WEIGHT_ALMOST_NOT_ALLIES); - sGiftInfo.bQuickBoost = true; + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_AGAINST_SOMEONE, eTarget); + CvDiplomacyRequests::SendRequest(ePlayer, eTarget, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } - // Not going to lose status, so not worth going after this guy - else - iValue = 0; } - // Are we already Friends? - else if (pMinorCivAI->IsFriends(eID)) + else { - // Are we close to losing our status? - if (pMinorCivAI->IsCloseToNotBeingFriends(eID)) + if(eTarget == GC.getGame().getActivePlayer()) { - iValue += /*150*/ GD_INT_GET(MC_GIFT_WEIGHT_ALMOST_NOT_FRIENDS); - sGiftInfo.bQuickBoost = true; + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WORK_AGAINST_SOMEONE, eTarget); + CvDiplomacyRequests::SendRequest(ePlayer, eTarget, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } - // Not going to lose status, so not worth going after this guy - else if (!IsGoingForDiploVictory() || !GC.getGame().IsUnitedNationsActive()) - iValue = 0; } - - // Did we bully you recently? If so, giving you gold now would be very odd. - if (pMinorCivAI->IsRecentlyBulliedByMajor(eID)) + } + else + { + // Oh, you're gonna say no, are you? + if(IsFriendDenounceRefusalUnacceptable(ePlayer, eTarget)) { - iValue -= 100; //antonjs: todo: constant/XML + DoDenouncePlayer(ePlayer); + LogDenounce(ePlayer, /*bBackstab*/ false, /*bRefusal*/ true); } + } + } + } + } - //antonjs: consider: different behavior to CS that have been bullied by others, bullied by rival, etc. + // We'd like to declare war on someone + else if (eStatement == DIPLO_STATEMENT_COOP_WAR_REQUEST) + { + PlayerTypes eAgainstPlayer = (PlayerTypes) iData1; + ASSERT(IsPlayerValid(eAgainstPlayer)); + if (IsPlayerValid(eAgainstPlayer)) + { + // Send message to human + if (bHuman) + { + const char* strAgainstPlayerKey = GET_PLAYER(eAgainstPlayer).getNameKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_REQUEST, ePlayer, strAgainstPlayerKey); - // Do we want it enough? - if (iValue > /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_THRESHOLD)) - { - veMinorsToGiveGold.push_back(sGiftInfo, iValue); - bWantsToGiveGoldToThisMinor = true; - } + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_COOP_WAR, szText, LEADERHEAD_ANIM_POSITIVE, eAgainstPlayer); + } + // AI resolution + else + { + CoopWarStates eResponse = GET_PLAYER(ePlayer).GetDiplomacyAI()->RespondToCoopWarRequest(GetID(), eAgainstPlayer); + + if (eResponse == COOP_WAR_STATE_REJECTED) + { + int iAssistPenalty = AdjustConditionalModifier(-100, GetNeediness()); + ChangeRecentAssistValue(ePlayer, iAssistPenalty); + ChangeCoopWarAgreementScore(ePlayer, -1); } } + } + } - // Calculate desirability to bully a unit from this minor - if (bWantsToBullyUnit && !bWantsToBuyoutThisMinor && !bWantsToMarryThisMinor && !bWantsToBullyAnnexThisMinor && !bWantsToGiveGoldToThisMinor) + /* + // We'd like to declare war on someone + else if(eStatement == DIPLO_STATEMENT_COOP_WAR_TIME) + { + PlayerTypes eAgainstPlayer = (PlayerTypes) iData1; + ASSERT(eAgainstPlayer != NO_PLAYER); + if(eAgainstPlayer != NO_PLAYER) + { + // Send message to human + if(bHuman) { - int iValue = 100; //antonjs: todo: XML, bully threshold - if (MOD_BALANCE_HEAVY_TRIBUTE) - iValue = pMinor->GetMinorCivAI()->CalculateBullyScore(eID, true); + const char* strAgainstPlayerKey = GET_PLAYER(eAgainstPlayer).getNameKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_COOP_WAR_TIME, ePlayer, strAgainstPlayerKey); - if (iValue <= 0) - continue; + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_COOP_WAR_TIME, szText, LEADERHEAD_ANIM_POSITIVE, eAgainstPlayer); + } + } - if(eApproach == CIV_APPROACH_HOSTILE) + // No AI resolution! This is handled automatically in DoCounters() - no need for diplo exchange + } + */ + + // We're making a demand of this player + else if (eStatement == DIPLO_STATEMENT_DEMAND) + { + // Active human + if (bHuman) + { + // Assume they accepted - if they refuse, we'll reset this to 0 in FROM_UI_DIPLO_EVENT_DEMAND_HUMAN_REFUSAL + ChangeNumConsecutiveDemandsTheyAccepted(ePlayer, 1); + + szText = GetDiploStringForMessage(DIPLO_MESSAGE_DEMAND); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_DEMAND, szText, LEADERHEAD_ANIM_DEMAND); + } + // AI player + else + { + // Process diplomacy consequences from the AI on the receiving end + DemandResponseTypes eResponse = GET_PLAYER(ePlayer).GetDealAI()->GetDemandResponse(pDeal); + GET_PLAYER(ePlayer).GetDiplomacyAI()->DoDemandMade(GetID(), eResponse); + + // Did they accept? + if (eResponse == DEMAND_RESPONSE_ACCEPT) + { + if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - // Only bother if we can successfully bully - if (pMinor->GetMinorCivAI()->CanMajorBullyUnit(eID)) - { - if (MOD_BALANCE_HEAVY_TRIBUTE) - { - iValue += (GET_PLAYER(eMinor).GetMinorCivAI()->GetBullyGoldAmount(GetID(), false, /*bForUnit*/ true) * iGoldFlavor) / 10; + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; - // yields from quests - int iNumCities = GetPlayer()->getNumCities(); - int iNumUnits = GetPlayer()->getNumMilitaryUnits(); - QuestListForPlayer::iterator itr_quest; - for (itr_quest = pMinorCivAI->m_QuestsGiven[eID].begin(); itr_quest != pMinorCivAI->m_QuestsGiven[eID].end(); itr_quest++) - { - if (itr_quest->IsObsolete(true)) // is this quest canceled by demanding heavy tribute? - { - // half of the quest rewards are given - iValue += itr_quest->GetGold() / 2 * iGoldFlavor / 10; - iValue += itr_quest->GetScience() / 2 * iScienceFlavor / 10; - iValue += itr_quest->GetCulture() / 2 * iCultureFlavor / 10; - iValue += itr_quest->GetFaith() / 2 * iFaithFlavor / 10; - iValue += itr_quest->GetGoldenAgePoints() / 2 * iCultureFlavor / 10; - iValue += itr_quest->GetFood() / 2 * iGrowthFlavor / 10; - iValue += itr_quest->GetProduction() / 2 * iProductionFlavor / 10; - iValue += itr_quest->GetTourism() / 2 * iCultureFlavor / 10; - iValue += itr_quest->GetHappiness() / 2 * iHappinessFlavor / 10; - iValue += itr_quest->GetGP() / 2 * (iGoldFlavor + iScienceFlavor + 3 * iCultureFlavor + iProductionFlavor) / 10; // we get GP for each specialist - iValue += itr_quest->GetGPGlobal() / 2 * (iGoldFlavor + iScienceFlavor + 3 * iCultureFlavor + iProductionFlavor) * iNumCities / 10; - iValue += itr_quest->GetGeneralPoints() / 2 * iOffenseFlavor / 10; - iValue += itr_quest->GetAdmiralPoints() / 2 * iOffenseFlavor / 10; - iValue += itr_quest->GetExperience() / 2 * iNumUnits * iOffenseFlavor / 10; - iValue += itr_quest->GetJuggernauts() * iOffenseFlavor / 10; - } - } - } - else - { - // The closer we are the better, because the unit travels less distance to get home - if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) - iValue += 25; - else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) - iValue += 15; - else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) - iValue += -15; - else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_DISTANT) - iValue += -25; - //antonjs: consider: knock it down if is there a chance the worker will get captured by a nearby rival - } + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } + } + // Demand rebuffed + else + { + pDeal->ClearItems(); - // We like to keep bullying the same minor - if (pMinor->GetMinorCivAI()->IsEverBulliedByMajor(eID)) - { - iValue += 25; - } + // Does the AI declare war? + bool bDeclareWar = GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID()) && !GetPlayer()->IsNoNewWars(); - // If this minor has a PtP from someone, bullying it could have big consequences - if (!GetPlayer()->GetPlayerTraits()->IgnoreBullyPenalties()) + // Must be a potential war target + if (bDeclareWar && !IsPotentialWarTarget(ePlayer)) + bDeclareWar = false; + + // Sanity check - who else would we go to war with? + if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(ePlayer, true)) + bDeclareWar = false; + + // Sanity check - avoid going bankrupt + int iMinIncome = 2 + (GetPlayer()->GetCurrentEra() * 2); + if (bDeclareWar && IsWarWouldBankruptUs(ePlayer, iMinIncome)) + bDeclareWar = false; + + // Sanity check - who else would we go to war with? + if (bDeclareWar) + { + vector vDefensiveWarAllies = GetDefensiveWarAllies(ePlayer, /*bIncludeMinors*/ true, /*bReverseMode*/ true, /*bNewWarsOnly*/ true); + + for (std::vector::iterator it = vDefensiveWarAllies.begin(); it != vDefensiveWarAllies.end(); ++it) + { + // Would we be declaring war on a powerful neighbor? + if (GET_PLAYER(*it).GetProximityToPlayer(GetID()) >= PLAYER_PROXIMITY_CLOSE) { - if (pMinor->GetMinorCivAI()->IsProtectedByAnyMajor()) - { - iValue += -20; - //antonjs: consider: scale based on which major is protecting it - } - else if (pMinor->GetMinorCivAI()->GetAlly() != NO_PLAYER) + if (GET_PLAYER(*it).isMajorCiv()) { - iValue += -20; + if (GetCivApproach(*it) == CIV_APPROACH_AFRAID) + { + bDeclareWar = false; + break; + } + // If we're already planning a war/demand against them, then we don't care. + else if (GetCivApproach(*it) != CIV_APPROACH_WAR && GetDemandTargetPlayer() != *it) + { + if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) + { + bDeclareWar = false; + break; + } + } } else { - iValue += 20; - } - } - else - { - iValue += 25; - } - - //Do we get a bonus from this? - if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0) - { - iValue += 25; - } - for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) - { - YieldTypes eYield = (YieldTypes)iI; - if (eYield != NO_YIELD) - { - if (GetPlayer()->GetYieldFromMinorDemand(eYield) > 0) + if (GetCivApproach(*it) > CIV_APPROACH_HOSTILE) { - iValue += 10; + if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE) + { + bDeclareWar = false; + break; + } } } } - - //antonjs: consider: allies or friends with another major - //antonjs: consider: distance to other majors - // If we are getting a bonus, don't mess that up! - if(pMinor->GetMinorCivAI()->IsAllies(eID) || pMinor->GetMinorCivAI()->IsFriends(eID)) - { - if (!GetPlayer()->IsCanBullyFriendlyCS()) - iValue = 0; - } - // Do we want it enough? - if(iValue > 100) //antonjs: todo: XML for threshold - { - veMinorsToBullyUnit.push_back(eMinor, iValue); - bWantsToBullyUnitFromThisMinor = true; - } } } - } - - // Calculate desirability to bully gold from this minor - if(bWantsToBullyGold && !bWantsToBuyoutThisMinor && !bWantsToMarryThisMinor && !bWantsToBullyAnnexThisMinor && !bWantsToGiveGoldToThisMinor && !bWantsToBullyUnitFromThisMinor) - { - int iValue = 100; //antonjs: todo: XML, bully threshold - if (MOD_BALANCE_HEAVY_TRIBUTE) - iValue = pMinor->GetMinorCivAI()->CalculateBullyScore(eID, false); - - if (iValue <= 0) - continue; - if(eApproach == CIV_APPROACH_HOSTILE) + if (bDeclareWar) { - // Only bother if we can successfully bully - if (pMinor->GetMinorCivAI()->CanMajorBullyGold(eID, MOD_BALANCE_HEAVY_TRIBUTE ? iValue - 25 : 0)) + // If already at war or non-HOSTILE and not decisively stronger, AI has a chance to bluff + int iCurrentWars = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, true); + bool bCanBluff = iCurrentWars > 0 || (GetCivApproach(ePlayer) > CIV_APPROACH_HOSTILE && GetTargetValue(ePlayer) <= TARGET_VALUE_AVERAGE); + if (bCanBluff) { - iValue += (GET_PLAYER(eMinor).GetMinorCivAI()->GetBullyGoldAmount(GetID()) * iGoldFlavor) / 10; - // We like to keep bullying the same minor - if(pMinor->GetMinorCivAI()->IsEverBulliedByMajor(eID)) - { - iValue += 25; - } - - // If this minor has a PtP from someone, bullying it could have big consequences - if(pMinor->GetMinorCivAI()->IsProtectedByAnyMajor()) - { - iValue += -20; - //antonjs: consider: scale based on which major is protecting it - } - else - { - iValue += 20; - } - //antonjs: consider: allies or friends another major - //antonjs: consider: distance to other majors - - //Do we get a bonus from this? - if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0) - { - iValue += 25; - } - - // If we are getting a bonus, don't mess that up! - if(pMinor->GetMinorCivAI()->IsAllies(eID) || pMinor->GetMinorCivAI()->IsFriends(eID)) + if (GetCivOpinion(ePlayer) == CIV_OPINION_UNFORGIVABLE || IsEndgameAggressiveTo(ePlayer) || IsCapitalCapturedBy(ePlayer, true, true) || IsHolyCityCapturedBy(ePlayer, true, true)) { - if (!GetPlayer()->IsCanBullyFriendlyCS()) - iValue = 0; + bCanBluff = false; } + } + if (bCanBluff) + { + int iBluffChance = 20 + 20 * min(iCurrentWars, 3); + if (GetBiggestCompetitor() == ePlayer) + iBluffChance /= 2; - // Do we want it enough? - if(iValue > 100) //antonjs: todo: XML for threshold - { - veMinorsToBullyGold.push_back(eMinor, iValue); - } + if (GC.getGame().randRangeInclusive(1, 100, CvSeeder::fromRaw(0x40f558f0).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) > iBluffChance) + bCanBluff = false; + } + // Not bluffing - declare war! + if (!bCanBluff && DeclareWar(ePlayer)) + { + bool bCareful = iCurrentWars > 0 && GetGlobalCoopWarAgainstState(ePlayer) < COOP_WAR_STATE_PREPARING; + GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer, 3, bCareful); } } } - } - - SetWantToRouteConnectToMinor(eMinor, bWantsToConnect); } - int iGoldReserve = GetPlayer()->GetTreasury()->GetGold(); - - // Do we want to buyout a minor? - veMinorsToBuyout.StableSortItems(); - int iGoldLeft = GetPlayer()->GetTreasury()->GetGold(); - for (int i = 0; i < veMinorsToBuyout.size(); i++) + // We're making a request of this player + else if(eStatement == DIPLO_STATEMENT_REQUEST) { - PlayerTypes eLoopMinor = veMinorsToBuyout.GetElement(i); - int iBuyoutCost = GET_PLAYER(eLoopMinor).GetMinorCivAI()->GetBuyoutCost(eID); - if (iGoldLeft >= iBuyoutCost) + // Active human + if(bHuman) { - if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBuyout(eID)) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_REQUEST); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_REQUEST, szText, LEADERHEAD_ANIM_REQUEST); + } + // AI player + else + { + // For now the AI will always give in - may eventually write additional logic here + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - GC.getGame().DoMinorBuyout(eID, eLoopMinor); - break; // Don't buyout more than once in a single turn + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { - ASSERT(false, "Chose a minor to buyout that cannot actually be bought!"); + CvDeal kDeal = *pDeal; + + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } + } + + // Player has a Luxury Resource we'd like to trade for + else if(eStatement == DIPLO_STATEMENT_LUXURY_TRADE) + { + // Active human + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_LUXURY_TRADE); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + // Offer to an AI player else { - if (!GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - LogMinorCivBuyout(eLoopMinor, iBuyoutCost, /*bSaving*/ true); - GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT, iBuyoutCost, /*350*/ GD_INT_GET(AI_GOLD_PRIORITY_BUYOUT_CITY_STATE)); + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } - // Do we want to marry a city-state? - veMinorsToMarry.StableSortItems(); - iGoldLeft = GetPlayer()->GetTreasury()->GetGold(); - for (int i = 0; i < veMinorsToMarry.size(); i++) + // We'd like to exchange embassies with this player + else if(eStatement == DIPLO_STATEMENT_EMBASSY_EXCHANGE) { - PlayerTypes eLoopMinor = veMinorsToMarry.GetElement(i); - int iMarriageCost = GET_PLAYER(eLoopMinor).GetMinorCivAI()->GetMarriageCost(eID); - if (iGoldLeft >= iMarriageCost) + if(bHuman) { - if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorDiploMarriage(eID)) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_EMBASSY_EXCHANGE); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - GET_PLAYER(eLoopMinor).GetMinorCivAI()->DoBuyout(eID); - iMarriageCost = GET_PLAYER(eLoopMinor).GetMinorCivAI()->GetMarriageCost(eID); - iGoldLeft -= iMarriageCost; + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { - ASSERT(false, "Chose a city-state to marry that cannot actually be married!"); + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } + } + + // We want an embassy in this player's capital + else if(eStatement == DIPLO_STATEMENT_EMBASSY_OFFER) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_EMBASSY_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } else { - if (!GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - LogMinorCivBuyout(eLoopMinor, iMarriageCost, /*bSaving*/ true); - GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT, iMarriageCost, /*350*/ GD_INT_GET(AI_GOLD_PRIORITY_BUYOUT_CITY_STATE)); + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } - // Do we want to annex a minor? - veMinorsToBullyAnnex.StableSortItems(); - for (int i = 0; i < veMinorsToBullyAnnex.size(); i++) + // We'd like mutual Open Borders with this player + else if(eStatement == DIPLO_STATEMENT_OPEN_BORDERS_EXCHANGE) { - PlayerTypes eLoopMinor = veMinorsToBullyAnnex.GetElement(i); - PRECONDITION(eLoopMinor != NO_PLAYER, "Trying to bully-annex NO_PLAYER!"); - if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBullyUnit(eID)) + // Active human + if(bHuman) { - GC.getGame().DoMinorBullyAnnex(eID, eLoopMinor); - break; // Don't annex more than one city-state in a single turn + szText = GetDiploStringForMessage(DIPLO_MESSAGE_OPEN_BORDERS_EXCHANGE); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } + // Offer to an AI player else { - ASSERT(false, "Chose a minor to bully-annex that cannot actually be bullied!"); + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } } } - // Do we want to give someone Gold enough to actually do it? - veMinorsToGiveGold.StableSortItems(); // Sort from highest desirability to lowest - for (int i = 0; i < veMinorsToGiveGold.size(); i++) + // We'd like this player to open their borders to us + else if(eStatement == DIPLO_STATEMENT_OPEN_BORDERS_OFFER) { - int iGoldLeft = GetPlayer()->GetTreasury()->GetGold(); - MinorGoldGiftInfo sGift = veMinorsToGiveGold.GetElement(i); - - //Interception! Let's do a tile improvement if we can (and we'll benefit from it) - if (sGift.eMinor != NO_PLAYER && GET_PLAYER(sGift.eMinor).GetMinorCivAI()->IsAllies(GetID())) + if(bHuman) { - CvPlot* pImprovementPlot = GET_PLAYER(sGift.eMinor).GetMinorCivAI()->GetMajorGiftTileImprovement(GetID()); - if (pImprovementPlot != NULL) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_OPEN_BORDERS_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + // Offer to an AI player + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - GET_PLAYER(sGift.eMinor).GetMinorCivAI()->DoTileImprovementGiftFromMajor(GetID(), pImprovementPlot->getX(), pImprovementPlot->getY()); - LogMinorCivGiftTile(sGift.eMinor); + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } + } - //Default is 1 - this will prevent the AI from trying to spam gold gifts of zero gold. - if (GD_INT_GET(CSD_GOLD_GIFT_DISABLED) > 0) + // Offer a Research Agreement + else if(eStatement == DIPLO_STATEMENT_RESEARCH_AGREEMENT_OFFER) + { + if(bHuman) { - continue; + szText = GetDiploStringForMessage(DIPLO_MESSAGE_RESEARCH_AGREEMENT_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); } + // Offer to an AI player + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; - sGift.iGoldAmount = 0; - - if(iGoldLeft >= iSmallGift && sGift.bQuickBoost) - sGift.iGoldAmount = iSmallGift; - else if(iGoldLeft >= iLargeGift) - sGift.iGoldAmount = iLargeGift; - else if(iGoldLeft >= iMediumGift) - sGift.iGoldAmount = iMediumGift; - - int iOldFriendship = GET_PLAYER(sGift.eMinor).GetMinorCivAI()->GetEffectiveFriendshipWithMajor(eID); + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } + } + } - // Able to give a gift? Don't gift more than half of the gold we have in one turn - if(sGift.iGoldAmount > 0 && iGoldLeft >= (iGoldReserve / 2)) + // Offer to renew an existing trade deal + else if (eStatement == DIPLO_STATEMENT_RENEW_DEAL) + { + if(bHuman) { - GET_PLAYER(sGift.eMinor).GetMinorCivAI()->DoGoldGiftFromMajor(GetID(), sGift.iGoldAmount); //antonjs: todo: go through CvGame instead? + int iDealValueToMe = 0; + DiploMessageTypes eMessageType = NUM_DIPLO_MESSAGE_TYPES; + bool bCantMatchOffer = false; + bool bDealAcceptable = m_pPlayer->GetDealAI()->IsDealWithHumanAcceptable(pDeal, ePlayer, iDealValueToMe, &bCantMatchOffer, false); - LogMinorCivGiftGold(sGift.eMinor, iOldFriendship, sGift.iGoldAmount, /*bSaving*/ false, sGift.bQuickBoost, sGift.eMajorRival); + if(bDealAcceptable) + { + eMessageType = DIPLO_MESSAGE_RENEW_DEAL; + } + // We want more from this deal + else + { + eMessageType = DIPLO_MESSAGE_WANT_MORE_RENEW_DEAL; + } - if(GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) - GetPlayer()->GetEconomicAI()->CancelSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT); + CvDeal kDeal = *pDeal; + szText = GetDiploStringForMessage(eMessageType); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, &kDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST, /* bRenew */ true); } - // Can't afford gift yet, so start saving + // Offer to an AI player else { - if(!GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) - { - int iAmountToSaveFor = iMediumGift; - - if(sGift.bQuickBoost) - iAmountToSaveFor = iSmallGift; - - LogMinorCivGiftGold(sGift.eMinor, iOldFriendship, iAmountToSaveFor, /*bSaving*/ true, sGift.bQuickBoost, sGift.eMajorRival); + CvDeal kDeal = *pDeal; - int iPriority = /*150*/ GD_INT_GET(AI_GOLD_PRIORITY_DIPLOMACY_BASE); - iPriority += iDiplomacyFlavor * /*25*/ GD_INT_GET(AI_GOLD_PRIORITY_DIPLOMACY_PER_FLAVOR_POINT); - GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT, iAmountToSaveFor, iPriority); + if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(kDeal, true); + } + else + { + // Don't need to call DoOffer because we have already checked that the deal is acceptable for both sides + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + bool bFinalized = GC.getGame().GetGameDeals().FinalizeDeal(kDeal.GetFromPlayer(), kDeal.GetToPlayer(), true); + ASSERT(bFinalized, "Renewal Deal can't be finalized, even though it was checked that the deal is acceptable for both sides.") } } } - // Do we want a unit enough to bully someone? - veMinorsToBullyUnit.StableSortItems(); - for (int i = 0; i < veMinorsToBullyUnit.size(); i++) + // Our Opinion of them is now Unforgivable + else if(eStatement == DIPLO_STATEMENT_NOW_UNFORGIVABLE) { - PlayerTypes eLoopMinor = veMinorsToBullyUnit.GetElement(i); - PRECONDITION(eLoopMinor != NO_PLAYER, "Trying to bully a unit from NO_PLAYER!"); - if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBullyUnit(eID)) - { - GC.getGame().DoMinorBullyUnit(eID, eLoopMinor); - break; // Don't bully a unit more than once in a single turn - } - else + if(bHuman) { - ASSERT(false, "Chose a minor to bully unit from that cannot actually be bullied!"); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_NOW_UNFORGIVABLE); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } - // Do we want gold enough to bully someone? - veMinorsToBullyGold.StableSortItems(); - for (int i = 0; i < veMinorsToBullyGold.size(); i++) + // Our Opinion of them is now Enemy + else if(eStatement == DIPLO_STATEMENT_NOW_ENEMY) { - PlayerTypes eLoopMinor = veMinorsToBullyGold.GetElement(i); - PRECONDITION(eLoopMinor != NO_PLAYER, "Trying to bully gold from NO_PLAYER!"); - if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBullyGold(eID)) + if(bHuman) { - GC.getGame().DoMinorBullyGold(eID, eLoopMinor); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_NOW_ENEMY); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } - else + } + + // They caught one of our spies + else if(eStatement == DIPLO_STATEMENT_CAUGHT_YOUR_SPY) + { + if(bHuman) { - ASSERT(false, "Chose a minor to bully gold from that cannot actually be bullied!"); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_CAUGHT_YOUR_SPY); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_CAUGHT_YOUR_SPY, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } -} -void CvDiplomacyAI::DoUpdateMinorCivProtection(PlayerTypes eMinor) -{ - if (GetCivApproach(eMinor) == CIV_APPROACH_FRIENDLY || GET_PLAYER(eMinor).GetMinorCivAI()->GetAlly() == GetID()) + // They killed one of our spies + else if(eStatement == DIPLO_STATEMENT_KILLED_YOUR_SPY) { - // We are protective, so do a PtP if we are able to and haven't already - if (GET_PLAYER(eMinor).GetMinorCivAI()->CanMajorStartProtection(GetID())) + if(bHuman) { - GC.getGame().DoMinorPledgeProtection(GetID(), eMinor, true); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_KILLED_YOUR_SPY); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_KILLED_YOUR_SPY, szText, LEADERHEAD_ANIM_NEGATIVE); } } - // Don't cancel a pledge in VP unless the City-State has no capital (we won't get the Influence boost from quests) or they've taken damage (so we'll lose Influence faster) - // Pledges will be automatically cancelled if AI decides to bully or war the City-State, so there's no benefit to cancelling now - we might change our mind - else if (GD_INT_GET(BALANCE_INFLUENCE_BOOST_PROTECTION_MINOR) <= 0 || GET_PLAYER(eMinor).getCapitalCity() == NULL || (GET_PLAYER(eMinor).getCapitalCity()->getDamage() > 0 && GD_INT_GET(MINOR_FRIENDSHIP_DROP_PER_TURN_DAMAGED_CAPITAL_MULTIPLIER) > 100)) + + // We killed one of their spies + else if(eStatement == DIPLO_STATEMENT_KILLED_MY_SPY) { - // We are not protective, so revoke PtP if we can - if (GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(GetID()) && GET_PLAYER(eMinor).GetMinorCivAI()->CanMajorWithdrawProtection(GetID())) + if(bHuman) { - GC.getGame().DoMinorPledgeProtection(GetID(), eMinor, false); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_KILLED_MY_SPY); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_KILLED_MY_SPY, szText, LEADERHEAD_ANIM_DEFEATED); } } -} - -/* -/// Possible Contact Statement - Notify human it's time for a coop war they agreed to -void CvDiplomacyAI::DoCoopWarTimeStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // We (the AI) have intrigue information to share with them + else if(eStatement == DIPLO_STATEMENT_SHARE_INTRIGUE) { - // Don't send this to AI players - coop war timer is automatically handled in DoCounters() - if(!GET_PLAYER(ePlayer).isHuman()) - return; - - CvTeam* pTeam = &GET_TEAM(GET_PLAYER(ePlayer).getTeam()); - - PlayerTypes eTargetPlayer; - TeamTypes eTargetTeam; - - for(int iTargetLoop = 0; iTargetLoop < MAX_MAJOR_CIVS; iTargetLoop++) + IntrigueNotificationMessage* pNotificationMessage = GetPlayer()->GetEspionage()->GetRecentIntrigueInfo(ePlayer); + ASSERT(pNotificationMessage, "pNotificationMessage is null. Whut?"); + if (pNotificationMessage) { - eTargetPlayer = (PlayerTypes) iTargetLoop; - - bool bInvalid = false; - - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GET_PLAYER(eTargetPlayer).getTeam(), ePlayer)) - { - bInvalid = true; - } - - if (!IsPlayerValid(eTargetPlayer)) - { - bInvalid = true; - } - - if (bInvalid) + PRECONDITION(pNotificationMessage->m_eSourcePlayer != NO_PLAYER, "There is no plotter! What's going on"); + PlayerTypes ePlotterPlayer = pNotificationMessage->m_eSourcePlayer; + CvIntrigueType eIntrigueType = (CvIntrigueType)pNotificationMessage->m_iIntrigueType; + // don't share intrigue about two parties if they are already at war, except for the information that someone has been bribed to go to war + if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isAtWar(GET_PLAYER(ePlotterPlayer).getTeam()) || eIntrigueType == INTRIGUE_TYPE_BRIBE_WAR) { - if (GetCoopWarAcceptedState(ePlayer, eTargetPlayer) == COOP_WAR_STATE_SOON) + CvCity* pCity = NULL; + if(pNotificationMessage->m_iCityX != -1 && pNotificationMessage->m_iCityY != -1) { - SetCoopWarAcceptedState(ePlayer, eTargetPlayer, NO_COOP_WAR_STATE); - SetCoopWarCounter(ePlayer, eTargetPlayer, -666); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCoopWarAcceptedState(GetID(), eTargetPlayer, NO_COOP_WAR_STATE); - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetCoopWarCounter(GetID(), eTargetPlayer, -666); + CvPlot* pPlot = GC.getMap().plot(pNotificationMessage->m_iCityX, pNotificationMessage->m_iCityY); + if(pPlot) + { + pCity = pPlot->getPlotCity(); + } } - continue; - } - // Agreed to go to war soon... what's the counter at? - if(GetCoopWarAcceptedState(ePlayer, eTargetPlayer) == COOP_WAR_STATE_SOON) - { - if(GetCoopWarCounter(ePlayer, eTargetPlayer) == GD_INT_GET(COOP_WAR_SOON_COUNTER)) - { - eTargetTeam = GET_PLAYER(eTargetPlayer).getTeam(); + // add the notification to the player + GET_PLAYER(ePlayer).GetEspionage()->AddIntrigueMessage(GetID(), ePlotterPlayer, ePlayer, pNotificationMessage->m_eDiplomacyPlayer, NO_BUILDING, NO_PROJECT, NO_UNIT, eIntrigueType, 0, pCity, false); - // If they're already at war, don't bother - if(!pTeam->isAtWar(eTargetTeam) && GET_PLAYER(eTargetPlayer).isAlive()) + if(bHuman) + { + const char* szPlayerName = NULL; + if(GC.getGame().isGameMultiPlayer() && GET_PLAYER(ePlotterPlayer).isHuman(ISHUMAN_UI)) { - eStatement = DIPLO_STATEMENT_COOP_WAR_TIME; - iData1 = eTargetPlayer; - - // Don't evaluate other players - break; + szPlayerName = GET_PLAYER(ePlotterPlayer).getNickName(); } - // Human is already at war - process what we would have if he'd agreed at this point else { - SetCoopWarAcceptedState(ePlayer, eTargetPlayer, COOP_WAR_STATE_ACCEPTED); + szPlayerName = GET_PLAYER(ePlotterPlayer).getNameKey(); + } - // AI declaration - if(!IsAtWar(eTargetPlayer) && GET_PLAYER(eTargetPlayer).isAlive()) + const char* szBribedPlayerName = NULL; + szText = ""; + + switch(eIntrigueType) + { + case INTRIGUE_TYPE_ARMY_SNEAK_ATTACK: + if(pCity) { - if (DeclareWar(eTargetPlayer)) - { - GetPlayer()->GetMilitaryAI()->RequestBasicAttack(eTargetPlayer, 1); - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_KNOWN_CITY, NO_PLAYER, szPlayerName, pCity->getNameKey()); + } + else + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_ARMY_SNEAK_ATTACK_UNKNOWN_CITY, NO_PLAYER, szPlayerName); + } + break; + case INTRIGUE_TYPE_AMPHIBIOUS_SNEAK_ATTACK: + if(pCity) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_KNOWN_CITY, NO_PLAYER, szPlayerName, pCity->getNameKey()); + } + else + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_AMPHIBIOUS_SNEAK_ATTACK_UNKNOWN_CITY, NO_PLAYER, szPlayerName); + } + break; + case INTRIGUE_TYPE_DECEPTION: + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE, NO_PLAYER, szPlayerName); + break; + case INTRIGUE_TYPE_BRIBE_WAR: + if (GC.getGame().isGameMultiPlayer() && GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).isHuman(ISHUMAN_UI)) + { + szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNickName(); + } + else + { + szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNameKey(); + } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_BRIBE_WAR, NO_PLAYER, szPlayerName, szBribedPlayerName); + break; + case INTRIGUE_TYPE_COOP_WAR: + if (GC.getGame().isGameMultiPlayer() && GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).isHuman(ISHUMAN_UI)) + { + szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNickName(); + } + else + { + szBribedPlayerName = GET_PLAYER(pNotificationMessage->m_eDiplomacyPlayer).getNameKey(); } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SHARE_INTRIGUE_COOP_WAR, NO_PLAYER, szPlayerName, szBribedPlayerName); + break; + default: + ASSERT(false, "Unknown intrigue type"); + break; } + + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + } + else + { + GET_PLAYER(ePlayer).GetDiplomacyAI()->ChangeNumTimesIntrigueSharedBy(GetID(), 1); } } - } - } -} -*/ - -/// Possible Contact Statement - Coop War Request -void CvDiplomacyAI::DoCoopWarStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_COOP_WAR_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - // Don't start a war if our empire is in bad shape for it - if (GetPlayer()->IsNoNewWars()) - return; - - if (IsAvoidDeals()) - return; - if (eStatement == NO_DIPLO_STATEMENT_TYPE) - { - PlayerTypes eTargetPlayer; - if (DoTestCoopWarDesire(ePlayer, /*passed by address*/ eTargetPlayer)) - { - if (eTargetPlayer != NO_PLAYER) + // mark the messages as shared so the player isn't told the same thing repeatedly + for(uint ui = 0; ui < MAX_MAJOR_CIVS; ui++) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_COOP_WAR_REQUEST; - int iTurnsBetweenStatements = 10; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - iData1 = eTargetPlayer; - } + PlayerTypes eLoopPlayer = (PlayerTypes)ui; + GET_PLAYER(eLoopPlayer).GetEspionage()->MarkRecentIntrigueAsShared(ePlayer, ePlotterPlayer, pNotificationMessage->m_eDiplomacyPlayer, eIntrigueType); } } } -} - -/// Possible Contact Statement - Demand -void CvDiplomacyAI::DoMakeDemand(PlayerTypes ePlayer) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - //End the gift exchange at the start of each round. - GetPlayer()->GetDiplomacyAI()->SetOfferingGift(ePlayer, false); - GetPlayer()->GetDiplomacyAI()->SetOfferedGift(ePlayer, false); - // We can use this deal pointer to form a trade offer - CvDeal* pDeal = GC.getGame().GetGameDeals().GetTempDeal(); - - // Clear this data out before any deals are offered. - pDeal->ClearItems(); - pDeal->SetRequestingPlayer(NO_PLAYER); - pDeal->SetDuration(GC.getGame().getGameSpeedInfo().GetDealDuration()); - pDeal->SetFromPlayer(GetID()); - pDeal->SetToPlayer(ePlayer); - SetCantMatchDeal(ePlayer, false); - - //set up the deal - if (GetPlayer()->GetDealAI()->IsMakeDemand(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + // Stop converting our cities + else if(eStatement == DIPLO_STATEMENT_STOP_CONVERSIONS) { - int iTurnsBetweenStatements = 10; - if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_DEMAND) >= iTurnsBetweenStatements) + if(bHuman) { - DiploStatementTypes eStatement = DIPLO_STATEMENT_DEMAND; - DoSendStatementToPlayer(ePlayer, eStatement, -1, pDeal); - LogStatementToPlayer(ePlayer, eStatement); - SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_DEMAND, GC.getGame().getGameTurn()); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_CONVERSIONS); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_STOP_CONVERSIONS, szText, LEADERHEAD_ANIM_NEGATIVE); } } - // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at - pDeal->ClearItems(); -} - -/// Possible Contact Statement - guy has his military positioned aggressively near us -void CvDiplomacyAI::DoAggressiveMilitaryStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + // Stop digging up our yard + else if(eStatement == DIPLO_STATEMENT_STOP_DIGGING) { - // Don't bother if they've already made or broken a military promise to us - if (GetMilitaryPromiseState(ePlayer) > NO_PROMISE_STATE) - return; - - // We must be able to declare war on each other - in both directions, since the promise is mutual - TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); - if (!GET_TEAM(eTeam).canDeclareWar(GetTeam(), ePlayer) || !GET_TEAM(GetTeam()).canDeclareWar(eTeam, GetID())) - return; - - // They're HIGH or INCREDIBLE this turn - if (GetMilitaryAggressivePosture(ePlayer) < AGGRESSIVE_POSTURE_HIGH) - return; - - // AI teammates of humans can't send this, otherwise the humans on our team might get a backstabbing penalty for something they weren't aware of - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - // Don't send this to AI teammates of humans, otherwise the humans on their team might get a backstabbing penalty for something they weren't aware of - if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) - return; - - // This promise is mutual, so don't send the statement if we (or our teammates) are planning war - if (AvoidExchangesWithPlayer(ePlayer, /*bWarOnly*/ true)) - return; - - // Check other player status - for (int iThirdPartyLoop = 0; iThirdPartyLoop < MAX_MAJOR_CIVS; iThirdPartyLoop++) + if(bHuman) { - PlayerTypes eThirdParty = (PlayerTypes) iThirdPartyLoop; - if (eThirdParty == GetID() || eThirdParty == ePlayer) - continue; - - // Are they at war with anyone we're neighbors with? - if (GetPlayer()->GetProximityToPlayer(eThirdParty) == PLAYER_PROXIMITY_NEIGHBORS && GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isAtWar(GET_PLAYER(eThirdParty).getTeam())) - return; - - // Are they an AI preparing for a coop war against us with a human? Don't send this statement, because being dragged into a war early is unfun for humans - if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eThirdParty).isHuman(ISHUMAN_AI_DIPLOMACY) && GET_PLAYER(eThirdParty).GetDiplomacyAI()->GetCoopWarState(ePlayer, GetID()) == COOP_WAR_STATE_PREPARING) - return; + szText = GetDiploStringForMessage(DIPLO_MESSAGE_STOP_DIGGING); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_STOP_DIGGING, szText, LEADERHEAD_ANIM_NEGATIVE); } - - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_AGGRESSIVE_MILITARY_WARNING; - int iTurnsBetweenStatements = 20; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - eStatement = eTempStatement; } -} - -/// Possible Contact Statement - Killed a City-State we're protective towards -void CvDiplomacyAI::DoKilledCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Insult + else if(eStatement == DIPLO_STATEMENT_INSULT) { - // They must be able to declare war on us - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) - return; + // Change other players' guess as to our Approach (right now it falls in line exactly with the Approach...) + //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuess(GetID(), CIV_APPROACH_HOSTILE); + //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuessCounter(GetID(), 0); - if(MadeAttackCityStatePromise(ePlayer)) + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_KILLED_PROTECTED_CITY_STATE; - int iTurnsBetweenStatements = 9999; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - PlayerTypes eMinorCiv = GetOtherPlayerProtectedMinorKilled(ePlayer); - if(eMinorCiv != NO_PLAYER) - { - PRECONDITION(eMinorCiv >= MAX_MAJOR_CIVS); - PRECONDITION(eMinorCiv < MAX_CIV_PLAYERS); - - eStatement = eTempStatement; - - iData1 = eMinorCiv; - } - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_INSULT_ROOT); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } -} - -/// Possible Contact Statement - Attacked a City-State we're protective towards -void CvDiplomacyAI::DoAttackedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Compliment + else if(eStatement == DIPLO_STATEMENT_COMPLIMENT) { - // They must be able to declare war on us - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) - return; - - // Don't send this to AI teammates of humans, otherwise the humans on their team might get a backstabbing penalty for something they weren't aware of - if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) - return; + // Change other players' guess as to our Approach (right now it falls in line exactly with the Approach...) + //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuess(GetID(), CIV_APPROACH_FRIENDLY); + //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuessCounter(GetID(), 0); - if (GetOtherPlayerAttackedProtectedMinorTurn(ePlayer) == GC.getGame().getGameTurn()) + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_ATTACKED_PROTECTED_CITY_STATE; - int iTurnsBetweenStatements = 1; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - PlayerTypes eMinorCiv = GetOtherPlayerProtectedMinorAttacked(ePlayer); - if(eMinorCiv != NO_PLAYER) - { - PRECONDITION(eMinorCiv >= MAX_MAJOR_CIVS); - PRECONDITION(eMinorCiv < MAX_CIV_PLAYERS); - // Minor civ must still be alive! - if(GET_PLAYER(eMinorCiv).isAlive()) - { - eStatement = eTempStatement; - iData1 = eMinorCiv; - } - } - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_COMPLIMENT); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} -/// Possible Contact Statement - Bullied a City-State we're protective towards -void CvDiplomacyAI::DoBulliedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index. "); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index. "); - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Boot-kissing of a stronger power + else if(eStatement == DIPLO_STATEMENT_BOOT_KISSING) { - // They must be able to declare war on us - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) - return; + // Change other players' guess as to our Approach (right now it falls in line exactly with the Approach...) + //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuess(GetID(), CIV_APPROACH_AFRAID); + //GET_PLAYER(ePlayer).GetDiplomacyAI()->SetApproachTowardsUsGuessCounter(GetID(), 0); - // Bullied a City State we're protective towards - if (GetOtherPlayerBulliedProtectedMinorTurn(ePlayer) == GC.getGame().getGameTurn()) + if(bHuman) { - // Have we asked you to make a promise before? - if(BrokeBullyCityStatePromise(ePlayer) || - IgnoredBullyCityStatePromise(ePlayer)) - { - // We don't even want to bother with you again, so do nothing - if (GetCivApproach(ePlayer) <= CIV_APPROACH_HOSTILE) - { - const char* strText = NULL; - bool bActivePlayer = GC.getGame().getActivePlayer() == ePlayer; - if (DeclareWar(ePlayer)) - { - GetPlayer()->GetMilitaryAI()->RequestCityAttack(ePlayer,1); - } - - if(bActivePlayer) - { - strText = GetDiploStringForMessage(DIPLO_MESSAGE_ATTACKED_WARMONGER); - gDLL->GameplayDiplomacyAILeaderMessage(GetID(), DIPLO_UI_STATE_BLANK_DISCUSSION, strText, LEADERHEAD_ANIM_HATE_NEGATIVE); - } - } - } - // Otherwise, ask you to make a promise - else if (GetBullyCityStatePromiseState(ePlayer) == NO_PROMISE_STATE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_BULLIED_PROTECTED_CITY_STATE; - int iTurnsBetweenStatements = 1; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - PlayerTypes eMinorCiv = GetOtherPlayerProtectedMinorBullied(ePlayer); - if(eMinorCiv != NO_PLAYER) - { - PRECONDITION(eMinorCiv >= MAX_MAJOR_CIVS); - PRECONDITION(eMinorCiv < MAX_CIV_PLAYERS); - // Minor civ must still be alive! - if(GET_PLAYER(eMinorCiv).isAlive()) - { - eStatement = eTempStatement; - iData1 = eMinorCiv; - } - } - } - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_BOOT_KISSING); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} -/// Possible Contact Statement - Comment on aggressive expansion by this player -void CvDiplomacyAI::DoExpansionWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - if (eStatement == NO_DIPLO_STATEMENT_TYPE && IsAngryAboutExpansion(ePlayer)) + // We're warning a player that his warmongering behavior is attracting attention + else if(eStatement == DIPLO_STATEMENT_WARMONGER) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EXPANSION_WARNING; - int iTurnsBetweenStatements = (/*50*/ GD_INT_GET(EXPANSION_PROMISE_TURNS_EFFECTIVE) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - eStatement = eTempStatement; + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WARMONGER); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); + } } -} -/// Possible Contact Statement - Tell the player he broke his expansion promise -void CvDiplomacyAI::DoExpansionBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - if (eStatement == NO_DIPLO_STATEMENT_TYPE && BrokeExpansionPromise(ePlayer)) -{ - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EXPANSION_BROKEN_PROMISE; - int iTurnsBetweenStatements = (/*50*/ GD_INT_GET(EXPANSION_PROMISE_BROKEN_TURNS_UNTIL_FORGIVEN) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; + // We're warning a player that his interactions with City-States are not to our liking + else if(eStatement == DIPLO_STATEMENT_MINOR_CIV_COMPETITION) + { + if(bHuman) + { + PlayerTypes eMinorCiv = (PlayerTypes) iData1; + const char* strMinorCivKey = GET_PLAYER(eMinorCiv).getNameKey(); - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - eStatement = eTempStatement; + szText = GetDiploStringForMessage(DIPLO_MESSAGE_MINOR_CIV_COMPETITION, NO_PLAYER, strMinorCivKey); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_NEGATIVE); + } } -} -/// Possible Contact Statement - Comment on aggressive Plot Buying by this player -void CvDiplomacyAI::DoPlotBuyingWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Human befriended an enemy of this AI! + else if(eStatement == DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY) { - bool bSendStatement = false; - if (!EverMadeBorderPromise(ePlayer) && GetBorderPromiseState(ePlayer) == NO_PROMISE_STATE) + if(bHuman) { - if(GetLandDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) - { - // We've spotten them buying up Plots - if(GetPlotBuyingAggressivePosture(ePlayer) >= AGGRESSIVE_POSTURE_LOW) - bSendStatement = true; - } + PlayerTypes eTarget = (PlayerTypes) iData1; + const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOFED_ENEMY, ePlayer, strTargetCivKey); + + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } + } - if(bSendStatement) + // Human denounced a friend of this AI! + else if(eStatement == DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND) + { + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_PLOT_BUYING_WARNING; - int iTurnsBetweenStatements = (/*50*/ GD_INT_GET(BORDER_PROMISE_TURNS_EFFECTIVE) * GC.getGame().getGameSpeedInfo().getOpinionDurationPercent()) / 100; + PlayerTypes eTarget = (PlayerTypes) iData1; + const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCED_FRIEND, ePlayer, strTargetCivKey); - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - eStatement = eTempStatement; + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } - } -} -/// Possible Contact Statement - Tell the player he broke his Plot Buying promise -void CvDiplomacyAI::DoPlotBuyingBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Human denounced an enemy of this AI! + else if(eStatement == DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY) { - if(BrokeBorderPromise(ePlayer)) + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_PLOT_BUYING_BROKEN_PROMISE; - int iTurnsBetweenStatements = 9999; + PlayerTypes eTarget = (PlayerTypes) iData1; + const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCED_ENEMY, ePlayer, strTargetCivKey); - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - eStatement = eTempStatement; + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - We attacked a minor that is protected by someone -void CvDiplomacyAI::DoWeAttackedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + // Human befriended a friend of this AI! + else if(eStatement == DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND) { - // They must be able to declare war on us - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) - return; - - for (int iMinorCivLoop = MAX_MAJOR_CIVS; iMinorCivLoop < MAX_CIV_PLAYERS; iMinorCivLoop++) + if(bHuman) { - PlayerTypes eMinor = (PlayerTypes) iMinorCivLoop; - - if (IsAtWar(eMinor) && GET_PLAYER(eMinor).isAlive() && GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(ePlayer)) - { - // Did we declare war on them recently? - int iTurn = GET_PLAYER(eMinor).GetMinorCivAI()->GetTurnLastAttacked(GetTeam()); - if (iTurn < 0) - return; - - int iTurnDifference = GC.getGame().getGameTurn() - iTurn; - - if (iTurnDifference < 10) - { - // Has this message not yet been sent during this war? - if (!HasSentAttackProtectedMinorTaunt(ePlayer, eMinor)) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_WE_ATTACKED_YOUR_MINOR; - int iTurnsBetweenStatements = 1; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - ASSERT(eMinor >= MAX_MAJOR_CIVS); - PRECONDITION(eMinor < MAX_CIV_PLAYERS); + PlayerTypes eTarget = (PlayerTypes) iData1; + const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOFED_FRIEND, ePlayer, strTargetCivKey); - eStatement = eTempStatement; - iData1 = eMinor; - } - } - } - } + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} -/// Possible Contact Statement - We bullied a minor that is protected by someone -void CvDiplomacyAI::DoWeBulliedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index. "); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index. "); - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // AI befriended an enemy of the human! + else if(eStatement == DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY) { - // They must be able to declare war on us - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) - return; - PlayerTypes eMinor; - for(int iMinorCivLoop = MAX_MAJOR_CIVS; iMinorCivLoop < MAX_CIV_PLAYERS; iMinorCivLoop++) + if(bHuman) { - eMinor = (PlayerTypes) iMinorCivLoop; - - // Minor must be alive - if(!GET_PLAYER(eMinor).isAlive()) - continue; - - // Did we bully this minor last turn? - if(GET_PLAYER(eMinor).GetMinorCivAI()->IsEverBulliedByMajor(GetID())) - { - if(GET_PLAYER(eMinor).GetMinorCivAI()->GetTurnLastBulliedByMajor(GetID()) == (GC.getGame().getGameTurn() - 1)) - { - // Is this minor protected by this player? - if(GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(ePlayer)) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_WE_BULLIED_YOUR_MINOR; - - int iTurnsBetweenStatements = 1; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - ASSERT(eMinor >= MAX_MAJOR_CIVS); - PRECONDITION(eMinor < MAX_CIV_PLAYERS); + PlayerTypes eTarget = (PlayerTypes) iData1; + const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCE_SO_AI_DOF, ePlayer, strTargetCivKey); - eStatement = eTempStatement; - iData1 = eMinor; - } - } - } - } + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } -} - -/// Possible Contact Statement - We caught this player spying on us -void CvDiplomacyAI::DoCaughtYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + // AI denounced a friend of the human! + else if(eStatement == DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND) { - if (m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyCaught[ePlayer] == GC.getGame().getGameTurn()) + if(bHuman) { - // Ask you to make a promise if you haven't before - if (GetSpyPromiseState(ePlayer) == NO_PROMISE_STATE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_CAUGHT_YOUR_SPY; - int iTurnsBetweenStatements = 40; + PlayerTypes eTarget = (PlayerTypes) iData1; + const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOF_SO_AI_DENOUNCE, ePlayer, strTargetCivKey); - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - } - } + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_HATE_NEGATIVE); } } -} - -/// Possible Contact Statement - We killed this player's spy -void CvDiplomacyAI::DoKilledYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // AI denounced an enemy of the human! + else if(eStatement == DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY) { - if(m_pPlayer->GetEspionageAI()->m_aiNumSpiesKilled[ePlayer] > 0) + if(bHuman) { - if(m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyKilled[ePlayer] == GC.getGame().getGameTurn()) - { - // Ask you to make a promise if you haven't before - if (GetSpyPromiseState(ePlayer) == NO_PROMISE_STATE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_KILLED_YOUR_SPY; - int iTurnsBetweenStatements = 40; + PlayerTypes eTarget = (PlayerTypes) iData1; + const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DENOUNCE_SO_AI_DENOUNCE, ePlayer, strTargetCivKey); - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - } - } - } + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} -/// Possible Contact Statement - This player killed our spy -void CvDiplomacyAI::DoKilledMySpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + // AI befriended a friend of the human! + else if(eStatement == DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND) { - if (m_pPlayer->GetEspionageAI()->m_aiTurnLastSpyDied[ePlayer] == GC.getGame().getGameTurn() - 1) + if(bHuman) { - // Don't send the message if they ignored our request before, or we ignored theirs. - if (GetSpyPromiseState(ePlayer) < PROMISE_STATE_IGNORED && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetSpyPromiseState(GetID()) < PROMISE_STATE_IGNORED) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_KILLED_MY_SPY; - int iTurnsBetweenStatements = 40; + PlayerTypes eTarget = (PlayerTypes) iData1; + const char* strTargetCivKey = GET_PLAYER(eTarget).getCivilizationShortDescriptionKey(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_HUMAN_DOF_SO_AI_DOF, ePlayer, strTargetCivKey); - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - } - } + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - Share intrigue with this player -void CvDiplomacyAI::DoShareIntrigueStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // AI chose same late game policy tree as the human! + else if(eStatement == DIPLO_STATEMENT_SAME_POLICIES_FREEDOM) { - IntrigueNotificationMessage* pNotificationMessage = m_pPlayer->GetEspionage()->GetRecentIntrigueInfo(ePlayer); - if (pNotificationMessage) + if(bHuman) { - // if this player has an untold plot against a player - if(pNotificationMessage->m_eSourcePlayer != NO_PLAYER) - { - bool bIsNewIntrigue = true; - // has any other player told the player about this plot? - for(uint ui = 0; ui < MAX_MAJOR_CIVS; ui++) - { - PlayerTypes eOtherPlayer = (PlayerTypes)ui; - // don't evaluate the plotting player - if(eOtherPlayer == pNotificationMessage->m_eSourcePlayer) - { - continue; - } - - if(GET_PLAYER(eOtherPlayer).GetEspionage()->HasSharedIntrigue(ePlayer, pNotificationMessage->m_eSourcePlayer, pNotificationMessage->m_eDiplomacyPlayer, (CvIntrigueType)(pNotificationMessage->m_iIntrigueType))) - { - bIsNewIntrigue = false; - break; - } - } - - if(bIsNewIntrigue) - { - CivApproachTypes eApproachTowardsTarget = GetCivApproach(ePlayer); - CivOpinionTypes eOpinionOfTarget = GetCivOpinion(ePlayer); - CivOpinionTypes eOpinionOfPlotter = GetCivOpinion(pNotificationMessage->m_eSourcePlayer); - - if (eApproachTowardsTarget > CIV_APPROACH_GUARDED || (eApproachTowardsTarget > CIV_APPROACH_HOSTILE && (eOpinionOfTarget > eOpinionOfPlotter))) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_SHARE_INTRIGUE; - int iTurnsBetweenStatements = 1; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - } - } - } - else - { - // mark this as shared so it doesn't try to interrupt the player - m_pPlayer->GetEspionage()->MarkRecentIntrigueAsShared(ePlayer, pNotificationMessage->m_eSourcePlayer, pNotificationMessage->m_eDiplomacyPlayer, (CvIntrigueType)(pNotificationMessage->m_iIntrigueType)); - } - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SAME_POLICIES_FREEDOM); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - They converted one of our cities, and we want them to stop that -void CvDiplomacyAI::DoConvertedMyCityStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_SAME_POLICIES_ORDER) { - if (HasEverConvertedCity(ePlayer) && GetNoConvertPromiseState(ePlayer) == NO_PROMISE_STATE && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsPlayerAskedNotToConvert(GetID())) + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_STOP_CONVERSIONS; - int iTurnsBetweenStatements = 50; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SAME_POLICIES_ORDER); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - They dug up one of our artifacts, and we want them to stop that -void CvDiplomacyAI::DoDugUpMyYardStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_SAME_POLICIES_AUTOCRACY) { - if (GetNumArtifactsEverDugUp(ePlayer) > 0) // TODO: arch + if(bHuman) { - // Have we asked you to make a promise before? - if (GetNoDiggingPromiseState(ePlayer) == NO_PROMISE_STATE && !GET_PLAYER(ePlayer).GetDiplomacyAI()->IsPlayerAskedNotToDig(GetID())) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_STOP_DIGGING; - int iTurnsBetweenStatements = 30; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - } - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SAME_POLICIES_AUTOCRACY); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - We want to make a Declaration of Friendship with them -void CvDiplomacyAI::DoDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_FRIENDSHIP_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL) { - // Have we already made the agreement? - if (!IsDoFAccepted(ePlayer) && !HasEndedFriendshipThisTurn()) + if(bHuman) { - // Do we actually want a DoF with ePlayer? - if (IsWantsDoFWithPlayer(ePlayer)) + Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); + CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); + if (pLeague != NULL) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_WORK_WITH_US; - - if (GetDoFType(ePlayer) == DOF_TYPE_BATTLE_BROTHERS) - { - eTempStatement = DIPLO_STATEMENT_DOF_BB; - } - else if (GetDoFType(ePlayer) == DOF_TYPE_ALLIES) - { - eTempStatement = DIPLO_STATEMENT_DOF_ALLIES; - } - else if (GetDoFType(ePlayer) == DOF_TYPE_FRIENDS) - { - eTempStatement = DIPLO_STATEMENT_DOF_FRIENDS; - } - else if (GetDoFType(ePlayer) == DOF_TYPE_UNTRUSTWORTHY) - { - eTempStatement = DIPLO_STATEMENT_DOF_UNTRUSTWORTHY; - } - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 30) - { - eStatement = eTempStatement; - } + sLeagueName = pLeague->GetName(); } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_LIKED_THEIR_PROPOSAL, ePlayer, sLeagueName); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - We're denouncing one of our friends (backstabbing) -void CvDiplomacyAI::DoDenounceFriendStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL) { - // Must have already made the agreement - if (IsDoFAccepted(ePlayer)) + if(bHuman) { - // Done working with this guy, and willing to denounce? - if (IsWantsToEndDoFWithPlayer(ePlayer) && IsDenounceFriendAcceptable(ePlayer)) + Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); + CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); + if (pLeague != NULL) { - eStatement = DIPLO_STATEMENT_DENOUNCE_FRIEND; + sLeagueName = pLeague->GetName(); } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WE_DISLIKED_THEIR_PROPOSAL, ePlayer, sLeagueName); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} - -/// Possible Contact Statement - We're ending our Declaration of Friendship with them -void CvDiplomacyAI::DoEndDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) const -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_THEY_SUPPORTED_OUR_PROPOSAL) { - // Must have already made the agreement - if (IsDoFAccepted(ePlayer)) + if(bHuman) { - // Done working with this guy, but not willing to denounce? - if (IsWantsToEndDoFWithPlayer(ePlayer)) - { - eStatement = DIPLO_STATEMENT_END_WORK_WITH_US; - } + Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_THEY_SUPPORTED_OUR_PROPOSAL, ePlayer, sLeagueName); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - We're denouncing a player -void CvDiplomacyAI::DoDenounceStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_THEY_FOILED_OUR_PROPOSAL) { - if(IsDenounceAcceptable(ePlayer, /*bBias*/ false)) + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_DENOUNCE; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= GC.getGame().GetDealDuration() && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_DENOUNCE_RANDFAILED) >= 10) - { - bool bSendStatement = true; - - // 1 in 2 chance we don't actually send the message (don't want full predictability) - //if (50 < GC.getGame().getJonRandNum(100, "Diplomacy AI: rand roll to see if we ask to work with a player")) - // bSendStatement = false; - - if(bSendStatement) - { - eStatement = eTempStatement; - } - - // Add this statement to the log so we don't evaluate it again until time has passed - else - SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_DENOUNCE_RANDFAILED, GC.getGame().getGameTurn()); - } + Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_THEY_FOILED_OUR_PROPOSAL, ePlayer, sLeagueName); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} - -/// Possible Contact Statement - We're requesting that a player denounce someone -void CvDiplomacyAI::DoRequestFriendDenounceStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_THEY_SUPPORTED_OUR_HOSTING) { - bool bRandFailed = false; - - PlayerTypes eTarget = GetRequestFriendToDenounce(ePlayer, bRandFailed); - if(eTarget != NO_PLAYER) + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST_FRIEND_DENOUNCE_RANDFAILED) >= 10) + Localization::String sLeagueName = Localization::Lookup("TXT_KEY_LEAGUE_WORLD_CONGRESS_GENERIC"); + CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); + if (pLeague != NULL) { - if(!bRandFailed) - { - eStatement = eTempStatement; - iData1 = eTarget; - } - // Add this statement to the log so we don't evaluate it again until time has passed - else - { - SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_DENOUNCE_RANDFAILED, GC.getGame().getGameTurn()); - } + sLeagueName = pLeague->GetName(); } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_THEY_SUPPORTED_OUR_HOSTING, ePlayer, sLeagueName); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - Luxury Trade -void CvDiplomacyAI::DoLuxuryTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Ideological statements + else if(eStatement == DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_LUXURY_TRADE; - int iTurnsBetweenStatements = 20; - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + if(bHuman) { - if (GetPlayer()->GetDealAI()->IsMakeOfferForLuxuryResource(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); + } + } + else if(eStatement == DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} - -/// Possibile Contact Statement - Embassy Exchange -void CvDiplomacyAI::DoEmbassyExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY) { - // Can both sides open an embassy - if(pDeal->IsPossibleToTradeItem(GetID(), ePlayer, TRADE_ITEM_ALLOW_EMBASSY) && - pDeal->IsPossibleToTradeItem(ePlayer, GetID(), TRADE_ITEM_ALLOW_EMBASSY)) + if(bHuman) { - // Does this guy want to exchange embassies? - if(IsEmbassyExchangeAcceptable(ePlayer)) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EMBASSY_EXCHANGE; - int iTurnsBetweenStatements = 20; - if ((GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EMBASSY_OFFER) >= 20) - { - bool bSendStatement = false; - - // AI - if(!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - { - if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsEmbassyExchangeAcceptable(GetID())) - bSendStatement = true; - } - // Human - else - bSendStatement = true; - - if(bSendStatement) - { - pDeal->AddAllowEmbassy(GetID()); - pDeal->AddAllowEmbassy(ePlayer); - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = GetPlayer()->GetDealAI()->DoEqualizeDeal(pDeal, ePlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - - if (bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0) - { - eStatement = eTempStatement; - } - else - { - bSendStatement = false; - } - } - - if(!bSendStatement) - { - SetTurnStatementLastSent(ePlayer, eTempStatement, GC.getGame().getGameTurn()); - pDeal->ClearItems(); - } - } - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} - -/// Possible Contact Statement - Embassy -void CvDiplomacyAI::DoEmbassyOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM) { - if(GetPlayer()->GetDealAI()->IsMakeOfferForEmbassy(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EMBASSY_OFFER; - int iTurnsBetweenStatements = 15; - if ((GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) && GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_EMBASSY_EXCHANGE) >= 10) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } - else + } + else if(eStatement == DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER) + { + if(bHuman) { - // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at - pDeal->ClearItems(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + } + } + else if(eStatement == DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - Open Borders Exchange -void CvDiplomacyAI::DoOpenBordersExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM) { - int iDuration = GC.getGame().GetDealDuration(); - - // Can both sides trade OB? - if(pDeal->IsPossibleToTradeItem(GetID(), ePlayer, TRADE_ITEM_OPEN_BORDERS, iDuration) && - pDeal->IsPossibleToTradeItem(ePlayer, GetID(), TRADE_ITEM_OPEN_BORDERS, iDuration)) + if(bHuman) { - // Does this guy want to exchange OB? - if(IsOpenBordersExchangeAcceptable(ePlayer)) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_OPEN_BORDERS_EXCHANGE; - int iTurnsBetweenStatements = 25; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - // OB on each side - pDeal->AddOpenBorders(GetID(), iDuration); - pDeal->AddOpenBorders(ePlayer, iDuration); - - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bool bDealAcceptable = GetPlayer()->GetDealAI()->DoEqualizeDeal(pDeal, ePlayer, bUselessReferenceVariable, bCantMatchOffer); // Change the deal as necessary to make it work - if (bDealAcceptable && !bCantMatchOffer && pDeal->GetNumItems() > 0) - { - eStatement = eTempStatement; - } - // Add this statement to the log so we don't evaluate it again until 20 turns has come back around - else - { - SetTurnStatementLastSent(ePlayer, eTempStatement, GC.getGame().getGameTurn()); - pDeal->ClearItems(); - } - } - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_FREEDOM); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - Open Borders -void CvDiplomacyAI::DoOpenBordersOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_OPEN_BORDERS_OFFER; - int iTurnsBetweenStatements = 25; - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + if(bHuman) { - if(GetPlayer()->GetDealAI()->IsMakeOfferForOpenBorders(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_ORDER); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } - else + } + else if(eStatement == DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY) + { + if(bHuman) { - // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at - pDeal->ClearItems(); + szText = GetDiploStringForMessage(DIPLO_MESSAGE_SWITCH_OUR_IDEOLOGY_AUTOCRACY); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + } + } + else if(eStatement == DIPLO_STATEMENT_YOUR_CULTURE_INFLUENTIAL) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_YOUR_CULTURE_INFLUENTIAL); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} - -/// Possible Contact Statement - Research Agreement Offer -void CvDiplomacyAI::DoResearchAgreementOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_OUR_CULTURE_INFLUENTIAL) { - if (IsCanMakeResearchAgreementRightNow(ePlayer)) + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_RESEARCH_AGREEMENT_OFFER; - int iTurnsBetweenStatements = 20; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - if (GetPlayer()->GetDealAI()->IsMakeOfferForResearchAgreement(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_OUR_CULTURE_INFLUENTIAL); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); } } -} - -/// Possible Contact Statement - Strategic Resource Offer -void CvDiplomacyAI::DoStrategicTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Player has a Strategic Resource we'd like + else if(eStatement == DIPLO_STATEMENT_STRATEGIC_TRADE) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_STRATEGIC_TRADE; - int iTurnsBetweenStatements = 20; - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_STRATEGIC_TRADE); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + // Offer to an AI player + else { - if (GetPlayer()->GetDealAI()->IsMakeOfferForStrategicResource(ePlayer, /*pDeal can be modified in this function*/ pDeal)) + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - eStatement = eTempStatement; + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } else { - pDeal->ClearItems(); + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } -} - -/// Possible Contact Statement - Defensive Pact Offer -void CvDiplomacyAI::DoDefensivePactOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + + // Announce to the human that this AI is competing with them for the same victory condition + else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST) { - if (IsWantsDefensivePactWithPlayer(ePlayer)) + if(bHuman) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_DEFENSIVE_PACT_REQUEST; - int iTurnsBetweenStatements = 20; - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - if (GetPlayer()->GetDealAI()->IsMakeOfferForDefensivePact(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} - -/// Possible Contact Statement - City Exchange -void CvDiplomacyAI::DoCityExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_EXCHANGE_CITIES; - int iTurnsBetweenStatements = 30; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + if(bHuman) { - if (GetPlayer()->GetDealAI()->IsMakeOfferForCityExchange(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} -/// Possible Contact Statement - Third Party War Trade -void CvDiplomacyAI::DoThirdPartyWarTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CULTURE) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_THIRDPARTY_WAR_REQUEST; - int iTurnsBetweenStatements = 40; - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + if(bHuman) { - if (GetPlayer()->GetDealAI()->IsMakeOfferForThirdPartyWar(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CULTURE); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} -/// Possible Contact Statement - Peace Trade -void CvDiplomacyAI::DoThirdPartyPeaceTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_THIRDPARTY_PEACE_REQUEST; - int iTurnsBetweenStatements = 30; - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + if(bHuman) { - if (GetPlayer()->GetDealAI()->IsMakeOfferForThirdPartyPeace(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} -/// Possible Contact Statement - Vote Trade -void CvDiplomacyAI::DoVoteTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + else if(eStatement == DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CONFUSED) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_VOTE_REQUEST; - int iTurnsBetweenStatements = 10; - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + if(bHuman) { - if (GetPlayer()->GetDealAI()->IsMakeOfferForVote(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_COMPETITION_ANNOUNCE_CONFUSED); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); } } -} - -/// Possible Contact Statement - Renew Recently Expired Deal -CvDeal* CvDiplomacyAI::DoRenewExpiredDeal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (eStatement != NO_DIPLO_STATEMENT_TYPE) + // Announce to the human that this AI wants to block them from achieving victory + else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST) { - return NULL; + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); + } } - - if (IsAvoidDeals()) + else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS) { - return NULL; + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); + } } - - if (MOD_DIPLOAI_SHUT_UP_TRADE_RENEWALS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_CULTURE) { - return NULL; + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_CULTURE); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); + } } - - // Find all potentially renewable deals: deals which will expire after this turn and don't have any non-renewable items - CvDeal* pBestDeal = NULL; - int iBestDealValue = -INT_MAX; - - CvGameDeals& kGameDeals = GC.getGame().GetGameDeals(); - DealList::iterator it; - for (it = kGameDeals.m_CurrentDeals.begin(); it != kGameDeals.m_CurrentDeals.end(); ++it) + else if(eStatement == DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_SPACESHIP) { - if (it->m_iFinalTurn == GC.getGame().getGameTurn()) + if(bHuman) { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VICTORY_BLOCK_ANNOUNCE_SPACESHIP); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_NEGATIVE); + } + } - if ((it->m_eToPlayer == ePlayer && it->m_eFromPlayer == GetID()) || - (it->m_eFromPlayer == ePlayer && it->m_eToPlayer == GetID())) + // We'd like to purchase this player's World Map + else if(eStatement == DIPLO_STATEMENT_MAPS_OFFER) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_MAPS_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - // if there are non-renewable items in the deal, move on - if (!it->IsPotentiallyRenewable()) - continue; - - // if this deal is a gift or peace deal, move on. - if (it->m_bIsGift || it->IsPeaceTreatyTrade(it->GetFromPlayer()) || it->IsPeaceTreatyTrade(it->GetToPlayer())) - { - continue; - } - - CvDeal* pCurrentDeal = &(*it); - - // Make a copy of the deal because we don't want the items of the original deal to change when trying to equalize - CvDeal dealCopy = *pCurrentDeal; - // set pCurrentDeal as the deal that's being considered for renewal: deal valuations will be calculated as if pCurrentDeal had expired - GC.getGame().GetGameDeals().SetRenewDealID(GetID(), ePlayer, pCurrentDeal->m_iID); - m_pPlayer->GetDealAI()->ClearCachedDealItemValues(); - GET_PLAYER(ePlayer).GetDealAI()->ClearCachedDealItemValues(); - - int iValue = m_pPlayer->GetDealAI()->GetDealValue(&dealCopy); - if (iValue != INT_MAX) - { - bool bAbleToEqualize = false; - if (!GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - { - bool bUselessReferenceVariable = false; - bool bCantMatchOffer = false; - bAbleToEqualize = m_pPlayer->GetDealAI()->DoEqualizeDeal(&dealCopy, ePlayer, bUselessReferenceVariable, bCantMatchOffer); - } - else - bAbleToEqualize = true; + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; - if (bAbleToEqualize) - { - eStatement = DIPLO_STATEMENT_RENEW_DEAL; - if (dealCopy.m_iToPlayerValue > iBestDealValue) - { - iBestDealValue = dealCopy.m_iToPlayerValue; - pBestDeal = pCurrentDeal; - } - } - } + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } - - GC.getGame().GetGameDeals().SetRenewDealID(GetID(), ePlayer, pBestDeal ? pBestDeal->m_iID : -1); - return pBestDeal; -} - -/// Possible Contact Statement - Request Help -void CvDiplomacyAI::DoRequest(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_HELP_REQUESTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - // If we just sent out a generous offer, don't ask for a request until some time has passed - if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER) < 25) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // We'd like to purchase a technology from this player + else if(eStatement == DIPLO_STATEMENT_TECH_OFFER) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REQUEST; - - // If a request was accepted or rejected, wait 60 turns. If we rolled for rand and failed, wait 15 turns before we try again - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 60 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST_RANDFAILED) >= 15) + if(bHuman) { - bool bRandPassed = false; // This is used to see if we WOULD have made a request, but the rand roll failed (so add an entry to the log) - bool bMakeRequest = IsMakeRequest(ePlayer, pDeal, bRandPassed); - - // Want to make a request of ePlayer? Pass pDeal in to see if there's actually anything we want - if(bMakeRequest) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_TECH_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - eStatement = eTempStatement; - pDeal->SetRequestingPlayer(GetID()); - pDeal->m_bIsGift = true; + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } - - // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at else - pDeal->ClearItems(); - - // Add this statement to the log so we don't evaluate it again until 15 turns has come back around - if(!bRandPassed) { - SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_REQUEST_RANDFAILED, GC.getGame().getGameTurn()); - pDeal->ClearItems(); + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } -} - -/// Possible Contact Statement - Gift -void CvDiplomacyAI::DoGift(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_GIFT_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // We're making a generous offer to this player + else if(eStatement == DIPLO_STATEMENT_GENEROUS_OFFER) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_GIFT; - - // If a request was accepted or rejected, wait 60 turns. If we rolled for rand and failed, wait 15 turns before we try again - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 60 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GIFT_RANDFAILED) >= 15) + if(bHuman) { - bool bRandPassed = false; // This is used to see if we WOULD have made a gift, but the rand roll failed (so add an entry to the log) - bool bMakeGift = false;//IsMakeGift(ePlayer, pDeal, bRandPassed); - - // Want to make a request of ePlayer? Pass pDeal in to see if there's actually anything we want - if(bMakeGift) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_GENEROUS_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_GENEROUS_OFFER, szText, LEADERHEAD_ANIM_REQUEST); + } + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - eStatement = eTempStatement; - pDeal->SetRequestingPlayer(GetID()); + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } - - // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at else - pDeal->ClearItems(); - - // Add this statement to the log so we don't evaluate it again until 15 turns has come back around - if(!bRandPassed) { - SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_GIFT_RANDFAILED, GC.getGame().getGameTurn()); - pDeal->ClearItems(); + // For now the AI will always accept - may eventually write additional logic here + CvDeal kDeal = *pDeal; + + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); } } } -} - -/// Possible Contact Statement -//void CvDiplomacyAI::DoNowUnforgivableStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement) -//{ -// ASSERT(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); -// ASSERT(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); -// -// if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) -// return; -// -// if (eStatement == NO_DIPLO_STATEMENT_TYPE) -// { -// bool bSendStatement = false; -// -// // Unforgivable! -// if (GetCivOpinion(ePlayer) == CIV_OPINION_UNFORGIVABLE) -// { -// // Our approach (real or fake) can't be Friendly -// if (GetSurfaceApproach(ePlayer) != CIV_APPROACH_FRIENDLY) -// { -// bSendStatement = true; -// } -// } -// -// if (bSendStatement) -// { -// DiploStatementTypes eTempStatement = DIPLO_STATEMENT_NOW_UNFORGIVABLE; -// int iTurnsBetweenStatements = 9999; -// -// if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) -// { -// eStatement = eTempStatement; -// } -// } -// } -//} - -/// Possible Contact Statement -//void CvDiplomacyAI::DoNowEnemyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement) -//{ -// ASSERT(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); -// ASSERT(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); -// -// if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) -// return; -// -// if (eStatement == NO_DIPLO_STATEMENT_TYPE) -// { -// bool bSendStatement = false; -// -// // Don't show this message if we've already given a more severe one -// if (GetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_NOW_UNFORGIVABLE) > -1) -// { -// // An enemy -// if (GetCivOpinion(ePlayer) == CIV_OPINION_ENEMY) -// { -// // Our approach (real or fake) can't be Friendly -// if (GetSurfaceApproach(ePlayer) != CIV_APPROACH_FRIENDLY) -// { -// bSendStatement = true; -// } -// } -// -// if (bSendStatement) -// { -// DiploStatementTypes eTempStatement = DIPLO_STATEMENT_NOW_ENEMY; -// int iTurnsBetweenStatements = 9999; -// -// if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) -// { -// eStatement = eTempStatement; -// } -// } -// } -// } -//} - -/// Possible Contact Statement - Approach towards player is now HOSTILE -void CvDiplomacyAI::DoHostileStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (WasResurrectedBy(ePlayer)) - return; - - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - return; + //We want to declare independence from our master + else if(eStatement == DIPLO_STATEMENT_REVOKE_VASSALAGE) + { + if (bHuman) + { + if (IsActHostileTowardsHuman(ePlayer)) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_REVOKE_VASSALAGE_HOSTILE); + else + szText = GetDiploStringForMessage(DIPLO_MESSAGE_REVOKE_VASSALAGE); - CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_DISCUSS_AI_REVOKE_VASSALAGE, szText, LEADERHEAD_ANIM_NEGATIVE); + } + // AI resolution + else + { + CvPlayer& kVassalPlayer = GET_PLAYER(GetID()); + CvPlayer& kMasterPlayer = GET_PLAYER(ePlayer); - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + bool bPeaceful = kMasterPlayer.GetDiplomacyAI()->IsEndVassalageAcceptable(kVassalPlayer.GetID()); + GET_TEAM(kVassalPlayer.getTeam()).DoEndVassal(kMasterPlayer.getTeam(), bPeaceful, false); + } + } + //We want this player to liberate their vassals + else if(eStatement == DIPLO_STATEMENT_REVOKE_VASSALAGE_THIRD_PARTY) { - if (!IsTooEarlyForDoF(ePlayer) && (eApproach == CIV_APPROACH_HOSTILE) && (GetMeanness() > 6)) + if(bHuman) { - TeamTypes eTeam = GET_PLAYER(ePlayer).getTeam(); - - // If we've made peace recently, don't go mouthing off right away - int iPeaceTreatyTurn = GET_TEAM(GetTeam()).GetTurnMadePeaceTreatyWithTeam(eTeam); - if (iPeaceTreatyTurn != -1) + szText = GetDiploStringForMessage(DIPLO_MESSAGE_REVOKE_VASSAL_THIRD_OFFER); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_POSITIVE); + } + // Offer to an AI player + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - int iTurnsSincePeace = GC.getGame().getElapsedGameTurns() - iPeaceTreatyTurn; - if (iTurnsSincePeace < GC.getGame().getHandicapInfo().getPeaceTreatyDampenerTurns()) - return; + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } + else + { + CvDeal kDeal = *pDeal; - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_INSULT; - int iTurnsBetweenStatements = 75; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - eStatement = eTempStatement; + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } } } -} - -/// Possible Contact Statement -//void CvDiplomacyAI::DoFriendlyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement) -//{ -// ASSERT(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); -// ASSERT(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); -// -// if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) -// return; -// -// CivApproachTypes eApproach = GetCivApproach(ePlayer); -// -// if (eStatement == NO_DIPLO_STATEMENT_TYPE) -// { -// if (eApproach == CIV_APPROACH_FRIENDLY) -// { -// DiploStatementTypes eTempStatement = DIPLO_STATEMENT_COMPLIMENT; -// int iTurnsBetweenStatements = 35; -// -// if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) -// { -// eStatement = eTempStatement; -// } -// } -// } -//} - -/// Possible Contact Statement - Approach towards player is now AFRAID -void CvDiplomacyAI::DoAfraidStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // AI offers to make ePlayer his voluntary vassal + else if(eStatement == DIPLO_STATEMENT_BECOME_MY_VASSAL) { - if(eApproach == CIV_APPROACH_AFRAID) + if (bHuman) + { + ASSERT(false, "Don't send vassalage statement to human!"); + } + else { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_BOOT_KISSING; - int iTurnsBetweenStatements = 35; + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - eStatement = eTempStatement; + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } } } -} - -/// Possible Contact Statement - Warning the player about their warmongering -void CvDiplomacyAI::DoWarmongerStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (WasResurrectedBy(ePlayer)) - return; - - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - return; - - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + // AI offers to become voluntary vassal of ePlayer + else if (eStatement == DIPLO_STATEMENT_ACCEPT_VASSALAGE) { - if (GetWarmongerThreat(ePlayer) >= THREAT_SEVERE) + if (bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_BECOME_VASSAL); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_POSITIVE); + } + else { - bool bSendStatement = true; + if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); + } + else + { + CvDeal kDeal = *pDeal; - // Don't send statement if we're going for conquest ourselves - if (IsGoingForWorldConquest() || IsCloseToWorldConquest()) - bSendStatement = false; + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } + } + } + // AI is happy that they were liberated from vassalage + else if(eStatement == DIPLO_STATEMENT_LIBERATE_VASSAL) + { + if(bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSALAGE_LIBERATE_VASSAL); + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + } - // 2 in 3 chance we don't actually send the message (don't want to bombard the player from all sides) - if (GC.getGame().randRangeInclusive(1, 3, CvSeeder::fromRaw(0x4a229eef).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) < 3) - bSendStatement = false; + // Liberate this vassal + GET_TEAM(GetTeam()).DoLiberateVassal(GET_PLAYER(ePlayer).getTeam()); + } + // AI is upset that their taxes were raised + else if (eStatement == DIPLO_STATEMENT_VASSAL_TAXES_RAISED_HUMAN_MASTER) + { + if (bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_RAISED_HUMAN_MASTER, ePlayer); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN, szText, LEADERHEAD_ANIM_NEGATIVE); + } + } + // AI is happy that their taxes were lowered + else if (eStatement == DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_HUMAN_MASTER) + { + if (bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_LOWERED_HUMAN_MASTER, ePlayer); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + } + } + // AI notifies human that their taxes were RAISED + else if (eStatement == DIPLO_STATEMENT_VASSAL_TAXES_RAISED_AI_MASTER) + { + if (bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_RAISED_AI_MASTER, ePlayer); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI, szText, LEADERHEAD_ANIM_NEGATIVE); + } + } + // AI notifies human that their taxes were LOWERED + else if (eStatement == DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_AI_MASTER) + { + if (bHuman) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_VASSAL_TAXES_LOWERED_AI_MASTER, ePlayer); + CvDiplomacyRequests::SendRequest(GetID(), ePlayer, DIPLO_UI_STATE_BLANK_DISCUSSION, szText, LEADERHEAD_ANIM_POSITIVE); + } + } - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_WARMONGER; + // Do we want peace with ePlayer? + else if (eStatement == DIPLO_STATEMENT_REQUEST_PEACE) + { + if (bHuman) + { + int iOurWarScore = GetWarScore(ePlayer); + if (iOurWarScore >= 10) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_WINNER_PEACE_OFFER); + } + else if (iOurWarScore <= -10) + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_PEACE_MADE_BY_HUMAN_GRACIOUS); + } + else + { + szText = GetDiploStringForMessage(DIPLO_MESSAGE_PEACE_OFFER); + } - if (bSendStatement) + CvDiplomacyRequests::SendDealRequest(GetID(), ePlayer, pDeal, DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER, szText, LEADERHEAD_ANIM_POSITIVE); + } + // Offer to an AI player + else + { + if(GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) { - if (GetTurnStatementLastSent(ePlayer, eTempStatement) == -1) - eStatement = eTempStatement; + GC.getGame().GetGameDeals().FinalizeMPDeal(*pDeal, true); } - // Add this statement to the log so we don't evaluate it again next turn else - SetTurnStatementLastSent(ePlayer, eTempStatement, GC.getGame().getGameTurn()); + { + CvDeal kDeal = *pDeal; + + // Don't need to call DoOffer because we check to see if the deal works for both sides BEFORE sending + GC.getGame().GetGameDeals().AddProposedDeal(kDeal); + GC.getGame().GetGameDeals().FinalizeDeal(GetID(), ePlayer, true); + } + + LogPeaceMade(ePlayer); } } } -/// Possible Contact Statement - Warning the player that we don't like their interactions with "our" City-States -void CvDiplomacyAI::DoMinorCivCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1, bool bIgnoreTurnsBetweenLimit) +/// Any Major Civs we want to chat with? +void CvDiplomacyAI::DoContactMajorCivs() { - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + DetermineVassalToLiberate(); - if (WasResurrectedBy(ePlayer)) - return; + // NOTE: This function is broken up into two sections: AI contact opportunities, and then human contact opportunities + // This is to prevent a nasty bug where the AI will continue making decisions as the diplo screen is firing up. Making humans + // handled at the end prevents the Diplo AI from having this problem - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + // Loop through AI Players + if (GC.getGame().isReallyNetworkMultiPlayer() && MOD_ACTIVE_DIPLOMACY) + { + if (m_eDiploMode == DIPLO_SPECIFIC_PLAYER) + { + DoContactPlayer(m_eTargetPlayer); + } + else if (m_eDiploMode == DIPLO_ALL_PLAYERS || m_eDiploMode == DIPLO_AI_PLAYERS) + { + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + { + PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - return; + if (!IsPlayerValid(eLoopPlayer)) + continue; - if (eStatement == NO_DIPLO_STATEMENT_TYPE) - { - // They must be able to declare war on us - if (!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canDeclareWar(GetTeam(), ePlayer)) - return; + // No humans + if (GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + continue; - if (GetMinorCivDisputeLevel(ePlayer) >= DISPUTE_LEVEL_STRONG) + DoContactPlayer(eLoopPlayer); + } + } + if (m_eDiploMode == DIPLO_ALL_PLAYERS || m_eDiploMode == DIPLO_HUMAN_PLAYERS) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_MINOR_CIV_COMPETITION; - int iTurnsBetweenStatements = 9999; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements || bIgnoreTurnsBetweenLimit) + // JdH => contact humans by priority, but use a notification system instead of pop up the diplo screen + // every AI can only talk to one human a time (as a human can only talk to one human a time + // TODO: the one to one restriction should be removed in favor of a trade resource pool allocation + if (!CvDiplomacyRequests::HasActiveDiploRequestWithHuman(GetID())) { - // Find a city state we're upset over - for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) + vector aeHumansByPriority; + vector::const_iterator priorityIter, humanIter; + // bring players in priority order + for (humanIter = CvDiplomacyRequests::s_aDiploHumans.begin(); humanIter != CvDiplomacyRequests::s_aDiploHumans.end(); ++humanIter) { - PlayerTypes eMinor = (PlayerTypes) iMinorLoop; - - // Don't evaluate City-States that are unmet/dead - if (!IsPlayerValid(eMinor)) + PlayerTypes eLoopPlayer = *humanIter; + + ASSERT(CvPreGame::isHuman(eLoopPlayer)); + ASSERT(GET_PLAYER(eLoopPlayer).isTurnActive()); + + if (!IsPlayerValid(eLoopPlayer)) continue; - // Ignore if League resolutions make it irrelevant - if (GET_PLAYER(eMinor).GetMinorCivAI()->IsNoAlly() || GET_PLAYER(eMinor).GetMinorCivAI()->GetPermanentAlly() == GetID()) + // No AI + if (!GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) continue; - // Must be a minor we aren't attacking/bullying - if (GetCivApproach(eMinor) <= CIV_APPROACH_HOSTILE) + // Only active Players + if (!GET_PLAYER(eLoopPlayer).isTurnActive()) continue; - // We have a PtP with this minor - if (GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(GetID())) + for (priorityIter = aeHumansByPriority.begin(); priorityIter != aeHumansByPriority.end(); ++priorityIter) { - if (GET_PLAYER(eMinor).GetMinorCivAI()->IsAllies(ePlayer)) - { - iData1 = eMinor; - break; - } - else if (GET_PLAYER(eMinor).GetMinorCivAI()->IsFriends(ePlayer)) + if (m_aTradePriority[*priorityIter] < m_aTradePriority[eLoopPlayer]) { - iData1 = eMinor; + aeHumansByPriority.insert(priorityIter, eLoopPlayer); break; } } + if (priorityIter == aeHumansByPriority.end()) + { + aeHumansByPriority.push_back(eLoopPlayer); + } } - // Don't change the statement unless we found a minor to complain about - if (iData1 != NO_PLAYER) + for (humanIter = aeHumansByPriority.begin(); humanIter != aeHumansByPriority.end(); ++humanIter) { - eStatement = eTempStatement; + DoContactPlayer(*humanIter); + if (GET_PLAYER(*humanIter).GetDiplomacyRequests()->HasActiveRequestFrom(GetID())) + { + // we actually found someone worth talking with, the others must wait... + break; + } } } } } + else + { + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + { + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (!IsPlayerValid(eLoopPlayer)) + continue; + + // No humans + if (GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + continue; + + DoContactPlayer(eLoopPlayer); + } + + // Loop through HUMAN Players - if we're not in MP + if (!CvPreGame::isNetworkMultiplayerGame()) + { + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + { + PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + + if (!IsPlayerValid(eLoopPlayer)) + continue; + + // No AI + if (!GET_PLAYER(eLoopPlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + continue; + + DoContactPlayer(eLoopPlayer); + } + } + } } -/// Possible Contact Statement - We're angry that they befriended a player we denounced -void CvDiplomacyAI::DoAngryBefriendedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +/// Individual contact opportunity +void CvDiplomacyAI::DoContactPlayer(PlayerTypes ePlayer) { - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "Player index out of bounds"); - if (WasResurrectedBy(ePlayer)) - return; + // reset these + SetCurrentDealOfferChanged(false); + SetCurrentDealOfferGenerous(false); - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + if (!IsValidUIDiplomacyTarget(ePlayer)) + return; // Can't contact this player at the moment. - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + // Can't contact this player because of game options + if (MOD_DIPLOAI_SHUT_UP && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; - // We denounced the leader we're talking to - no use whining at this point - if(IsDenouncedPlayer(ePlayer) || IsAtWar(ePlayer) || GetSurfaceApproach(ePlayer) == CIV_APPROACH_HOSTILE) + // If this is the same turn we've met a player, don't send anything his way quite yet - wait until we've said hello at least + if (GET_TEAM(GetTeam()).GetTurnsSinceMeetingTeam(GET_PLAYER(ePlayer).getTeam()) == 0) return; - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY; + DiploStatementTypes eStatement = NO_DIPLO_STATEMENT_TYPE; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY_RANDFAILED) >= 20) - { - CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + // This can be used for info about statements, e.g., what City-State we're telling the guy to stay away from, etc. + int iData1 = -1; - // Loop through all players until we find one that is our enemy, that ePlayer befriended - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + // We can use this deal pointer to form a trade offer + CvDeal* pDeal = GC.getGame().GetGameDeals().GetTempDeal(); + CvDeal* pRenewDeal = NULL; - // Must be alive - if(!GET_PLAYER(eLoopPlayer).isAlive()) - continue; + // Clear this data out before any deals are offered. + GetPlayer()->GetDiplomacyAI()->SetOfferingGift(ePlayer, false); + GetPlayer()->GetDiplomacyAI()->SetOfferedGift(ePlayer, false); + SetCantMatchDeal(ePlayer, false); + pDeal->SetRequestingPlayer(NO_PLAYER); + pDeal->ClearItems(); + pDeal->SetFromPlayer(GetID()); + pDeal->SetToPlayer(ePlayer); + pDeal->SetDuration(GC.getGame().getGameSpeedInfo().GetDealDuration()); - // Can't be either of us - if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) - continue; + // JON: Add in some randomization here? + // How predictable do we want the AI to be with regards to what state they're in? - // We haven't denounced this guy and we're not at war with them - if(!IsDenouncedPlayer(eLoopPlayer) && !IsAtWar(eLoopPlayer)) - continue; + // Note that the order in which the following functions are called is very important to how the AI behaves - first come, first served + // All members but ePlayer are passed by address - // They haven't befriended this guy - if(!pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) - continue; + // AT PEACE + if (!IsAtWar(ePlayer)) + { + // Sanctioned? No deals can be made during peacetime, except between masters and their vassals. + bool bAvoidDeals = false; + if (MOD_BALANCE_VP) + { + CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); + if (pLeague && pLeague->IsTradeEmbargoed(m_pPlayer->GetID(), ePlayer)) + bAvoidDeals = true; + } - // Too much time has passed (or maybe we already sent a message recently) - if(pTheirDiploAI->GetTurnsSinceBefriendedPlayer(eLoopPlayer) > 1) - continue; + // Or, we might have other reasons not to trade with them, such as a war we're planning. + if (bAvoidDeals || AvoidExchangesWithPlayer(ePlayer)) + SetAvoidDeals(true); - // Found a match! - int iWeight = GetMeanness(); // Usually ranges from 3 to 7 - iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x7e74ea9f).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); + // Many messages are not sent to teammates + if (IsTeammate(ePlayer)) + SetSkipForTeammates(true); - // We're mean enough to say something - if(iWeight >= 10) - { - eStatement = eTempStatement; - iData1 = eLoopPlayer; - } - // We're going to be nice! - else - { - eStatement = DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY_RANDFAILED; - } + // Highest priority statements - these can trigger WAR! + DoWeAttackedYourMinorStatement(ePlayer, eStatement, iData1); // game mechanic requires immediate response + DoWeBulliedYourMinorStatement(ePlayer, eStatement, iData1); // game mechanic requires immediate response + DoEndVassalageStatement(ePlayer, eStatement); + DoAttackedCityStateStatement(ePlayer, eStatement, iData1); + DoBulliedCityStateStatement(ePlayer, eStatement, iData1); + //DoCoopWarTimeStatement(ePlayer, eStatement, iData1); + DoAggressiveMilitaryStatement(ePlayer, eStatement); - // We're done in here - break; - } + // After the war stuff but before any other statement, try to renew expired deals + pRenewDeal = DoRenewExpiredDeal(ePlayer, eStatement); + + // Broken promises + DoKilledCityStateStatement(ePlayer, eStatement, iData1); + DoExpansionBrokenPromiseStatement(ePlayer, eStatement); + DoPlotBuyingBrokenPromiseStatement(ePlayer, eStatement); + + // Other things the player has done to piss off the AI + DoDugUpMyYardStatement(ePlayer, eStatement); + DoConvertedMyCityStatement(ePlayer, eStatement); + //DoSeriousExpansionWarningStatement(ePlayer, eStatement); + //DoSeriousPlotBuyingWarningStatement(ePlayer, eStatement); + DoExpansionWarningStatement(ePlayer, eStatement); + DoPlotBuyingWarningStatement(ePlayer, eStatement); + + // Spying + DoKilledMySpyStatement(ePlayer, eStatement); + DoKilledYourSpyStatement(ePlayer, eStatement); + DoCaughtYourSpyStatement(ePlayer, eStatement); + + // "Now Influential" & Ideology Statements + DoIdeologicalStatement(ePlayer, eStatement); + + // AI wants to worsen diplomatic relations with a player + DoEndDoFStatement(ePlayer, eStatement); + DoDenounceStatement(ePlayer, eStatement); + //DoRequestFriendDenounceStatement(ePlayer, eStatement, iData1); + + // AI wants to improve diplomatic relations with a player + DoLiberateMyVassalStatement(ePlayer, eStatement); + DoShareIntrigueStatement(ePlayer, eStatement); + DoDoFStatement(ePlayer, eStatement); + DoBecomeVassalageStatement(ePlayer, eStatement, pDeal); + DoMakeVassalageStatement(ePlayer, eStatement, pDeal); + DoDefensivePactOffer(ePlayer, eStatement, pDeal); + DoCoopWarStatement(ePlayer, eStatement, iData1); + + // Vassal tax statements + DoVassalTaxesRaisedStatement(ePlayer, eStatement); + DoVassalTaxesLoweredStatement(ePlayer, eStatement); + + // Very time-sensitive compliments & insults + DoFYIBefriendedHumanEnemy(ePlayer, eStatement, iData1); + DoFYIDenouncedHumanFriend(ePlayer, eStatement, iData1); + DoAngryDenouncedFriend(ePlayer, eStatement, iData1); + DoAngryBefriendedEnemy(ePlayer, eStatement, iData1); + DoHappyDenouncedEnemy(ePlayer, eStatement, iData1); + DoFYIDenouncedHumanEnemy(ePlayer, eStatement, iData1); + DoHappyBefriendedFriend(ePlayer, eStatement, iData1); + DoFYIBefriendedHumanFriend(ePlayer, eStatement, iData1); + + // Time-sensitive compliments & insults + DoTheyFoiledOurProposal(ePlayer, eStatement); + DoTheySupportedOurHosting(ePlayer, eStatement); + DoTheySupportedOurProposal(ePlayer, eStatement); + DoWeLikedTheirProposal(ePlayer, eStatement); + DoWeDislikedTheirProposal(ePlayer, eStatement); + + // Request help from them? + DoRequest(ePlayer, eStatement, pDeal); + + // OFFERS + // Try to get the things we want most first! + DoRevokeVassalageStatement(ePlayer, eStatement, pDeal); + DoCityExchange(ePlayer, eStatement, pDeal); + DoTechOffer(ePlayer, eStatement, pDeal); + if (MOD_BALANCE_PERMANENT_VOTE_COMMITMENTS) + { + DoVoteTrade(ePlayer, eStatement, pDeal); + DoMapsOffer(ePlayer, eStatement, pDeal); + } + else + { + DoMapsOffer(ePlayer, eStatement, pDeal); + DoVoteTrade(ePlayer, eStatement, pDeal); } + DoThirdPartyWarTrade(ePlayer, eStatement, pDeal); + DoThirdPartyPeaceTrade(ePlayer, eStatement, pDeal); + if (GetPlayer()->IsAtWarAnyMajor()) + { + DoStrategicTrade(ePlayer, eStatement, pDeal); + if (GetPlayer()->IsEmpireUnhappy()) + DoLuxuryTrade(ePlayer, eStatement, pDeal); + } + else + { + if (GetPlayer()->IsEmpireUnhappy()) + DoLuxuryTrade(ePlayer, eStatement, pDeal); + + DoStrategicTrade(ePlayer, eStatement, pDeal); + } + DoEmbassyExchange(ePlayer, eStatement, pDeal); + DoEmbassyOffer(ePlayer, eStatement, pDeal); + DoResearchAgreementOffer(ePlayer, eStatement, pDeal); + DoOpenBordersExchange(ePlayer, eStatement, pDeal); + DoOpenBordersOffer(ePlayer, eStatement, pDeal); + if (!GetPlayer()->IsEmpireUnhappy()) + DoLuxuryTrade(ePlayer, eStatement, pDeal); + + // Give help to them? + DoGenerousOffer(ePlayer, eStatement, pDeal); + + // Non-time-sensitive compliments & insults + //DoNowUnforgivableStatement(ePlayer, eStatement); + //DoNowEnemyStatement(ePlayer, eStatement); + DoHostileStatement(ePlayer, eStatement); + DoAfraidStatement(ePlayer, eStatement); + DoWarmongerStatement(ePlayer, eStatement); + DoHappySamePolicyTree(ePlayer, eStatement); + DoMinorCivCompetitionStatement(ePlayer, eStatement, iData1); + //DoVictoryCompetitionStatement(ePlayer, eStatement); enable this after fixing Victory Dispute code + //DoVictoryBlockStatement(ePlayer, eStatement); enable this after fixing Victory Dispute code + //DoFriendlyStatement(ePlayer, eStatement); + } + // AT WAR + else if (!IsAlwaysAtWar(ePlayer)) + { + DoPeaceOffer(ePlayer, eStatement, pDeal); + DoDenounceStatement(ePlayer, eStatement); // If not offering peace, can still denounce while at war! + } + + // Reset these values + SetAvoidDeals(false); + SetSkipForTeammates(false); + +#if !defined(FINAL_RELEASE) || defined(VPDEBUG) + // Check for an optional message injection from the Tuner + if (eStatement == NO_DIPLO_STATEMENT_TYPE && m_eTestStatement != NO_DIPLO_STATEMENT_TYPE && ePlayer == m_eTestToPlayer) + { + eStatement = m_eTestStatement; + iData1 = m_iTestStatementArg1; + m_eTestStatement = NO_DIPLO_STATEMENT_TYPE; + } +#endif + + // Now send the message + if (eStatement != NO_DIPLO_STATEMENT_TYPE) + { + LogStatementToPlayer(ePlayer, eStatement); + SetTurnStatementLastSent(ePlayer, eStatement, GC.getGame().getGameTurn()); + DoSendStatementToPlayer(ePlayer, eStatement, iData1, pRenewDeal ? pRenewDeal : pDeal); } } -/// Possible Contact Statement - We're angry that they denounced one of our friends -void CvDiplomacyAI::DoAngryDenouncedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) +/// Any Minor Civs we want to chat with? +void CvDiplomacyAI::DoContactMinorCivs() { - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (WasResurrectedBy(ePlayer)) + if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR)) return; - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + int iDiplomacyFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_DIPLOMACY")); + int iGoldFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_GOLD")); + int iTileImprovementFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_TILE_IMPROVEMENT")); - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - return; + bool bExpandToOtherContinents = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_EXPAND_TO_OTHER_CONTINENTS")); + bool bNeedHappiness = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_HAPPINESS")); + bool bNeedHappinessCritical = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_NEED_HAPPINESS_CRITICAL")); + bool bLosingMoney = GetPlayer()->GetEconomicAI()->IsUsingStrategy((EconomicAIStrategyTypes)GC.getInfoTypeForString("ECONOMICAISTRATEGY_LOSING_MONEY")); - // We denounced the leader we're talking to - no use whining at this point - if(IsDenouncedPlayer(ePlayer) || IsAtWar(ePlayer) || GetSurfaceApproach(ePlayer) == CIV_APPROACH_HOSTILE) - return; + // ************************** + // Would we like to buyout a minor this turn? (Austria UA) + // ************************** - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND; + bool bWantsToBuyout = GetPlayer()->IsAbleToAnnexCityStates(); + bool bWantsToMarry = GetPlayer()->GetPlayerTraits()->IsDiplomaticMarriage(); - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND_RANDFAILED) >= 20) - { - CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + // ************************** + // Would we like to forcefully annex a minor this turn? + // ************************** - // Loop through all players until we find one that is our friend, that ePlayer denounced - for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + bool bWantsToBullyAnnex = GetPlayer()->GetPlayerTraits()->IsBullyAnnex(); - // Must be alive - if(!GET_PLAYER(eLoopPlayer).isAlive()) - continue; + // ************************** + // Would we like to give a gold gift this turn? + // ************************** - // Can't be either of us - if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) - continue; + bool bWantsToMakeGoldGift = false; - // We haven't befriended this guy - if(!IsDoFAccepted(eLoopPlayer)) - continue; + // If we're a highly diplomatic leader, then always look for an opportunity + if(iDiplomacyFlavor >= /*4*/ GD_INT_GET(MC_ALWAYS_GIFT_DIPLO_THRESHOLD) || + IsGoingForDiploVictory() || + IsGoingForCultureVictory() || + GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT) || + IsHasActiveGoldQuest() || + m_pPlayer->calculateGoldRate() > 100) // if we are very wealthy always do this + { + bWantsToMakeGoldGift = true; + } + // Otherwise, do a random roll + else + { + int iThreshold = iDiplomacyFlavor; + int iRandRoll = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xbe42edf0).mix(GetID())); - // They haven't denounced this guy - if(!pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) - continue; + // Threshold will be 15 for a player (3 flavor * 5) + // Threshold will be 5 for non-diplomatic player (2 flavor * 5) + + if(iRandRoll < iThreshold) + bWantsToMakeGoldGift = true; + } + + // ************************** + // Would we like to get a unit by bullying this turn? + // ************************** + + bool bWantsToBullyUnit = false; + + // Would we like to get Heavy Tribute by bullying this turn? + // Loop through all (known) Minors + for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) + { + PlayerTypes eMinor = (PlayerTypes) iMinorLoop; - // Too much time has passed (or maybe we already sent a message recently) - if(pTheirDiploAI->GetTurnsSinceDenouncedPlayer(eLoopPlayer) > 1) - continue; + if (!GET_PLAYER(eMinor).isMinorCiv()) + continue; - // Found a match! - int iWeight = GetMeanness(); // Usually ranges from 3 to 7 - iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x2207e072).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); + if (GetCivApproach(eMinor) > CIV_APPROACH_HOSTILE) + continue; - // We're mean enough to say something - if(iWeight >= 10) - { - eStatement = eTempStatement; - iData1 = eLoopPlayer; - } - // We're going to be nice! - else - { - eStatement = DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND_RANDFAILED; - } + if (MOD_BALANCE_HEAVY_TRIBUTE) + { + if (GET_PLAYER(eMinor).GetMinorCivAI()->CalculateBullyScore(GetID(), false) >= 50) + { + bWantsToBullyUnit = true; + } + } + else + { + if (GetPlayer()->GetEconomicAI()->GetWorkersToCitiesRatio() < 0.25 && GetPlayer()->GetEconomicAI()->GetImprovedToImprovablePlotsRatio() < 0.50) + { + bWantsToBullyUnit = true; + } + // Otherwise, do a random roll + else + { + int iThreshold = iTileImprovementFlavor; //antonjs: todo: XML + int iRandRoll = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xc87e263f).mix(GetID())); - // We're done in here - break; + if (iRandRoll < iThreshold) + bWantsToBullyUnit = true; } } } -} - -/// Possible Contact Statement - We're happy that they denounced a player we denounced -void CvDiplomacyAI::DoHappyDenouncedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + // ************************** + // Would we like to get some gold by bullying this turn? + // ************************** - // We denounced the leader we're talking to - no use talking at this point - if(IsDenouncedPlayer(ePlayer) || IsAtWar(ePlayer) || GetSurfaceApproach(ePlayer) == CIV_APPROACH_HOSTILE) - return; + bool bWantsToBullyGold = false; - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + if(iGoldFlavor >= 6 || //antonjs: todo: GD_INT_GET(MC_ALWAYS_BULLY_GOLD_THRESHOLD) + IsGoingForWorldConquest() || + GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_UNIT) || + GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_BUILDING) || + bLosingMoney || + m_pPlayer->calculateGoldRate() < 0) // if we are losing gold per turn { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY_RANDFAILED) >= 20) - { - CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); - - // Loop through all players until we find one that is our enemy, that ePlayer denounced - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - - // Must be alive - if(!GET_PLAYER(eLoopPlayer).isAlive()) - continue; - - // Can't be either of us - if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) - continue; + bWantsToBullyGold = true; + } + // Otherwise, do a random roll + else + { + int iThreshold = iGoldFlavor; //antonjs: todo: XML + int iRandRoll = GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x8ee31b26).mix(GetID())); - // We haven't denounced this guy - if(!IsDenouncedPlayer(eLoopPlayer)) - continue; + if(iRandRoll < iThreshold) + bWantsToBullyGold = true; + } - // They haven't denounced this guy - if(!pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) - continue; + CvWeightedVector veMinorsToBuyout; // BNW Austria UA + CvWeightedVector veMinorsToMarry; // VP Austria UA + CvWeightedVector veMinorsToBullyAnnex; //Rome UA + CvWeightedVector veMinorsToGiveGold; + CvWeightedVector veMinorsToBullyGold; + CvWeightedVector veMinorsToBullyUnit; - // Too much time has passed (or maybe we already sent a message recently) - if(pTheirDiploAI->GetTurnsSinceDenouncedPlayer(eLoopPlayer) > 1) - continue; + int iLargeGift = /*1000*/ GD_INT_GET(MINOR_GOLD_GIFT_LARGE); + int iMediumGift = /*500*/ GD_INT_GET(MINOR_GOLD_GIFT_MEDIUM); + int iSmallGift = /*250*/ GD_INT_GET(MINOR_GOLD_GIFT_SMALL); - // Found a match! - int iWeight = GetChattiness(); // Usually ranges from 3 to 7 - iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xd55f1785).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); + PlayerTypes eID = GetID(); - // We're chatty enough to say something - if(iWeight >= 10) - { - eStatement = eTempStatement; - iData1 = eLoopPlayer; - } - // We're going to be nice! - else - { - eStatement = DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY_RANDFAILED; - } + int iGrowthFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_GROWTH")); + int iScienceFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_SCIENCE")); + int iCultureFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_CULTURE")); + int iFaithFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_RELIGION")); + int iOffenseFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_OFFENSE")); + int iHappinessFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_HAPPINESS")); + int iProductionFlavor = GetPlayer()->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)GC.getInfoTypeForString("FLAVOR_PRODUCTION")) / 2; - // We're done in here - break; - } + if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0) + { + if (!GetPlayer()->IsEmpireUnhappy()) + { + bWantsToBullyUnit = true; + bWantsToBullyGold = true; + bWantsToMakeGoldGift = false; } } -} - -/// Possible Contact Statement - We're happy they befriended one of our friends -void CvDiplomacyAI::DoHappyBefriendedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - // We denounced the leader we're talking to - no use talking at this point - if(IsDenouncedPlayer(ePlayer) || IsAtWar(ePlayer) || GetSurfaceApproach(ePlayer) == CIV_APPROACH_HOSTILE) - return; - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + if (GetPlayer()->IsCanBullyFriendlyCS()) { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND_RANDFAILED) >= 20) - { - CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + bWantsToBullyUnit = true; + bWantsToBullyGold = true; + bWantsToMakeGoldGift = false; + } + else if (GetPlayer()->GetBullyGlobalCSReduction()) + { + bWantsToBullyUnit = true; + bWantsToBullyGold = true; + bWantsToMakeGoldGift = false; + } - // Loop through all players until we find one that is our friend, that ePlayer DoFed - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + // Loop through all (known) Minors + for (int iMinorLoop = MAX_MAJOR_CIVS; iMinorLoop < MAX_CIV_PLAYERS; iMinorLoop++) + { + PlayerTypes eMinor = (PlayerTypes) iMinorLoop; - // Must be alive - if(!GET_PLAYER(eLoopPlayer).isAlive()) - continue; + CvPlayer* pMinor = &GET_PLAYER(eMinor); + CvMinorCivAI* pMinorCivAI = pMinor->GetMinorCivAI(); - // Can't be either of us - if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) - continue; + bool bWantsToConnect = false; + bool bWantsToGiveGoldToThisMinor = false; + bool bWantsToBullyUnitFromThisMinor = false; + bool bWantsToBuyoutThisMinor = false; + bool bWantsToMarryThisMinor = false; + bool bWantsToBullyAnnexThisMinor = false; - // We haven't DoFed this guy - if(!IsDoFAccepted(eLoopPlayer)) - continue; + if(IsPlayerValid(eMinor)) + { + // Can't do anything with minors we're at war with, besides make peace (which isn't done here, but in DoMakePeaceWithMinors()) + if(IsAtWar(eMinor)) + continue; - // They haven't DoFed this guy - if(!pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) - continue; + CivApproachTypes eApproach = GetCivApproach(eMinor); - // Too much time has passed (or maybe we already sent a message recently) - if(pTheirDiploAI->GetTurnsSinceBefriendedPlayer(eLoopPlayer) > 1) - continue; + // Do we want to change our protection of this minor? + DoUpdateMinorCivProtection(eMinor); - // Found a match! - int iWeight = GetChattiness(); // Usually ranges from 3 to 7 - iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x2b7c6537).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); + // Do we want to connect to this player? + int iEra = GetPlayer()->GetCurrentEra(); + if (iEra <= 0) + iEra = 1; - // We're chatty enough to say something - if(iWeight >= 10) - { - eStatement = eTempStatement; - iData1 = eLoopPlayer; - } - // We're going to be nice! - else + if (eApproach == CIV_APPROACH_FRIENDLY && GetPlayer()->getAvgGoldRate() > min(20 * iEra,50)) + { + if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) { - eStatement = DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND_RANDFAILED; + if (pMinorCivAI->IsAllies(eID)) + { + bWantsToConnect = true; + } + else if (pMinorCivAI->IsActiveQuestForPlayer(eID, MINOR_CIV_QUEST_ROUTE)) + { + bWantsToConnect = true; + } } - - // We're done in here - break; } - } - } -} - -/// Possible Contact Statement - Peace -void CvDiplomacyAI::DoPeaceOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (MOD_DIPLOAI_SHUT_UP_PEACE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + // Calculate desirability to forcefully annex this minor + if (bWantsToBullyAnnex) + { + int iValue = 100; //antonjs: todo: xml + // Only bother if we actually can annex + CvCity* pMinorCapital = pMinor->getCapitalCity(); + if (pMinor->GetMinorCivAI()->CanMajorBullyUnit(eID) && pMinorCapital != NULL) + { + // Determine presence of player cities on this continent + CvArea* pMinorArea = pMinorCapital->plot()->area(); + bool bPresenceInArea = false; + int iMajorCapitalsInArea = 0; + if (pMinorArea) + { + // Do we have a city here? + if (pMinorArea->getCitiesPerPlayer(eID) > 0) + bPresenceInArea = true; - if (!IsAtWar(ePlayer)) - return; + // Does another major civ have their capital here? (must be visible) + for (int iMajorRivalLoop = 0; iMajorRivalLoop < MAX_MAJOR_CIVS; iMajorRivalLoop++) + { + PlayerTypes eMajorRivalLoop = (PlayerTypes)iMajorRivalLoop; + if (eMajorRivalLoop == eID) + continue; - if (eStatement == NO_DIPLO_STATEMENT_TYPE) - { - // Have to have been at war for at least a little while - GetPlayer()->SetCachedValueOfPeaceWithHuman(0); + if (GET_PLAYER(eMajorRivalLoop).isAlive()) + { + CvCity* pCapital = GET_PLAYER(eMajorRivalLoop).getCapitalCity(); + if (pCapital && pCapital->plot()) + { + CvPlot* pPlot = pCapital->plot(); + if (pPlot->isVisible(GetTeam())) + iMajorCapitalsInArea++; + } + } + } + } + else + { + ASSERT(false, "Could not lookup minor civ's area!"); + } - if (IsWantsPeaceWithPlayer(ePlayer)) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REQUEST_PEACE; - int iTurnsBetweenStatementsCityTrade = 2; - int iTurnsBetweenStatements = 5; + // How many units does the city-state have? + int iMinorMilitaryUnits = 0; + int iMinorUnits = 0; + int iLoop = 0; + for (CvUnit* pLoopUnit = pMinor->firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = pMinor->nextUnit(&iLoop)) + { + if (pLoopUnit->IsCanAttack() && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE_SEA) + { + iMinorMilitaryUnits++; + } + iMinorUnits++; + } - // cities can be added to a peace deal if they are in danger of falling, so that check needs to be done more frequently - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatementsCityTrade) - { - if (GetPlayer()->GetDealAI()->IsOfferPeace(ePlayer, /*pDeal can be modified in this function*/ pDeal, false /*bEqualizingDeals*/) && pDeal->GetNumItems() > 0 && pDeal->ContainsItemType(TRADE_ITEM_CITIES, ePlayer)) - { - eStatement = eTempStatement; - } - else - { - // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at - pDeal->ClearItems(); - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) + // Foreign continent + if (!bPresenceInArea) { - if (GetPlayer()->GetDealAI()->IsOfferPeace(ePlayer, /*pDeal can be modified in this function*/ pDeal, false /*bEqualizingDeals*/) && pDeal->GetNumItems() > 0) + // Military foothold to attack other majors + if (IsGoingForWorldConquest() && iMajorCapitalsInArea > 0) + { + iValue += 100; //antonjs: todo: xml + } + // Expansion + else if (bExpandToOtherContinents) { - eStatement = eTempStatement; + iValue += 60; //antonjs: todo: xml } else { - // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at - pDeal->ClearItems(); + iValue += -50; //antonjs: todo: xml + } + } + // Continent we have presence on + else + { + // Proximity plays a large factor, since we don't want a remote, isolated city + if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) + { + iValue += 100; //antonjs: todo: xml + // Military units could come to our rescue quickly + if (GetStateAllWars() == STATE_ALL_WARS_LOSING) + { + if (iMinorMilitaryUnits > 0) //antonjs: todo: xml + { + iValue += (iMinorMilitaryUnits) * 10; //antonjs: todo: xml + } + else + { + iValue -= 50; //antonjs: todo: xml + } + } + } + else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) + { + iValue += 10; //antonjs: todo: xml + } + else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) + { + iValue += -50; //antonjs: todo: xml + } + else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_DISTANT) + { + iValue += -100; //antonjs: todo: xml } } - } - } - } - } -} - -/// Possible Contact Statement - We befriended one of the human's enemies and we're letting them know -void CvDiplomacyAI::DoFYIBefriendedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (WasResurrectedBy(ePlayer)) - return; - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + // Military units - How many, and can we support them? + if (GetPlayer()->GetNumUnitsSupplied() >= GetPlayer()->getNumUnits() + iMinorUnits) + { + iValue += (iMinorMilitaryUnits) * 5; + } - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - return; + // Happiness + if (bNeedHappiness) + iValue += -50; //antonjs: todo: xml + if (bNeedHappinessCritical) + iValue += -150; //antonjs: todo: xml - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY; + // Bonuses from Annexed City-States + if (GetPlayer()->GetPlayerTraits()->IsAnnexedCityStatesGiveYields()) { + MinorCivTraitTypes eTrait = pMinorCivAI->GetTrait(); + if (eTrait == MINOR_CIV_TRAIT_MILITARISTIC) + { + iValue += (iScienceFlavor + iOffenseFlavor); + } + else if (eTrait == MINOR_CIV_TRAIT_MERCANTILE) + { + iValue += iHappinessFlavor * 2; + } + else if (eTrait == MINOR_CIV_TRAIT_CULTURED) + { + iValue += iCultureFlavor * 2; + } + else if (eTrait == MINOR_CIV_TRAIT_RELIGIOUS) + { + iValue += iFaithFlavor * 2; + } + else if (eTrait == MINOR_CIV_TRAIT_MARITIME) + { + iValue += iGrowthFlavor * 2; + } + } - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY_RANDFAILED) >= 20) - { - CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + // Annexing the Ally of another player? + PlayerTypes eAlly = pMinorCivAI->GetAlly(); + if (eAlly != NO_PLAYER) + { + iValue -= (GetPlayer()->GetDiplomacyAI()->GetCivOpinion(eAlly) - CIV_OPINION_NEUTRAL) * 10; + } + // Time to decide - Do we want it enough? + if (iValue > 100) //antonjs: todo: xml + { + veMinorsToBullyAnnex.push_back(eMinor, iValue); + bWantsToBullyAnnexThisMinor = true; + } + } + } - // Loop through all players until we find one that we just made friends with, that ePlayer denounced - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + // Calculate desirability to buyout this minor + if (bWantsToBuyout) { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - - // Must be alive - if(!GET_PLAYER(eLoopPlayer).isAlive()) - continue; - - // Can't be either of us - if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) - continue; - - // We haven't befriended this guy - if(!IsDoFAccepted(eLoopPlayer)) - continue; - - // They haven't denounced this guy - if(!pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) - continue; - - // Too much time has passed (or maybe we already sent a message recently) - if(GetTurnsSinceBefriendedPlayer(eLoopPlayer) > 1) - continue; - - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); - - // Don't say mean things if we like ePlayer - if(eOpinion >= CIV_OPINION_FAVORABLE) - continue; - if(eApproach == CIV_APPROACH_FRIENDLY) - continue; - - int iWeight = 0; - - if(eOpinion == CIV_OPINION_COMPETITOR) - iWeight += 2; - else if(eOpinion == CIV_OPINION_ENEMY) - iWeight += 5; - else if(eOpinion == CIV_OPINION_UNFORGIVABLE) - iWeight += 10; - - iWeight += GetMeanness(); // Usually ranges from 3 to 7 - iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x85433256).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); - - // We're mean enough to say something - if(iWeight >= 10) - { - eStatement = eTempStatement; - iData1 = eLoopPlayer; - } - // We're going to be nice! - else + int iValue = 100; //antonjs: todo: xml + // Only bother if we actually can buyout + CvCity* pMinorCapital = pMinor->getCapitalCity(); + if(GetPlayer()->IsAbleToAnnexCityStates() && pMinorCivAI->CanMajorBuyout(eID) && pMinorCapital != NULL) { - eStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY_RANDFAILED; - } + // Determine presence of player cities on this continent + CvArea* pMinorArea = pMinorCapital->plot()->area(); + bool bPresenceInArea = false; + int iMajorCapitalsInArea = 0; + if(pMinorArea) + { + // Do we have a city here? + if(pMinorArea->getCitiesPerPlayer(eID) > 0) + bPresenceInArea = true; - // We're done in here - break; - } - } - } -} + // Does another major civ have their capital here? (must be visible) + for(int iMajorRivalLoop = 0; iMajorRivalLoop < MAX_MAJOR_CIVS; iMajorRivalLoop++) + { + PlayerTypes eMajorRivalLoop = (PlayerTypes) iMajorRivalLoop; + if(eMajorRivalLoop == eID) + continue; -/// Possible Contact Statement - We denounced one of the human's friends and we're letting them know -void CvDiplomacyAI::DoFYIDenouncedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + if(GET_PLAYER(eMajorRivalLoop).isAlive()) + { + CvCity* pCapital = GET_PLAYER(eMajorRivalLoop).getCapitalCity(); + if(pCapital && pCapital->plot()) + { + CvPlot* pPlot = pCapital->plot(); + if(pPlot->isVisible(GetTeam())) + iMajorCapitalsInArea++; + } + } + } + } + else + { + ASSERT(false, "Could not lookup minor civ's area!"); + } - if (WasResurrectedBy(ePlayer)) - return; + // How many units does the city-state have? + int iMinorMilitaryUnits = 0; + int iMinorUnits = 0; + int iLoop = 0; + for (CvUnit* pLoopUnit = pMinor->firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = pMinor->nextUnit(&iLoop)) + { + if(pLoopUnit->IsCanAttack() && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE && pLoopUnit->AI_getUnitAIType() != UNITAI_EXPLORE_SEA) + { + iMinorMilitaryUnits++; + } + iMinorUnits++; + } + + // Foreign continent + if(!bPresenceInArea) + { + // Military foothold to attack other majors + if(IsGoingForWorldConquest() && iMajorCapitalsInArea > 0) + { + iValue += 100; //antonjs: todo: xml + } + // Expansion + else if(bExpandToOtherContinents) + { + iValue += 60; //antonjs: todo: xml + } + else + { + iValue += -50; //antonjs: todo: xml + } + } + // Continent we have presence on + else + { + // Proximity plays a large factor, since we don't want a remote, isolated city + if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) + { + iValue += 100; //antonjs: todo: xml + // Military units could come to our rescue quickly + if(GetStateAllWars() == STATE_ALL_WARS_LOSING) + { + if(iMinorMilitaryUnits > 0) //antonjs: todo: xml + { + iValue += (iMinorMilitaryUnits) * 10; //antonjs: todo: xml + } + else + { + iValue -= 50; //antonjs: todo: xml + } + } + } + else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) + { + iValue += 10; //antonjs: todo: xml + } + else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) + { + iValue += -50; //antonjs: todo: xml + } + else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_DISTANT) + { + iValue += -100; //antonjs: todo: xml + } + } - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + // Military units - How many, and can we support them? + if(GetPlayer()->GetNumUnitsSupplied() >= GetPlayer()->getNumUnits() + iMinorUnits) + { + iValue += (iMinorMilitaryUnits) * 5; + } - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - return; + // Happiness + if(bNeedHappiness) + iValue += -50; //antonjs: todo: xml + if(bNeedHappinessCritical) + iValue += -150; //antonjs: todo: xml - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND; + // Potential bonuses lost + MinorCivTraitTypes eTrait = pMinorCivAI->GetTrait(); + if(eTrait == MINOR_CIV_TRAIT_CULTURED && IsGoingForCultureVictory()) + { + iValue += -70; //antonjs: todo: xml + } + else if(eTrait == MINOR_CIV_TRAIT_MERCANTILE) + { + if(bNeedHappiness) + iValue += -100; //antonjs: todo: xml + if(bNeedHappinessCritical) + iValue += -150; //antonjs: todo: xml + } - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND_RANDFAILED) >= 20) - { - CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + // Time to decide - Do we want it enough? + if(iValue > 100) //antonjs: todo: xml + { + veMinorsToBuyout.push_back(eMinor, iValue); + bWantsToBuyoutThisMinor = true; + } + } + } - // Loop through all players until we find one that we just denoucned, that ePlayer has befriended - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + if (bWantsToMarry) { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - - // Must be alive - if (!GET_PLAYER(eLoopPlayer).isAlive()) - continue; - - // Can't be either of us - if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) - continue; - - // We haven't denounced this guy - if (!IsDenouncedPlayer(eLoopPlayer)) - continue; - - // They haven't befriended this guy - if (!pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) - continue; - - // Too much time has passed (or maybe we already sent a message recently) - if (GetTurnsSinceDenouncedPlayer(eLoopPlayer) > 1) - continue; - - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); - - // Don't say mean things if we like ePlayer - if(eOpinion >= CIV_OPINION_FAVORABLE) - continue; - if(eApproach == CIV_APPROACH_FRIENDLY) - continue; - - int iWeight = 0; - - if(eOpinion == CIV_OPINION_COMPETITOR) - iWeight += 2; - else if(eOpinion == CIV_OPINION_ENEMY) - iWeight += 5; - else if(eOpinion == CIV_OPINION_UNFORGIVABLE) - iWeight += 10; - - iWeight += GetMeanness(); // Usually ranges from 3 to 7 - iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x58262340).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); + // No reason not to, as long as we have the gold (checked below) + if (pMinorCivAI->CanMajorDiploMarriage(eID)) + { + // Random weight to shuffle the list + veMinorsToMarry.push_back(eMinor, GC.getGame().urandLimitExclusive(1000, CvSeeder::fromRaw(0x977c30aa).mix(GC.getGame().getGameTurn()).mix(eID).mix(eMinor))); + bWantsToMarryThisMinor = true; + } + } - // We're mean enough to say something - if(iWeight >= 10) + // Calculate desirability to give this minor gold + if (bWantsToMakeGoldGift && !bWantsToBuyoutThisMinor && !bWantsToBullyAnnexThisMinor) + { + if (GET_PLAYER(eMinor).GetMinorCivAI()->IsNoAlly()) { - eStatement = eTempStatement; - iData1 = eLoopPlayer; + continue; } - // We're going to be nice! - else + if (GET_PLAYER(eMinor).GetMinorCivAI()->GetPermanentAlly() == GetID()) { - eStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND_RANDFAILED; + continue; } - // We're done in here - break; - } - } - } -} + int iValue = /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_THRESHOLD); + // If we're not protective, then don't bother with minor diplo + if (eApproach == CIV_APPROACH_FRIENDLY) + { + MinorGoldGiftInfo sGiftInfo; + sGiftInfo.eMinor = eMinor; + sGiftInfo.eMajorRival = NO_PLAYER; + sGiftInfo.bQuickBoost = false; + sGiftInfo.iGoldAmount = 0; -/// Possible Contact Statement - We denounced someone the human has denounced and we're letting them know -void CvDiplomacyAI::DoFYIDenouncedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + iValue += /*10*/ GD_INT_GET(MC_GIFT_WEIGHT_PROTECTIVE); // some base value - if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + // if we are rich we are more likely to, conversely if we are poor... + iValue += min(max(0, m_pPlayer->calculateGoldRate() - 50),100); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY; + CvMinorCivInfo* pMinorInfo = GC.getMinorCivInfo(pMinorCivAI->GetMinorCivType()); - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 50 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY_RANDFAILED) >= 20) - { - CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); + // Diplo victory makes us more likely to spend gold + if (IsGoingForDiploVictory()) + iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_DIPLO_VICTORY); + // double up if this is the home stretch + if (GC.getGame().IsUnitedNationsActive()) + { + iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_DIPLO_VICTORY); + } + // Going for Culture victory, focus on Cultural city states + else if (IsGoingForCultureVictory()) + { + if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_CULTURED) + iValue += /*200*/ GD_INT_GET(MC_GIFT_WEIGHT_CULTURE_VICTORY); + } + // Going for Conquest victory, focus on Militaristic city states + else if (IsGoingForWorldConquest()) + { + if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_MILITARISTIC) + iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_CONQUEST_VICTORY); + } - // Loop through all players until we find one that we just denounced, that ePlayer has denounced - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + //antonjs: todo: work extra gold quest INF potential into the friends/allies/passing logic as well + // Gold gift quest is active, so we would get more bang for our bucks + if (pMinorCivAI->IsActiveQuestForPlayer(eID, MINOR_CIV_QUEST_GIVE_GOLD)) + { + iValue += 150; //antonjs: todo: constant/XML + } - // Must be alive - if (!GET_PLAYER(eLoopPlayer).isAlive()) - continue; + // Invest quest is active, so we would get more bang for our bucks + if (pMinorCivAI->IsActiveQuestForPlayer(eID, MINOR_CIV_QUEST_INVEST)) + { + iValue += 100; //antonjs: todo: constant/XML + } - // Can't be either of us - if (eLoopPlayer == GetID() || eLoopPlayer == ePlayer) - continue; + // having traits that give us bonuses also make us want to spend gold + if (m_pPlayer->GetPlayerTraits()->GetCityStateFriendshipModifier() > 0 || m_pPlayer->GetPlayerTraits()->GetCityStateBonusModifier()) + { + iValue += /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_DIPLO_VICTORY); + } - // We haven't denounced this guy - if (!IsDenouncedPlayer(eLoopPlayer)) - continue; + // Nearly everyone likes to grow + if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_MARITIME && !GetPlayer()->IsEmpireUnhappy()) + { + iValue += iGrowthFlavor * max(1, GetPlayer()->getNumCities() / 3) * /*20*/ GD_INT_GET(MC_GIFT_WEIGHT_MARITIME_GROWTH); + } - // They haven't denounced this guy - if (!pTheirDiploAI->IsDenouncedPlayer(eLoopPlayer)) - continue; + // Slight negative weight towards militaristic + if (pMinorInfo->GetMinorCivTrait() == MINOR_CIV_TRAIT_MILITARISTIC && !IsGoingForWorldConquest()) + iValue += /*-50*/ GD_INT_GET(MC_GIFT_WEIGHT_MILITARISTIC); - // Too much time has passed (or maybe we already sent a message recently) - if (GetTurnsSinceDenouncedPlayer(eLoopPlayer) > 1) - continue; + // If they have a resource we don't have, add extra weight + int iResourcesWeLack = pMinorCivAI->GetNumResourcesMajorLacks(eID); + if (iResourcesWeLack > 0) + iValue += iResourcesWeLack * /*80*/ GD_INT_GET(MC_GIFT_WEIGHT_RESOURCE_WE_NEED); - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); + // If the minor is hostile, then reduce the weighting + if (pMinorCivAI->GetPersonality() == MINOR_CIV_PERSONALITY_HOSTILE) + iValue += /*-20*/ GD_INT_GET(MC_GIFT_WEIGHT_HOSTILE); - // Don't say nice things if we dislike ePlayer - if (eOpinion <= CIV_OPINION_COMPETITOR) - continue; - if (eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) - continue; + // The closer we are the better + if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) + iValue += /*5*/ GD_INT_GET(MC_GIFT_WEIGHT_NEIGHBORS); + else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) + iValue += /*4*/ GD_INT_GET(MC_GIFT_WEIGHT_CLOSE); + else if (GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) + iValue += /*3*/ GD_INT_GET(MC_GIFT_WEIGHT_FAR); - int iWeight = 0; + int iMediumGiftFriendship = pMinorCivAI->GetFriendshipFromGoldGift(eID, iMediumGift); + int iSmallGiftFriendship = pMinorCivAI->GetFriendshipFromGoldGift(eID, iSmallGift); - if(eOpinion == CIV_OPINION_FAVORABLE) - iWeight += 2; - else if(eOpinion == CIV_OPINION_FRIEND) - iWeight += 5; - else if(eOpinion == CIV_OPINION_ALLY) - iWeight += 10; + int iFriendshipWithMinor = pMinorCivAI->GetEffectiveFriendshipWithMajor(eID); - if(eApproach == CIV_APPROACH_FRIENDLY) - iWeight += 2; + // Only care if we'll actually be Allies or better + bool bMediumGiftAllies = iFriendshipWithMinor + iMediumGiftFriendship >= pMinorCivAI->GetAlliesThreshold(eID); + bool bSmallGiftAllies = iFriendshipWithMinor + iSmallGiftFriendship >= pMinorCivAI->GetAlliesThreshold(eID); - // Add weight if they're strong - if(GetMilitaryStrengthComparedToUs(ePlayer) > STRENGTH_AVERAGE) - iWeight += 3; + // Loop through other players to see if we can pass them + for (int iOtherMajorLoop = 0; iOtherMajorLoop < MAX_MAJOR_CIVS; iOtherMajorLoop++) + { + PlayerTypes eOtherMajor = (PlayerTypes) iOtherMajorLoop; - iWeight += GetChattiness(); // Usually ranges from 3 to 7 - iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xea12329a).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); + // Player must be alive + if (!GET_PLAYER(eOtherMajor).isAlive()) + continue; - // We're mean enough to say something - if(iWeight >= 10) - { - eStatement = eTempStatement; - iData1 = eLoopPlayer; - } - // We're going to be nice! - else - { - eStatement = DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY_RANDFAILED; - } + int iOtherPlayerFriendshipWithMinor = pMinorCivAI->GetEffectiveFriendshipWithMajor(eOtherMajor); - // We're done in here - break; - } - } - } -} + // Player must have friendship with this major + if (iOtherPlayerFriendshipWithMinor <= 0) + continue; -/// Possible Contact Statement - We befriended one of the human's friends, and we're letting them know -void CvDiplomacyAI::DoFYIBefriendedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + // They must have more friendship with this guy than us + if (iFriendshipWithMinor <= iOtherPlayerFriendshipWithMinor) + continue; - if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + // If we can pass them with a small gift, great + if (bSmallGiftAllies && iOtherPlayerFriendshipWithMinor - iFriendshipWithMinor < iSmallGiftFriendship) + { + iValue += /*30*/ GD_INT_GET(MC_SMALL_GIFT_WEIGHT_PASS_OTHER_PLAYER); + sGiftInfo.bQuickBoost = true; + sGiftInfo.eMajorRival = eOtherMajor; + } + // If a medium gift passes them up, that's good too + else if (bMediumGiftAllies && iOtherPlayerFriendshipWithMinor - iFriendshipWithMinor < iMediumGiftFriendship) + { + iValue += /*15*/ GD_INT_GET(MC_GIFT_WEIGHT_PASS_OTHER_PLAYER); + sGiftInfo.eMajorRival = eOtherMajor; + } + // We're behind and we can't catch up right now, so zero-out the value + else + iValue = 0; + } - if (eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND; + // Are we already allies? + if (pMinorCivAI->IsAllies(eID)) + { + // Are we close to losing our status? + if (pMinorCivAI->IsCloseToNotBeingAllies(eID)) + { + iValue += /*250*/ GD_INT_GET(MC_GIFT_WEIGHT_ALMOST_NOT_ALLIES); + sGiftInfo.bQuickBoost = true; + } + // Not going to lose status, so not worth going after this guy + else + iValue = 0; + } + // Are we already Friends? + else if (pMinorCivAI->IsFriends(eID)) + { + // Are we close to losing our status? + if (pMinorCivAI->IsCloseToNotBeingFriends(eID)) + { + iValue += /*150*/ GD_INT_GET(MC_GIFT_WEIGHT_ALMOST_NOT_FRIENDS); + sGiftInfo.bQuickBoost = true; + } + // Not going to lose status, so not worth going after this guy + else if (!IsGoingForDiploVictory() || !GC.getGame().IsUnitedNationsActive()) + iValue = 0; + } - int iMessage = 0; - int iMessageMax = MAX_INT; - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + // Did we bully you recently? If so, giving you gold now would be very odd. + if (pMinorCivAI->IsRecentlyBulliedByMajor(eID)) + { + iValue -= 100; //antonjs: todo: constant/XML + } - if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) - { - iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); - if(iMessage < iMessageMax) + //antonjs: consider: different behavior to CS that have been bullied by others, bullied by rival, etc. + + // Do we want it enough? + if (iValue > /*100*/ GD_INT_GET(MC_GIFT_WEIGHT_THRESHOLD)) { - iMessageMax = iMessage; + veMinorsToGiveGold.push_back(sGiftInfo, iValue); + bWantsToGiveGoldToThisMinor = true; } } } - if(iMessageMax >= 40 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND_RANDFAILED) >= 20) - { - CvDiplomacyAI* pTheirDiploAI = GET_PLAYER(ePlayer).GetDiplomacyAI(); - // Loop through all players until we find one that we just befriended, that ePlayer has befriended - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + // Calculate desirability to bully a unit from this minor + if (bWantsToBullyUnit && !bWantsToBuyoutThisMinor && !bWantsToMarryThisMinor && !bWantsToBullyAnnexThisMinor && !bWantsToGiveGoldToThisMinor) { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - - // Must be alive - if(!GET_PLAYER(eLoopPlayer).isAlive()) - continue; - - // Can't be either of us - if(eLoopPlayer == GetID() || eLoopPlayer == ePlayer) - continue; - - // We haven't befriended this guy - if(!IsDoFAccepted(eLoopPlayer)) - continue; - - // They haven't befriended this guy - if(!pTheirDiploAI->IsDoFAccepted(eLoopPlayer)) - continue; + int iValue = 100; //antonjs: todo: XML, bully threshold + if (MOD_BALANCE_HEAVY_TRIBUTE) + iValue = pMinor->GetMinorCivAI()->CalculateBullyScore(eID, true); - // Too much time has passed (or maybe we already sent a message recently) - if(GetTurnsSinceBefriendedPlayer(eLoopPlayer) > 1) + if (iValue <= 0) continue; - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); - - // Don't say nice things if we dislike ePlayer - if (eOpinion <= CIV_OPINION_COMPETITOR) - continue; - if (eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) - continue; + if(eApproach == CIV_APPROACH_HOSTILE) + { + // Only bother if we can successfully bully + if (pMinor->GetMinorCivAI()->CanMajorBullyUnit(eID)) + { + if (MOD_BALANCE_HEAVY_TRIBUTE) + { + iValue += (GET_PLAYER(eMinor).GetMinorCivAI()->GetBullyGoldAmount(GetID(), false, /*bForUnit*/ true) * iGoldFlavor) / 10; - int iWeight = 0; + // yields from quests + int iNumCities = GetPlayer()->getNumCities(); + int iNumUnits = GetPlayer()->getNumMilitaryUnits(); + QuestListForPlayer::iterator itr_quest; + for (itr_quest = pMinorCivAI->m_QuestsGiven[eID].begin(); itr_quest != pMinorCivAI->m_QuestsGiven[eID].end(); itr_quest++) + { + if (itr_quest->IsObsolete(true)) // is this quest canceled by demanding heavy tribute? + { + // half of the quest rewards are given + iValue += itr_quest->GetGold() / 2 * iGoldFlavor / 10; + iValue += itr_quest->GetScience() / 2 * iScienceFlavor / 10; + iValue += itr_quest->GetCulture() / 2 * iCultureFlavor / 10; + iValue += itr_quest->GetFaith() / 2 * iFaithFlavor / 10; + iValue += itr_quest->GetGoldenAgePoints() / 2 * iCultureFlavor / 10; + iValue += itr_quest->GetFood() / 2 * iGrowthFlavor / 10; + iValue += itr_quest->GetProduction() / 2 * iProductionFlavor / 10; + iValue += itr_quest->GetTourism() / 2 * iCultureFlavor / 10; + iValue += itr_quest->GetHappiness() / 2 * iHappinessFlavor / 10; + iValue += itr_quest->GetGP() / 2 * (iGoldFlavor + iScienceFlavor + 3 * iCultureFlavor + iProductionFlavor) / 10; // we get GP for each specialist + iValue += itr_quest->GetGPGlobal() / 2 * (iGoldFlavor + iScienceFlavor + 3 * iCultureFlavor + iProductionFlavor) * iNumCities / 10; + iValue += itr_quest->GetGeneralPoints() / 2 * iOffenseFlavor / 10; + iValue += itr_quest->GetAdmiralPoints() / 2 * iOffenseFlavor / 10; + iValue += itr_quest->GetExperience() / 2 * iNumUnits * iOffenseFlavor / 10; + iValue += itr_quest->GetJuggernauts() * iOffenseFlavor / 10; + } + } + } + else + { + // The closer we are the better, because the unit travels less distance to get home + if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_NEIGHBORS) + iValue += 25; + else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_CLOSE) + iValue += 15; + else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_FAR) + iValue += -15; + else if(GetPlayer()->GetProximityToPlayer(eMinor) == PLAYER_PROXIMITY_DISTANT) + iValue += -25; + //antonjs: consider: knock it down if is there a chance the worker will get captured by a nearby rival + } - if (eOpinion == CIV_OPINION_FAVORABLE) - iWeight += 2; - else if (eOpinion == CIV_OPINION_FRIEND) - iWeight += 5; - else if (eOpinion == CIV_OPINION_ALLY) - iWeight += 10; + // We like to keep bullying the same minor + if (pMinor->GetMinorCivAI()->IsEverBulliedByMajor(eID)) + { + iValue += 25; + } - if (eApproach == CIV_APPROACH_FRIENDLY) - iWeight += 2; + // If this minor has a PtP from someone, bullying it could have big consequences + if (!GetPlayer()->GetPlayerTraits()->IgnoreBullyPenalties()) + { + if (pMinor->GetMinorCivAI()->IsProtectedByAnyMajor()) + { + iValue += -20; + //antonjs: consider: scale based on which major is protecting it + } + else if (pMinor->GetMinorCivAI()->GetAlly() != NO_PLAYER) + { + iValue += -20; + } + else + { + iValue += 20; + } + } + else + { + iValue += 25; + } - iWeight += GetChattiness(); // Usually ranges from 3 to 7 - iWeight += GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x5eec20f2).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())); + //Do we get a bonus from this? + if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0) + { + iValue += 25; + } + for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) + { + YieldTypes eYield = (YieldTypes)iI; + if (eYield != NO_YIELD) + { + if (GetPlayer()->GetYieldFromMinorDemand(eYield) > 0) + { + iValue += 10; + } + } + } - // We're mean enough to say something - if (iWeight >= 10) - { - eStatement = eTempStatement; - iData1 = eLoopPlayer; - } - // We're going to be nice! - else - { - eStatement = DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND_RANDFAILED; + //antonjs: consider: allies or friends with another major + //antonjs: consider: distance to other majors + // If we are getting a bonus, don't mess that up! + if(pMinor->GetMinorCivAI()->IsAllies(eID) || pMinor->GetMinorCivAI()->IsFriends(eID)) + { + if (!GetPlayer()->IsCanBullyFriendlyCS()) + iValue = 0; + } + // Do we want it enough? + if(iValue > 100) //antonjs: todo: XML for threshold + { + veMinorsToBullyUnit.push_back(eMinor, iValue); + bWantsToBullyUnitFromThisMinor = true; + } + } } - - // We're done in here - break; } - } - } -} - -/// Possible Contact Statement - We're happy we're following the same ideology as the human -void CvDiplomacyAI::DoHappySamePolicyTree(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + // Calculate desirability to bully gold from this minor + if(bWantsToBullyGold && !bWantsToBuyoutThisMinor && !bWantsToMarryThisMinor && !bWantsToBullyAnnexThisMinor && !bWantsToGiveGoldToThisMinor && !bWantsToBullyUnitFromThisMinor) + { + int iValue = 100; //antonjs: todo: XML, bully threshold + if (MOD_BALANCE_HEAVY_TRIBUTE) + iValue = pMinor->GetMinorCivAI()->CalculateBullyScore(eID, false); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; + if (iValue <= 0) + continue; - PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); - PolicyBranchTypes eTheirBranch = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree(); - if(eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE && eMyBranch == eTheirBranch && GET_PLAYER(ePlayer).GetCulture()->GetTurnIdeologySwitch() < 0) - { - // Don't say nice things if we dislike ePlayer - bool bSkip = false; - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); - if(eOpinion <= CIV_OPINION_COMPETITOR) - bSkip = true; - if(eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) - bSkip = true; + if(eApproach == CIV_APPROACH_HOSTILE) + { + // Only bother if we can successfully bully + if (pMinor->GetMinorCivAI()->CanMajorBullyGold(eID, MOD_BALANCE_HEAVY_TRIBUTE ? iValue - 25 : 0)) + { + iValue += (GET_PLAYER(eMinor).GetMinorCivAI()->GetBullyGoldAmount(GetID()) * iGoldFlavor) / 10; + // We like to keep bullying the same minor + if(pMinor->GetMinorCivAI()->IsEverBulliedByMajor(eID)) + { + iValue += 25; + } - // Check chattiness to see if we send the message this turn - if (!bSkip && GetChattiness() > GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0xf47d6bbc).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID()))) - { - DiploStatementTypes eOtherStatementToCheck = NO_DIPLO_STATEMENT_TYPE; + // If this minor has a PtP from someone, bullying it could have big consequences + if(pMinor->GetMinorCivAI()->IsProtectedByAnyMajor()) + { + iValue += -20; + //antonjs: consider: scale based on which major is protecting it + } + else + { + iValue += 20; + } + //antonjs: consider: allies or friends another major + //antonjs: consider: distance to other majors - if(eMyBranch == GD_INT_GET(POLICY_BRANCH_FREEDOM)) - { - eTempStatement = DIPLO_STATEMENT_SAME_POLICIES_FREEDOM; - eOtherStatementToCheck = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM; - } - else if(eMyBranch == GD_INT_GET(POLICY_BRANCH_ORDER)) - { - eTempStatement = DIPLO_STATEMENT_SAME_POLICIES_ORDER; - eOtherStatementToCheck = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER; - } - else if(eMyBranch == GD_INT_GET(POLICY_BRANCH_AUTOCRACY)) - { - eTempStatement = DIPLO_STATEMENT_SAME_POLICIES_AUTOCRACY; - eOtherStatementToCheck = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY; - } + //Do we get a bonus from this? + if (GetPlayer()->GetPlayerTraits()->GetBullyMilitaryStrengthModifier() != 0 || GetPlayer()->GetPlayerTraits()->GetBullyValueModifier() != 0) + { + iValue += 25; + } - if(eTempStatement != NO_DIPLO_STATEMENT_TYPE) - { - int iTurnsBetweenStatements = 9999; + // If we are getting a bonus, don't mess that up! + if(pMinor->GetMinorCivAI()->IsAllies(eID) || pMinor->GetMinorCivAI()->IsFriends(eID)) + { + if (!GetPlayer()->IsCanBullyFriendlyCS()) + iValue = 0; + } - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - // Also check the statement for joining the ideology. Don't want to send messages like this on back-to-back turns - if(GetNumTurnsSinceStatementSent(ePlayer, eOtherStatementToCheck) >= iTurnsBetweenStatements) + // Do we want it enough? + if(iValue > 100) //antonjs: todo: XML for threshold { - eStatement = eTempStatement; + veMinorsToBullyGold.push_back(eMinor, iValue); } } } } - } - } -} -/// Possible Contact Statement - Either AI or human has switched ideologies due to the other's pressure -void CvDiplomacyAI::DoIdeologicalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + } - PolicyBranchTypes eFreedom = (PolicyBranchTypes)GD_INT_GET(POLICY_BRANCH_FREEDOM); - PolicyBranchTypes eOrder = (PolicyBranchTypes)GD_INT_GET(POLICY_BRANCH_ORDER); + SetWantToRouteConnectToMinor(eMinor, bWantsToConnect); + } - CvPlayer &kTheirPlayer = GET_PLAYER(ePlayer); + int iGoldReserve = GetPlayer()->GetTreasury()->GetGold(); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Do we want to buyout a minor? + veMinorsToBuyout.StableSortItems(); + int iGoldLeft = GetPlayer()->GetTreasury()->GetGold(); + for (int i = 0; i < veMinorsToBuyout.size(); i++) { - DiploStatementTypes eTempStatement; - - if (m_pPlayer->GetCulture()->GetInfluenceLevel(ePlayer) >= INFLUENCE_LEVEL_INFLUENTIAL) + PlayerTypes eLoopMinor = veMinorsToBuyout.GetElement(i); + int iBuyoutCost = GET_PLAYER(eLoopMinor).GetMinorCivAI()->GetBuyoutCost(eID); + if (iGoldLeft >= iBuyoutCost) { - eTempStatement = DIPLO_STATEMENT_OUR_CULTURE_INFLUENTIAL; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 9999) + if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBuyout(eID)) { - eStatement = eTempStatement; - return; + GC.getGame().DoMinorBuyout(eID, eLoopMinor); + break; // Don't buyout more than once in a single turn + } + else + { + ASSERT(false, "Chose a minor to buyout that cannot actually be bought!"); } } - - if (kTheirPlayer.GetCulture()->GetInfluenceLevel(GetID()) >= INFLUENCE_LEVEL_INFLUENTIAL) + else { - eTempStatement = DIPLO_STATEMENT_YOUR_CULTURE_INFLUENTIAL; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 9999) + if (!GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) { - eStatement = eTempStatement; - return; + LogMinorCivBuyout(eLoopMinor, iBuyoutCost, /*bSaving*/ true); + GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT, iBuyoutCost, /*350*/ GD_INT_GET(AI_GOLD_PRIORITY_BUYOUT_CITY_STATE)); } } + } - // Everything below are "insult" type messages - if (MOD_DIPLOAI_SHUT_UP_INSULTS && kTheirPlayer.isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(kTheirPlayer.getTeam(), GetID())) - return; - - PolicyBranchTypes eMyBranch = m_pPlayer->GetPlayerPolicies()->GetLateGamePolicyTree(); - PolicyBranchTypes eTheirBranch = kTheirPlayer.GetPlayerPolicies()->GetLateGamePolicyTree(); - - if(eMyBranch != NO_POLICY_BRANCH_TYPE && eTheirBranch != NO_POLICY_BRANCH_TYPE && eMyBranch == eTheirBranch) + // Do we want to marry a city-state? + veMinorsToMarry.StableSortItems(); + iGoldLeft = GetPlayer()->GetTreasury()->GetGold(); + for (int i = 0; i < veMinorsToMarry.size(); i++) + { + PlayerTypes eLoopMinor = veMinorsToMarry.GetElement(i); + int iMarriageCost = GET_PLAYER(eLoopMinor).GetMinorCivAI()->GetMarriageCost(eID); + if (iGoldLeft >= iMarriageCost) { - PublicOpinionTypes eOpinionInMyCiv = m_pPlayer->GetCulture()->GetPublicOpinionType(); - PlayerTypes eMyGreatestInfluence = m_pPlayer->GetCulture()->GetPublicOpinionBiggestInfluence(); - PublicOpinionTypes eOpinionInTheirCiv = kTheirPlayer.GetCulture()->GetPublicOpinionType(); - PlayerTypes eTheirGreatestInfluence = kTheirPlayer.GetCulture()->GetPublicOpinionBiggestInfluence(); - - // Did this player recently switch ideology due to our pressure? - int iIdeologySwitchTurn = kTheirPlayer.GetCulture()->GetTurnIdeologySwitch(); - if (iIdeologySwitchTurn > 0 && iIdeologySwitchTurn + 10 > GC.getGame().getGameTurn()) + if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorDiploMarriage(eID)) { - DiploStatementTypes eSwitchStatement = NO_DIPLO_STATEMENT_TYPE; - if (eTheirBranch == eFreedom) - { - eSwitchStatement = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_FREEDOM; - } - else if (eTheirBranch == eOrder) - { - eSwitchStatement = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_ORDER; - } - else - { - eSwitchStatement = DIPLO_STATEMENT_SWITCH_OUR_IDEOLOGY_AUTOCRACY; - } - //don't spam the message - bool bNotRecentlySent = true; - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop; - - if (eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) - { - if (GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eSwitchStatement) < 9999) - bNotRecentlySent = false; - } - } - - if (bNotRecentlySent) - { - eStatement = eSwitchStatement; - return; - } + GET_PLAYER(eLoopMinor).GetMinorCivAI()->DoBuyout(eID); + iMarriageCost = GET_PLAYER(eLoopMinor).GetMinorCivAI()->GetMarriageCost(eID); + iGoldLeft -= iMarriageCost; } - - if (eOpinionInMyCiv >= PUBLIC_OPINION_CIVIL_RESISTANCE && eMyGreatestInfluence == ePlayer) + else { - if (eTheirBranch == eFreedom) - { - eTempStatement = DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM; - } - else if (eTheirBranch == eOrder) - { - eTempStatement = DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER; - } - else - { - eTempStatement = DIPLO_STATEMENT_YOUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY; - } - if (eTempStatement != NO_DIPLO_STATEMENT_TYPE) - { - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 9999) - { - eStatement = eTempStatement; - return; - } - } + ASSERT(false, "Chose a city-state to marry that cannot actually be married!"); } - if (eOpinionInTheirCiv >= PUBLIC_OPINION_CIVIL_RESISTANCE && eTheirGreatestInfluence == GetID()) + } + else + { + if (!GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) { - if (eMyBranch == eFreedom) - { - eTempStatement = DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_FREEDOM; - } - else if (eMyBranch == eOrder) - { - eTempStatement = DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_ORDER; - } - else - { - eTempStatement = DIPLO_STATEMENT_OUR_IDEOLOGY_CAUSING_CIVIL_UNREST_AUTOCRACY; - } - if (eTempStatement != NO_DIPLO_STATEMENT_TYPE) - { - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 9999) - { - eStatement = eTempStatement; - return; - } - } + LogMinorCivBuyout(eLoopMinor, iMarriageCost, /*bSaving*/ true); + GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT, iMarriageCost, /*350*/ GD_INT_GET(AI_GOLD_PRIORITY_BUYOUT_CITY_STATE)); } } } -} - -/// Possible Contact Statement - Message to human if the AI thinks they are getting close to the victory they're also going for. -void CvDiplomacyAI::DoVictoryCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (!IsCompetingForVictory()) - return; - - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - return; - int iTurnsBetweenStatements = 50; - AIGrandStrategyTypes eMyGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetActiveGrandStrategy(); - if(eMyGrandStrategy == NO_AIGRANDSTRATEGY) - { - return; - } - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Do we want to annex a minor? + veMinorsToBullyAnnex.StableSortItems(); + for (int i = 0; i < veMinorsToBullyAnnex.size(); i++) { - EraTypes eModern = (EraTypes) GC.getInfoTypeForString("ERA_MODERN", true); - DisputeLevelTypes eDispute = GetVictoryDisputeLevel(ePlayer); - if(eDispute < DISPUTE_LEVEL_STRONG) + PlayerTypes eLoopMinor = veMinorsToBullyAnnex.GetElement(i); + PRECONDITION(eLoopMinor != NO_PLAYER, "Trying to bully-annex NO_PLAYER!"); + if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBullyUnit(eID)) { - return; + GC.getGame().DoMinorBullyAnnex(eID, eLoopMinor); + break; // Don't annex more than one city-state in a single turn } - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - if(eOpinion >= CIV_OPINION_FAVORABLE) + else { - return; + ASSERT(false, "Chose a minor to bully-annex that cannot actually be bullied!"); } - bool bLeagueCompetitor = false; - bool bSpaceRace = false; - bool bCulture = false; - bool bWar = false; - int iVotes = 0; - int iNeededVotes = 0; - CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); - if(pLeague != NULL) + } + + // Do we want to give someone Gold enough to actually do it? + veMinorsToGiveGold.StableSortItems(); // Sort from highest desirability to lowest + for (int i = 0; i < veMinorsToGiveGold.size(); i++) + { + int iGoldLeft = GetPlayer()->GetTreasury()->GetGold(); + MinorGoldGiftInfo sGift = veMinorsToGiveGold.GetElement(i); + + //Interception! Let's do a tile improvement if we can (and we'll benefit from it) + if (sGift.eMinor != NO_PLAYER && GET_PLAYER(sGift.eMinor).GetMinorCivAI()->IsAllies(GetID())) { - iVotes = pLeague->CalculateStartingVotesForMember(ePlayer); - iNeededVotes = GC.getGame().GetVotesNeededForDiploVictory(); - if(iNeededVotes > 0) + CvPlot* pImprovementPlot = GET_PLAYER(sGift.eMinor).GetMinorCivAI()->GetMajorGiftTileImprovement(GetID()); + if (pImprovementPlot != NULL) { - // 33% there? Close! - if(iVotes >= (iNeededVotes / 3)) - { - bLeagueCompetitor = true; - } + GET_PLAYER(sGift.eMinor).GetMinorCivAI()->DoTileImprovementGiftFromMajor(GetID(), pImprovementPlot->getX(), pImprovementPlot->getY()); + LogMinorCivGiftTile(sGift.eMinor); } } - int iProjectCount = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetSSProjectCount(); - if (iProjectCount > 1) + + //Default is 1 - this will prevent the AI from trying to spam gold gifts of zero gold. + if (GD_INT_GET(CSD_GOLD_GIFT_DISABLED) > 0) { - bSpaceRace = true; + continue; } - else - { - int iTheirTechNum = GET_TEAM(GET_PLAYER(ePlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); - int iNumOtherPlayers = 0; - int iNumPlayersAheadInTech = 0; - for(uint ui = 0; ui < MAX_MAJOR_CIVS; ui++) - { - PlayerTypes eOtherPlayer = (PlayerTypes)ui; - if(!GET_PLAYER(eOtherPlayer).isAlive()) - { - continue; - } + sGift.iGoldAmount = 0; - if (eOtherPlayer == ePlayer) - { - continue; - } + if(iGoldLeft >= iSmallGift && sGift.bQuickBoost) + sGift.iGoldAmount = iSmallGift; + else if(iGoldLeft >= iLargeGift) + sGift.iGoldAmount = iLargeGift; + else if(iGoldLeft >= iMediumGift) + sGift.iGoldAmount = iMediumGift; - iNumOtherPlayers++; - int iNumTechs = GET_TEAM(GET_PLAYER(eOtherPlayer).getTeam()).GetTeamTechs()->GetNumTechsKnown(); - if (iTheirTechNum > iNumTechs ) - { - iNumPlayersAheadInTech++; - } - } - if(iNumPlayersAheadInTech >= iNumOtherPlayers) - { - bSpaceRace = true; - } - } - if(GetWarmongerThreat(ePlayer) >= THREAT_SEVERE && GET_PLAYER(ePlayer).GetNumCapitalCities() > 1 && GetPlayer()->GetNumCapitalCities() > 1) - { - bWar = true; - } - //More than double our influence, and we both have some? - if(GET_PLAYER(ePlayer).GetCulture()->GetNumCivsInfluentialOn() > 1 && GetPlayer()->GetCulture()->GetNumCivsInfluentialOn() > 0 && (GET_PLAYER(ePlayer).GetCulture()->GetNumCivsInfluentialOn() > (GetPlayer()->GetCulture()->GetNumCivsInfluentialOn() * 2))) - { - bCulture = true; - } - DiploStatementTypes eTempStatement; + int iOldFriendship = GET_PLAYER(sGift.eMinor).GetMinorCivAI()->GetEffectiveFriendshipWithMajor(eID); - if(GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategy(ePlayer) == eMyGrandStrategy) + // Able to give a gift? Don't gift more than half of the gold we have in one turn + if(sGift.iGoldAmount > 0 && iGoldLeft >= (iGoldReserve / 2)) { - if(GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategyConfidence(ePlayer) >= GUESS_CONFIDENCE_LIKELY) - { - //Conquered a capital? You are in our way! - if(IsGoingForWorldConquest() && bWar) - { - eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONQUEST; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - return; - } - } - else if(IsGoingForDiploVictory() && bLeagueCompetitor) - { - eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_WORLD_CONGRESS; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - return; - } - } - else if(IsGoingForCultureVictory() && bCulture) - { - //We've both influenced someone? Competitor! - eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CULTURE; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - return; - } - } - else if(IsGoingForSpaceshipVictory() && bSpaceRace) - { - eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_SPACESHIP; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - return; - } - } - //Don't have it figured out, but we're competitive? Grr! - else if(GetPlayer()->GetCurrentEra() > eModern) - { - eTempStatement = DIPLO_STATEMENT_VICTORY_COMPETITION_ANNOUNCE_CONFUSED; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - return; - } - } - } - } - } -} - -/// Possible Contact Statement - Message to human if the AI thinks they are getting close to a victory that they're not going for. -void CvDiplomacyAI::DoVictoryBlockStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + GET_PLAYER(sGift.eMinor).GetMinorCivAI()->DoGoldGiftFromMajor(GetID(), sGift.iGoldAmount); //antonjs: todo: go through CvGame instead? - if (!IsCompetingForVictory()) - return; + LogMinorCivGiftGold(sGift.eMinor, iOldFriendship, sGift.iGoldAmount, /*bSaving*/ false, sGift.bQuickBoost, sGift.eMajorRival); - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + if(GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) + GetPlayer()->GetEconomicAI()->CancelSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT); + } + // Can't afford gift yet, so start saving + else + { + if(!GetPlayer()->GetEconomicAI()->IsSavingForThisPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT)) + { + int iAmountToSaveFor = iMediumGift; - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - return; + if(sGift.bQuickBoost) + iAmountToSaveFor = iSmallGift; - int iTurnsBetweenStatements = 50; - AIGrandStrategyTypes eConquestGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CONQUEST"); - AIGrandStrategyTypes eCultureGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_CULTURE"); - AIGrandStrategyTypes eUNGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_UNITED_NATIONS"); - AIGrandStrategyTypes eSpaceshipGrandStrategy = (AIGrandStrategyTypes) GC.getInfoTypeForString("AIGRANDSTRATEGY_SPACESHIP"); - EraTypes eAtomic = (EraTypes) GC.getInfoTypeForString("ERA_POSTMODERN", true); - bool bSkip = false; + LogMinorCivGiftGold(sGift.eMinor, iOldFriendship, iAmountToSaveFor, /*bSaving*/ true, sGift.bQuickBoost, sGift.eMajorRival); - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - if(eOpinion >= CIV_OPINION_FAVORABLE) - { - bSkip = true; + int iPriority = /*150*/ GD_INT_GET(AI_GOLD_PRIORITY_DIPLOMACY_BASE); + iPriority += iDiplomacyFlavor * /*25*/ GD_INT_GET(AI_GOLD_PRIORITY_DIPLOMACY_PER_FLAVOR_POINT); + GetPlayer()->GetEconomicAI()->StartSaveForPurchase(PURCHASE_TYPE_MINOR_CIV_GIFT, iAmountToSaveFor, iPriority); + } + } } - //Let's not send this before the Atomic Era, okay? - if(GetPlayer()->GetCurrentEra() < eAtomic) + + // Do we want a unit enough to bully someone? + veMinorsToBullyUnit.StableSortItems(); + for (int i = 0; i < veMinorsToBullyUnit.size(); i++) { - bSkip = true; + PlayerTypes eLoopMinor = veMinorsToBullyUnit.GetElement(i); + PRECONDITION(eLoopMinor != NO_PLAYER, "Trying to bully a unit from NO_PLAYER!"); + if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBullyUnit(eID)) + { + GC.getGame().DoMinorBullyUnit(eID, eLoopMinor); + break; // Don't bully a unit more than once in a single turn + } + else + { + ASSERT(false, "Chose a minor to bully unit from that cannot actually be bullied!"); + } } - if(eStatement == NO_DIPLO_STATEMENT_TYPE && !bSkip) - { - DiploStatementTypes eTempStatement; - if(GetVictoryBlockLevel(ePlayer) >= BLOCK_LEVEL_STRONG) + // Do we want gold enough to bully someone? + veMinorsToBullyGold.StableSortItems(); + for (int i = 0; i < veMinorsToBullyGold.size(); i++) + { + PlayerTypes eLoopMinor = veMinorsToBullyGold.GetElement(i); + PRECONDITION(eLoopMinor != NO_PLAYER, "Trying to bully gold from NO_PLAYER!"); + if (GET_PLAYER(eLoopMinor).GetMinorCivAI()->CanMajorBullyGold(eID)) { - AIGrandStrategyTypes eGrandStrategy = GetPlayer()->GetGrandStrategyAI()->GetGuessOtherPlayerActiveGrandStrategy(ePlayer); - if(eGrandStrategy == eConquestGrandStrategy) - { - eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONQUEST; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - return; - } - } - else if(eGrandStrategy == eUNGrandStrategy) - { - eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_WORLD_CONGRESS; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - return; - } - } - else if(eGrandStrategy == eCultureGrandStrategy) - { - eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_CULTURE; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - return; - } - } - else if(eGrandStrategy == eSpaceshipGrandStrategy) - { - eTempStatement = DIPLO_STATEMENT_VICTORY_BLOCK_ANNOUNCE_SPACESHIP; - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - eStatement = eTempStatement; - return; - } - } + GC.getGame().DoMinorBullyGold(eID, eLoopMinor); + } + else + { + ASSERT(false, "Chose a minor to bully gold from that cannot actually be bullied!"); } } } -/// Possible Contact Statement - We liked the human's proposal to the World Congress -void CvDiplomacyAI::DoWeLikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +void CvDiplomacyAI::DoUpdateMinorCivProtection(PlayerTypes eMinor) { - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + if (GetCivApproach(eMinor) == CIV_APPROACH_FRIENDLY || GET_PLAYER(eMinor).GetMinorCivAI()->GetAlly() == GetID()) { - DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; - if (WeLikedTheirProposal(ePlayer)) + // We are protective, so do a PtP if we are able to and haven't already + if (GET_PLAYER(eMinor).GetMinorCivAI()->CanMajorStartProtection(GetID())) { - bool bSkip = false; - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); - if(eOpinion <= CIV_OPINION_COMPETITOR) - bSkip = true; - if(eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) - bSkip = true; - if ((GC.getGame().getGameTurn() - GetWeLikedTheirProposalTurn(ePlayer)) > 10) - bSkip = true; - if (!bSkip && GetChattiness() > GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x4ce615ad).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID()))) - { - eTempStatement = DIPLO_STATEMENT_WE_LIKED_THEIR_PROPOSAL; - int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_NUM_TURNS); - int iMessage = 0; - int iMessageMax = MAX_INT; - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - - if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) - { - iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); - if(iMessage < iMessageMax) - { - iMessageMax = iMessage; - } - } - } - if(iMessageMax >= iTurnsBetweenStatements && (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements)) - eStatement = eTempStatement; - } + GC.getGame().DoMinorPledgeProtection(GetID(), eMinor, true); } } -} - -/// Possible Contact Statement - We disliked the human's proposal to the World Congress -void CvDiplomacyAI::DoWeDislikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Don't cancel a pledge in VP unless the City-State has no capital (we won't get the Influence boost from quests) or they've taken damage (so we'll lose Influence faster) + // Pledges will be automatically cancelled if AI decides to bully or war the City-State, so there's no benefit to cancelling now - we might change our mind + else if (GD_INT_GET(BALANCE_INFLUENCE_BOOST_PROTECTION_MINOR) <= 0 || GET_PLAYER(eMinor).getCapitalCity() == NULL || (GET_PLAYER(eMinor).getCapitalCity()->getDamage() > 0 && GD_INT_GET(MINOR_FRIENDSHIP_DROP_PER_TURN_DAMAGED_CAPITAL_MULTIPLIER) > 100)) { - DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; - if (WeDislikedTheirProposal(ePlayer)) + // We are not protective, so revoke PtP if we can + if (GET_PLAYER(eMinor).GetMinorCivAI()->IsProtectedByMajor(GetID()) && GET_PLAYER(eMinor).GetMinorCivAI()->CanMajorWithdrawProtection(GetID())) { - bool bSkip = false; - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); - if(eOpinion >= CIV_OPINION_FAVORABLE) - bSkip = true; - if(eApproach == CIV_APPROACH_FRIENDLY) - bSkip = true; - if ((GC.getGame().getGameTurn() - GetWeDislikedTheirProposalTurn(ePlayer)) > 10) - bSkip = true; - if (!bSkip && GetChattiness() > GC.getGame().randRangeExclusive(0, 10, CvSeeder::fromRaw(0x9b0e7357).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID()))) - { - eTempStatement = DIPLO_STATEMENT_WE_DISLIKED_THEIR_PROPOSAL; - int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_NUM_TURNS); - int iMessage = 0; - int iMessageMax = MAX_INT; - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - - if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) - { - iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); - if(iMessage < iMessageMax) - { - iMessageMax = iMessage; - } - } - } - if(iMessageMax >= iTurnsBetweenStatements && (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements)) - eStatement = eTempStatement; - } + GC.getGame().DoMinorPledgeProtection(GetID(), eMinor, false); } } } -/// Possible Contact Statement - The human helped our proposal pass in the World Congress -void CvDiplomacyAI::DoTheySupportedOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +/// Possible Contact Statement - Demand +void CvDiplomacyAI::DoMakeDemand(PlayerTypes ePlayer) { - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + PRECONDITION(ePlayer >= 0 && ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + // We can use this deal pointer to form a trade offer + CvDeal* pDeal = GC.getGame().GetGameDeals().GetTempDeal(); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Clear this data out before any deals are offered. + GetPlayer()->GetDiplomacyAI()->SetOfferingGift(ePlayer, false); + GetPlayer()->GetDiplomacyAI()->SetOfferedGift(ePlayer, false); + SetCantMatchDeal(ePlayer, false); + pDeal->ClearItems(); + pDeal->SetRequestingPlayer(NO_PLAYER); + pDeal->SetDuration(GC.getGame().getGameSpeedInfo().GetDealDuration()); + pDeal->SetFromPlayer(GetID()); + pDeal->SetToPlayer(ePlayer); + + //set up the deal + if (GetPlayer()->GetDealAI()->IsMakeDemand(ePlayer, /*pDeal can be modified in this function*/ pDeal)) { - DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; - bool bEffect = GetTheySupportedOurProposalTurn(ePlayer) > -1; - if (bEffect) + int iTurnsBetweenStatements = 10; + if (GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_DEMAND) >= iTurnsBetweenStatements) { - bool bSkip = false; - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); - if(eOpinion <= CIV_OPINION_COMPETITOR) - bSkip = true; - if(eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) - bSkip = true; - if ((GC.getGame().getGameTurn() - GetTheySupportedOurProposalTurn(ePlayer)) > 10) - bSkip = true; - if (!bSkip) - { - eTempStatement = DIPLO_STATEMENT_THEY_SUPPORTED_OUR_PROPOSAL; - int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_PROPOSAL_NUM_TURNS); - int iMessage = 0; - int iMessageMax = MAX_INT; - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - - if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) - { - iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); - if(iMessage < iMessageMax) - { - iMessageMax = iMessage; - } - } - } - if(iMessageMax >= iTurnsBetweenStatements && (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements)) - eStatement = eTempStatement; - } + DiploStatementTypes eStatement = DIPLO_STATEMENT_DEMAND; + DoSendStatementToPlayer(ePlayer, eStatement, -1, pDeal); + LogStatementToPlayer(ePlayer, eStatement); + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_DEMAND, GC.getGame().getGameTurn()); } } + + // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at + pDeal->ClearItems(); } -/// Possible Contact Statement - The human helped our proposal fail in the World Congress -void CvDiplomacyAI::DoTheyFoiledOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement) +/// Possible Contact Statement - Gift +void CvDiplomacyAI::DoGift(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) { PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if (MOD_DIPLOAI_SHUT_UP_INSULTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) + if (IsAvoidDeals()) return; - // We must be able to declare war on them - if (!GET_TEAM(GetTeam()).canDeclareWar(GET_PLAYER(ePlayer).getTeam(), GetID())) + if (MOD_DIPLOAI_SHUT_UP_GIFT_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) return; if(eStatement == NO_DIPLO_STATEMENT_TYPE) { - DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; - bool bEffect = GetTheyFoiledOurProposalTurn(ePlayer) > -1; - if (bEffect) + DiploStatementTypes eTempStatement = DIPLO_STATEMENT_GIFT; + + // If a request was accepted or rejected, wait 60 turns. If we rolled for rand and failed, wait 15 turns before we try again + if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 60 && + GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GIFT_RANDFAILED) >= 15) { - bool bSkip = false; - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); - if(eOpinion >= CIV_OPINION_FAVORABLE) - bSkip = true; - if(eApproach == CIV_APPROACH_FRIENDLY) - bSkip = true; - if ((GC.getGame().getGameTurn() - GetTheyFoiledOurProposalTurn(ePlayer)) > 10) - bSkip = true; - if (!bSkip) - { - eTempStatement = DIPLO_STATEMENT_THEY_FOILED_OUR_PROPOSAL; - int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_FOILED_OUR_PROPOSAL_NUM_TURNS); - int iMessage = 0; - int iMessageMax = MAX_INT; - for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; + bool bRandPassed = false; // This is used to see if we WOULD have made a gift, but the rand roll failed (so add an entry to the log) + bool bMakeGift = false;//IsMakeGift(ePlayer, pDeal, bRandPassed); - if(eLoopPlayer != NO_PLAYER && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).isMajorCiv() && eLoopPlayer != ePlayer) - { - iMessage = GET_PLAYER(eLoopPlayer).GetDiplomacyAI()->GetNumTurnsSinceStatementSent(ePlayer, eTempStatement); - if(iMessage < iMessageMax) - { - iMessageMax = iMessage; - } - } - } - if(iMessageMax >= iTurnsBetweenStatements && (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements)) - eStatement = eTempStatement; + // Want to make a request of ePlayer? Pass pDeal in to see if there's actually anything we want + if(bMakeGift) + { + eStatement = eTempStatement; + pDeal->SetRequestingPlayer(GetID()); } - } - } -} - -/// Possible Contact Statement - The human helped relocate the World Congress to our lands -void CvDiplomacyAI::DoTheySupportedOurHosting(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (MOD_DIPLOAI_SHUT_UP_COMPLIMENTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = NO_DIPLO_STATEMENT_TYPE; - if (TheySupportedOurHosting(ePlayer)) - { - bool bSkip = false; - CivOpinionTypes eOpinion = GetCivOpinion(ePlayer); - CivApproachTypes eApproach = GetCivApproach(ePlayer); - if(eOpinion <= CIV_OPINION_COMPETITOR) - bSkip = true; - if(eApproach == CIV_APPROACH_WAR || eApproach == CIV_APPROACH_HOSTILE) - bSkip = true; - if ((GC.getGame().getGameTurn() - GetTheySupportedOurHostingTurn(ePlayer)) > 20) - bSkip = true; - if (!bSkip) - { - eTempStatement = DIPLO_STATEMENT_THEY_SUPPORTED_OUR_HOSTING; - int iTurnsBetweenStatements = /*50*/ GD_INT_GET(OPINION_WEIGHT_THEY_SUPPORTED_OUR_HOSTING_NUM_TURNS); + // Clear out the deal if we don't want to offer it so that it's not tainted for the next trade possibility we look at + else + pDeal->ClearItems(); - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - eStatement = eTempStatement; + // Add this statement to the log so we don't evaluate it again until 15 turns has come back around + if(!bRandPassed) + { + SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_GIFT_RANDFAILED, GC.getGame().getGameTurn()); + pDeal->ClearItems(); } } } @@ -38153,7 +38872,7 @@ void CvDiplomacyAI::DoFromUIDiploEvent(PlayerTypes eFromPlayer, FromUIDiploEvent if (bDeclareWar && !IsPotentialWarTarget(eFromPlayer)) bDeclareWar = false; - // Sanity check - who else would we go to war with? + // Sanity check - are we backstabbing? if (bDeclareWar && !DoUpdateOnePlayerSaneDiplomaticTarget(eFromPlayer, true)) bDeclareWar = false; @@ -39347,24 +40066,44 @@ void CvDiplomacyAI::DoFromUIDiploEvent(PlayerTypes eFromPlayer, FromUIDiploEvent case DEMAND_RESPONSE_REFUSE_PROTECTED_BY_MASTER: { PlayerTypes eProtector = NO_PLAYER; - TeamTypes eOurMaster = GET_TEAM(GetTeam()).GetMaster(); - vector vMasterTeam = GET_TEAM(eOurMaster).getPlayers(); - for (size_t i=0; i < vMasterTeam.size(); i++) + if (GetPlayer()->IsAITeammateOfHuman()) { - // Find the first player on the master's team who triggered this response, see CvDealAI::GetDemandResponse() - CvPlayer* pMaster = &GET_PLAYER(vMasterTeam[i]); - if (!pMaster->isAlive() || pMaster->getNumCities() == 0) - continue; + vector vMyTeam = GET_TEAM(GetTeam()).getPlayers(); + for (size_t i=0; i < vMyTeam.size(); i++) + { + // Find the first human player on our team + CvPlayer* pPlayer = &GET_PLAYER(vMyTeam[i]); + if (pPlayer->isAlive() && pPlayer->isHuman(ISHUMAN_AI_DIPLOMACY)) + { + eProtector = pPlayer->GetID(); + break; + } + } + } + else + { + TeamTypes eOurMaster = GET_TEAM(GetTeam()).GetMaster(); + vector vMasterTeam = GET_TEAM(eOurMaster).getPlayers(); + for (size_t i=0; i < vMasterTeam.size(); i++) + { + // Find the first player on the master's team who triggered this response, see CvDealAI::GetDemandResponse() + CvPlayer* pMaster = &GET_PLAYER(vMasterTeam[i]); + if (!pMaster->isAlive() || pMaster->getNumCities() == 0) + continue; - if (pMaster->GetDiplomacyAI()->GetRawMilitaryStrengthComparedToUs(eFromPlayer) > STRENGTH_AVERAGE) - continue; + if (pMaster->GetDiplomacyAI()->GetRawMilitaryStrengthComparedToUs(eFromPlayer) > STRENGTH_AVERAGE) + continue; - if (GetVassalProtectValue(vMasterTeam[i]) < 0) - continue; + if (GetVassalProtectValue(vMasterTeam[i]) < 0) + continue; - // Is our master neighbors with them? - if (pMaster->GetProximityToPlayer(eFromPlayer) == PLAYER_PROXIMITY_NEIGHBORS || pMaster->GetProximityToPlayer(eMyPlayer) >= GET_PLAYER(eFromPlayer).GetProximityToPlayer(eMyPlayer)) - eProtector = vMasterTeam[i]; + // Is our master neighbors with them? + if (pMaster->GetProximityToPlayer(eFromPlayer) == PLAYER_PROXIMITY_NEIGHBORS || pMaster->GetProximityToPlayer(eMyPlayer) >= GET_PLAYER(eFromPlayer).GetProximityToPlayer(eMyPlayer)) + { + eProtector = vMasterTeam[i]; + break; + } + } } PRECONDITION(eProtector != NO_PLAYER); const char* strProtectorCivKey = GET_PLAYER(eProtector).getCivilizationShortDescriptionKey(); @@ -41252,13 +41991,22 @@ bool CvDiplomacyAI::DoTestCoopWarDesire(PlayerTypes eAllyPlayer, PlayerTypes& eC if (!IsDoFAccepted(eAllyPlayer)) return false; - // If we hate them for some reason, don't bother. + // Don't start a war if our empire is in bad shape for it + if (GetPlayer()->IsNoNewWars()) + return false; + + // Opinion and Approach must not be negative if (GetCivApproach(eAllyPlayer) <= CIV_APPROACH_GUARDED) return false; if (GetCivOpinion(eAllyPlayer) <= CIV_OPINION_ENEMY) return false; + // If either of us is an AI teammate of a human, abort! + if (GetPlayer()->IsAITeammateOfHuman() || GET_PLAYER(eAllyPlayer).IsAITeammateOfHuman()) + return false; + + // Backstabber? Yeah...no. if (IsUntrustworthy(eAllyPlayer)) return false; @@ -41284,7 +42032,6 @@ bool CvDiplomacyAI::DoTestCoopWarDesire(PlayerTypes eAllyPlayer, PlayerTypes& eC continue; int iScore = GetCoopWarDesireScore(eAllyPlayer, eTarget, false); - if (iScore > iBestTargetScore) { eBestTarget = eTarget; @@ -45322,7 +46069,7 @@ void CvDiplomacyAI::TestOpinionModifiers() /*50*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_NUM_TURNS), GetWorkAgainstWillingness()); - int iSupportValue = GetSupportedOurProposalValue(ePlayer); + int iSupportValue = GetProposalSupportDelta(ePlayer); // They foiled our proposals! if (iSupportValue > 0) @@ -45351,7 +46098,7 @@ void CvDiplomacyAI::TestOpinionModifiers() { SetTheySupportedOurProposalTurn(ePlayer, -1); SetTheyFoiledOurProposalTurn(ePlayer, -1); - SetSupportedOurProposalValue(ePlayer, 0); + SetProposalSupportDelta(ePlayer, 0); } } // They supported our proposals! @@ -45381,7 +46128,7 @@ void CvDiplomacyAI::TestOpinionModifiers() { SetTheySupportedOurProposalTurn(ePlayer, -1); SetTheyFoiledOurProposalTurn(ePlayer, -1); - SetSupportedOurProposalValue(ePlayer, 0); + SetProposalSupportDelta(ePlayer, 0); } } @@ -48085,7 +48832,7 @@ int CvDiplomacyAI::GetLikedTheirProposalScore(PlayerTypes ePlayer) if (WeLikedTheirProposal(ePlayer)) { - iOpinionWeight = GetLikedTheirProposalValue(ePlayer); + iOpinionWeight = GetProposalAgreementValue(ePlayer); if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { @@ -48109,7 +48856,7 @@ int CvDiplomacyAI::GetDislikedTheirProposalScore(PlayerTypes ePlayer) if (WeDislikedTheirProposal(ePlayer)) { - iOpinionWeight = GetLikedTheirProposalValue(ePlayer); + iOpinionWeight = GetProposalAgreementValue(ePlayer); if (IsDiplomat() || IsSecondaryDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat()) { @@ -48130,7 +48877,7 @@ int CvDiplomacyAI::GetSupportedOurProposalScore(PlayerTypes ePlayer) return 0; int iOpinionWeight = 0; - int iSupportValue = GetSupportedOurProposalValue(ePlayer); + int iSupportValue = GetProposalSupportDelta(ePlayer); // Foiled our proposals! if (iSupportValue > 0) @@ -52720,9 +53467,6 @@ void CvDiplomacyAI::LogStatementToPlayer(PlayerTypes ePlayer, DiploStatementType case DIPLO_STATEMENT_DENOUNCE: strTemp.Format("Denounce!"); break; - case DIPLO_STATEMENT_DENOUNCE_RANDFAILED: - strTemp.Format("Denounce RANDFAILED"); - break; case DIPLO_STATEMENT_END_WORK_AGAINST_SOMEONE: strTemp.Format("***** We're done working against someone with you. *****"); break; @@ -52793,54 +53537,39 @@ void CvDiplomacyAI::LogStatementToPlayer(PlayerTypes ePlayer, DiploStatementType case DIPLO_STATEMENT_REQUEST_FRIEND_WAR: strTemp.Format("***** DECLARE WAR ON SOMEONE FOR ME? *****"); break; + case DIPLO_STATEMENT_COMPLIMENT_THROTTLE: + strTemp.Format("***** Compliment sent! Cooldown now active *****"); + break; + case DIPLO_STATEMENT_INSULT_THROTTLE: + strTemp.Format("***** Insult sent! Cooldown now active *****"); + break; + case DIPLO_STATEMENT_TAUNT_THROTTLE: + strTemp.Format("***** Taunt sent! Cooldown now active *****"); + break; case DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY: strTemp.Format("***** YOU BEFRIENDED AN ENEMY OF MINE! *****"); break; - case DIPLO_STATEMENT_ANGRY_BEFRIEND_ENEMY_RANDFAILED: - strTemp.Format("***** You befriended an enemy of mine! RANDFAILED *****"); - break; case DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND: strTemp.Format("***** YOU DENOUNCED A FRIEND OF MINE! *****"); break; - case DIPLO_STATEMENT_ANGRY_DENOUNCED_FRIEND_RANDFAILED: - strTemp.Format("***** You denounced a friend of mine! RANDFAILED *****"); - break; case DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY: strTemp.Format("***** YAY - YOU DENOUNCED AN ENEMY! *****"); break; - case DIPLO_STATEMENT_HAPPY_DENOUNCED_ENEMY_RANDFAILED: - strTemp.Format("***** Yay - you denounced an enemy! RANDFAILED *****"); - break; case DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND: strTemp.Format("***** YAY - YOU BEFRIENDED A FRIEND! *****"); break; - case DIPLO_STATEMENT_HAPPY_BEFRIENDED_FRIEND_RANDFAILED: - strTemp.Format("***** Yay - you befriended a friend! RANDFAILED *****"); - break; case DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY: strTemp.Format("***** JUST FYI - I BEFRIENDED YOUR ENEMY! *****"); break; - case DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_ENEMY_RANDFAILED: - strTemp.Format("***** Just FYI - I befriended your enemy! RANDFAILED *****"); - break; case DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND: strTemp.Format("***** JUST FYI - I DENOUNCED YOUR FRIEND! *****"); break; - case DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_FRIEND_RANDFAILED: - strTemp.Format("***** Just FYI - I denounced your friend! RANDFAILED *****"); - break; case DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY: strTemp.Format("***** JUST FYI - I DENOUNCED YOUR ENEMY! *****"); break; - case DIPLO_STATEMENT_FYI_DENOUNCED_HUMAN_ENEMY_RANDFAILED: - strTemp.Format("***** Just FYI - I denounced your enemy! RANDFAILED *****"); - break; case DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND: strTemp.Format("***** JUST FYI - I BEFRIENDED YOUR FRIEND! *****"); break; - case DIPLO_STATEMENT_FYI_BEFRIEND_HUMAN_FRIEND_RANDFAILED: - strTemp.Format("***** Just FYI - I befriended your friend! RANDFAILED *****"); - break; case DIPLO_STATEMENT_SAME_POLICIES_AUTOCRACY: case DIPLO_STATEMENT_SAME_POLICIES_FREEDOM: case DIPLO_STATEMENT_SAME_POLICIES_ORDER: @@ -54415,14 +55144,14 @@ bool CvDiplomacyAIHelpers::IgnoresBackstabbing(PlayerTypes eObserver, PlayerType return GET_PLAYER(eObserver).GetDiplomacyAI()->IsUntrustworthy(eVictim); } -bool CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(PlayerTypes ePlayer, PlayerTypes eOtherPlayer) +bool CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(PlayerTypes ePlayer, PlayerTypes eOtherPlayer, bool bStrict /* = false */) { CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague(); if (!pLeague) return false; CvDiplomacyAI* pDiplo = GET_PLAYER(ePlayer).GetDiplomacyAI(); - bool bRecentLiberation = pDiplo->IsCityRecentlyLiberatedBy(eOtherPlayer) && GET_PLAYER(ePlayer).getCitiesLost() > 0 && !GET_PLAYER(eOtherPlayer).GetDiplomacyAI()->IsCloseToWorldConquest() && !pDiplo->IsEndgameAggressiveTo(eOtherPlayer); + bool bRecentLiberation = !bStrict && pDiplo->IsCityRecentlyLiberatedBy(eOtherPlayer) && GET_PLAYER(ePlayer).getCitiesLost() > 0 && !GET_PLAYER(eOtherPlayer).GetDiplomacyAI()->IsCloseToWorldConquest() && !pDiplo->IsEndgameAggressiveTo(eOtherPlayer); // Is there a current sanction proposal in either direction? for (EnactProposalList::iterator it = pLeague->m_vEnactProposals.begin(); it != pLeague->m_vEnactProposals.end(); ++it) @@ -54440,6 +55169,9 @@ bool CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(PlayerTypes ePlaye } else if (eProposerTeam == GET_PLAYER(ePlayer).getTeam() && eTargetTeam == GET_PLAYER(eOtherPlayer).getTeam()) { + if (bStrict) + return true; + // Add some one-way exceptions if the player has proposed sanctions, but has been liberated by the other player, or the other player is a vassal of someone. return !pDiplo->IsLiberator(eOtherPlayer, false, true) && !bRecentLiberation && !GET_PLAYER(eOtherPlayer).IsVassalOfSomeone(); } @@ -54449,186 +55181,113 @@ bool CvDiplomacyAIHelpers::ProposedSanctionsBlockingDiplomacy(PlayerTypes ePlaye return false; } -/// Possible Contact Statement - AI only -void CvDiplomacyAI::DoMakeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) +/// Helper function to calculate the AI's talkativeness about World Congress proposals that they like or dislike +/// Weak like/dislike is already excluded if we reach this point +int CvDiplomacyAIHelpers::GetProposalAgreementTalkativeness(PlayerTypes eObserver, PlayerTypes eProposer, bool bGood, int iTurnsSinceTrigger, int iEligibleWindow) { - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + CvDiplomacyAI* pDiplo = GET_PLAYER(eObserver).GetDiplomacyAI(); + int iChattiness = pDiplo->GetChattiness(); + int iAllianceFlavor = bGood ? pDiplo->GetWorkWithWillingness() : pDiplo->GetWorkAgainstWillingness(); + int iValue = pDiplo->GetProposalAgreementValue(eProposer); + + // "T" parameter: How TALKative is the AI about proposals generally? + int iTalkativeness = 20 + (4 * iChattiness) + (2 * iAllianceFlavor) + (iChattiness * iChattiness * (iAllianceFlavor + 2) / 20); + int iGamespeedDenominator = iEligibleWindow > 8 ? 80 + (iEligibleWindow - 8) * 6 : 80; + int iTalkPerMille = (iTalkativeness * 80) / iGamespeedDenominator; + + // "D" parameter: How much do they (not) DESIRE this particular proposal? + int iDesireScore = 0; + if (bGood) + iDesireScore = iValue <= /*-60*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_OVERWHELMING) ? 100 : iValue <= /*-45*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_STRONG) ? 50 : 0; + else + iDesireScore = iValue >= /*60*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_OVERWHELMING) ? 100 : iValue >= /*45*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_STRONG) ? 50 : 0; - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; + int iDesireAmplifier = 30 + (6 * iAllianceFlavor); + iGamespeedDenominator = iEligibleWindow > 8 ? 8 + (iEligibleWindow - 8) / 2 : 8; + int iDesirePerMille = (((iDesireScore * iDesireAmplifier + 50) / 100) * 8 + (iGamespeedDenominator / 2)) / iGamespeedDenominator; - // note: we check to see if it's possible in IsMakeOfferForVassalage() + // "L" parameter: How LONG has it been since we (dis)liked their proposal? Scales also based on desire - how much the AI cares about the proposal heavily impacts its willingness to speak up about it. + int iLengthPerMille = 1300 - (400 * iTurnsSinceTrigger / iEligibleWindow); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + if (bGood) { - // Can we make an offer for vassalage? - if(GetPlayer()->GetDealAI()->IsMakeOfferForVassalage(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_BECOME_MY_VASSAL; - int iTurnsBetweenStatement = 50; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) - { - // Send the statement - eStatement = eTempStatement; - } - } - else - { - pDeal->ClearItems(); - } + if (iValue <= GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_OVERWHELMING)) + iLengthPerMille = 1800 - (900 * iTurnsSinceTrigger / iEligibleWindow); + else if (iValue <= GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_STRONG)) + iLengthPerMille = 1500 - (600 * iTurnsSinceTrigger / iEligibleWindow); } -} - -/// Possible Contact Statement - AI only -void CvDiplomacyAI::DoBecomeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (IsAvoidDeals()) - return; - - if (MOD_DIPLOAI_SHUT_UP_TRADE_OFFERS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - // note: we check to see if it's possible in IsMakeOfferForVassalage() - - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + else { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_ACCEPT_VASSALAGE; - int iTurnsBetweenStatement = 50; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) - { - // Can we make an offer for vassalage? - if (GetPlayer()->GetDealAI()->IsMakeOfferToBecomeVassal(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } - } + if (iValue >= GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_OVERWHELMING)) + iLengthPerMille = 1800 - (900 * iTurnsSinceTrigger / iEligibleWindow); + else if (iValue >= GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_STRONG)) + iLengthPerMille = 1500 - (600 * iTurnsSinceTrigger / iEligibleWindow); } -} -/// Possible Contact Statement - Vassal taxes have been raised -void CvDiplomacyAI::DoVassalTaxesRaisedStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); + // Calculate the chance + int iChance = ((iTalkPerMille * iLengthPerMille + 500) / 1000) + ((iDesirePerMille * iLengthPerMille + 500) / 1000); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Chance drops after the 3rd turn + if (iTurnsSinceTrigger >= 3) { - // Which player is actually the vassal? - PlayerTypes eVassal = NO_PLAYER; - if(IsVassal(ePlayer)) - eVassal = GetID(); - else if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassal(GetID())) - eVassal = ePlayer; - - if(eVassal != NO_PLAYER) - { - // We are ePlayer's vassal - if(eVassal == GetID()) - { - if(IsVassalTaxRaised(ePlayer)) - { - eStatement = DIPLO_STATEMENT_VASSAL_TAXES_RAISED_HUMAN_MASTER; - SetVassalTaxRaised(ePlayer, false); - } - } - // ePlayer is our vassal - else if(eVassal == ePlayer) - { - if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassalTaxRaised(GetID())) - { - eStatement = DIPLO_STATEMENT_VASSAL_TAXES_RAISED_AI_MASTER; - - for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - // Modify player view to all AI teammates - if (GET_PLAYER(eLoopPlayer).getTeam() == GetTeam()) - { - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetVassalTaxRaised(eLoopPlayer, false); - } - } - } - } - } + iChance += 1; + iChance /= 2; } -} -/// Possible Contact Statement - Vassal taxes have been lowered -void CvDiplomacyAI::DoVassalTaxesLoweredStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - if(eStatement == NO_DIPLO_STATEMENT_TYPE) + // Finally, apply a multiplier for diplomatic players + if (pDiplo->IsGoingForDiploVictory() || GET_PLAYER(eObserver).GetPlayerTraits()->IsDiplomat()) { - // Which player is actually the vassal? - PlayerTypes eVassal = NO_PLAYER; - if(IsVassal(ePlayer)) - eVassal = GetID(); - else if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassal(GetID())) - eVassal = ePlayer; - - if(eVassal != NO_PLAYER) - { - // We are ePlayer's vassal - if(eVassal == GetID()) - { - if(IsVassalTaxLowered(ePlayer)) - { - eStatement = DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_HUMAN_MASTER; - SetVassalTaxLowered(ePlayer, false); - } - } - // ePlayer is our vassal - else if(eVassal == ePlayer) - { - if(GET_PLAYER(ePlayer).GetDiplomacyAI()->IsVassalTaxLowered(GetID())) - { - eStatement = DIPLO_STATEMENT_VASSAL_TAXES_LOWERED_AI_MASTER; - - for(int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) - { - PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop; - // Modify player view to all AI teammates - if (GET_PLAYER(eLoopPlayer).getTeam() == GetTeam()) - { - GET_PLAYER(ePlayer).GetDiplomacyAI()->SetVassalTaxLowered(eLoopPlayer, false); - } - } - } - } - - } + iChance *= 12; + iChance /= 10; } -} -/// Possible Contact Statement - Vassal has been liberated -void CvDiplomacyAI::DoLiberateMyVassalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (eStatement == NO_DIPLO_STATEMENT_TYPE) + else if (pDiplo->IsDiplomat()) { - if (GetVassalPlayerToLiberate() == ePlayer) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_LIBERATE_VASSAL; - int iTurnsBetweenStatement = 1; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) - { - eStatement = eTempStatement; - } - } + iChance *= 11; + iChance /= 10; } + + /* + Probability reference (math-only, assuming we reach the RNG roll each eligible turn). + Common early case: SessionTurnInterval=20 => eligible window = 8 turns (t=0..8). + + Each line shows: chance to fire at least once on turns 0–2, and on turns 0–8. + In practice, these probabilities will be significantly lower after the first trigger due to the 3-turn global cooldown and the chance dropping after the 3rd turn. + + Quiet (Chattiness=2, Alliance=4): + Normal: 0–2: 13.2% 0–8: 22.9% + Strong: 0–2: 25.0% 0–8: 39.4% + Overwhelming: 0–2: 39.3% 0–8: 56.6% + + Typical (Chattiness=5, Alliance=5): + Normal: 0–2: 20.3% 0–8: 33.6% + Strong: 0–2: 33.2% 0–8: 50.3% + Overwhelming: 0–2: 48.6% 0–8: 66.8% + + Diplomatic (Chattiness=8, Alliance=8, x1.2 diplomat multiplier applied): + Normal: 0–2: 38.6% 0–8: 58.0% + Strong: 0–2: 55.7% 0–8: 74.9% + Overwhelming: 0–2: 73.9% 0–8: 88.4% + + Max diplo (Chattiness=10, Alliance=10, x1.2 diplomat multiplier applied): + Normal: 0–2: 50.6% 0–8: 71.2% + Strong: 0–2: 68.1% 0–8: 85.3% + Overwhelming: 0–2: 84.8% 0–8: 94.8% + + Low chattiness / high Alliance (Chattiness=2, Alliance=10): + Normal: 0–2: 17.6% 0–8: 29.7% + Strong: 0–2: 35.4% 0–8: 53.1% + Overwhelming: 0–2: 55.5% 0–8: 73.5% + + High chattiness / low Alliance (Chattiness=10, Alliance=2): + Normal: 0–2: 28.3% 0–8: 45.0% + Strong: 0–2: 38.5% 0–8: 56.9% + Overwhelming: 0–2: 51.2% 0–8: 69.4% + + For longer session intervals (e.g., 50 => eligible window=20, 60 => 24), the full-window cumulative chance remains close to the short-window cumulative chance. + For shorter session intervals, the chances will drop, but that is intentional as these messages should appear less frequently later in the game. + */ + + return iChance; } void CvDiplomacyAI::DetermineVassalToLiberate() @@ -55110,35 +55769,6 @@ bool CvDiplomacyAI::IsWantToLiberateVassal(PlayerTypes ePlayer, int& iScoreForLi return iScoreForLiberate > /*100*/ GD_INT_GET(VASSALAGE_LIBERATE_BASE_THRESHOLD); } -/// Possible Contact Statement - Third-party offer for ePlayer to liberate their vassals -void CvDiplomacyAI::DoRevokeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - // note: we check to see if it's possible in IsMakeOfferForVassalage() - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - // Can we make an offer for vassalage? - if(GetPlayer()->GetDealAI()->IsMakeOfferForRevokeVassalage(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REVOKE_VASSALAGE_THIRD_PARTY; - int iTurnsBetweenStatement = 50; - - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) - { - // Send the statement - eStatement = eTempStatement; - } - } - else - { - pDeal->ClearItems(); - } - } -} - /// Do we want to become the vassal of ePlayer? bool CvDiplomacyAI::IsVassalageAcceptable(PlayerTypes ePlayer, bool bMasterEvaluation) { @@ -56458,160 +57088,9 @@ bool CvDiplomacyAI::IsEndVassalageRequestAcceptable(PlayerTypes ePlayer) return GC.getGame().randRangeInclusive(1, 10, CvSeeder::fromRaw(0xda4c95ed).mix(GetID()).mix(GET_PLAYER(ePlayer).GetID())) <= iChanceToGiveIn; } -/// Possible Contact Statement - We're done being ePlayer's vassal -void CvDiplomacyAI::DoEndVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - // Don't send this to an AI teammate of a human - if (GET_PLAYER(ePlayer).IsAITeammateOfHuman()) - return; - - if (MOD_DIPLOAI_SHUT_UP_INDEPENDENCE_REQUESTS && GET_PLAYER(ePlayer).isHuman(ISHUMAN_AI_DIPLOMACY)) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - // Must be vassal of ePlayer - if(IsVassal(ePlayer)) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_REVOKE_VASSALAGE; - int iTurnsBetweenStatement = 10; - - // Done being this player's vassal? - if (IsEndVassalageAcceptable(ePlayer)) - { - if(GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatement) - eStatement = eTempStatement; - } - } - } -} - -/// Possible Contact Statement - World Map -void CvDiplomacyAI::DoMapsOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - //problem: on larger maps evaluating the map value every turn for every player is significant performance overhead - //solution: we could cache the value for the active player, saving half the effort, but it's simpler to just not even contemplate the offer every turn - if ((GC.getGame().getGameTurn() + GetID()) % 5 != 0) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_MAPS_OFFER; - int iTurnsBetweenStatements = 30; - - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - if (GetPlayer()->GetDealAI()->IsMakeOfferForMaps(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } - } - } -} - -/// Possible Contact Statement - Purchase technology -void CvDiplomacyAI::DoTechOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (GetPlayer()->IsAITeammateOfHuman()) - return; - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_TECH_OFFER; - int iTurnsBetweenStatements = 30; - if (GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= iTurnsBetweenStatements) - { - if (GetPlayer()->GetDealAI()->IsMakeOfferForTech(ePlayer, /*pDeal can be modified in this function*/ pDeal)) - { - eStatement = eTempStatement; - } - else - { - pDeal->ClearItems(); - } - } - } -} - -/// Possible Contact Statement - Generous Offer -void CvDiplomacyAI::DoGenerousOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if(eStatement == NO_DIPLO_STATEMENT_TYPE) - { - DiploStatementTypes eTempStatement = DIPLO_STATEMENT_GENEROUS_OFFER; - - // Check if we have a bonus on exporting luxuries - bool bWantExport = false; - for (int iYieldLoop = 0; iYieldLoop < GC.getNUM_YIELD_TYPES(); iYieldLoop++) - { - YieldTypes eYield = static_cast(iYieldLoop); - if (GetPlayer()->GetPlayerTraits()->GetYieldFromExport(eYield) > 0) - { - bWantExport = true; - break; - } - } - - bool bOffCooldown = GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 60 && - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER_RANDFAILED) >= 15 && - // Don't send a generous offer request if we recently sent a Request to this player - GetNumTurnsSinceStatementSent(ePlayer, DIPLO_STATEMENT_REQUEST) >= 25; - - // Shorter cooldown if we want to export - if ((bWantExport && GetNumTurnsSinceStatementSent(ePlayer, eTempStatement) >= 10) || bOffCooldown) - { - bool bRandPassed = false; - bool bMakeGenerousOffer = false; - if (bWantExport && IsMakeLuxuryOffer(ePlayer, pDeal)) - bMakeGenerousOffer = true; - - if (!bMakeGenerousOffer && bOffCooldown) - bMakeGenerousOffer = IsMakeGenerousOffer(ePlayer, pDeal, bRandPassed); - - if (bMakeGenerousOffer && pDeal->GetNumItems() > 0) - { - eStatement = eTempStatement; - SetOfferingGift(ePlayer, true); - bRandPassed = true; - pDeal->m_bIsGift = true; - } - else - { - pDeal->ClearItems(); - } - - // Add this statement to the log so we don't evaluate it again until 15 turns has come back around - if (!bRandPassed) - SetTurnStatementLastSent(ePlayer, DIPLO_STATEMENT_GENEROUS_OFFER_RANDFAILED, GC.getGame().getGameTurn()); - } - } -} - /// Does this AI want to purchase a technology? bool CvDiplomacyAI::IsTechRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias) { - if (GetPlayer()->IsAITeammateOfHuman()) - return false; - iWeightBias = 0; TechTypes eTechToAskFor = NO_TECH; @@ -56656,26 +57135,6 @@ bool CvDiplomacyAI::IsTechRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeig return true; } -/// Do we want to trade world maps with eOtherPlayer? - this is only used for when to trigger an AI request, not whether or not the AI will accept a deal period -bool CvDiplomacyAI::WantsMapsFromPlayer(PlayerTypes ePlayer) -{ - PRECONDITION(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index."); - PRECONDITION(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index."); - - if (GetPlayer()->IsAITeammateOfHuman()) - return false; - - CivApproachTypes eApproach = GetSurfaceApproach(ePlayer); - if (eApproach == CIV_APPROACH_HOSTILE) - { - return false; - } - - // Physically see how much the deal will cost us. Only send request if it's in an acceptable range - int iMapValue = GetPlayer()->GetDealAI()->GetMapValue(false, ePlayer); - return iMapValue > 750; -} - /// Do we want to make a luxury offer to ePlayer? Only called if we have a bonus on exporting luxury bool CvDiplomacyAI::IsMakeLuxuryOffer(PlayerTypes ePlayer, CvDeal* pDeal) const { @@ -56895,10 +57354,8 @@ bool CvDiplomacyAI::IsTechGenerousOffer(PlayerTypes ePlayer, CvDeal* pDeal) cons { if (GetPlayer()->IsAITeammateOfHuman()) return false; - - TechTypes eTechToOffer = NO_TECH; - int iTechLoop = 0; + TechTypes eTechToOffer = NO_TECH; // See if the other player is lagging in science int iOurScience = GetPlayer()->GetScience(); @@ -56908,7 +57365,7 @@ bool CvDiplomacyAI::IsTechGenerousOffer(PlayerTypes ePlayer, CvDeal* pDeal) cons return false; // See if there's any Technologies WE can trade - for(iTechLoop = 0; iTechLoop < GC.getNumTechInfos(); iTechLoop++) + for(int iTechLoop = 0; iTechLoop < GC.getNumTechInfos(); iTechLoop++) { const TechTypes eTech = static_cast(iTechLoop); @@ -57341,13 +57798,13 @@ void CvDiplomacyAI::DoWeMadeVassalageWithSomeone(TeamTypes eMasterTeam, bool bVo SetWeDislikedTheirProposalTurn(eOtherTeamPlayer, -1); SetTheyFoiledOurProposalTurn(eOtherTeamPlayer, -1); - if (GetLikedTheirProposalValue(eOtherTeamPlayer) > 0) + if (GetProposalAgreementValue(eOtherTeamPlayer) > 0) { - SetLikedTheirProposalValue(eOtherTeamPlayer, 0); + SetProposalAgreementValue(eOtherTeamPlayer, 0); } - if (GetSupportedOurProposalValue(eOtherTeamPlayer) > 0) + if (GetProposalSupportDelta(eOtherTeamPlayer) > 0) { - SetSupportedOurProposalValue(eOtherTeamPlayer, 0); + SetProposalSupportDelta(eOtherTeamPlayer, 0); } if (GetVotingHistoryScore(eOtherTeamPlayer) < 0) { diff --git a/CvGameCoreDLL_Expansion2/CvDiplomacyAI.h b/CvGameCoreDLL_Expansion2/CvDiplomacyAI.h index 498ace6e6d..57c4e3f439 100644 --- a/CvGameCoreDLL_Expansion2/CvDiplomacyAI.h +++ b/CvGameCoreDLL_Expansion2/CvDiplomacyAI.h @@ -800,11 +800,11 @@ class CvDiplomacyAI void SetNumTimesPerformedCoupAgainstUs(PlayerTypes ePlayer, int iValue); void ChangeNumTimesPerformedCoupAgainstUs(PlayerTypes ePlayer, int iChange); - int GetLikedTheirProposalValue(PlayerTypes ePlayer) const; - void SetLikedTheirProposalValue(PlayerTypes ePlayer, int iValue); + int GetProposalAgreementValue(PlayerTypes ePlayer) const; + void SetProposalAgreementValue(PlayerTypes ePlayer, int iValue); - int GetSupportedOurProposalValue(PlayerTypes ePlayer) const; - void SetSupportedOurProposalValue(PlayerTypes ePlayer, int iValue); + int GetProposalSupportDelta(PlayerTypes ePlayer) const; + void SetProposalSupportDelta(PlayerTypes ePlayer, int iValue); bool IsSupportedOurProposalAndThenFoiledUs(PlayerTypes ePlayer) const; bool IsFoiledOurProposalAndThenSupportedUs(PlayerTypes ePlayer) const; @@ -939,6 +939,9 @@ class CvDiplomacyAI bool IsAvoidDeals() const; void SetAvoidDeals(bool bValue); + bool SkipForTeammates() const; + void SetSkipForTeammates(bool bValue); + bool IsIgnoreWarmonger() const; void SetIgnoreWarmonger(bool bValue); @@ -1185,7 +1188,6 @@ class CvDiplomacyAI bool IsGoodChoiceForDoF(PlayerTypes ePlayer); bool IsGoodChoiceForDefensivePact(PlayerTypes ePlayer); bool IsGoodChoiceForResearchAgreement(PlayerTypes ePlayer); - bool IsCanMakeResearchAgreementRightNow(PlayerTypes ePlayer); PlayerTypes GetHighestScoringDefensivePact(vector& vAcceptableChoices, vector& vPlayersToExclude); int ScoreDefensivePactChoice(PlayerTypes eChoice, bool bCoastal); @@ -1243,6 +1245,112 @@ class CvDiplomacyAI // Diplomatic Interactions // ************************************ + // Highest priority statements - these can trigger WAR! + void DoWeAttackedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoWeBulliedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoEndVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoAttackedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoBulliedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + //void DoCoopWarTimeStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoAggressiveMilitaryStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + + // After the war stuff but before any other statement, try to renew expired deals + CvDeal* DoRenewExpiredDeal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + + // Broken promises + void DoKilledCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoExpansionBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoPlotBuyingBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + + // Other things the player has done to piss off the AI + void DoDugUpMyYardStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoConvertedMyCityStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + //void DoSeriousExpansionWarningStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); + //void DoSeriousPlotBuyingWarningStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); + void DoExpansionWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoPlotBuyingWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + + // Spying + void DoKilledMySpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoKilledYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoCaughtYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + + // "Now Influential" & Ideology Statements + void DoIdeologicalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + + // AI wants to worsen diplomatic relations with a player + void DoEndDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoDenounceStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoRequestFriendDenounceStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + + // AI wants to improve diplomatic relations with a player + void DoLiberateMyVassalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoShareIntrigueStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoBecomeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoMakeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoDefensivePactOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoCoopWarStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + + // Vassal tax statements + void DoVassalTaxesRaisedStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoVassalTaxesLoweredStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + + // Very time-sensitive compliments & insults + void DoFYIBefriendedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoFYIDenouncedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoAngryDenouncedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoAngryBefriendedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoHappyDenouncedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoFYIDenouncedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoHappyBefriendedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + void DoFYIBefriendedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); + + // Time-sensitive compliments & insults + void DoTheyFoiledOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoTheySupportedOurHosting(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoTheySupportedOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoWeLikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoWeDislikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + + // Request help from them? + void DoRequest(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + + // OFFERS + // Try to get the things we want most first! + void DoRevokeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoCityExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoTechOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoVoteTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoMapsOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoThirdPartyWarTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoThirdPartyPeaceTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoStrategicTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoEmbassyExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoEmbassyOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoResearchAgreementOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoOpenBordersExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoOpenBordersOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + void DoLuxuryTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + + // Give help to them? + void DoGenerousOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + + // Non-time-sensitive compliments & insults + //void DoNowUnforgivableStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); + //void DoNowEnemyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); + void DoHostileStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoAfraidStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoWarmongerStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoHappySamePolicyTree(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoMinorCivCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1, bool bFromAdvisor = false); + void DoVictoryCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + void DoVictoryBlockStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); + //void DoFriendlyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); + + // Peace Offer + void DoPeaceOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); + // ************************************ // Counters // ************************************ @@ -1268,7 +1376,6 @@ class CvDiplomacyAI bool IsGoldRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias); bool IsEmbassyExchangeAcceptable(PlayerTypes ePlayer); - bool WantsEmbassyAtPlayer(PlayerTypes ePlayer) const; bool IsWantsOpenBordersWithPlayer(PlayerTypes ePlayer); bool IsWillingToGiveOpenBordersToPlayer(PlayerTypes ePlayer); bool IsOpenBordersExchangeAcceptable(PlayerTypes ePlayer); @@ -1331,87 +1438,10 @@ class CvDiplomacyAI void DoUpdateMinorCivProtection(PlayerTypes eMinor); - //void DoCoopWarTimeStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoCoopWarStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoMakeDemand(PlayerTypes ePlayer); - void DoAggressiveMilitaryStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoKilledCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoAttackedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoBulliedCityStateStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - //void DoSeriousExpansionWarningStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); - void DoExpansionWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoExpansionBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - //void DoSeriousPlotBuyingWarningStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); - void DoPlotBuyingWarningStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoPlotBuyingBrokenPromiseStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - - void DoWeAttackedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoWeBulliedYourMinorStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - - void DoCaughtYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoKilledYourSpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoKilledMySpyStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - - void DoShareIntrigueStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - - void DoConvertedMyCityStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoDugUpMyYardStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - - void DoDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoEndDoFStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement) const; - void DoDenounceFriendStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoDenounceStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoRequestFriendDenounceStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - - void DoLuxuryTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoEmbassyExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoEmbassyOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoOpenBordersExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoOpenBordersOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoResearchAgreementOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoStrategicTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoDefensivePactOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoCityExchange(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoThirdPartyWarTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoThirdPartyPeaceTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoVoteTrade(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - CvDeal* DoRenewExpiredDeal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - - void DoRequest(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); void DoGift(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - //void DoNowUnforgivableStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); - //void DoNowEnemyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); - - void DoHostileStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - //void DoFriendlyStatement(PlayerTypes ePlayer, DiploStatementTypes &eStatement); - void DoAfraidStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoWarmongerStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoMinorCivCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1, bool bIgnoreTurnsBetweenLimit = false); - - void DoAngryBefriendedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoAngryDenouncedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoHappyDenouncedEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoHappyBefriendedFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoFYIBefriendedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoFYIDenouncedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoFYIDenouncedHumanEnemy(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoFYIBefriendedHumanFriend(PlayerTypes ePlayer, DiploStatementTypes& eStatement, int& iData1); - void DoHappySamePolicyTree(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoIdeologicalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoVictoryCompetitionStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoVictoryBlockStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - - void DoWeLikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoWeDislikedTheirProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoTheySupportedOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoTheyFoiledOurProposal(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoTheySupportedOurHosting(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - - void DoPeaceOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - ///////////////////////////////////////////////////////// // Diplo stuff relating to UI ///////////////////////////////////////////////////////// @@ -1516,16 +1546,9 @@ class CvDiplomacyAI PlayerTypes GetRequestFriendToDenounce(PlayerTypes ePlayer, bool& bRandFailed); bool IsFriendDenounceRefusalUnacceptable(PlayerTypes ePlayer, PlayerTypes eAgainstPlayer); - // Contact Statements - void DoMapsOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoTechOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoGenerousOffer(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - // Requests bool IsTechRequest(PlayerTypes ePlayer, CvDeal* pDeal, int& iWeightBias); - bool WantsMapsFromPlayer(PlayerTypes ePlayer); - // Offers bool IsMakeLuxuryOffer(PlayerTypes ePlayer, CvDeal* pDeal) const; bool IsMakeGenerousOffer(PlayerTypes ePlayer, CvDeal* pDeal, bool& bRandPassed) const; @@ -1556,16 +1579,6 @@ class CvDiplomacyAI bool IsEndVassalageWithPlayerAcceptable(PlayerTypes ePlayer); // vassal only, evaluates one master bool IsEndVassalageRequestAcceptable(PlayerTypes ePlayer); // master only, evaluates one vassal - void DoBecomeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoMakeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - void DoEndVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoRevokeVassalageStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement, CvDeal* pDeal); - - void DoLiberateMyVassalStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - - void DoVassalTaxesRaisedStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - void DoVassalTaxesLoweredStatement(PlayerTypes ePlayer, DiploStatementTypes& eStatement); - bool IsHappyAboutPlayerVassalagePeacefullyRevoked(PlayerTypes ePlayer); bool IsAngryAboutPlayerVassalageForcefullyRevoked(PlayerTypes ePlayer); @@ -1886,6 +1899,7 @@ class CvDiplomacyAI // Other Global Memory bool m_bAvoidDeals; // Not serialized! + bool m_bSkipForTeammates; // Not serialized! bool m_bIgnoreWarmonger; // Not serialized! PlayerTypes m_eVassalPlayerToLiberate; // Not serialized! bool m_bWasHumanLastUpdate; @@ -2076,8 +2090,8 @@ class CvDiplomacyAI unsigned short m_aiNegativeReligiousConversionPoints[MAX_MAJOR_CIVS]; unsigned char m_aiNumTimesRobbedBy[MAX_MAJOR_CIVS]; unsigned char m_aiPerformedCoupAgainstUs[MAX_MAJOR_CIVS]; - char m_aiLikedTheirProposalValue[MAX_MAJOR_CIVS]; - char m_aiSupportedOurProposalValue[MAX_MAJOR_CIVS]; + char m_aiProposalAgreementValue[MAX_MAJOR_CIVS]; + char m_aiProposalSupportDelta[MAX_MAJOR_CIVS]; short m_aiVotingHistoryScore[MAX_MAJOR_CIVS]; char m_aiSupportedOurHostingValue[MAX_MAJOR_CIVS]; unsigned char m_aiNegativeArchaeologyPoints[MAX_MAJOR_CIVS]; @@ -2168,7 +2182,8 @@ namespace CvDiplomacyAIHelpers int GetCityLiberationValue(CvCity* pCity, PlayerTypes eLiberator, PlayerTypes eNewOwner, PlayerTypes eObserver); bool BackstabbedPlayer(PlayerTypes eBackstabber, PlayerTypes eVictim, bool bIncludeDoFEnd); bool IgnoresBackstabbing(PlayerTypes eObserver, PlayerTypes eVictim, bool bCheckCurrent = false); - bool ProposedSanctionsBlockingDiplomacy(PlayerTypes ePlayer, PlayerTypes eOtherPlayer); + bool ProposedSanctionsBlockingDiplomacy(PlayerTypes ePlayer, PlayerTypes eOtherPlayer, bool bStrict = false); + int GetProposalAgreementTalkativeness(PlayerTypes eObserver, PlayerTypes eProposer, bool bGood, int iTurnsSinceTrigger, int iEligibleWindow); } #endif //CIV5_AI_DIPLOMACY_H \ No newline at end of file diff --git a/CvGameCoreDLL_Expansion2/CvDllDatabaseUtility.cpp b/CvGameCoreDLL_Expansion2/CvDllDatabaseUtility.cpp index f200279cf1..a3f57acdb6 100644 --- a/CvGameCoreDLL_Expansion2/CvDllDatabaseUtility.cpp +++ b/CvGameCoreDLL_Expansion2/CvDllDatabaseUtility.cpp @@ -175,6 +175,11 @@ bool CvDllDatabaseUtility::CacheGameDatabaseData() //Clear out database cache and tune for runtime use. DB.ClearCountCache(); + // Refresh query planner statistics for indexes added by the mod. + // The engine runs ANALYZE before mods are loaded, so post-mod + // indexes (60+ from AddTableIndexes.sql) lack statistics. + DB.Analyze(); + //Log Database Memory statistics LogMsg(DB.CalculateMemoryStats()); @@ -224,17 +229,15 @@ bool CvDllDatabaseUtility::PerformDatabasePostProcessing() const char* szTableName = kPostDefines.GetText("Table"); char szSQL[512]; - sprintf_s(szSQL, "select ROWID from %s where Type = '%s' LIMIT 1", szTableName, szKeyName); + // Table name must be concatenated (SQLite doesn't support parameterized identifiers) + sprintf_s(szSQL, "select ROWID from %s where Type = ? LIMIT 1", szTableName); Database::Results kLookup; - - //Compile the command. if(db->Execute(kLookup, szSQL)) { - //Run the command. + kLookup.Bind(1, szKeyName); if(kLookup.Step()) { - //Perform insertion kInsert.Bind(1, szName); kInsert.Bind(2, kLookup.GetInt(0)); kInsert.Step(); @@ -486,19 +489,27 @@ void CvDllDatabaseUtility::DatabaseRemapper() } vTableIDs.push_back(kQuery.GetInt(0)); } + // Prepare UPDATE once per table, bind+execute for each row + char szUpdate[512]; + sprintf_s(szUpdate, "UPDATE %s SET ID = ? WHERE ID = ?", szTableName); + Database::Results kUpdate; bool bFirst = true; - for (uint ui = 0; ui < vTableIDs.size(); ui++) + if (DB.Execute(kUpdate, szUpdate)) { - if (vTableIDs[ui] != static_cast(ui)) + for (uint ui = 0; ui < vTableIDs.size(); ui++) { - if (bFirst) + if (vTableIDs[ui] != static_cast(ui)) { - LogMsg("Table %s: Remapping %u incorrect IDs, starting with %d -> %u. ", szTableName, vTableIDs.size() - ui, vTableIDs[ui], ui); - bFirst = false; + if (bFirst) + { + LogMsg("Table %s: Remapping %u incorrect IDs, starting with %d -> %u. ", szTableName, vTableIDs.size() - ui, vTableIDs[ui], ui); + bFirst = false; + } + kUpdate.Bind(1, (int)ui); + kUpdate.Bind(2, vTableIDs[ui]); + kUpdate.Execute(); + kUpdate.Reset(); } - char szUpdate[512]; - sprintf_s(szUpdate, "UPDATE %s SET ID = %d WHERE ID = %d;", szTableName, (int)ui, vTableIDs[ui]); - DB.Execute(szUpdate); } } } diff --git a/CvGameCoreDLL_Expansion2/CvFlavorManager.cpp b/CvGameCoreDLL_Expansion2/CvFlavorManager.cpp index e566c4b660..c09460392d 100644 --- a/CvGameCoreDLL_Expansion2/CvFlavorManager.cpp +++ b/CvGameCoreDLL_Expansion2/CvFlavorManager.cpp @@ -347,8 +347,8 @@ void CvFlavorManager::AdjustWeightsForMap() float fAdjust = log10(fTilesPerPlayer) - /*2.1f*/ GD_FLOAT_GET(FLAVOR_STANDARD_LOG10_TILES_PER_PLAYER); int iAdjust = (int)(fAdjust * /*8*/ GD_INT_GET(FLAVOR_EXPANDGROW_COEFFICIENT)); - int iMax = range(/*10*/ GD_INT_GET(PERSONALITY_FLAVOR_MAX_VALUE), 1, 100); - int iMin = range(/*0*/ GD_INT_GET(PERSONALITY_FLAVOR_MIN_VALUE), 1, iMax); + int iMax = /*10*/ range(GD_INT_GET(PERSONALITY_FLAVOR_MAX_VALUE), 1, 100); + int iMin = /*1*/ range(GD_INT_GET(PERSONALITY_FLAVOR_MIN_VALUE), 1, iMax); int iExpansionIndex = GC.getInfoTypeForString("FLAVOR_EXPANSION"); int iGrowthIndex = GC.getInfoTypeForString("FLAVOR_GROWTH"); @@ -387,7 +387,7 @@ CvEnumMap& CvFlavorManager::GetAllPersonalityFlavors() /// Retrieve the value of one Personality flavor, modified for the diplomacy AI int CvFlavorManager::GetPersonalityFlavorForDiplomacy(FlavorTypes eType) { - int iMax = range(GD_INT_GET(PERSONALITY_FLAVOR_MAX_VALUE), 1, 100); + int iMax = /*10*/ range(GD_INT_GET(PERSONALITY_FLAVOR_MAX_VALUE), 1, 100); int iRawValue = GetPersonalityIndividualFlavor(eType); // If the flavor was zeroed out, always return the minimum value @@ -412,9 +412,9 @@ int CvFlavorManager::GetPersonalityFlavorForDiplomacy(FlavorTypes eType) /// Make a random adjustment to each flavor value for this leader so they don't play exactly the same void CvFlavorManager::RandomizeWeights() { - int iMax = range(/*10*/ GD_INT_GET(PERSONALITY_FLAVOR_MAX_VALUE), 1, 100); - int iMin = range(/*0*/ GD_INT_GET(PERSONALITY_FLAVOR_MIN_VALUE), 1, iMax); - int iPlusMinus = max(/*2*/ GD_INT_GET(FLAVOR_RANDOMIZATION_RANGE), 0); + int iMax = /*10*/ range(GD_INT_GET(PERSONALITY_FLAVOR_MAX_VALUE), 1, 100); + int iMin = /*1*/ range(GD_INT_GET(PERSONALITY_FLAVOR_MIN_VALUE), 1, iMax); + int iPlusMinus = /*2*/ max(GD_INT_GET(FLAVOR_RANDOMIZATION_RANGE), 0); for (int iI = 0; iI < GC.getNumFlavorTypes(); iI++) { diff --git a/CvGameCoreDLL_Expansion2/CvGame.cpp b/CvGameCoreDLL_Expansion2/CvGame.cpp index 5b92c0a600..84598b47b9 100644 --- a/CvGameCoreDLL_Expansion2/CvGame.cpp +++ b/CvGameCoreDLL_Expansion2/CvGame.cpp @@ -8656,6 +8656,11 @@ UnitTypes CvGame::GetRandomUniqueUnitType(bool bIncludeCivsInGame, bool bInclude continue; } + // No units that have a cap on instances (player/team/global) + // gifting these could push a player over their limit + if (isLimitedUnitClass(eLoopUnitClass)) + continue; + // We only want unique units that are not in the game already, or are explicitly Minor Civ Gifts if (!pkUnitInfo->IsMinorCivGift() ) { diff --git a/CvGameCoreDLL_Expansion2/CvGlobals.cpp b/CvGameCoreDLL_Expansion2/CvGlobals.cpp index 5bb20416fc..75da26150b 100644 --- a/CvGameCoreDLL_Expansion2/CvGlobals.cpp +++ b/CvGameCoreDLL_Expansion2/CvGlobals.cpp @@ -1782,6 +1782,8 @@ CvGlobals::CvGlobals() : GD_INT_INIT(BARBARIAN_TECH_PERCENT, 75), GD_INT_INIT(CITY_RESOURCE_WLTKD_TURNS, 20), GD_INT_INIT(WLTKD_RESOURCE_RESET_TURNS, 0), + GD_INT_INIT(RESEARCH_AGREEMENT_PER_TURN_YIELD_PERCENT, 0), + GD_INT_INIT(RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT, 0), GD_INT_INIT(MAX_SPECIALISTS_FROM_BUILDING, 4), GD_INT_INIT(GREAT_PERSON_THRESHOLD_BASE, 100), GD_INT_INIT(GREAT_PERSON_THRESHOLD_INCREASE, 100), @@ -2456,8 +2458,6 @@ typedef BOOL (WINAPI *PFN_SymInitialize)( PCSTR UserSearchPath, BOOL fInvadeProcess); -typedef DWORD (WINAPI *PFN_ImagehlpApiVersion)(void); - // Define newer minidump flags if not present in older SDK headers #ifndef MiniDumpIgnoreInaccessibleMemory #define MiniDumpIgnoreInaccessibleMemory ((MINIDUMP_TYPE)0x00020000) @@ -2470,7 +2470,7 @@ typedef DWORD (WINAPI *PFN_ImagehlpApiVersion)(void); static HMODULE g_hDbgHelp = NULL; static PFN_MiniDumpWriteDump g_pfnMiniDumpWriteDump = NULL; static PFN_SymInitialize g_pfnSymInitialize = NULL; -static DWORD g_dwDbgHelpVersion = 0; +static char g_szDbgHelpPath[MAX_PATH] = {0}; // Store the last minidump path for display in crash dialogs static char g_szLastMiniDumpPath[MAX_PATH] = {0}; @@ -2553,17 +2553,12 @@ static bool LoadBestDbgHelp() return false; } - // Get version information - PFN_ImagehlpApiVersion pfnVersion = (PFN_ImagehlpApiVersion)GetProcAddress(g_hDbgHelp, "ImagehlpApiVersion"); - if (pfnVersion) + GetModuleFileNameA(g_hDbgHelp, g_szDbgHelpPath, MAX_PATH); { - g_dwDbgHelpVersion = pfnVersion(); - TCHAR szVersion[128]; - _stprintf_s(szVersion, sizeof(szVersion) / sizeof(TCHAR), - _T("dbghelp.dll version: %d.%d.%d.%d\n"), - HIWORD(g_dwDbgHelpVersion), LOWORD(g_dwDbgHelpVersion), - 0, 0); - OutputDebugString(szVersion); + TCHAR szMsg[MAX_PATH + 32]; + _stprintf_s(szMsg, sizeof(szMsg) / sizeof(TCHAR), + _T("dbghelp.dll: %hs\n"), g_szDbgHelpPath); + OutputDebugString(szMsg); } return true; @@ -2732,13 +2727,13 @@ void CreateMiniDump(EXCEPTION_POINTERS* pep) #endif "Architecture: Win32 (x86)\n" "OS: Windows %d.%d (Build %d) SP%d.%d\n" - "dbghelp.dll: %d.%d", + "dbghelp.dll: %s", CURRENT_GAMECORE_VERSION, MOD_DLL_NAME, MOD_DLL_VERSION_NUMBER, MOD_DLL_VERSION_STATUS, __DATE__, __TIME__, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber, osvi.wServicePackMajor, osvi.wServicePackMinor, - HIWORD(g_dwDbgHelpVersion), LOWORD(g_dwDbgHelpVersion)); + g_szDbgHelpPath[0] ? g_szDbgHelpPath : "(not loaded)"); user_streams[0].Type = 10; // CommentStreamA user_streams[0].Buffer = version_info; @@ -7027,6 +7022,8 @@ void CvGlobals::cacheGlobals() GD_INT_CACHE(BARBARIAN_TECH_PERCENT); GD_INT_CACHE(CITY_RESOURCE_WLTKD_TURNS); GD_INT_CACHE(WLTKD_RESOURCE_RESET_TURNS); + GD_INT_CACHE(RESEARCH_AGREEMENT_PER_TURN_YIELD_PERCENT); + GD_INT_CACHE(RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT); GD_INT_CACHE(MAX_SPECIALISTS_FROM_BUILDING); GD_INT_CACHE(GREAT_PERSON_THRESHOLD_BASE); GD_INT_CACHE(GREAT_PERSON_THRESHOLD_INCREASE); diff --git a/CvGameCoreDLL_Expansion2/CvGlobals.h b/CvGameCoreDLL_Expansion2/CvGlobals.h index 00bd604479..7e1929a927 100644 --- a/CvGameCoreDLL_Expansion2/CvGlobals.h +++ b/CvGameCoreDLL_Expansion2/CvGlobals.h @@ -2348,6 +2348,8 @@ class CvGlobals GD_INT_MEMBER(BARBARIAN_TECH_PERCENT); GD_INT_MEMBER(CITY_RESOURCE_WLTKD_TURNS); GD_INT_MEMBER(WLTKD_RESOURCE_RESET_TURNS); // VP + GD_INT_MEMBER(RESEARCH_AGREEMENT_PER_TURN_YIELD_PERCENT); // VP + GD_INT_MEMBER(RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT); // VP GD_INT_MEMBER(MAX_SPECIALISTS_FROM_BUILDING); GD_INT_MEMBER(GREAT_PERSON_THRESHOLD_BASE); GD_INT_MEMBER(GREAT_PERSON_THRESHOLD_INCREASE); diff --git a/CvGameCoreDLL_Expansion2/CvGrandStrategyAI.cpp b/CvGameCoreDLL_Expansion2/CvGrandStrategyAI.cpp index 0fd4bbdfdf..55a9442dc6 100644 --- a/CvGameCoreDLL_Expansion2/CvGrandStrategyAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvGrandStrategyAI.cpp @@ -743,9 +743,13 @@ int CvGrandStrategyAI::GetConquestPriority() { iPriorityBonus += pEntry->GetCombatModifierEnemyCities(); } - if (pEntry->GetCombatVersusOtherReligionTheirLands() > 0) + if (pEntry->GetCombatBonusTheirLands() > 0) { - iPriorityBonus += pEntry->GetCombatVersusOtherReligionTheirLands(); + iPriorityBonus += pEntry->GetCombatBonusTheirLands(); + } + if (pEntry->GetCombatBonusVersusOtherReligionTheirLands() > 0) + { + iPriorityBonus += pEntry->GetCombatBonusVersusOtherReligionTheirLands(); } if (pEntry->GetFaithFromKills() > 0) { diff --git a/CvGameCoreDLL_Expansion2/CvHomelandAI.cpp b/CvGameCoreDLL_Expansion2/CvHomelandAI.cpp index 9f3468626e..dd254d57f3 100644 --- a/CvGameCoreDLL_Expansion2/CvHomelandAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvHomelandAI.cpp @@ -340,7 +340,7 @@ CvPlot* CvHomelandAI::GetBestExploreTarget(const CvUnit* pUnit, int iMaxTurns) c iHealRate -= pUnit->GetDanger(pEvalPlot); if (iHealRate >= 7 || (iHealRate > 0 && iDamage > 30)) { - int iScore = iHealRate * 20; + int iScore = iHealRate * 40; vReachablePlotByScore.push_back(SPlotWithScore(pEvalPlot, -iScore)); } } @@ -1894,28 +1894,29 @@ void CvHomelandAI::PlotUpgradeMoves() for(CHomelandUnitArray::iterator moveUnitIt = m_CurrentMoveUnits.begin(); moveUnitIt != m_CurrentMoveUnits.end(); ++moveUnitIt) { CvUnit* pUnit = m_pPlayer->getUnit(moveUnitIt->GetID()); + bool bUnitUpgraded = false; if(pUnit->CanUpgradeRightNow(false) && pUnit->GetDanger()GetCurrHitPoints()) { - //if the unit is currently in an army we need some extra bookkeeping - CvArmyAI* pArmy = m_pPlayer->getArmyAI(pUnit->getArmyID()); - int iArmySlot = -1; - if (pArmy) - iArmySlot = pArmy->RemoveUnit(pUnit->GetID(), true); - // Resource requirement UnitTypes eUnit = pUnit->getUnitType(); UnitTypes eUpgradeUnit = pUnit->GetUpgradeUnitType(); if (!m_pPlayer->HasResourceForNewUnit(eUpgradeUnit, false, true, eUnit)) continue; - //avoid a warning, reset the last move - pUnit->setHomelandMove(AI_HOMELAND_MOVE_NONE); - // Don't upgrade if we will go over supply if (bUnderSupplyLimit || !pUnit->isNoSupply()) { + //if the unit is currently in an army we need some extra bookkeeping + CvArmyAI* pArmy = m_pPlayer->getArmyAI(pUnit->getArmyID()); + int iArmySlot = -1; + if (pArmy) + iArmySlot = pArmy->RemoveUnit(pUnit->GetID(), true); + + //avoid a warning, reset the last move + pUnit->setHomelandMove(AI_HOMELAND_MOVE_NONE); //this removes the unit from the army (if any) CvUnit* pNewUnit = pUnit->DoUpgrade(); + bUnitUpgraded = true; //if it worked the old unit is now a zombie ... UnitProcessed(pUnit->GetID()); @@ -1924,7 +1925,7 @@ void CvHomelandAI::PlotUpgradeMoves() { //restore the army if (pArmy) - pArmy->AddUnit(pNewUnit->GetID(), iArmySlot, true); + pArmy->AddUnit(pNewUnit->GetID(), iArmySlot, pArmy->GetSlotInfo(iArmySlot).m_requiredSlot); UnitProcessed(pNewUnit->GetID()); @@ -1941,7 +1942,7 @@ void CvHomelandAI::PlotUpgradeMoves() } } } - else if (pFirstNonUpgradedUnit == NULL) + if (!bUnitUpgraded && pFirstNonUpgradedUnit == NULL) pFirstNonUpgradedUnit = pUnit; } @@ -4013,7 +4014,7 @@ void CvHomelandAI::ExecuteWorkerMoves() for (list::iterator it = allWorkers.begin(); it != allWorkers.end(); ++it) { CvUnit* pUnit = m_pPlayer->getUnit(*it); - if (!pUnit || pUnit->TurnProcessed() || pUnit->getUnitInfo().GetCombat() > 0 || (!pUnit->IsAutomated() && m_pPlayer->isHuman(ISHUMAN_AI_UNITS))) + if (!pUnit || pUnit->TurnProcessed() || pUnit->isDelayedDeath() || pUnit->IsDead() || pUnit->getUnitInfo().GetCombat() > 0 || (!pUnit->IsAutomated() && m_pPlayer->isHuman(ISHUMAN_AI_UNITS))) continue; //find the city which is most in need of workers diff --git a/CvGameCoreDLL_Expansion2/CvInfos.cpp b/CvGameCoreDLL_Expansion2/CvInfos.cpp index 8767f10014..85d54648bc 100644 --- a/CvGameCoreDLL_Expansion2/CvInfos.cpp +++ b/CvGameCoreDLL_Expansion2/CvInfos.cpp @@ -4530,11 +4530,9 @@ bool CvHandicapInfo::CacheResults(Database::Results& kResults, CvDatabaseUtility kUtility.InitializeArray(m_piGoodies, m_iNumGoodies, 0); Database::Results kArrayResults; - char szSQL[512]; - sprintf_s(szSQL, "select GoodyHuts.ID from HandicapInfo_Goodies inner join GoodyHuts on GoodyType = GoodyHuts.Type where HandicapType = '%s';", szHandicapType); - - if (DB.Execute(kArrayResults, szSQL)) + if (DB.Execute(kArrayResults, "select GoodyHuts.ID from HandicapInfo_Goodies inner join GoodyHuts on GoodyType = GoodyHuts.Type where HandicapType = ?")) { + kArrayResults.Bind(1, szHandicapType); int i = 0; while (kArrayResults.Step()) { @@ -4930,21 +4928,22 @@ bool CvGameSpeedInfo::CacheResults(Database::Results& kResults, CvDatabaseUtilit const char* szGameSpeedInfoType = GetType(); //Calculate number of turn increments - char szCountSQL[256]; - sprintf_s(szCountSQL, "select count(*) from GameSpeed_Turns where GameSpeedType = '%s'", szGameSpeedInfoType); - Database::SingleResult kCount; - if(DB.Execute(kCount, szCountSQL)) + Database::Results kCount; + if(DB.Execute(kCount, "select count(*) from GameSpeed_Turns where GameSpeedType = ?")) { - m_iNumTurnIncrements = kCount.GetInt(0); + kCount.Bind(1, szGameSpeedInfoType); + if(kCount.Step()) + { + m_iNumTurnIncrements = kCount.GetInt(0); + } } //Update turn increments allocateGameTurnInfos(getNumTurnIncrements()); - char szSQL[256]; - sprintf_s(szSQL, "select * from GameSpeed_Turns where GameSpeedType = '%s'", szGameSpeedInfoType); Database::Results kArrayResults; - if(DB.Execute(kArrayResults, szSQL)) + if(DB.Execute(kArrayResults, "select * from GameSpeed_Turns where GameSpeedType = ?")) { + kArrayResults.Bind(1, szGameSpeedInfoType); int i = 0; while(kArrayResults.Step()) { @@ -5372,13 +5371,10 @@ bool CvBuildInfo::CacheResults(Database::Results& kResults, CvDatabaseUtility& k kUtility.InitializeArray(m_paiFeatureObsoleteTech, "Features"); kUtility.InitializeArray(m_pabFeatureRemoveOnly, "Features"); - char szQuery[512]; - const char* szFeatureQuery = "select * from BuildFeatures where BuildType = '%s'"; - sprintf_s(szQuery, 512, szFeatureQuery, GetType()); - Database::Results kArrayResults; - if(DB.Execute(kArrayResults, szQuery)) + if(DB.Execute(kArrayResults, "select * from BuildFeatures where BuildType = ?")) { + kArrayResults.Bind(1, GetType()); while(kArrayResults.Step()) { const char* szFeatureType = kArrayResults.GetText("FeatureType"); @@ -6572,11 +6568,9 @@ bool CvResourceInfo::CacheResults(Database::Results& kResults, CvDatabaseUtility m_piResourceQuantityTypes[0] = 1; Database::Results kArrayResults; - char szQuery[512]; - sprintf_s(szQuery, "select Quantity from Resource_QuantityTypes where ResourceType = '%s';", szResourceType); - - if(DB.Execute(kArrayResults, szQuery)) + if(DB.Execute(kArrayResults, "select Quantity from Resource_QuantityTypes where ResourceType = ?")) { + kArrayResults.Bind(1, szResourceType); int i = 0; while(kArrayResults.Step()) { @@ -8454,6 +8448,8 @@ CvProcessInfo::CvProcessInfo() : m_iTechPrereq(NO_TECH), m_iRequiredPolicy(NO_POLICY), m_iDefenseValue(0), + m_iDefenseValuePerTurn(0), + m_iDefenseValueCap(0), m_eRequiredCivilization(NO_CIVILIZATION), m_paiProductionToYieldModifier(NULL), m_paiFlavorValue(NULL) @@ -8482,6 +8478,16 @@ int CvProcessInfo::getDefenseValue() const { return m_iDefenseValue; } +//------------------------------------------------------------------------------ +int CvProcessInfo::getDefenseValuePerTurn() const +{ + return m_iDefenseValuePerTurn; +} +//------------------------------------------------------------------------------ +int CvProcessInfo::getDefenseValueCap() const +{ + return m_iDefenseValueCap; +} //------------------------------------------------------------------------------ CivilizationTypes CvProcessInfo::GetRequiredCivilization() const @@ -8519,6 +8525,8 @@ bool CvProcessInfo::CacheResults(Database::Results& kResults, CvDatabaseUtility& m_iRequiredPolicy = GC.getInfoTypeForString(szRequiredPolicy, true); m_iDefenseValue = kResults.GetInt("DefenseValue"); + m_iDefenseValuePerTurn = kResults.GetInt("DefenseValuePerTurn"); + m_iDefenseValueCap = kResults.GetInt("DefenseValueCap"); const char* szCivilizationType = kResults.GetText("CivilizationType"); m_eRequiredCivilization = (CivilizationTypes)GC.getInfoTypeForString(szCivilizationType, true); diff --git a/CvGameCoreDLL_Expansion2/CvInfos.h b/CvGameCoreDLL_Expansion2/CvInfos.h index 2628b1dfc8..6c3eaa923f 100644 --- a/CvGameCoreDLL_Expansion2/CvInfos.h +++ b/CvGameCoreDLL_Expansion2/CvInfos.h @@ -2535,6 +2535,8 @@ class CvProcessInfo : public CvBaseInfo int getTechPrereq() const; int getRequiredPolicy() const; int getDefenseValue() const; + int getDefenseValuePerTurn() const; + int getDefenseValueCap() const; CivilizationTypes GetRequiredCivilization() const; @@ -2548,6 +2550,8 @@ class CvProcessInfo : public CvBaseInfo int m_iTechPrereq; int m_iRequiredPolicy; int m_iDefenseValue; + int m_iDefenseValuePerTurn; + int m_iDefenseValueCap; CivilizationTypes m_eRequiredCivilization; diff --git a/CvGameCoreDLL_Expansion2/CvMinorCivAI.cpp b/CvGameCoreDLL_Expansion2/CvMinorCivAI.cpp index daf78411c8..f9a614d4ec 100644 --- a/CvGameCoreDLL_Expansion2/CvMinorCivAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvMinorCivAI.cpp @@ -14874,7 +14874,7 @@ int CvMinorCivAI::GetSpawnBaseTurns(PlayerTypes ePlayer, bool bCityStateAnnexed) return 0; // Not friends - // Rome Exception: Annexed Militaristic City-States continue to give units at an allied level + // Trait Exception: Annexed Militaristic City-States continue to give units at an allied level if (!bCityStateAnnexed && !IsFriends(ePlayer)) return 0; @@ -15255,7 +15255,7 @@ void CvMinorCivAI::DoBuyout(PlayerTypes eMajor) } } -/// Transfers all of this City-State's units and cities to eMajor. Called by an Austrian buyout (see above) as well as by the Merchant of Venice's buyout mission and the Rome forced annex. +/// Transfers all of this City-State's units and cities to eMajor. Called by an Austrian buyout (see above) as well as by the Merchant of Venice's buyout mission and trait ability to force annex. int CvMinorCivAI::TransferUnitsAndCitiesToMajor(PlayerTypes eMajor, bool bForced) { // Transfer all units diff --git a/CvGameCoreDLL_Expansion2/CvPlayer.cpp b/CvGameCoreDLL_Expansion2/CvPlayer.cpp index 75fdea46df..e7aeb6dc00 100644 --- a/CvGameCoreDLL_Expansion2/CvPlayer.cpp +++ b/CvGameCoreDLL_Expansion2/CvPlayer.cpp @@ -370,6 +370,7 @@ CvPlayer::CvPlayer() : , m_aiCapitalYieldRateModifier() , m_aiExtraYieldThreshold() , m_aiSpecialistExtraYield() + , m_aiLastTurnYieldsTimes100() , m_aiLastCityCaptureTurn() , m_aiWarValueLost() , m_aiWarDamageValue() @@ -630,6 +631,7 @@ CvPlayer::CvPlayer() : , m_paiBuildingChainSteps() , m_paiJFDPoliticPercent() , m_paiResourceFromCSAlliances() + , m_paiFreeResourceFromPolicies() , m_paiResourceShortageValue() , m_aiGlobalTourismAlreadyReceived() , m_aiYieldFromMinors() @@ -1843,6 +1845,9 @@ void CvPlayer::reset(PlayerTypes eID, bool bConstructorCall) m_aiSpecialistExtraYield.clear(); m_aiSpecialistExtraYield.resize(NUM_YIELD_TYPES, 0); + m_aiLastTurnYieldsTimes100.clear(); + m_aiLastTurnYieldsTimes100.resize(NUM_YIELD_TYPES, 0); + m_aiLastCityCaptureTurn.clear(); m_aiLastCityCaptureTurn.resize(MAX_PLAYERS, -1); @@ -1990,6 +1995,9 @@ void CvPlayer::reset(PlayerTypes eID, bool bConstructorCall) m_paiResourceFromCSAlliances.clear(); m_paiResourceFromCSAlliances.resize(GC.getNumResourceInfos(), 0); + m_paiFreeResourceFromPolicies.clear(); + m_paiFreeResourceFromPolicies.resize(GC.getNumResourceInfos(), 0); + m_paiResourceShortageValue.clear(); m_paiResourceShortageValue.resize(GC.getNumResourceInfos(), 0); @@ -4238,7 +4246,7 @@ CvCity* CvPlayer::acquireCity(CvCity* pCity, bool bConquest, bool bGift, bool bO GET_PLAYER(eOldOwner).DoUpdateProximityToPlayers(); DoUpdateProximityToPlayers(); - // Rome UA + // Annexed CS if (bOriginalMinorCapital) { if (isMajorCiv()) @@ -5024,7 +5032,7 @@ void CvPlayer::UpdateBestMilitaryCities() if(isMinorCiv() || isBarbarian()) return; - //First let's test domain, then we'll test combat class. + //First let's test combat class, then we'll test domain. CvCity* pLoopCity = NULL; int iLoop = 0; @@ -5041,6 +5049,17 @@ void CvPlayer::UpdateBestMilitaryCities() { //Production is king, and also our base value. int iCombatClassValue = (pLoopCity->getRawProductionPerTurnTimes100() / 500); + if(pLoopCity->getUnitCombatProductionModifier(eUnitCombatClass) > 0) + { + iCombatClassValue += max(1, pLoopCity->getUnitCombatProductionModifier(eUnitCombatClass)); + } + // Production bonuses from the City's Religion + const CvReligion* pReligion = pLoopCity->GetCityReligions()->GetMajorityReligion(); + if (pReligion) + { + // iCombatClassValue += max(1, pReligion->m_Beliefs.GetUnitProductionModifier()); // if this could be guarded somehow... right now would apply to workers, diplos, etc. + iCombatClassValue += max(1, pReligion->m_Beliefs.GetUnitCombatProductionModifiers(eUnitCombatClass, GetID(), pLoopCity)); + } //Also get our XP boosts local to this city. iCombatClassValue += pLoopCity->getFreeExperience(); @@ -5050,10 +5069,6 @@ void CvPlayer::UpdateBestMilitaryCities() { iCombatClassValue += max(1, pLoopCity->getUnitCombatFreeExperience(eUnitCombatClass)); } - if(pLoopCity->getUnitCombatProductionModifier(eUnitCombatClass) > 0) - { - iCombatClassValue += max(1, pLoopCity->getUnitCombatProductionModifier(eUnitCombatClass)); - } //Promotion Bonus vector freePromotions = pLoopCity->getFreePromotions(); @@ -8745,13 +8760,13 @@ void CvPlayer::DoLiberatePlayer(PlayerTypes ePlayer, int iOldCityID, bool bForce pDiploAI->SetWeDislikedTheirProposalTurn(*it, -1); pDiploAI->SetTheyFoiledOurProposalTurn(*it, -1); - if (pDiploAI->GetLikedTheirProposalValue(*it) > 0) + if (pDiploAI->GetProposalAgreementValue(*it) > 0) { - pDiploAI->SetLikedTheirProposalValue(*it, 0); + pDiploAI->SetProposalAgreementValue(*it, 0); } - if (pDiploAI->GetSupportedOurProposalValue(*it) > 0) + if (pDiploAI->GetProposalSupportDelta(*it) > 0) { - pDiploAI->SetSupportedOurProposalValue(*it, 0); + pDiploAI->SetProposalSupportDelta(*it, 0); } if (pDiploAI->GetVotingHistoryScore(*it) < 0) { @@ -10056,7 +10071,7 @@ void CvPlayer::doTurn() if(GetTurnsSinceSettledLastCity() >= 0) ChangeTurnsSinceSettledLastCity(1); - // update timers for unit gifts from annexed city states (Rome UA) and spawn units + // update timers for unit gifts from annexed city states and spawn units if (GetPlayerTraits()->IsAnnexedCityStatesGiveYields()) { updateTimerAnnexedMilitaryCityStates(); @@ -19918,6 +19933,9 @@ void CvPlayer::DoUpdateTotalHappiness() // Increase from Natural Wonders m_iHappiness += GetHappinessFromNaturalWonders(); + // Increase from constructed Improvements + m_iHappiness += GetHappinessFromImprovements(); + // Friendship with Minors can provide Happiness m_iHappiness += GetHappinessFromMinorCivs(); @@ -21566,7 +21584,12 @@ int CvPlayer::GetHappinessFromNaturalWonders() const iHappiness += iPlotHappiness; } - return iHappiness + GET_TEAM(getTeam()).GetNumLandmarksBuilt(); + return iHappiness; +} + +int CvPlayer::GetHappinessFromImprovements() const +{ + return GET_TEAM(getTeam()).GetHappinessFromImprovements(); } void CvPlayer::SetNaturalWonderOwned(FeatureTypes eFeature, bool bValue) @@ -22404,7 +22427,7 @@ int CvPlayer::GetUnhappinessFromCityJFDSpecial() const iUnhappiness += pLoopCity->getJFDSpecialUnhappinessSources(); } - iUnhappiness += (GetUnhappinessFromCitySpecialists() / 100); + iUnhappiness += GetUnhappinessFromCitySpecialists(); return iUnhappiness; } @@ -22916,7 +22939,7 @@ void CvPlayer::ChangeConversionModifier(int iChange) m_iConversionModifier = (m_iConversionModifier + iChange); } -// set of functions needed to operate bonus from annexed city states trait (rome UA) +// set of functions needed to operate bonus from annexed city states trait void CvPlayer::addAnnexedCityState(PlayerTypes eMinor) { // city states shouldn't be already in the list @@ -22993,14 +23016,14 @@ void CvPlayer::ChangeNumAnnexedCityStates(int iChange) } } -/// Get the yields in the capital from annexed City-States (Rome UA) +/// Get the yields in the capital from annexed City-States int CvPlayer::GetYieldInCapitalPerTurnFromAnnexedMinorsTimes100(YieldTypes eYield) const { PRECONDITION(eYield >= 0, "eIndex is expected to be non-negative (invalid Index)"); PRECONDITION(eYield < NUM_YIELD_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)"); return m_piYieldInCapitalFromAnnexedMinorsTimes100[eYield]; } -/// Update the yields in the capital from annexed City-States (Rome UA) +/// Update the yields in the capital from annexed City-States void CvPlayer::ChangeYieldInCapitalPerTurnFromAnnexedMinorTimes100(PlayerTypes eMinor, int iSign, EraTypes eEra) { if (GetPlayerTraits()->IsAnnexedCityStatesGiveYields() && getCapitalCity()) @@ -23014,24 +23037,25 @@ void CvPlayer::ChangeYieldInCapitalPerTurnFromAnnexedMinorTimes100(PlayerTypes e int iBonus = 0; YieldTypes eYield = (YieldTypes)iI; + iBonus += GET_PLAYER(eMinor).GetMinorCivAI()->GetCityYieldFriendFlatBonusTimes100(GetID(), eYield, eEra, true); iBonus += GET_PLAYER(eMinor).GetMinorCivAI()->GetCityYieldAllyFlatBonusTimes100(GetID(), eYield, eEra, true); iBonus *= iSign; //update if necessary //bonus yields are tracked on city level - getCapitalCity()->ChangeBaseYieldRateFromCSAllianceTimes100(eYield, iBonus); + getCapitalCity()->ChangeBaseYieldRateFromCSAllianceTimes100(eYield, iBonus); // all recorded under ally m_piYieldInCapitalFromAnnexedMinorsTimes100[eYield] += iBonus; } } } -/// Get the yields in other cities from annexed City-States (Rome UA) +/// Get the yields in other cities from annexed City-States int CvPlayer::GetYieldInOtherCitiesPerTurnFromAnnexedMinorsTimes100(YieldTypes eYield) const { PRECONDITION(eYield >= 0, "eIndex is expected to be non-negative (invalid Index)"); PRECONDITION(eYield < NUM_YIELD_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)"); return m_piYieldInOtherCitiesFromAnnexedMinorsTimes100[eYield]; } -/// Update the yields in other cities from annexed City-States (Rome UA) +/// Update the yields in other cities from annexed City-States void CvPlayer::ChangeYieldInOtherCitiesPerTurnFromAnnexedMinorTimes100(PlayerTypes eMinor, int iSign, EraTypes eEra) { if (GetPlayerTraits()->IsAnnexedCityStatesGiveYields()) @@ -23045,6 +23069,7 @@ void CvPlayer::ChangeYieldInOtherCitiesPerTurnFromAnnexedMinorTimes100(PlayerTyp int iBonus = 0; YieldTypes eYield = (YieldTypes)iI; + iBonus += GET_PLAYER(eMinor).GetMinorCivAI()->GetCityYieldFriendFlatBonusTimes100(GetID(), eYield, eEra, false); iBonus += GET_PLAYER(eMinor).GetMinorCivAI()->GetCityYieldAllyFlatBonusTimes100(GetID(), eYield, eEra, false); iBonus *= iSign; @@ -23058,14 +23083,14 @@ void CvPlayer::ChangeYieldInOtherCitiesPerTurnFromAnnexedMinorTimes100(PlayerTyp } } } -/// Get the yields per turn from annexed City-States (Rome UA) +/// Get the yields per turn from annexed City-States int CvPlayer::GetYieldPerTurnFromAnnexedMinorsTimes100(YieldTypes eYield) const { PRECONDITION(eYield >= 0, "eIndex is expected to be non-negative (invalid Index)"); PRECONDITION(eYield < NUM_YIELD_TYPES, "eIndex is expected to be within maximum bounds (invalid Index)"); return m_piYieldPerTurnFromAnnexedMinorsTimes100[eYield]; } -/// Update the yields per turn from annexed City-States (Rome UA) +/// Update the yields per turn from annexed City-States void CvPlayer::ChangeYieldPerTurnFromAnnexedMinorTimes100(PlayerTypes eMinor, int iSign, EraTypes eEra) { if (GetPlayerTraits()->IsAnnexedCityStatesGiveYields()) @@ -23077,20 +23102,23 @@ void CvPlayer::ChangeYieldPerTurnFromAnnexedMinorTimes100(PlayerTypes eMinor, in const CvMinorCivAI* kMinor = GET_PLAYER(eMinor).GetMinorCivAI(); for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) { + int iBonus = 0; YieldTypes eYield = (YieldTypes)iI; - int iBonus = kMinor->GetYieldAllyFlatBonusTimes100(GetID(), eYield, eEra); + iBonus += kMinor->GetYieldFriendFlatBonusTimes100(GetID(), eYield, eEra); + iBonus += kMinor->GetYieldAllyFlatBonusTimes100(GetID(), eYield, eEra); + iBonus *= iSign; - m_piYieldPerTurnFromAnnexedMinorsTimes100[eYield] += iBonus * iSign; + m_piYieldPerTurnFromAnnexedMinorsTimes100[eYield] += iBonus; } } } -/// Get the happiness per turn from annexed City-States (Rome UA) +/// Get the happiness per turn from annexed City-States int CvPlayer::GetHappinessFromAnnexedMinors() const { return m_iHappinessFromAnnexedMinors; } -/// Update the happiness per turn from annexed City-States (Rome UA) +/// Update the happiness per turn from annexed City-States void CvPlayer::ChangeHappinessFromAnnexedMinor(PlayerTypes eMinor, int iSign, EraTypes eEra) { if (GetPlayerTraits()->IsAnnexedCityStatesGiveYields() && (GET_PLAYER(eMinor).GetMinorCivAI()->GetTrait() == MINOR_CIV_TRAIT_MERCANTILE)) @@ -26022,7 +26050,7 @@ void CvPlayer::doInstantYield(InstantYieldType iType, bool bCityFaith, GreatPers iValue += pLoopCity->GetYieldFromPillage(eYield); break; } - case INSTANT_YIELD_TYPE_RESEARCH_AGREMEENT: + case INSTANT_YIELD_TYPE_RESEARCH_AGREEMENT: { if (eYield == ePassYield) iValue += iPassYield; @@ -26500,9 +26528,21 @@ void CvPlayer::doInstantYield(InstantYieldType iType, bool bCityFaith, GreatPers } break; } + case INSTANT_YIELD_TYPE_SCRAP_OR_UPGRADE: + { + if (eYield != ePassYield) + continue; + + // if (bEvent) then its an upgrade, else a scrap + + iValue = iPassYield; + iValue /= 100; + + break; + } case INSTANT_YIELD_TYPE_HEALING: { - if(pUnit == NULL) + if (pUnit == NULL) continue; int iYieldPer100HP = 0; @@ -26564,7 +26604,7 @@ void CvPlayer::doInstantYield(InstantYieldType iType, bool bCityFaith, GreatPers //Exclusions if(eYield != YIELD_POPULATION) { - if (iType != INSTANT_YIELD_TYPE_TR_MOVEMENT && iType != INSTANT_YIELD_TYPE_BARBARIAN_CAMP_CLEARED && iType != INSTANT_YIELD_TYPE_PURCHASE && iType != INSTANT_YIELD_TYPE_FAITH_PURCHASE && iType != INSTANT_YIELD_TYPE_U_PROD && iType != INSTANT_YIELD_TYPE_MINOR_QUEST_REWARD && iType != INSTANT_YIELD_TYPE_TR_MOVEMENT_IN_FOREIGN && iType != INSTANT_YIELD_TYPE_BULLY && iType != INSTANT_YIELD_TYPE_FAITH_REFUND && iType != INSTANT_YIELD_TYPE_REFUND) + if (iType != INSTANT_YIELD_TYPE_TR_MOVEMENT && iType != INSTANT_YIELD_TYPE_BARBARIAN_CAMP_CLEARED && iType != INSTANT_YIELD_TYPE_PURCHASE && iType != INSTANT_YIELD_TYPE_FAITH_PURCHASE && iType != INSTANT_YIELD_TYPE_U_PROD && iType != INSTANT_YIELD_TYPE_MINOR_QUEST_REWARD && iType != INSTANT_YIELD_TYPE_TR_MOVEMENT_IN_FOREIGN && iType != INSTANT_YIELD_TYPE_BULLY && iType != INSTANT_YIELD_TYPE_FAITH_REFUND && iType != INSTANT_YIELD_TYPE_REFUND && iType != INSTANT_YIELD_TYPE_RESEARCH_AGREEMENT) { if (ePlayer == NO_PLAYER && eYield == YIELD_TOURISM) { @@ -26754,6 +26794,7 @@ void CvPlayer::doInstantYield(InstantYieldType iType, bool bCityFaith, GreatPers switch (iType) { case INSTANT_YIELD_TYPE_COMBAT_EXPERIENCE: + case INSTANT_YIELD_TYPE_SCRAP_OR_UPGRADE: case INSTANT_YIELD_TYPE_HEALING: case INSTANT_YIELD_TYPE_CITY_DAMAGE: { @@ -27513,7 +27554,7 @@ void CvPlayer::doInstantYield(InstantYieldType iType, bool bCityFaith, GreatPers localizedText << totalyieldString; break; } - case INSTANT_YIELD_TYPE_RESEARCH_AGREMEENT: + case INSTANT_YIELD_TYPE_RESEARCH_AGREEMENT: { localizedText = Localization::Lookup("TXT_KEY_INSTANT_YIELD_RESEARCH_AGREEMENT"); localizedText << totalyieldString; @@ -27599,6 +27640,7 @@ void CvPlayer::doInstantYield(InstantYieldType iType, bool bCityFaith, GreatPers } // These yields intentionally have no notification. case INSTANT_YIELD_TYPE_COMBAT_EXPERIENCE: + case INSTANT_YIELD_TYPE_SCRAP_OR_UPGRADE: case INSTANT_YIELD_TYPE_HEALING: case INSTANT_YIELD_TYPE_CITY_DAMAGE: { @@ -30720,6 +30762,26 @@ int CvPlayer::GetMonopolyModFlat() const void CvPlayer::ChangeMonopolyModPercent(int iChange) { m_iMonopolyModPercent += iChange; + + // all the yield monopolies you already have need to be updated + const std::vector& vGlobalMonopolies = GetGlobalMonopolies(); + for (int iResourceLoop = 0; iResourceLoop < vGlobalMonopolies.size(); iResourceLoop++) + { + ResourceTypes eResource = vGlobalMonopolies[iResourceLoop]; + CvResourceInfo* pkResource = GC.getResourceInfo(eResource); + if (pkResource && m_pabHasGlobalMonopoly[eResource]) + { + for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) + { + YieldTypes eYield = (YieldTypes)iI; + + if (pkResource->getCityYieldModFromMonopoly(eYield) != 0) + { + changeCityYieldModFromMonopoly(eYield, iChange); + } + } + } + } } void CvPlayer::SetMonopolyModPercent(int iChange) { @@ -33069,7 +33131,7 @@ void CvPlayer::setTurnActive(bool bNewValue, bool bDoTurn) // R: bDoTurn default iExpectedAlliance += GetPlayerTraits()->GetYieldFromCSAlly(eYield) * iNumAllies * iEra * 100; iExpectedFriendship += GetPlayerTraits()->GetYieldFromCSFriend(eYield) * iNumFriends * iEra * 100; } - // Rome UA + // Annexed CS if (GetPlayerTraits()->IsAnnexedCityStatesGiveYields()) { iExpectedAlliance += pCity->isCapital() ? GetYieldInCapitalPerTurnFromAnnexedMinorsTimes100(eYield) : GetYieldInOtherCitiesPerTurnFromAnnexedMinorsTimes100(eYield); @@ -35512,7 +35574,7 @@ int CvPlayer::GetScience() const return GetScienceTimes100() / 100; } -int CvPlayer::GetScienceTimes100() const +int CvPlayer::GetScienceTimes100(bool bExcludeResearchAgreements) const { // If we're in anarchy, then no Research is done! if(IsAnarchy()) @@ -35536,8 +35598,11 @@ int CvPlayer::GetScienceTimes100() const // Happiness converted to Science? (Policies, etc.) iValue += GetScienceFromHappinessTimes100(); - // Research Agreement bonuses - iValue += GetScienceFromResearchAgreementsTimes100(); + if (!bExcludeResearchAgreements) + { + // Research Agreement bonuses + iValue += GetScienceFromResearchAgreementsTimes100(); + } // If we have a negative Treasury + GPT then it gets removed from Science iValue += GetScienceFromBudgetDeficitTimes100(); @@ -35626,13 +35691,47 @@ int CvPlayer::GetScienceFromHappinessTimes100() const /// Where is our Science coming from? int CvPlayer::GetScienceFromResearchAgreementsTimes100() const { - int iScience = GetScienceFromCitiesTimes100(false); + int iResult = 0; int iResearchAgreementBonus = /*0*/ GD_INT_GET(RESEARCH_AGREEMENT_MOD) * GET_TEAM(getTeam()).GetTotalNumResearchAgreements(); // RAs currently do not have this effect + int iScience = GetScienceFromCitiesTimes100(false); iScience *= iResearchAgreementBonus; // Apply to the % to the current value iScience /= 100; - return iScience; + iResult += iScience; + + PlayerTypes ePlayerLoop; + TeamTypes eTeamLoop; + for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++) + { + ePlayerLoop = (PlayerTypes)iPlayerLoop; + eTeamLoop = GET_PLAYER(ePlayerLoop).getTeam(); + if (ePlayerLoop == GetID()) + continue; + if (eTeamLoop == getTeam()) + continue; + + if (GET_TEAM(getTeam()).IsHasResearchAgreement(eTeamLoop)) + { + // Beaker boost = (RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT * (average of both players' beakers over term of RA) + (1 - RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT) * (minimum of both players' beakers over term of RA)) / 3) * (median tech percentage rate (50%)) + int iToPlayerBeakers = GetLastTurnYieldsTimes100(YIELD_SCIENCE); + int iFromPlayerBeakers = GET_PLAYER(ePlayerLoop).GetLastTurnYieldsTimes100(YIELD_SCIENCE); + int iBeakersBonus = 0; + + iBeakersBonus = ((100 - GD_INT_GET(RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT)) * min(iToPlayerBeakers, iFromPlayerBeakers) + GD_INT_GET(RESEARCH_AGREEMENT_PLAYER_AVERAGE_YIELD_PERCENT) * (iToPlayerBeakers + iFromPlayerBeakers) / 2) / (100 * /*3*/ GD_INT_GET(RESEARCH_AGREEMENT_BOOST_DIVISOR)); + + iBeakersBonus *= GetMedianTechPercentage(); + iBeakersBonus /= 100; + + // some of the yields of the RA are given per turn, not as instant yield when the agreement ends + iBeakersBonus *= GD_INT_GET(RESEARCH_AGREEMENT_PER_TURN_YIELD_PERCENT); + iBeakersBonus /= 100; + + iResult += iBeakersBonus; + } + } + + return iResult; } /// Where is our Science coming from? @@ -36541,6 +36640,23 @@ void CvPlayer::DoUpdateProximityToPlayers() } } +// currently only used for science to keep track of research agreement yields +void CvPlayer::SetLastTurnYieldsTimes100(YieldTypes eYield, int iValueTimes100) +{ + PRECONDITION(eYield >= 0, "eYield expected to be >= 0"); + PRECONDITION(eYield < NUM_YIELD_TYPES, "eYield expected to be < NUM_YIELD_TYPES"); + + m_aiLastTurnYieldsTimes100[eYield] = iValueTimes100; +} + +int CvPlayer::GetLastTurnYieldsTimes100(YieldTypes eYield) const +{ + PRECONDITION(eYield >= 0, "eYield expected to be >= 0"); + PRECONDITION(eYield < NUM_YIELD_TYPES, "eYield expected to be < NUM_YIELD_TYPES"); + + return m_aiLastTurnYieldsTimes100[eYield]; +} + /// Update the beakers accumulated during the term of RAs void CvPlayer::UpdateResearchAgreements(int iValue) { @@ -37457,6 +37573,9 @@ int CvPlayer::getNumResourcesFromOther(ResourceTypes eIndex) const //GP resources? ie. Admiral, Diplomat iTotalNumResource += getResourceFromGP(eIndex); + + // Free resources from policies? + iTotalNumResource += getFreeResourceFromPolicies(eIndex); int iLoop = 0; int iCityPOPResource = 0; @@ -37726,16 +37845,28 @@ void CvPlayer::changeResourceFromCSAlliances(ResourceTypes eIndex, int iChange) } } -void CvPlayer::setResourceFromCSAlliances(ResourceTypes eIndex, int iChange) +int CvPlayer::getFreeResourceFromPolicies(ResourceTypes eIndex) const +{ + PRECONDITION(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)"); + PRECONDITION(eIndex < GC.getNumResourceInfos(), "eIndex is expected to be within maximum bounds (invalid Index)"); + return m_paiFreeResourceFromPolicies[eIndex]; +} + +void CvPlayer::changeFreeResourceFromPolicies(ResourceTypes eIndex, int iChange) { ASSERT(eIndex >= 0); PRECONDITION(eIndex < GC.getNumResourceInfos()); - m_paiResourceFromCSAlliances[eIndex] = iChange; + if (iChange != 0) + { + m_paiFreeResourceFromPolicies[eIndex] = m_paiFreeResourceFromPolicies[eIndex] + iChange; + ASSERT(m_paiFreeResourceFromPolicies[eIndex] >= 0); - GC.GetEngineUserInterface()->setDirty(GameData_DIRTY_BIT, true); + if (iChange > 0) + CheckForLuxuryResourceGainInstantYields(eIndex); - ASSERT(m_paiResourceFromCSAlliances[eIndex] >= 0); + GC.GetEngineUserInterface()->setDirty(GameData_DIRTY_BIT, true); + } } bool CvPlayer::IsResourceNotForSale(ResourceTypes eResource) @@ -37937,8 +38068,6 @@ int CvPlayer::GetNumGlobalMonopolies() const void CvPlayer::UpdateMonopolyCache() { - int iNumGlobalMonopoliesBefore = m_vResourcesWGlobalMonopoly.size(); - m_vResourcesWStrategicMonopoly.clear(); m_vResourcesWGlobalMonopoly.clear(); m_iCombatAttackBonusFromMonopolies = 0; @@ -37955,28 +38084,6 @@ void CvPlayer::UpdateMonopolyCache() m_vResourcesWStrategicMonopoly.push_back((ResourceTypes)iResourceLoop); } - // if the number of global monopolies has changed, update city yields - if (iNumGlobalMonopoliesBefore != static_cast(m_vResourcesWGlobalMonopoly.size())) - { - int iLoop = 0; - for (CvCity* pLoopCity = firstCity(&iLoop); pLoopCity != NULL; pLoopCity = nextCity(&iLoop)) - { - for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) - { - YieldTypes eYield = (YieldTypes)iI; - int iCityYieldPerMonopoly = 0; - for (int iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++) - { - BuildingTypes eLoopBuilding = (BuildingTypes)iJ; - CvBuildingEntry* pkLoopBuilding = GC.getBuildingInfo(eLoopBuilding); - iCityYieldPerMonopoly += pLoopCity->GetCityBuildings()->GetNumRealBuilding(eLoopBuilding) * pkLoopBuilding->GetYieldChangePerMonopoly(eYield); - } - // subtract the old value and add the new one - pLoopCity->ChangeBaseYieldRateFromBuildings(eYield, iCityYieldPerMonopoly * (m_vResourcesWGlobalMonopoly.size() - iNumGlobalMonopoliesBefore)); - } - } - } - // Strategic monopoly of resources const std::vector& vStrategicMonopolies = GetStrategicMonopolies(); for (size_t iResourceLoop = 0; iResourceLoop < vStrategicMonopolies.size(); iResourceLoop++) @@ -38053,6 +38160,7 @@ void CvPlayer::SetHasGlobalMonopoly(ResourceTypes eResource, bool bNewValue) int iModValue = pResource->getCityYieldModFromMonopoly(eYield); if (iModValue != 0) { + iModValue += GetMonopolyModPercent(); if (bNewValue) { changeCityYieldModFromMonopoly(eYield, iModValue); @@ -38098,6 +38206,24 @@ void CvPlayer::SetHasGlobalMonopoly(ResourceTypes eResource, bool bNewValue) } } + int iSign = bNewValue ? 1 : -1; + int iCityLoop = 0; + for (CvCity* pLoopCity = firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = nextCity(&iCityLoop)) + { + for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) + { + int iCityYieldPerMonopoly = 0; + for (int iJ = 0; iJ < GC.getNumBuildingInfos(); iJ++) + { + BuildingTypes eLoopBuilding = (BuildingTypes)iJ; + CvBuildingEntry* pkLoopBuilding = GC.getBuildingInfo(eLoopBuilding); + iCityYieldPerMonopoly += pLoopCity->GetCityBuildings()->GetNumRealBuilding(eLoopBuilding) * pkLoopBuilding->GetYieldChangePerMonopoly(iI); + } + if (iCityYieldPerMonopoly != 0) + pLoopCity->ChangeBaseYieldRateFromBuildings((YieldTypes)iI, iCityYieldPerMonopoly * iSign); + } + } + UpdateMonopolyCache(); } } @@ -42057,7 +42183,7 @@ void CvPlayer::LogInstantYield(YieldTypes eYield, int iValue, InstantYieldType e instantYieldName = "LUA"; break; } - case INSTANT_YIELD_TYPE_RESEARCH_AGREMEENT: + case INSTANT_YIELD_TYPE_RESEARCH_AGREEMENT: { instantYieldName = "RA"; break; @@ -42087,6 +42213,11 @@ void CvPlayer::LogInstantYield(YieldTypes eYield, int iValue, InstantYieldType e instantYieldName = "Unit Combat"; break; } + case INSTANT_YIELD_TYPE_SCRAP_OR_UPGRADE: + { + instantYieldName = "Scrap or Upgrade"; + break; + } case INSTANT_YIELD_TYPE_HEALING: { instantYieldName = "Healing"; @@ -42224,10 +42355,13 @@ void CvPlayer::doResearch() { int iBeakersTowardsTechTimes100 = GetScienceTimes100() + iOverflowResearch; GET_TEAM(getTeam()).GetTeamTechs()->ChangeResearchProgressTimes100(eCurrentTech, iBeakersTowardsTechTimes100, GetID(), iOverflowResearch, 100); - UpdateResearchAgreements(GetScienceTimes100() / 100); } } + int iScienceForResearchAgreements = GetScienceTimes100(true); + UpdateResearchAgreements(iScienceForResearchAgreements); + SetLastTurnYieldsTimes100(YIELD_SCIENCE, iScienceForResearchAgreements); + // Force player to pick Research if he doesn't have anything assigned if (GetPlayerTechs()->GetCurrentResearch() == NO_TECH) { @@ -42581,6 +42715,8 @@ void CvPlayer::processPolicies(PolicyTypes ePolicy, int iChange) changeResourceFromCSAlliances(eResource, pkPolicyInfo->GetResourceFromCSAlly(iI) * iChange); + changeFreeResourceFromPolicies(eResource, pkPolicyInfo->GetFreeResource(iI) * iChange); + const CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); // Update strategic resource monopolies @@ -44070,6 +44206,7 @@ void CvPlayer::Serialize(Player& player, Visitor& visitor) visitor(player.m_paiJFDPoliticPercent); visitor(player.m_aiYieldFromMinors); visitor(player.m_paiResourceFromCSAlliances); + visitor(player.m_paiFreeResourceFromPolicies); visitor(player.m_paiResourceShortageValue); visitor(player.m_aiYieldFromBirth); visitor(player.m_aiYieldFromBirthCapital); @@ -44116,6 +44253,7 @@ void CvPlayer::Serialize(Player& player, Visitor& visitor) visitor(player.m_aiCapitalYieldRateModifier); visitor(player.m_aiExtraYieldThreshold); visitor(player.m_aiSpecialistExtraYield); + visitor(player.m_aiLastTurnYieldsTimes100); visitor(player.m_aiLastCityCaptureTurn); visitor(player.m_aiWarValueLost); visitor(player.m_aiWarDamageValue); diff --git a/CvGameCoreDLL_Expansion2/CvPlayer.h b/CvGameCoreDLL_Expansion2/CvPlayer.h index 5984600a5e..07541e6e23 100644 --- a/CvGameCoreDLL_Expansion2/CvPlayer.h +++ b/CvGameCoreDLL_Expansion2/CvPlayer.h @@ -669,6 +669,7 @@ class CvPlayer int GetHappinessFromResourceVariety() const; int GetHappinessFromReligion(); int GetHappinessFromNaturalWonders() const; + int GetHappinessFromImprovements() const; void SetNaturalWonderOwned(FeatureTypes eFeature, bool bValue); void ChangeUnitClassProductionModifier(UnitClassTypes eUnitClass, int iValue); @@ -1955,7 +1956,7 @@ class CvPlayer // Science int GetScience() const; - int GetScienceTimes100() const; + int GetScienceTimes100(bool bExcludeResearchAgreements = false) const; int GetScienceFromCitiesTimes100(bool bIgnoreTrade) const; @@ -2009,6 +2010,9 @@ class CvPlayer pair GetClosestCityPair(PlayerTypes ePlayer); void DoUpdateProximityToPlayers(); + void SetLastTurnYieldsTimes100(YieldTypes eYield, int iValueTimes100); + int GetLastTurnYieldsTimes100(YieldTypes eYield) const; + void UpdateResearchAgreements(int iValue); int GetResearchAgreementCounter(PlayerTypes ePlayer) const; void SetResearchAgreementCounter(PlayerTypes ePlayer, int iValue); @@ -2072,7 +2076,9 @@ class CvPlayer int getResourceFromCSAlliances(ResourceTypes eIndex) const; void changeResourceFromCSAlliances(ResourceTypes eIndex, int iChange); - void setResourceFromCSAlliances(ResourceTypes eIndex, int iChange); + + int getFreeResourceFromPolicies(ResourceTypes eIndex) const; + void changeFreeResourceFromPolicies(ResourceTypes eIndex, int iChange); const std::vector& GetResourcesNotForSale() const { return m_vResourcesNotForSale; } bool IsResourceNotForSale(ResourceTypes eResource); @@ -3417,6 +3423,7 @@ class CvPlayer std::vector m_paiJFDPoliticPercent; std::vector m_aiYieldFromMinors; std::vector m_paiResourceFromCSAlliances; + std::vector m_paiFreeResourceFromPolicies; std::vector m_paiResourceShortageValue; std::vector m_aiYieldFromBirth; std::vector m_aiYieldFromBirthCapital; @@ -3463,6 +3470,7 @@ class CvPlayer std::vector m_aiCapitalYieldRateModifier; std::vector m_aiExtraYieldThreshold; std::vector m_aiSpecialistExtraYield; + std::vector m_aiLastTurnYieldsTimes100; std::vector m_aiLastCityCaptureTurn; std::vector m_aiWarValueLost; std::vector m_aiWarDamageValue; @@ -4202,6 +4210,7 @@ SYNC_ARCHIVE_VAR(std::vector, m_aiYieldRateModifier) SYNC_ARCHIVE_VAR(std::vector, m_paiJFDPoliticPercent) SYNC_ARCHIVE_VAR(std::vector, m_aiYieldFromMinors) SYNC_ARCHIVE_VAR(std::vector, m_paiResourceFromCSAlliances) +SYNC_ARCHIVE_VAR(std::vector, m_paiFreeResourceFromPolicies) SYNC_ARCHIVE_VAR(std::vector, m_paiResourceShortageValue) SYNC_ARCHIVE_VAR(std::vector, m_aiYieldFromBirth) SYNC_ARCHIVE_VAR(std::vector, m_aiYieldFromBirthCapital) @@ -4242,6 +4251,7 @@ SYNC_ARCHIVE_VAR(bool, m_bAllowsFoodTradeRoutesGlobal) SYNC_ARCHIVE_VAR(std::vector, m_aiCapitalYieldRateModifier) SYNC_ARCHIVE_VAR(std::vector, m_aiExtraYieldThreshold) SYNC_ARCHIVE_VAR(std::vector, m_aiSpecialistExtraYield) +SYNC_ARCHIVE_VAR(std::vector, m_aiLastTurnYieldsTimes100) SYNC_ARCHIVE_VAR(std::vector, m_aiLastCityCaptureTurn) SYNC_ARCHIVE_VAR(std::vector, m_aiWarValueLost) SYNC_ARCHIVE_VAR(std::vector, m_aiWarDamageValue) diff --git a/CvGameCoreDLL_Expansion2/CvPlayerAI.cpp b/CvGameCoreDLL_Expansion2/CvPlayerAI.cpp index d65b8c4406..4bb771ddb3 100644 --- a/CvGameCoreDLL_Expansion2/CvPlayerAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvPlayerAI.cpp @@ -1310,7 +1310,7 @@ void CvPlayerAI::CityFinishedBuildingUnitForOperationSlot(OperationSlot thisSlot CvArmyAI* pThisArmy = getArmyAI(thisSlot.m_iArmyID); if(pThisOperation && pThisArmy && pThisUnit) { - pThisArmy->AddUnit(pThisUnit->GetID(), thisSlot.m_iSlotID,true); + pThisArmy->AddUnit(pThisUnit->GetID(), thisSlot.m_iSlotID, pThisArmy->GetSlotInfo(thisSlot.m_iSlotID).m_requiredSlot); pThisOperation->FinishedBuildingUnit(thisSlot); } } diff --git a/CvGameCoreDLL_Expansion2/CvPlot.cpp b/CvGameCoreDLL_Expansion2/CvPlot.cpp index 416b15a027..8f76868776 100644 --- a/CvGameCoreDLL_Expansion2/CvPlot.cpp +++ b/CvGameCoreDLL_Expansion2/CvPlot.cpp @@ -7501,6 +7501,11 @@ void CvPlot::setTerrainType(TerrainTypes eNewValue, bool bRecalculate, bool bReb { pOwningCity->ChangeNumTerrainWorked(eOldValue, -1); pOwningCity->ChangeNumTerrainWorked(eNewValue, 1); + if (getFeatureType() == NO_FEATURE && !isHills()) + { + pOwningCity->ChangeNumFeaturelessTerrainWorked(eOldValue, -1); + pOwningCity->ChangeNumFeaturelessTerrainWorked(eNewValue, 1); + } } } @@ -8183,8 +8188,6 @@ void CvPlot::setImprovementType(ImprovementTypes eNewValue, PlayerTypes eBuilder } } - bool bArchaeologyChoicePending = false; - if (eOldImprovement != eNewValue) { PlayerTypes eOldBuilder = GetPlayerThatBuiltImprovement(); @@ -8562,13 +8565,15 @@ void CvPlot::setImprovementType(ImprovementTypes eNewValue, PlayerTypes eBuilder { if (newImprovementEntry.GetHappinessOnConstruction() != 0) { - // this actually tracks improvement happiness, not num landmarks built - GET_TEAM(GET_PLAYER(eBuilder).getTeam()).ChangeNumLandmarksBuilt(newImprovementEntry.GetHappinessOnConstruction()); - if (getOwner() != NO_PLAYER && getOwner() != eBuilder && GET_PLAYER(getOwner()).isMajorCiv()) + TeamTypes eBuilderTeam = GET_PLAYER(eBuilder).getTeam(); + TeamTypes ePlotTeam = getTeam(); + GET_TEAM(eBuilderTeam).ChangeHappinessFromImprovements(newImprovementEntry.GetHappinessOnConstruction()); + if (ePlotTeam != NO_TEAM && ePlotTeam != eBuilderTeam && GET_PLAYER(getOwner()).isMajorCiv()) { - GET_TEAM(GET_PLAYER(getOwner()).getTeam()).ChangeNumLandmarksBuilt(newImprovementEntry.GetHappinessOnConstruction()); + GET_TEAM(ePlotTeam).ChangeHappinessFromImprovements(newImprovementEntry.GetHappinessOnConstruction()); } } + // if the improvement isn't a landmark but has yield changes by era, it needs the historical record set. static const ImprovementTypes eLandmark = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_LANDMARK"); if (eNewValue != eLandmark) @@ -9024,14 +9029,6 @@ void CvPlot::setImprovementType(ImprovementTypes eNewValue, PlayerTypes eBuilder } } } - - // Do the AI's dig site choice at the very end since it could replace the tile with a Landmark, so it's better to do that after all the other code updating things has run - if (bArchaeologyChoicePending) - { - CvPlayer& kBuilder = GET_PLAYER(eBuilder); - ArchaeologyChoiceType eChoice = kBuilder.GetCulture()->GetArchaeologyChoice(this); - kBuilder.GetCulture()->DoArchaeologyChoice(eChoice); - } } CvPlot* CvPlot::GetAdjacentResourceSpawnPlot(PlayerTypes ePlayer) const @@ -9137,11 +9134,14 @@ void CvPlot::SetImprovementPillaged(bool bPillaged, bool bEvents) // Quantified Resource changes if (getTeam() != NO_TEAM && getImprovementType() != NO_IMPROVEMENT) { - if (getResourceType(getTeam()) != NO_RESOURCE) + static const ResourceTypes eArtifact = static_cast(GD_INT_GET(ARTIFACT_RESOURCE)); + static const ResourceTypes eHiddenArtifact = static_cast(GD_INT_GET(HIDDEN_ARTIFACT_RESOURCE)); + ResourceTypes eRes = getResourceType(getTeam()); + if (eRes != NO_RESOURCE && eRes != eArtifact && eRes != eHiddenArtifact) { - if (GET_TEAM(getTeam()).IsResourceImproveable(getResourceType())) + if (GET_TEAM(getTeam()).IsResourceImproveable(eRes)) { - if (GC.getImprovementInfo(getImprovementType())->IsConnectsResource(getResourceType())) + if (GC.getImprovementInfo(getImprovementType())->IsConnectsResource(eRes)) { if (bPillaged) { diff --git a/CvGameCoreDLL_Expansion2/CvPolicyAI.cpp b/CvGameCoreDLL_Expansion2/CvPolicyAI.cpp index d705075bbc..7c57e1fa56 100644 --- a/CvGameCoreDLL_Expansion2/CvPolicyAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvPolicyAI.cpp @@ -4000,6 +4000,9 @@ Firaxis::Array< int, NUM_YIELD_TYPES > CvPolicyAI::WeightPolicyAttributes(CvPlay for (int iI = 0; iI < GC.getNumResourceInfos(); iI++) { eResource = (ResourceTypes)iI; + if (GET_TEAM(pPlayer->getTeam()).IsResourceObsolete(eResource)) + continue; + if (PolicyInfo->GetResourceFromCSAlly(eResource) != 0) { if (pPlayerTraits->IsDiplomat()) @@ -4011,6 +4014,28 @@ Firaxis::Array< int, NUM_YIELD_TYPES > CvPolicyAI::WeightPolicyAttributes(CvPlay yield[YIELD_GOLD] += PolicyInfo->GetResourceFromCSAlly(eResource) / 15; } } + if (PolicyInfo->GetFreeResource(eResource) != 0) + { + CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); + if (pkResourceInfo->getResourceUsage() == RESOURCEUSAGE_LUXURY) + { + if (pPlayer->getNumResourceAvailable(eResource, false) == 0) + { + yield[YIELD_GOLD] += 100 + (PolicyInfo->GetFreeResource(eResource) - 1) * 10; + } + else + { + yield[YIELD_GOLD] += PolicyInfo->GetFreeResource(eResource) * 10; + } + } + else if (pkResourceInfo->getResourceUsage() == RESOURCEUSAGE_STRATEGIC) + { + for (int iCnt = 0; iCnt < PolicyInfo->GetFreeResource(eResource); iCnt++) + { + yield[YIELD_PRODUCTION] += 50 * max(1, 10 - pPlayer->getNumResourceAvailable(eResource, false) - iCnt); + } + } + } CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource); if (pkResourceInfo != NULL && pkResourceInfo->getPolicyReveal() == ePolicy) diff --git a/CvGameCoreDLL_Expansion2/CvPolicyClasses.cpp b/CvGameCoreDLL_Expansion2/CvPolicyClasses.cpp index af9f280aa0..195855d8df 100644 --- a/CvGameCoreDLL_Expansion2/CvPolicyClasses.cpp +++ b/CvGameCoreDLL_Expansion2/CvPolicyClasses.cpp @@ -274,6 +274,7 @@ CvPolicyEntry::CvPolicyEntry(void): m_pabSpecialistValid(NULL), m_paiFreeChosenBuilding(NULL), m_piResourcefromCSAlly(NULL), + m_piFreeResource(NULL), m_piYieldFromBirth(NULL), m_piYieldFromBirthCapital(NULL), m_piYieldFromBirthRetroactive(NULL), @@ -418,6 +419,7 @@ CvPolicyEntry::~CvPolicyEntry(void) SAFE_DELETE_ARRAY(m_pabSpecialistValid); SAFE_DELETE_ARRAY(m_paiFreeChosenBuilding); SAFE_DELETE_ARRAY(m_piResourcefromCSAlly); + SAFE_DELETE_ARRAY(m_piFreeResource); SAFE_DELETE_ARRAY(m_piYieldFromBirth); SAFE_DELETE_ARRAY(m_piYieldFromBirthRetroactive); SAFE_DELETE_ARRAY(m_piYieldFromBirthCapital); @@ -806,6 +808,7 @@ bool CvPolicyEntry::CacheResults(Database::Results& kResults, CvDatabaseUtility& kUtility.SetYields(m_piGreatWorkYieldChange, "Policy_GreatWorkYieldChanges", "PolicyType", szPolicyType); kUtility.SetYields(m_piSpecialistExtraYield, "Policy_SpecialistExtraYields", "PolicyType", szPolicyType); kUtility.PopulateArrayByValue(m_piResourcefromCSAlly, "Resources", "Policy_ResourcefromCSAlly", "ResourceType", "PolicyType", szPolicyType, "Number"); + kUtility.PopulateArrayByValue(m_piFreeResource, "Resources", "Policy_FreeResource", "ResourceType", "PolicyType", szPolicyType, "Number"); kUtility.SetYields(m_piYieldFromBirth, "Policy_YieldFromBirth", "PolicyType", szPolicyType); kUtility.SetYields(m_piYieldFromBirthRetroactive, "Policy_YieldFromBirthRetroactive", "PolicyType", szPolicyType); kUtility.SetYields(m_piYieldFromBirthCapital, "Policy_YieldFromBirthCapital", "PolicyType", szPolicyType); @@ -2884,6 +2887,13 @@ int CvPolicyEntry::GetResourceFromCSAlly(int i) const PRECONDITION(i > -1, "Index out of bounds"); return m_piResourcefromCSAlly[i]; } +// Grants free resources when the policy is adopted +int CvPolicyEntry::GetFreeResource(int i) const +{ + PRECONDITION(i < GC.getNumResourceInfos(), "Index out of bounds"); + PRECONDITION(i > -1, "Index out of bounds"); + return m_piFreeResource[i]; +} /// Does this Policy grant yields from citizen birth? int CvPolicyEntry::GetYieldFromBirth(int i) const { diff --git a/CvGameCoreDLL_Expansion2/CvPolicyClasses.h b/CvGameCoreDLL_Expansion2/CvPolicyClasses.h index 22c23488c7..1c30fcbfe4 100644 --- a/CvGameCoreDLL_Expansion2/CvPolicyClasses.h +++ b/CvGameCoreDLL_Expansion2/CvPolicyClasses.h @@ -281,6 +281,7 @@ class CvPolicyEntry: public CvBaseInfo bool IsSpecialistValid(int i) const; int GetFreeChosenBuilding(int i) const; int GetResourceFromCSAlly(int i) const; + int GetFreeResource(int i) const; int GetYieldFromBirth(int i) const; int GetYieldFromBirthCapital(int i) const; int GetYieldFromBirthRetroactive(int i) const; @@ -719,6 +720,7 @@ class CvPolicyEntry: public CvBaseInfo int* m_paiTourismOnUnitCreation; int* m_paiFreeChosenBuilding; int* m_piResourcefromCSAlly; + int* m_piFreeResource; int* m_piYieldFromBirth; int* m_piYieldFromBirthCapital; int* m_piYieldFromBirthRetroactive; diff --git a/CvGameCoreDLL_Expansion2/CvProjectClasses.cpp b/CvGameCoreDLL_Expansion2/CvProjectClasses.cpp index 9e6bc5654d..242a2e1592 100644 --- a/CvGameCoreDLL_Expansion2/CvProjectClasses.cpp +++ b/CvGameCoreDLL_Expansion2/CvProjectClasses.cpp @@ -137,10 +137,9 @@ bool CvProjectEntry::CacheResults(Database::Results& kResults, CvDatabaseUtility kUtility.InitializeArray(m_piVictoryMinThreshold, iNumVictories); Database::Results kDBResults; - char szQuery[512] = {0}; - sprintf_s(szQuery, "select VictoryType, Threshold, MinThreshold from Project_VictoryThresholds where ProjectType = '%s';", szProjectType); - if(DB.Execute(kDBResults, szQuery)) + if(DB.Execute(kDBResults, "select VictoryType, Threshold, MinThreshold from Project_VictoryThresholds where ProjectType = ?")) { + kDBResults.Bind(1, szProjectType); while(kDBResults.Step()) { const char* szVictoryType = kDBResults.GetText("VictoryType"); diff --git a/CvGameCoreDLL_Expansion2/CvProjectProductionAI.cpp b/CvGameCoreDLL_Expansion2/CvProjectProductionAI.cpp index 619d769e19..711d8ac5ef 100644 --- a/CvGameCoreDLL_Expansion2/CvProjectProductionAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvProjectProductionAI.cpp @@ -252,109 +252,115 @@ int CvProjectProductionAI::CheckProjectBuildSanity(ProjectTypes eProject, int iT } } - bool bGoodforHappiness = false; + // if the project starts a player event, it's probably important + if (pkProjectInfo->GetEventToStart()) + { + iTempWeight += 1000; + } + // if the project starts a city event, it's probably fairly important + if (pkProjectInfo->GetEventToStart()) + { + iTempWeight += 500; + } + + // if we aren't currently unhappy, we need to massively reduce the value of happiness effects + int iScaleFactor = 30; + int iUnhappyWeight = GET_PLAYER(m_pCity->getOwner()).IsEmpireUnhappy() ? iScaleFactor : 1; + if (pkProjectInfo->GetHappiness() > 0) { - bGoodforHappiness = true; - iTempWeight += (30 * pkProjectInfo->GetHappiness()); + iTempWeight += (iUnhappyWeight * pkProjectInfo->GetHappiness()); + } + + if (pkProjectInfo->GetCitySupplyFlat() > 0) + { + iTempWeight += iScaleFactor * pkProjectInfo->GetCitySupplyFlat(); } if (pkProjectInfo->GetEmpireSizeModifierReduction() < 0) { - bGoodforHappiness = true; - iTempWeight += ((m_pCity->GetReducedEmpireSizeModifier(true,false)/2) * (pkProjectInfo->GetEmpireSizeModifierReduction() * -1)); + iTempWeight += (iUnhappyWeight * (m_pCity->GetReducedEmpireSizeModifier(true,false)/2) * (pkProjectInfo->GetEmpireSizeModifierReduction() * -1)) / iScaleFactor; } if (pkProjectInfo->GetEmpireSizeModifierPerCityMod() < 0) { - bGoodforHappiness = true; - iTempWeight += GET_PLAYER(m_pCity->getOwner()).getNumCities() * (pkProjectInfo->GetEmpireSizeModifierPerCityMod() * -1) / 100; + iTempWeight += (iUnhappyWeight * GET_PLAYER(m_pCity->getOwner()).getNumCities() * (pkProjectInfo->GetEmpireSizeModifierPerCityMod() * -1) / 100) / iScaleFactor; } if (pkProjectInfo->GetDistressFlatReduction() > 0) { - bGoodforHappiness = true; int iDistress = m_pCity->GetDistress(false); if (iDistress > 0) - iTempWeight += 75 * pkProjectInfo->GetDistressFlatReduction() * iDistress * iDistress; + iTempWeight += iUnhappyWeight * 3 * pkProjectInfo->GetDistressFlatReduction(); } if (pkProjectInfo->GetPovertyFlatReduction() > 0) { - bGoodforHappiness = true; int iPoverty = m_pCity->GetPoverty(false); if (iPoverty > 0) - iTempWeight += 75 * pkProjectInfo->GetPovertyFlatReduction() * iPoverty * iPoverty; + iTempWeight += iUnhappyWeight * 3 * pkProjectInfo->GetPovertyFlatReduction(); } if (pkProjectInfo->GetIlliteracyFlatReduction() > 0) { - bGoodforHappiness = true; int iIlliteracy = m_pCity->GetIlliteracy(false); if (iIlliteracy > 0) - iTempWeight += 75 * pkProjectInfo->GetIlliteracyFlatReduction() * iIlliteracy * iIlliteracy; + iTempWeight += iUnhappyWeight * 3 * pkProjectInfo->GetIlliteracyFlatReduction(); } if (pkProjectInfo->GetBoredomFlatReduction() > 0) { - bGoodforHappiness = true; int iBoredom = m_pCity->GetBoredom(false); if (iBoredom > 0) - iTempWeight += 75 * pkProjectInfo->GetBoredomFlatReduction() * iBoredom * iBoredom; + iTempWeight += iUnhappyWeight * 3 * pkProjectInfo->GetBoredomFlatReduction(); } if (pkProjectInfo->GetReligiousUnrestFlatReduction() > 0) { - bGoodforHappiness = true; int iReligiousUnrest = m_pCity->GetUnhappinessFromReligiousUnrest(); if (iReligiousUnrest > 0) - iTempWeight += 75 * pkProjectInfo->GetReligiousUnrestFlatReduction() * iReligiousUnrest * iReligiousUnrest; + iTempWeight += iUnhappyWeight * 3 * pkProjectInfo->GetReligiousUnrestFlatReduction(); } if (pkProjectInfo->GetBasicNeedsMedianModifier() < 0) { - bGoodforHappiness = true; int iDistress = m_pCity->GetDistress(false); if (iDistress > 0) - iTempWeight += -5 * pkProjectInfo->GetBasicNeedsMedianModifier() * iDistress * iDistress; + iTempWeight += (-iUnhappyWeight * 5 * pkProjectInfo->GetBasicNeedsMedianModifier() * iDistress * iDistress) / iScaleFactor; } if (pkProjectInfo->GetGoldMedianModifier() < 0) { - bGoodforHappiness = true; int iPoverty = m_pCity->GetPoverty(false); if (iPoverty > 0) - iTempWeight += -5 * pkProjectInfo->GetGoldMedianModifier() * iPoverty * iPoverty; + iTempWeight += (-iUnhappyWeight * 5 * pkProjectInfo->GetGoldMedianModifier() * iPoverty * iPoverty) / iScaleFactor; } if (pkProjectInfo->GetScienceMedianModifier() < 0) { - bGoodforHappiness = true; int iIlliteracy = m_pCity->GetIlliteracy(false); if (iIlliteracy > 0) - iTempWeight += -5 * pkProjectInfo->GetScienceMedianModifier() * iIlliteracy * iIlliteracy; + iTempWeight += (-iUnhappyWeight * 5 * pkProjectInfo->GetScienceMedianModifier() * iIlliteracy * iIlliteracy) / iScaleFactor; } if (pkProjectInfo->GetCultureMedianModifier() < 0) { - bGoodforHappiness = true; int iBoredom = m_pCity->GetBoredom(false); if (iBoredom > 0) - iTempWeight += -5 * pkProjectInfo->GetCultureMedianModifier() * iBoredom * iBoredom; + iTempWeight += (-iUnhappyWeight * 5 * pkProjectInfo->GetCultureMedianModifier() * iBoredom * iBoredom) / iScaleFactor; } if (pkProjectInfo->GetReligiousUnrestModifier() < 0) { - bGoodforHappiness = true; int iReligiousUnrest = m_pCity->GetUnhappinessFromReligiousUnrest(); if (iReligiousUnrest > 0) - iTempWeight += -5 * pkProjectInfo->GetReligiousUnrestModifier() * iReligiousUnrest * iReligiousUnrest; + iTempWeight += (-iUnhappyWeight * 5 * pkProjectInfo->GetReligiousUnrestModifier() * iReligiousUnrest * iReligiousUnrest) / iScaleFactor; } if (pkProjectInfo->GetSpySecurityModifier() > 0) { - int iEsp = /*2000*/ (GD_INT_GET(ESPIONAGE_GATHERING_INTEL_COST_PERCENT) * 2); - iTempWeight += (iEsp/2); + int iEsp = pkProjectInfo->GetSpySecurityModifier() * /*1000*/ GD_INT_GET(ESPIONAGE_GATHERING_INTEL_COST_PERCENT); + iTempWeight += iEsp / 100; } for (int iI = 0; iI < GC.getNumUnitCombatClassInfos(); iI++) @@ -381,9 +387,6 @@ int CvProjectProductionAI::CheckProjectBuildSanity(ProjectTypes eProject, int iT } } - if (bGoodforHappiness && !GET_PLAYER(m_pCity->getOwner()).IsEmpireUnhappy()) - iTempWeight /= 50; - return max(1,iTempWeight); } /// Log all potential builds diff --git a/CvGameCoreDLL_Expansion2/CvPromotionClasses.cpp b/CvGameCoreDLL_Expansion2/CvPromotionClasses.cpp index f292f884e3..36ee1f4262 100644 --- a/CvGameCoreDLL_Expansion2/CvPromotionClasses.cpp +++ b/CvGameCoreDLL_Expansion2/CvPromotionClasses.cpp @@ -3583,10 +3583,10 @@ bool CvPromotionEntry::IsUnitNaming(int i) const void CvPromotionEntry::GetUnitName(UnitTypes eUnit, CvString& sUnitName) const { Database::Results kDBResults; - char szQuery[512] = {0}; - sprintf_s(szQuery, "select Name from UnitPromotions_UnitName n, UnitPromotions p, Units u where n.PromotionType = p.Type and n.UnitType = u.Type and p.ID = %i and u.ID = %i;", GetID(), eUnit); - if(DB.Execute(kDBResults, szQuery)) + if(DB.Execute(kDBResults, "select Name from UnitPromotions_UnitName n, UnitPromotions p, Units u where n.PromotionType = p.Type and n.UnitType = u.Type and p.ID = ? and u.ID = ?")) { + kDBResults.Bind(1, GetID()); + kDBResults.Bind(2, (int)eUnit); while(kDBResults.Step()) { sUnitName = kDBResults.GetText("Name"); diff --git a/CvGameCoreDLL_Expansion2/CvReligionClasses.cpp b/CvGameCoreDLL_Expansion2/CvReligionClasses.cpp index 9643aa61de..4b14cc9bf9 100644 --- a/CvGameCoreDLL_Expansion2/CvReligionClasses.cpp +++ b/CvGameCoreDLL_Expansion2/CvReligionClasses.cpp @@ -7888,6 +7888,47 @@ int CvReligionAI::GetValidPlotYieldTimes100(CvBeliefEntry* pEntry, CvPlot* pPlot int iModifier = 0; // iModifier is between 0 and 100. 100 for yields that are instantly available, lower value if it takes time to get them (build improvements, remove features etc.) + // When RequiresImprovement=1 and no improvement is present, compute a tech-based confidence modifier + int iRequiresImprovementModifier = 75; // fallback when bConsiderFutureTech=false (preserves old behavior) + if (pEntry->RequiresImprovement() && eImprovement == NO_IMPROVEMENT && bConsiderFutureTech) + { + iRequiresImprovementModifier = 0; // will be raised if any improvement can be built here + for (int jJ = 0; jJ < GC.getNumImprovementInfos(); jJ++) + { + CvImprovementEntry* pkImprovementInfo = GC.getImprovementInfo((ImprovementTypes)jJ); + if (!pkImprovementInfo || pkImprovementInfo->IsCreatedByGreatPerson()) + continue; + if (pEntry->RequiresResource() && (eResource == NO_RESOURCE || !pkImprovementInfo->IsConnectsResource(eResource))) + continue; + if (!pPlot->canHaveImprovement((ImprovementTypes)jJ, m_pPlayer->GetID())) + continue; + if (pkImprovementInfo->IsSpecificCivRequired() && pkImprovementInfo->GetRequiredCivilization() != m_pPlayer->getCivilizationType()) + continue; + BuildTypes eThisBuild = NO_BUILD; + for (int iK = 0; iK < GC.getNumBuildInfos(); ++iK) + { + CvBuildInfo* pkBuildInfo = GC.getBuildInfo((BuildTypes)iK); + if (pkBuildInfo && (ImprovementTypes)pkBuildInfo->getImprovement() == (ImprovementTypes)jJ) + { + eThisBuild = (BuildTypes)iK; + break; + } + } + if (eThisBuild != NO_BUILD) + { + int iLoopModifier; + TechTypes eBuildTech = (TechTypes)GC.getBuildInfo(eThisBuild)->getTechPrereq(); + if (eBuildTech == NO_TECH || m_pPlayer->HasTech(eBuildTech)) + iLoopModifier = 90; + else if (m_pPlayer->GetPlayerTechs()->GetCurrentResearch() == eBuildTech) + iLoopModifier = 80; + else + iLoopModifier = 50; + iRequiresImprovementModifier = max(iRequiresImprovementModifier, iLoopModifier); + } + } + } + if (eTerrain != NO_TERRAIN) { int iTerrainYieldChangeTimes100 = pEntry->GetTerrainYieldChange(eTerrain, iI) * 100; @@ -7897,7 +7938,7 @@ int CvReligionAI::GetValidPlotYieldTimes100(CvBeliefEntry* pEntry, CvPlot* pPlot iModifier = 100; if (pEntry->RequiresImprovement() && eImprovement == NO_IMPROVEMENT) { - iModifier = 75; + iModifier = iRequiresImprovementModifier; } else if (pEntry->RequiresNoImprovement() && eImprovement != NO_IMPROVEMENT) { @@ -7920,7 +7961,7 @@ int CvReligionAI::GetValidPlotYieldTimes100(CvBeliefEntry* pEntry, CvPlot* pPlot iModifier = 100; if (pEntry->RequiresImprovement() && eImprovement == NO_IMPROVEMENT) { - iModifier = 75; + iModifier = iRequiresImprovementModifier; } else if (pEntry->RequiresNoImprovement() && eImprovement != NO_IMPROVEMENT) { @@ -8368,6 +8409,27 @@ int CvReligionAI::ScorePantheonBeliefAtCity(CvBeliefEntry* pEntry, CvCity* pCity iTempValue += iAvailabilityModifier * pEntry->GetCoastalCityYieldChange(iI); } + // Nearby terrain city yield change (max across terrain types - city qualifies once for any matching terrain) + { + int iMaxNearbyTerrainScore = 0; + for (int iTerrain = 0; iTerrain < GC.getNumTerrainInfos(); iTerrain++) + { + if (pEntry->GetNearbyTerrainYieldChange(iTerrain, iI) > 0) + { + if (pCity) + { + iAvailabilityModifier = (pCity->plot()->getTerrainType() == (TerrainTypes)iTerrain || pCity->IsAdjacentToTerrain((TerrainTypes)iTerrain)) ? 10 : 0; + } + else + { + iAvailabilityModifier = 3; + } + iMaxNearbyTerrainScore = max(iMaxNearbyTerrainScore, iAvailabilityModifier * pEntry->GetNearbyTerrainYieldChange(iTerrain, iI)); + } + } + iTempValue += iMaxNearbyTerrainScore; + } + // Trade route yield change if (pEntry->GetYieldChangeTradeRoute(iI) > 0) { @@ -8672,6 +8734,30 @@ int CvReligionAI::ScoreBeliefAtCity(CvBeliefEntry* pEntry, CvCity* pCity) const else iRtnValue += pEntry->GetUnitProductionModifier() / 2; + // UnitCombat loop + for(int iI = 0; iI < GC.getNumUnitCombatClassInfos(); iI++) + { + const UnitCombatTypes eUnitCombatClass = static_cast(iI); + if (pEntry->GetUnitCombatProductionModifiers(eUnitCombatClass) > 0) + { + if (eUnitCombatClass == GC.getInfoTypeForString("UNITCOMBAT_DIPLOMACY")) + { + if (m_pPlayer->GetPlayerTraits()->IsDiplomat()) + iRtnValue += pEntry->GetUnitCombatProductionModifiers(eUnitCombatClass); + else + iRtnValue += pEntry->GetUnitCombatProductionModifiers(eUnitCombatClass) / 2; + } + // assume military + else + { + if (m_pPlayer->GetPlayerTraits()->IsWarmonger() || m_pPlayer->GetPlayerTraits()->IsExpansionist()) + iRtnValue += pEntry->GetUnitCombatProductionModifiers(eUnitCombatClass); + else + iRtnValue += pEntry->GetUnitCombatProductionModifiers(eUnitCombatClass) / 2; + } + } + } + // River happiness if (pCity->plot()->isRiver()) { @@ -8978,6 +9064,27 @@ int CvReligionAI::ScoreBeliefAtCity(CvBeliefEntry* pEntry, CvCity* pCity) const iRtnValue += iTempValue; } + // Nearby terrain city yield change (max across terrain types - city qualifies once for any matching terrain) + { + int iMaxNearbyTerrainYield = 0; + for (int iTerrain = 0; iTerrain < GC.getNumTerrainInfos(); iTerrain++) + { + if (pCity->plot()->getTerrainType() == (TerrainTypes)iTerrain || pCity->IsAdjacentToTerrain((TerrainTypes)iTerrain)) + { + iMaxNearbyTerrainYield = max(iMaxNearbyTerrainYield, pEntry->GetNearbyTerrainYieldChange(iTerrain, iI)); + } + } + if (iMaxNearbyTerrainYield > 0) + { + iTempValue = iMaxNearbyTerrainYield * iEraBonus; + if (iMinPop > 0 && pCity->getPopulation() >= iMinPop) + { + iTempValue *= 2; + } + iRtnValue += iTempValue; + } + } + // Trade route yield change iTempValue = pEntry->GetYieldChangeTradeRoute(iI) * iEraBonus; if (iMinPop > 0 && pCity->getPopulation() >= iMinPop) @@ -9279,7 +9386,6 @@ int CvReligionAI::ScoreBeliefForPlayer(CvBeliefEntry* pEntry, bool bReturnConque } } - if (iNumNeighbors > 0) { if (pEntry->GetFaithFromKills() > 0) @@ -9349,14 +9455,22 @@ int CvReligionAI::ScoreBeliefForPlayer(CvBeliefEntry* pEntry, bool bReturnConque iWarTemp += (pEntry->GetCombatModifierEnemyCities() * iNumNeighbors) * 2; } - if (pEntry->GetCombatVersusOtherReligionOwnLands() > 0) + if (pEntry->GetCombatBonusOwnLands() > 0) + { + iWarTemp += (pEntry->GetCombatBonusOwnLands() * iIdealEmpireSize * iNumNeighbors) / 6; + } + if (pEntry->GetCombatBonusVersusOtherReligionOwnLands() > 0) { - iWarTemp += (pEntry->GetCombatVersusOtherReligionOwnLands() * iIdealEmpireSize * iNumNeighbors) / 4; + iWarTemp += (pEntry->GetCombatBonusVersusOtherReligionOwnLands() * iIdealEmpireSize * iNumNeighbors) / 6; } - if (pEntry->GetCombatVersusOtherReligionTheirLands() > 0) + if (pEntry->GetCombatBonusTheirLands() > 0) + { + iWarTemp += (pEntry->GetCombatBonusTheirLands() * iNumNeighbors) * 4 / 3; + } + if (pEntry->GetCombatBonusVersusOtherReligionTheirLands() > 0) { - iWarTemp += (pEntry->GetCombatVersusOtherReligionTheirLands() * iNumNeighbors) * 2; + iWarTemp += (pEntry->GetCombatBonusVersusOtherReligionTheirLands() * iNumNeighbors) * 4 / 3; } MilitaryAIStrategyTypes eStrategyBarbs = (MilitaryAIStrategyTypes)GC.getInfoTypeForString("MILITARYAISTRATEGY_ERADICATE_BARBARIANS"); @@ -9387,11 +9501,11 @@ int CvReligionAI::ScoreBeliefForPlayer(CvBeliefEntry* pEntry, bool bReturnConque { iWarTemp *= 2; } - if (pReligion->m_Beliefs.GetCombatVersusOtherReligionOwnLands(m_pPlayer->GetID(), pHolyCity) > 0) + if (pReligion->m_Beliefs.GetCombatBonusOwnLands(m_pPlayer->GetID(), pHolyCity) > 0 || pReligion->m_Beliefs.GetCombatBonusVersusOtherReligionOwnLands(m_pPlayer->GetID(), pHolyCity) > 0) { iWarTemp *= 2; } - if (pReligion->m_Beliefs.GetCombatVersusOtherReligionTheirLands(m_pPlayer->GetID(), pHolyCity) > 0) + if (pReligion->m_Beliefs.GetCombatBonusTheirLands(m_pPlayer->GetID(), pHolyCity) > 0 || pReligion->m_Beliefs.GetCombatBonusVersusOtherReligionTheirLands(m_pPlayer->GetID(), pHolyCity) > 0) { iWarTemp *= 2; } @@ -10069,6 +10183,18 @@ int CvReligionAI::ScoreBeliefForPlayer(CvBeliefEntry* pEntry, bool bReturnConque // Other /////////////////// + int iImprovementTemp = 0; + + if (pEntry->GetCivilianWorkRate() > 0) + { + if (pPlayerTraits->IsExpansionist()) + iImprovementTemp += pEntry->GetCivilianWorkRate() * 2; + else if (pPlayerTraits->IsSmaller()) + iImprovementTemp += pEntry->GetCivilianWorkRate() / 4; + else + iImprovementTemp += pEntry->GetCivilianWorkRate(); + } + int iPolicyGainTemp = 0; bool bHasPolicyBelief = false; @@ -10154,7 +10280,7 @@ int CvReligionAI::ScoreBeliefForPlayer(CvBeliefEntry* pEntry, bool bReturnConque } //sanity check - we don't want buildings to be the sole reason we get a founder. - if (pEntry->IsFounderBelief() && (iWarTemp + iHappinessTemp + iGoldenAgeTemp + iScienceTemp + iGPTemp + iCultureTemp + iPolicyGainTemp + iGoldTemp + iSpreadTemp + iDiploTemp) <= 250) + if (pEntry->IsFounderBelief() && (iWarTemp + iHappinessTemp + iGoldenAgeTemp + iScienceTemp + iGPTemp + iCultureTemp + iPolicyGainTemp + iGoldTemp + iSpreadTemp + iDiploTemp + iImprovementTemp) <= 250) iBuildingTemp /= 100; if (pPlayerTraits->IsWarmonger()) @@ -10227,7 +10353,7 @@ int CvReligionAI::ScoreBeliefForPlayer(CvBeliefEntry* pEntry, bool bReturnConque iGoldenAgeTemp /= 100; if (bReturnConquest) - return(iWarTemp + iHappinessTemp + iGoldenAgeTemp + iBuildingTemp) / 2; + return(iWarTemp + iHappinessTemp + iGoldenAgeTemp + iBuildingTemp + iImprovementTemp) / 2; iCultureTemp *= (100 + (iCultureInterest / 10)); iCultureTemp /= 100; @@ -10268,7 +10394,7 @@ int CvReligionAI::ScoreBeliefForPlayer(CvBeliefEntry* pEntry, bool bReturnConque if (bReturnScience) return(iGoldTemp + iScienceTemp + iBuildingTemp + iGPTemp + iGoldenAgeTemp) / 2; - iRtnValue = (iWarTemp + iHappinessTemp + iGoldenAgeTemp + iScienceTemp + iGPTemp + iCultureTemp + iPolicyGainTemp + iGoldTemp + iSpreadTemp + iBuildingTemp + iDiploTemp); + iRtnValue = (iWarTemp + iHappinessTemp + iGoldenAgeTemp + iScienceTemp + iGPTemp + iCultureTemp + iPolicyGainTemp + iGoldTemp + iSpreadTemp + iBuildingTemp + iDiploTemp + iImprovementTemp); if (iMissionary > 0 && bNoMissionary) iRtnValue /= 100; diff --git a/CvGameCoreDLL_Expansion2/CvTacticalAI.cpp b/CvGameCoreDLL_Expansion2/CvTacticalAI.cpp index f0646a6c32..a0a6e1dd2f 100644 --- a/CvGameCoreDLL_Expansion2/CvTacticalAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvTacticalAI.cpp @@ -2156,7 +2156,7 @@ CvUnit* SwitchEscort(CvUnit* pCivilian, CvPlot* pNewEscortPlot, CvUnit* pEscort, int iSlot = pThisArmy->RemoveUnit(pEscort->GetID(),true); if (iSlot>=0) { - pThisArmy->AddUnit(pPlotDefender->GetID(), iSlot, true); + pThisArmy->AddUnit(pPlotDefender->GetID(), iSlot, pThisArmy->GetSlotInfo(iSlot).m_requiredSlot); if (GC.getLogging() && GC.getAILogging()) { CvString strLogString; @@ -2442,7 +2442,7 @@ void CvTacticalAI::PlotArmyMovesEscort(CvArmyAI* pThisArmy) CvUnit* pDefender = pTurnTarget->getBestDefender(m_pPlayer->GetID()); if (pDefender && pDefender->getArmyID() == -1 && pDefender->getDomainType() == pCivilian->getDomainType()) { - pThisArmy->AddUnit(pDefender->GetID(), 1, true); + pThisArmy->AddUnit(pDefender->GetID(), 1, pThisArmy->GetSlotInfo(1).m_requiredSlot); if (GC.getLogging() && GC.getAILogging()) { CvString strLogString; @@ -7283,7 +7283,7 @@ bool ScoreAttackDamage(const CvTacticalPlot* tactPlot, const CvUnit* pUnit, cons // If we take the city, the unit also dies if (iPrevUnitHitPoints > 0) { - iGarrisonDamage = max(iGarrisonDamage, iPrevUnitHitPoints + 5); + iGarrisonDamage = iPrevUnitHitPoints; } } else @@ -7438,7 +7438,7 @@ bool ScoreAttackDamage(const CvTacticalPlot* tactPlot, const CvUnit* pUnit, cons //don't be too precise because of randomness in damage calculation added later //if it doesn't work out we can try again //better than assuming it's not a kill and having a melee unit end up in a bad place - if (iPrevCityHitPoints > 0 && iCityDamageDealt >= iPrevCityHitPoints - 1) + if (iPrevCityHitPoints > 0 && iCityDamageDealt >= iPrevCityHitPoints) { bScoreReduction = false; @@ -7448,21 +7448,29 @@ bool ScoreAttackDamage(const CvTacticalPlot* tactPlot, const CvUnit* pUnit, cons if (!bRanged && pUnit->IsCanAttackWithMove()) bCityKill = true; else - iCityDamageDealt = iPrevCityHitPoints - 1; + { + if (iPrevCityHitPoints > 1) + iBonusScore += min(iCityDamageDealt - iPrevCityHitPoints + 1, 3); + iCityDamageDealt = min(iCityDamageDealt, iPrevCityHitPoints - 1); + } } for (SUnitIDValueContainer::const_iterator it = unitDamageDealt.begin(); it != unitDamageDealt.end(); ++it) { int iPrevUnitHitPoints = prevUnitHitPoints.GetValue((*it).first); int iUnitDamageDealt = (*it).second; + if (iUnitDamageDealt > iPrevUnitHitPoints) + iBonusScore += min(iUnitDamageDealt - iPrevUnitHitPoints, 10); + actualDamageDealt.SetValue((*it).first, min(iPrevUnitHitPoints, iUnitDamageDealt)); iTotalUnitDamageDealt += iUnitDamageDealt; iTotalActualUnitDamageDealt += min(iPrevUnitHitPoints, iUnitDamageDealt); - if (iPrevUnitHitPoints > 0 && iUnitDamageDealt >= iPrevUnitHitPoints - 1) + if (iPrevUnitHitPoints > 0 && iUnitDamageDealt >= iPrevUnitHitPoints) { - bUnitKill = true; + if (pEnemyUnit && pEnemyUnit->GetID() == (*it).first) + bUnitKill = true; iBonusScore += pUnit->GetExtraXPOnKill(); @@ -7524,7 +7532,7 @@ bool ScoreAttackDamage(const CvTacticalPlot* tactPlot, const CvUnit* pUnit, cons { if (bRanged) result->eAssignmentType = A_RANGEKILL; - else if (pUnitPlot->isFortification(pUnit->getTeam())) + else if (pUnitPlot->isFortification(pUnit->getTeam()) || iPrevCityHitPoints > 0) result->eAssignmentType = A_MELEEKILL_NO_ADVANCE; else result->eAssignmentType = A_MELEEKILL; @@ -7537,7 +7545,7 @@ bool ScoreAttackDamage(const CvTacticalPlot* tactPlot, const CvUnit* pUnit, cons result->iCityDamage = iCityDamageDealt; //for melee units we check if the damage received is worth it ... - if (iDamageReceived > 0) + if (iDamageReceived > 0 && gSafePlotCount[pUnit->GetID()] > 0) { float fAggFactor = /*100*/ GD_INT_GET(COMBAT_AI_OFFENSE_DAMAGEWEIGHT) / 100.f; switch (eAggLvl) @@ -7593,10 +7601,7 @@ bool ScoreAttackDamage(const CvTacticalPlot* tactPlot, const CvUnit* pUnit, cons //todo: consider pEnemy->getUnitInfo().GetProductionCost() and pEnemy->GetBaseCombatStrength() //todo: normalize damage done by max hp to balance between city attacks and unit attacks? - int iActualCityDamageDealt = min(iCityDamageDealt, iPrevCityHitPoints); - - if (pUnit->IsCanAttackRanged() && iCityDamageDealt > 0 && iCityDamageDealt >= iPrevCityHitPoints) - iActualCityDamageDealt = iPrevCityHitPoints - 1; + int iActualCityDamageDealt = min(iCityDamageDealt, iPrevCityHitPoints + (pUnit->IsCanAttackRanged() ? 2 : 10)); int iActualDamageTaken = min(iDamageReceived, pUnit->GetCurrHitPoints() - iSelfDamage); if (iActualDamageTaken < 0) @@ -8060,7 +8065,7 @@ static STacticalAssignment* ScorePlotForCombatUnitMove(const SUnitStats& unit, c iPlotScore += 3; // if we are not in a good spot, try moving - if (iPlotScore <= 6 && pTestPlot == pUnit->plot()) + if (iPlotScore <= 6 && pTestPlot == pUnit->plot() && pUnit->getDamage() + unit.iSelfDamage < 10) iPlotScore -= 2; // Move melee units in to capture cities @@ -8198,7 +8203,7 @@ static STacticalAssignment* ScorePlotForCombatUnitMove(const SUnitStats& unit, c if (iDangerScore == INT_MAX) { - if (evalMode == EM_INITIAL || gSafePlotCount[unit.iUnitID] > 0) + if (gSafePlotCount[unit.iUnitID] > 0) return result; //don't do it //unit has no safe plots and must end turn somewhere (EM_FINAL) @@ -8445,7 +8450,7 @@ static STacticalAssignment* ScorePlotForMeleeAttack(const SUnitStats& unit, cons } // barbarian camp - if (pEnemyPlot->getRevealedImprovementType(pUnit->getTeam()) == GD_INT_GET(BARBARIAN_CAMP_IMPROVEMENT)) + if (result->eAssignmentType == A_MELEEKILL && pEnemyPlot->getRevealedImprovementType(pUnit->getTeam()) == GD_INT_GET(BARBARIAN_CAMP_IMPROVEMENT)) iBonusScore += 100; result->AddScore(0, iBonusScore, 0); @@ -9000,6 +9005,8 @@ void CvTacticalPosition::getPreferredAssignmentsForUnit(const SUnitStats& unit, CvTacticalPosition tempPosition; int iOldPlotDistanceToTarget = bTargetDistanceRelevant ? TacticalAIHelpers::GetPlotDistanceToTarget(unit.iPlotIndex, pUnit->getDomainType()) : 0; + if (iOldPlotDistanceToTarget < TACTICAL_COMBAT_MAX_TARGET_DISTANCE) + iOldPlotDistanceToTarget = TACTICAL_COMBAT_MAX_TARGET_DISTANCE; //check moves and melee attacks first const ReachablePlots& reachablePlots = getReachablePlotsForUnit(unit); @@ -9050,7 +9057,7 @@ void CvTacticalPosition::getPreferredAssignmentsForUnit(const SUnitStats& unit, { //try pillaging as an intermediate step STacticalAssignment* pillaging = ScorePlotForPillageMove(unit, testPlot, it->iMovesLeft, *this); - if (pillaging->Score() > 0) + if (pillaging->Score() > 0 && pillaging->IsAcceptable()) { GetNextPosition(*this, pillaging, tempPosition); SUnitStats tempUnit = GetNextUnit(unit, pillaging); @@ -9079,7 +9086,7 @@ void CvTacticalPosition::getPreferredAssignmentsForUnit(const SUnitStats& unit, if (enemyPlot && enemyPlot->isEnemy()) { STacticalAssignment* rangedAttack = ScorePlotForRangedAttack(unit, assumedUnitPlot, enemyPlot, *this); - if (rangedAttack->Score() <= 0) + if (rangedAttack->Score() <= 0 || !rangedAttack->IsAcceptable()) continue; GetNextPosition(*this, rangedAttack, tempPosition); @@ -9101,7 +9108,6 @@ void CvTacticalPosition::getPreferredAssignmentsForUnit(const SUnitStats& unit, STacticalAssignment* stayAfterAttack = ScorePlotForMove(tempUnit, testPlot, tempPosition, EM_INTERMEDIATE); rangedAttack->AddScore(stayAfterAttack); - gPossibleRangedAttacks.push_back(OptionWithScore(rangedAttack, rangedAttack->Score())); } } @@ -9153,10 +9159,12 @@ void CvTacticalPosition::getPreferredAssignmentsForUnit(const SUnitStats& unit, int iMoveTowardsTargetScore = 0; - if (getNumEnemies() == 0 && bTargetDistanceRelevant) + if (bTargetDistanceRelevant) { // Try to move towards the target int iNewPlotDistanceToTarget = TacticalAIHelpers::GetPlotDistanceToTarget(it->iPlotIndex, pUnit->getDomainType()); + if (iNewPlotDistanceToTarget < TACTICAL_COMBAT_MAX_TARGET_DISTANCE) + iNewPlotDistanceToTarget = TACTICAL_COMBAT_MAX_TARGET_DISTANCE; iMoveTowardsTargetScore = (iOldPlotDistanceToTarget - iNewPlotDistanceToTarget) * 40; if (iNewPlotDistanceToTarget > iOldPlotDistanceToTarget) continue; @@ -9819,17 +9827,18 @@ bool CvTacticalPosition::isKillOrImprovedPosition() const //note that RESTARTS are ignored here ... just hope that we still find good moves after the restart } + bool bMovingTowardsTarget = (bTargetDistanceRelevant && iAfter < iBefore); if (haveEnemies()) { //staying in place and bombarding is fine! //staying in place and healing is also progress TODO: should we check whether we are healing more than we are taking damage? //note that retreating a damaged unit counts as positive because it should be MS_THIRDLINE - return (iNegative < iPositive) || (iNegative == iPositive && (iAttacksNoMove > 0 || iHealingUnits > 1)); + return (iNegative < iPositive) || (iNegative == iPositive && (bMovingTowardsTarget || iAttacksNoMove > 0 || iHealingUnits > 1 || (iHealingUnits == root->GetNumAvailableUnits()))); } else { //did we get closer to the target plot? - return ((bTargetDistanceRelevant && iAfter < iBefore) || iHealingUnits > 0); + return bMovingTowardsTarget || iHealingUnits > 0; } } @@ -11302,7 +11311,7 @@ void CvSupportPosition::getPreferredAssignmentsForUnit(const SUnitStats& unit, i int iMoveTowardsTargetScore = 0; - if (GetFinalTacticalPosition()->getNumEnemies() == 0 && GetFinalTacticalPosition()->IsTargetToDistanceRelevant()) + if (GetFinalTacticalPosition()->IsTargetToDistanceRelevant()) { // Try to move towards the target int iNewPlotDistanceToTarget = TacticalAIHelpers::GetPlotDistanceToTarget(it->iPlotIndex, pUnit->getDomainType()); diff --git a/CvGameCoreDLL_Expansion2/CvTeam.cpp b/CvGameCoreDLL_Expansion2/CvTeam.cpp index 15a7b8c386..137c8e2d8b 100644 --- a/CvGameCoreDLL_Expansion2/CvTeam.cpp +++ b/CvGameCoreDLL_Expansion2/CvTeam.cpp @@ -194,7 +194,7 @@ void CvTeam::uninit() m_iDefensiveEmbarkCount = 0; m_iEmbarkedAllWaterPassageCount = 0; m_iNumNaturalWondersDiscovered = 0; - m_iNumLandmarksBuilt = 0; + m_iHappinessFromImprovements = 0; m_iBestPossibleRoute = NO_ROUTE; m_iNumMinorCivsAttacked = 0; m_iBuildingDefenseModifier = 0; @@ -3994,19 +3994,18 @@ void CvTeam::ChangeNumNaturalWondersDiscovered(int iChange) } // -------------------------------------------------------------------------------- -int CvTeam::GetNumLandmarksBuilt() const +int CvTeam::GetHappinessFromImprovements() const { - return m_iNumLandmarksBuilt; + return m_iHappinessFromImprovements; } // -------------------------------------------------------------------------------- -void CvTeam::ChangeNumLandmarksBuilt(int iChange) +void CvTeam::ChangeHappinessFromImprovements(int iChange) { if (iChange != 0) { - m_iNumLandmarksBuilt += iChange; + m_iHappinessFromImprovements += iChange; } - ASSERT(GetNumLandmarksBuilt() >= 0); } // -------------------------------------------------------------------------------- @@ -9110,7 +9109,7 @@ void CvTeam::Serialize(Team& team, Visitor& visitor) visitor(team.m_iDefensiveEmbarkCount); visitor(team.m_iEmbarkedAllWaterPassageCount); visitor(team.m_iNumNaturalWondersDiscovered); - visitor(team.m_iNumLandmarksBuilt); + visitor(team.m_iHappinessFromImprovements); visitor(team.m_iBestPossibleRoute); visitor(team.m_iNumMinorCivsAttacked); visitor(team.m_iBuildingDefenseModifier); diff --git a/CvGameCoreDLL_Expansion2/CvTeam.h b/CvGameCoreDLL_Expansion2/CvTeam.h index 6547214ac2..69010bd3f4 100644 --- a/CvGameCoreDLL_Expansion2/CvTeam.h +++ b/CvGameCoreDLL_Expansion2/CvTeam.h @@ -234,8 +234,8 @@ class CvTeam int GetNumNaturalWondersDiscovered() const; void ChangeNumNaturalWondersDiscovered(int iChange); - int GetNumLandmarksBuilt() const; - void ChangeNumLandmarksBuilt(int iChange); + int GetHappinessFromImprovements() const; + void ChangeHappinessFromImprovements(int iChange); bool isHasMet(TeamTypes eIndex) const; void makeHasMet(TeamTypes eIndex, bool bSuppressMessages); @@ -517,7 +517,7 @@ class CvTeam int m_iDefensiveEmbarkCount; int m_iEmbarkedAllWaterPassageCount; int m_iNumNaturalWondersDiscovered; - int m_iNumLandmarksBuilt; + int m_iHappinessFromImprovements; int m_iBestPossibleRoute; int m_iNumMinorCivsAttacked; int m_iBuildingDefenseModifier; diff --git a/CvGameCoreDLL_Expansion2/CvTreasury.cpp b/CvGameCoreDLL_Expansion2/CvTreasury.cpp index fcc9fc051f..6980c0102b 100644 --- a/CvGameCoreDLL_Expansion2/CvTreasury.cpp +++ b/CvGameCoreDLL_Expansion2/CvTreasury.cpp @@ -415,7 +415,7 @@ int CvTreasury::CalculateGrossGoldTimes100() iNetGold += m_pPlayer->GetYieldPerTurnFromMinors(YIELD_GOLD) * 100; } - // Annexed City-States (Rome UA) + // Annexed City-States iNetGold += m_pPlayer->GetYieldPerTurnFromAnnexedMinorsTimes100(YIELD_GOLD); //Espionage Events diff --git a/CvGameCoreDLL_Expansion2/CvUnit.cpp b/CvGameCoreDLL_Expansion2/CvUnit.cpp index 02049d0223..2192ba11fc 100644 --- a/CvGameCoreDLL_Expansion2/CvUnit.cpp +++ b/CvGameCoreDLL_Expansion2/CvUnit.cpp @@ -4606,17 +4606,17 @@ bool CvUnit::canEnterTerritory(TeamTypes eTeam, bool bEndTurn) const // Barbarians cannot enter owned plots in the beginning return false; + if (isRivalTerritory() || isTrade()) + return true; + TeamTypes eMyTeam = GET_PLAYER(getOwner()).getTeam(); CvTeam& kMyTeam = GET_TEAM(eMyTeam); CvTeam& kTheirTeam = GET_TEAM(eTeam); - if (isRivalTerritory() || isTrade()) - return true; - if (MOD_GLOBAL_CS_OVERSEAS_TERRITORY && eMyTeam != eTeam && kTheirTeam.isMinorCiv()) { - CvUnitEntry* pkUnitEntry = GC.getUnitInfo(getUnitType()); - if (pkUnitEntry->GetDefaultUnitAIType() != UNITAI_MESSENGER) + UnitAITypes eUnitAI = GC.getUnitInfo(getUnitType())->GetDefaultUnitAIType(); + if (eUnitAI != UNITAI_MESSENGER && eUnitAI != UNITAI_DIPLOMAT) { PlayerTypes eMinor = kTheirTeam.getLeaderID(); ASSERT(eMinor != NO_PLAYER); @@ -6026,20 +6026,12 @@ void CvUnit::scrap(bool bDelay) kOwner.GetTreasury()->ChangeGold(iGold); } - // TODO: change to instant yield - if (isCultureFromExperienceDisbandUpgrade()) + if (getCultureFromExperienceDisbandUpgrade() > 0) { - int iExperience = getExperienceTimes100() / 100; - if (iExperience > 0) - { - kOwner.changeJONSCulture(iExperience); - if (getOwner() == GC.getGame().getActivePlayer()) - { - char text[256] = { 0 }; - sprintf_s(text, "[COLOR_MAGENTA]+%d[ENDCOLOR][ICON_CULTURE]", iExperience); - SHOW_PLOT_POPUP(plot(), getOwner(), text); - } - } + int iExperienceTimes100 = getExperienceTimes100(); + if (iExperienceTimes100 > 0) + kOwner.doInstantYield(INSTANT_YIELD_TYPE_SCRAP_OR_UPGRADE, false, NO_GREATPERSON, NO_BUILDING, iExperienceTimes100 * getCultureFromExperienceDisbandUpgrade(), + false, NO_PLAYER, plot(), false, getOriginCity(), getDomainType()==DOMAIN_SEA, false, false, YIELD_CULTURE, this); } kill(bDelay); @@ -14222,20 +14214,13 @@ CvUnit* CvUnit::DoUpgradeTo(UnitTypes eUnitType, bool bFree) LuaSupport::CallHook(pkScriptSystem, "UnitUpgraded", args.get(), bResult); } } - - if(isCultureFromExperienceDisbandUpgrade()) + + if (getCultureFromExperienceDisbandUpgrade() > 0) { - int iExperience = getExperienceTimes100() / 100; - if(iExperience > 0) - { - thisPlayer.changeJONSCulture(iExperience); - if (getOwner() == GC.getGame().getActivePlayer()) - { - char text[256] = { 0 }; - sprintf_s(text, "[COLOR_MAGENTA]+%d[ENDCOLOR][ICON_CULTURE]", iExperience); - SHOW_PLOT_POPUP(plot(),getOwner(),text); - } - } + int iExperienceTimes100 = getExperienceTimes100(); + if (iExperienceTimes100 > 0) + thisPlayer.doInstantYield(INSTANT_YIELD_TYPE_SCRAP_OR_UPGRADE, false, NO_GREATPERSON, NO_BUILDING, iExperienceTimes100 * getCultureFromExperienceDisbandUpgrade(), + false, NO_PLAYER, plot(), false, getOriginCity(), getDomainType()==DOMAIN_SEA, false, true, YIELD_CULTURE, this); } if (MOD_SQUADS && GetSquadNumber() > -1) @@ -15678,6 +15663,19 @@ int CvUnit::workRate(bool bMax, BuildTypes /*eBuild*/) const Modifiers += kPlayer.getWorkerSpeedModifier() + kPlayer.GetPlayerTraits()->GetWorkerSpeedModifier(); + if (IsCivilianUnit()) + { + CvCity* pOriginCity = getOriginCity(); + if (pOriginCity) + { + const CvReligion* pCityReligion = pOriginCity->GetCityReligions()->GetMajorityReligion(); + if(pCityReligion) + { + Modifiers += pCityReligion->m_Beliefs.GetCivilianWorkRate(getOwner(), pOriginCity); + } + } + } + if (kPlayer.isMajorCiv()) { Modifiers += kPlayer.getHandicapInfo().getWorkRateModifier(); @@ -16297,17 +16295,25 @@ int CvUnit::GetGenericMeleeStrengthModifier(const CvUnit* pOtherUnit, const CvPl { CvCity* pHolyCity = pReligion->GetHolyCity(); - //Full bonus against different religion - int iScaler = (eTheirReligion != eOwnedReligion) ? 1 : 2; - int iOtherOwn = pReligion->m_Beliefs.GetCombatVersusOtherReligionOwnLands(getOwner(), pHolyCity); - int iOtherTheir = pReligion->m_Beliefs.GetCombatVersusOtherReligionTheirLands(getOwner(), pHolyCity); + int iOwn = pReligion->m_Beliefs.GetCombatBonusOwnLands(getOwner(), pHolyCity); + int iOwnOtherReligion = pReligion->m_Beliefs.GetCombatBonusVersusOtherReligionOwnLands(getOwner(), pHolyCity); + int iTheir = pReligion->m_Beliefs.GetCombatBonusTheirLands(getOwner(), pHolyCity); + int iTheirOtherReligion = pReligion->m_Beliefs.GetCombatBonusVersusOtherReligionTheirLands(getOwner(), pHolyCity); // Bonus in own land - if (iOtherOwn > 0 && pBattlePlot->IsFriendlyTerritory(getOwner())) - iModifier += iOtherOwn/iScaler; + if ((iOwn > 0 || iOwnOtherReligion > 0) && pBattlePlot->IsFriendlyTerritory(getOwner())) + { + iModifier += iOwn; + if (eOwnedReligion != eTheirReligion) + iModifier += iOwnOtherReligion; + } // Bonus in their lands - if (iOtherTheir > 0 && pBattlePlot->IsFriendlyTerritory(pOtherUnit->getOwner())) - iModifier += iOtherTheir/iScaler; + if ((iTheir > 0 || iTheirOtherReligion > 0) && pBattlePlot->IsFriendlyTerritory(pOtherUnit->getOwner())) + { + iModifier += iTheir; + if (eOwnedReligion != eTheirReligion) + iModifier += iTheirOtherReligion; + } } } } @@ -17154,17 +17160,25 @@ int CvUnit::GetMaxRangedCombatStrength(const CvUnit* pOtherUnit, const CvCity* p { CvCity* pHolyCity = pReligion->GetHolyCity(); - //Full bonus against different religion - int iScaler = (eTheirReligion != eOwnedReligion) ? 1 : 2; - int iOtherOwn = pReligion->m_Beliefs.GetCombatVersusOtherReligionOwnLands(getOwner(), pHolyCity); - int iOtherTheir = pReligion->m_Beliefs.GetCombatVersusOtherReligionTheirLands(getOwner(), pHolyCity); + int iOwn = pReligion->m_Beliefs.GetCombatBonusOwnLands(getOwner(), pHolyCity); + int iOwnOtherReligion = pReligion->m_Beliefs.GetCombatBonusVersusOtherReligionOwnLands(getOwner(), pHolyCity); + int iTheir = pReligion->m_Beliefs.GetCombatBonusTheirLands(getOwner(), pHolyCity); + int iTheirOtherReligion = pReligion->m_Beliefs.GetCombatBonusVersusOtherReligionTheirLands(getOwner(), pHolyCity); // Bonus in own land - if (iOtherOwn > 0 && pTargetPlot->IsFriendlyTerritory(getOwner())) - iModifier += iOtherOwn/iScaler; + if ((iOwn > 0 || iOwnOtherReligion > 0) && pTargetPlot->IsFriendlyTerritory(getOwner())) + { + iModifier += iOwn; + if (eOwnedReligion != eTheirReligion) + iModifier += iOwnOtherReligion; + } // Bonus in their lands - if (iOtherTheir > 0 && pTargetPlot->IsFriendlyTerritory(pOtherUnit->getOwner())) - iModifier += iOtherTheir/iScaler; + if ((iTheir > 0 || iTheirOtherReligion > 0) && pTargetPlot->IsFriendlyTerritory(pOtherUnit->getOwner())) + { + iModifier += iTheir; + if (eOwnedReligion != eTheirReligion) + iModifier += iTheirOtherReligion; + } } } } @@ -21828,12 +21842,12 @@ void CvUnit::changeExperienceTimes100(int iChangeTimes100, int iMax, bool bFromC } if (eNearestMinor != NO_PLAYER) { - int iInfluenceGained = iUnitExperienceTimes100 * GetInfluenceFromCombatXPTimes100() / 100; - GET_PLAYER(eNearestMinor).GetMinorCivAI()->ChangeFriendshipWithMajorTimes100(getOwner(), iInfluenceGained); + int iInfluenceGainedTimes100 = GetInfluenceFromCombatXPTimes100() * iUnitExperienceTimes100 / 100; + GET_PLAYER(eNearestMinor).GetMinorCivAI()->ChangeFriendshipWithMajorTimes100(getOwner(), iInfluenceGainedTimes100); if(getOwner() == GC.getGame().getActivePlayer()) { char text[256] = {0}; - sprintf_s(text, "[COLOR_WHITE]+%d[ENDCOLOR][ICON_INFLUENCE]", iInfluenceGained); + sprintf_s(text, "[COLOR_WHITE]+%d[ENDCOLOR][ICON_INFLUENCE]", iInfluenceGainedTimes100 / 100); SHOW_PLOT_POPUP(plot(), getOwner(), text); } } @@ -33914,10 +33928,10 @@ FDataStream& operator>>(FDataStream& loadFrom, CvUnit& writeTo) return loadFrom; } // -------------------------------------------------------------------------------- -bool CvUnit::isCultureFromExperienceDisbandUpgrade() const +int CvUnit::getCultureFromExperienceDisbandUpgrade() const { VALIDATE_OBJECT(); - return getUnitInfo().IsCultureFromExperienceDisbandUpgrade(); + return getUnitInfo().GetCultureFromExperienceDisbandUpgrade(); } bool CvUnit::isFreeUpgrade() const { diff --git a/CvGameCoreDLL_Expansion2/CvUnit.h b/CvGameCoreDLL_Expansion2/CvUnit.h index 12d73354e7..cbb720fecd 100644 --- a/CvGameCoreDLL_Expansion2/CvUnit.h +++ b/CvGameCoreDLL_Expansion2/CvUnit.h @@ -2010,7 +2010,7 @@ class CvUnit void changeYieldFromAncientRuins(YieldTypes eIndex, int iChange); int getYieldFromTRPlunder(YieldTypes eIndex) const; void changeYieldFromTRPlunder(YieldTypes eIndex, int iChange); - bool isCultureFromExperienceDisbandUpgrade() const; + int getCultureFromExperienceDisbandUpgrade() const; bool isFreeUpgrade() const; bool isUnitEraUpgrade() const; bool isReligiousUnit() const; diff --git a/CvGameCoreDLL_Expansion2/CvUnitClasses.cpp b/CvGameCoreDLL_Expansion2/CvUnitClasses.cpp index bbd29d8979..e8b6533924 100644 --- a/CvGameCoreDLL_Expansion2/CvUnitClasses.cpp +++ b/CvGameCoreDLL_Expansion2/CvUnitClasses.cpp @@ -51,7 +51,7 @@ CvUnitEntry::CvUnitEntry(void) : m_iProductionCostPerEra(0), m_iNumFreeLux(0), m_iBeliefUnlock(NO_BELIEF), - m_bCultureFromExperienceOnDisband(false), + m_iCultureFromExperienceOnDisband(0), m_bFreeUpgrade(false), m_bUnitEraUpgrade(false), m_bWarOnly(false), @@ -374,7 +374,7 @@ bool CvUnitEntry::CacheResults(Database::Results& kResults, CvDatabaseUtility& k szTextVal = kResults.GetText("BeliefRequired"); m_iBeliefUnlock = GC.getInfoTypeForString(szTextVal, true); - m_bCultureFromExperienceOnDisband = kResults.GetBool("CulExpOnDisbandUpgrade"); + m_iCultureFromExperienceOnDisband = kResults.GetInt("CulExpOnDisbandUpgrade"); m_bUnitEraUpgrade = kResults.GetBool("UnitEraUpgrade"); m_bWarOnly = kResults.GetBool("WarOnly"); m_bCopyYieldsFromExpendTile = kResults.GetBool("CopyYieldsFromExpendTile"); @@ -1328,9 +1328,9 @@ int CvUnitEntry::StackCombat() const { return m_iStackCombat; } -bool CvUnitEntry::IsCultureFromExperienceDisbandUpgrade() const +int CvUnitEntry::GetCultureFromExperienceDisbandUpgrade() const { - return m_bCultureFromExperienceOnDisband; + return m_iCultureFromExperienceOnDisband; } bool CvUnitEntry::IsUnitEraUpgrade() const { diff --git a/CvGameCoreDLL_Expansion2/CvUnitClasses.h b/CvGameCoreDLL_Expansion2/CvUnitClasses.h index 3a9d51ef22..c91e0af96d 100644 --- a/CvGameCoreDLL_Expansion2/CvUnitClasses.h +++ b/CvGameCoreDLL_Expansion2/CvUnitClasses.h @@ -182,7 +182,7 @@ class CvUnitEntry: public CvBaseInfo int GetLocalFaithCooldown() const; PromotionTypes GetFriendlyLandsPromotion() const; bool IsMounted() const; - bool IsCultureFromExperienceDisbandUpgrade() const; + int GetCultureFromExperienceDisbandUpgrade() const; bool IsUnitEraUpgrade() const; bool IsWarOnly() const; bool IsCopyYieldsFromExpendTile() const; @@ -343,7 +343,7 @@ class CvUnitEntry: public CvBaseInfo int m_iLocalFaithCooldown; int m_iFriendlyLandsPromotion; int m_iBeliefUnlock; - bool m_bCultureFromExperienceOnDisband; + int m_iCultureFromExperienceOnDisband; bool m_bIsConvertUnit; bool m_bUnitEraUpgrade; bool m_bWarOnly; @@ -460,4 +460,4 @@ void Read(FDataStream& kStream, int* paiUnitArray); void Write(FDataStream& kStream, int* paiUnitArray, int iArraySize); } -#endif //CIV5_UNIT_CLASSES_H \ No newline at end of file +#endif //CIV5_UNIT_CLASSES_H diff --git a/CvGameCoreDLL_Expansion2/CvUnitProductionAI.cpp b/CvGameCoreDLL_Expansion2/CvUnitProductionAI.cpp index 3946b1ec1f..16b4b0596e 100644 --- a/CvGameCoreDLL_Expansion2/CvUnitProductionAI.cpp +++ b/CvGameCoreDLL_Expansion2/CvUnitProductionAI.cpp @@ -931,13 +931,13 @@ int CvUnitProductionAI::CheckUnitBuildSanity(UnitTypes eUnit, bool bForOperation { iReligiousBonus += (pEntry->GetCombatModifierFriendlyCities()); } - if (pEntry->GetCombatVersusOtherReligionOwnLands() > 0 && (pkUnitEntry->GetUnitAIType(UNITAI_DEFENSE) || pkUnitEntry->GetUnitAIType(UNITAI_COUNTER))) + if ((pEntry->GetCombatBonusVersusOtherReligionOwnLands() > 0 || pEntry->GetCombatBonusOwnLands() > 0) && (pkUnitEntry->GetUnitAIType(UNITAI_DEFENSE) || pkUnitEntry->GetUnitAIType(UNITAI_COUNTER))) { - iReligiousBonus += (pEntry->GetCombatVersusOtherReligionOwnLands()); + iReligiousBonus += (pEntry->GetCombatBonusVersusOtherReligionOwnLands() + pEntry->GetCombatBonusOwnLands()) * 2 / 3; } - if (pEntry->GetCombatVersusOtherReligionTheirLands() > 0 && (pkUnitEntry->GetUnitAIType(UNITAI_ATTACK) || pkUnitEntry->GetUnitAIType(UNITAI_RANGED) || pkUnitEntry->GetUnitAIType(UNITAI_SKIRMISHER) || pkUnitEntry->GetUnitAIType(UNITAI_FAST_ATTACK) || pkUnitEntry->GetUnitAIType(UNITAI_CITY_BOMBARD))) + if ((pEntry->GetCombatBonusVersusOtherReligionTheirLands() > 0 || pEntry->GetCombatBonusTheirLands() > 0) && (pkUnitEntry->GetUnitAIType(UNITAI_ATTACK) || pkUnitEntry->GetUnitAIType(UNITAI_RANGED) || pkUnitEntry->GetUnitAIType(UNITAI_SKIRMISHER) || pkUnitEntry->GetUnitAIType(UNITAI_FAST_ATTACK) || pkUnitEntry->GetUnitAIType(UNITAI_CITY_BOMBARD))) { - iReligiousBonus += (pEntry->GetCombatVersusOtherReligionTheirLands()); + iReligiousBonus += (pEntry->GetCombatBonusVersusOtherReligionTheirLands() + pEntry->GetCombatBonusTheirLands()) * 2 / 3; } for (int iI = 0; iI < NUM_YIELD_TYPES; iI++) { diff --git a/CvGameCoreDLL_Expansion2/CvVotingClasses.cpp b/CvGameCoreDLL_Expansion2/CvVotingClasses.cpp index b442e95c06..8aef25c9fb 100644 --- a/CvGameCoreDLL_Expansion2/CvVotingClasses.cpp +++ b/CvGameCoreDLL_Expansion2/CvVotingClasses.cpp @@ -2106,6 +2106,7 @@ CvLeague::CvLeague(void) m_iConsecutiveHostedSessions = 0; m_eAssignedName = NO_LEAGUE_NAME; ZeroMemory(m_szCustomName, sizeof(m_szCustomName)); + m_iLastSessionEndedTurn = -1; m_eLastSpecialSession = NO_LEAGUE_SPECIAL_SESSION; m_eCurrentSpecialSession = NO_LEAGUE_SPECIAL_SESSION; m_vEnactProposalsOnHold.clear(); @@ -2129,6 +2130,7 @@ CvLeague::CvLeague(LeagueTypes eID) m_iConsecutiveHostedSessions = 0; m_eAssignedName = NO_LEAGUE_NAME; ZeroMemory(m_szCustomName, sizeof(m_szCustomName)); + m_iLastSessionEndedTurn = -1; m_eLastSpecialSession = NO_LEAGUE_SPECIAL_SESSION; m_eCurrentSpecialSession = NO_LEAGUE_SPECIAL_SESSION; m_vEnactProposalsOnHold.clear(); @@ -2181,6 +2183,7 @@ void CvLeague::Init(LeagueSpecialSessionTypes eGoverningSpecialSession) PRECONDITION(m_eID != NO_LEAGUE, "Initializing a CvLeague without a proper ID."); PRECONDITION(eGoverningSpecialSession != NO_LEAGUE_SPECIAL_SESSION); m_eLastSpecialSession = eGoverningSpecialSession; // Fake the last special session so we have data to inform the World Congress's status + m_iLastSessionEndedTurn = GC.getGame().getGameTurn(); AssignProposalPrivileges(); ResetTurnsUntilSession(); } @@ -2345,16 +2348,13 @@ bool CvLeague::IsInSpecialSession() void CvLeague::SetInSession(bool bInSession) { m_bInSession = bInSession; - if (bInSession) - { - } - else + if (!bInSession) { if (m_eCurrentSpecialSession != NO_LEAGUE_SPECIAL_SESSION) - { m_eLastSpecialSession = m_eCurrentSpecialSession; - } + m_eCurrentSpecialSession = NO_LEAGUE_SPECIAL_SESSION; + m_iLastSessionEndedTurn = GC.getGame().getGameTurn(); } } @@ -2481,6 +2481,11 @@ int CvLeague::GetVotesSpentThisSession() return iVotes; } +int CvLeague::GetLastSessionEndedTurn() const +{ + return m_iLastSessionEndedTurn; +} + LeagueSpecialSessionTypes CvLeague::GetLastSpecialSession() const { return m_eLastSpecialSession; @@ -4451,7 +4456,7 @@ void CvLeague::DoEnactProposalDiplomacy(ResolutionTypes eResolution, PlayerTypes { GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetWeDislikedTheirProposalTurn(eProposer, GC.getGame().getGameTurn()); GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetWeLikedTheirProposalTurn(eProposer, -1); - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*60*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_OVERWHELMING)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*60*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_OVERWHELMING)); GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetTheySanctionedUsTurn(eProposer, GC.getGame().getGameTurn()); GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetTheyUnsanctionedUsTurn(eProposer, -1); GET_PLAYER(it->ePlayer).GetDiplomacyAI()->ChangeRecentAssistValue(eProposer, -300); @@ -4477,16 +4482,16 @@ void CvLeague::DoEnactProposalDiplomacy(ResolutionTypes eResolution, PlayerTypes switch (eDesire) { case CvLeagueAI::DESIRE_ALWAYS: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*-60*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_OVERWHELMING)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*-60*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_OVERWHELMING)); break; case CvLeagueAI::DESIRE_STRONG_LIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*-45*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_STRONG)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*-45*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_STRONG)); break; case CvLeagueAI::DESIRE_LIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*-30*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*-30*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL)); break; case CvLeagueAI::DESIRE_WEAK_LIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*-15*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_WEAK)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*-15*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_WEAK)); break; case CvLeagueAI::DESIRE_NEUTRAL: case CvLeagueAI::DESIRE_WEAK_DISLIKE: @@ -4504,16 +4509,16 @@ void CvLeague::DoEnactProposalDiplomacy(ResolutionTypes eResolution, PlayerTypes switch (eDesire) { case CvLeagueAI::DESIRE_NEVER: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*60*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_OVERWHELMING)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*60*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_OVERWHELMING)); break; case CvLeagueAI::DESIRE_STRONG_DISLIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*45*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_STRONG)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*45*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_STRONG)); break; case CvLeagueAI::DESIRE_DISLIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)); break; case CvLeagueAI::DESIRE_WEAK_DISLIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*15*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_WEAK)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*15*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_WEAK)); break; case CvLeagueAI::DESIRE_NEUTRAL: case CvLeagueAI::DESIRE_WEAK_LIKE: @@ -4544,7 +4549,7 @@ void CvLeague::DoRepealProposalDiplomacy(int iTargetResolutionID, PlayerTypes eP { GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetWeLikedTheirProposalTurn(eProposer, GC.getGame().getGameTurn()); GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetWeDislikedTheirProposalTurn(eProposer, -1); - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*-60*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_OVERWHELMING)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*-60*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_OVERWHELMING)); // If they ever succeeded in sanctioning us, we don't apply the extra bonus if (!GET_PLAYER(it->ePlayer).GetDiplomacyAI()->HasEverSanctionedUs(eProposer)) @@ -4583,16 +4588,16 @@ void CvLeague::DoRepealProposalDiplomacy(int iTargetResolutionID, PlayerTypes eP switch (eDesire) { case CvLeagueAI::DESIRE_ALWAYS: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*-60*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_OVERWHELMING)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*-60*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_OVERWHELMING)); break; case CvLeagueAI::DESIRE_STRONG_LIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*-45*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_STRONG)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*-45*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_STRONG)); break; case CvLeagueAI::DESIRE_LIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*-30*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*-30*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL)); break; case CvLeagueAI::DESIRE_WEAK_LIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*-15*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_WEAK)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*-15*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL_WEAK)); break; case CvLeagueAI::DESIRE_NEUTRAL: case CvLeagueAI::DESIRE_WEAK_DISLIKE: @@ -4610,16 +4615,16 @@ void CvLeague::DoRepealProposalDiplomacy(int iTargetResolutionID, PlayerTypes eP switch (eDesire) { case CvLeagueAI::DESIRE_NEVER: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*60*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_OVERWHELMING)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*60*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_OVERWHELMING)); break; case CvLeagueAI::DESIRE_STRONG_DISLIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*45*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_STRONG)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*45*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_STRONG)); break; case CvLeagueAI::DESIRE_DISLIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)); break; case CvLeagueAI::DESIRE_WEAK_DISLIKE: - GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetLikedTheirProposalValue(eProposer, /*15*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_WEAK)); + GET_PLAYER(it->ePlayer).GetDiplomacyAI()->SetProposalAgreementValue(eProposer, /*15*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL_WEAK)); break; case CvLeagueAI::DESIRE_NEUTRAL: case CvLeagueAI::DESIRE_WEAK_LIKE: @@ -7510,7 +7515,7 @@ void CvLeague::FinishSession() if (GET_PLAYER(*playerIt).getTeam() == GET_PLAYER(eProposer).getTeam()) continue; - GET_PLAYER(eProposer).GetDiplomacyAI()->SetSupportedOurProposalValue(*playerIt, -iVotePercent); + GET_PLAYER(eProposer).GetDiplomacyAI()->SetProposalSupportDelta(*playerIt, -iVotePercent); GET_PLAYER(eProposer).GetDiplomacyAI()->SetTheySupportedOurProposalTurn(*playerIt, (GC.getGame().getGameTurn()+1)); GET_PLAYER(eProposer).GetDiplomacyAI()->SetTheyFoiledOurProposalTurn(*playerIt, -1); } @@ -7699,7 +7704,7 @@ void CvLeague::FinishSession() if (GET_PLAYER(*playerIt).getTeam() == GET_PLAYER(eProposer).getTeam() || GET_PLAYER(eProposer).GetLeagueAI()->IsSanctionProposal(&(*it), eProposer)) continue; - GET_PLAYER(eProposer).GetDiplomacyAI()->SetSupportedOurProposalValue(*playerIt, iVotePercent); + GET_PLAYER(eProposer).GetDiplomacyAI()->SetProposalSupportDelta(*playerIt, iVotePercent); GET_PLAYER(eProposer).GetDiplomacyAI()->SetTheyFoiledOurProposalTurn(*playerIt, (GC.getGame().getGameTurn()+1)); GET_PLAYER(eProposer).GetDiplomacyAI()->SetTheySupportedOurProposalTurn(*playerIt, -1); } @@ -7910,7 +7915,7 @@ void CvLeague::FinishSession() if (GET_PLAYER(*playerIt).getTeam() == GET_PLAYER(eProposer).getTeam()) continue; - GET_PLAYER(eProposer).GetDiplomacyAI()->SetSupportedOurProposalValue(*playerIt, -iVotePercent); + GET_PLAYER(eProposer).GetDiplomacyAI()->SetProposalSupportDelta(*playerIt, -iVotePercent); GET_PLAYER(eProposer).GetDiplomacyAI()->SetTheySupportedOurProposalTurn(*playerIt, (GC.getGame().getGameTurn()+1)); GET_PLAYER(eProposer).GetDiplomacyAI()->SetTheyFoiledOurProposalTurn(*playerIt, -1); } @@ -8129,7 +8134,7 @@ void CvLeague::FinishSession() if (GET_PLAYER(*playerIt).getTeam() == GET_PLAYER(eProposer).getTeam() || GET_PLAYER(eProposer).GetLeagueAI()->IsSanctionProposal(&(*it), eProposer)) continue; - GET_PLAYER(eProposer).GetDiplomacyAI()->SetSupportedOurProposalValue(*playerIt, iVotePercent); + GET_PLAYER(eProposer).GetDiplomacyAI()->SetProposalSupportDelta(*playerIt, iVotePercent); GET_PLAYER(eProposer).GetDiplomacyAI()->SetTheyFoiledOurProposalTurn(*playerIt, (GC.getGame().getGameTurn()+1)); GET_PLAYER(eProposer).GetDiplomacyAI()->SetTheySupportedOurProposalTurn(*playerIt, -1); } @@ -9279,6 +9284,7 @@ void CvLeague::Serialize(League& league, Visitor& visitor) visitor(league.m_iConsecutiveHostedSessions); visitor(league.m_eAssignedName); visitor(league.m_szCustomName); + visitor(league.m_iLastSessionEndedTurn); visitor(league.m_eLastSpecialSession); visitor(league.m_eCurrentSpecialSession); visitor(league.m_vEnactProposalsOnHold); @@ -10895,9 +10901,9 @@ CvLeagueAI::AlignmentLevels CvLeagueAI::EvaluateAlignment(PlayerTypes ePlayer, b } // Do we (dis)like their proposals or votes? - if (pDiplo->GetLikedTheirProposalValue(ePlayer) < 0) + if (pDiplo->GetProposalAgreementValue(ePlayer) < 0) { - if (pDiplo->GetLikedTheirProposalValue(ePlayer) < /*-30*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL)) + if (pDiplo->GetProposalAgreementValue(ePlayer) < /*-30*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL)) { iAlignment += 2; } @@ -10906,9 +10912,9 @@ CvLeagueAI::AlignmentLevels CvLeagueAI::EvaluateAlignment(PlayerTypes ePlayer, b iAlignment += 1; } } - else if (pDiplo->GetLikedTheirProposalValue(ePlayer) > 0) + else if (pDiplo->GetProposalAgreementValue(ePlayer) > 0) { - if (pDiplo->GetLikedTheirProposalValue(ePlayer) > /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)) + if (pDiplo->GetProposalAgreementValue(ePlayer) > /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL)) { iAlignment -= 2; } diff --git a/CvGameCoreDLL_Expansion2/CvVotingClasses.h b/CvGameCoreDLL_Expansion2/CvVotingClasses.h index 9ddb0cf0ab..9da8c732da 100644 --- a/CvGameCoreDLL_Expansion2/CvVotingClasses.h +++ b/CvGameCoreDLL_Expansion2/CvVotingClasses.h @@ -553,6 +553,7 @@ class CvLeague void ResetTurnsUntilSession(); int GetTurnsUntilVictorySession(); int GetVotesSpentThisSession(); + int GetLastSessionEndedTurn() const; LeagueSpecialSessionTypes GetLastSpecialSession() const; LeagueSpecialSessionTypes GetCurrentSpecialSession() const; bool CanStartSpecialSession(LeagueSpecialSessionTypes eSpecialSession); @@ -710,6 +711,7 @@ class CvLeague int m_iConsecutiveHostedSessions; LeagueNameTypes m_eAssignedName; char m_szCustomName[128]; + int m_iLastSessionEndedTurn; LeagueSpecialSessionTypes m_eLastSpecialSession; LeagueSpecialSessionTypes m_eCurrentSpecialSession; EnactProposalList m_vEnactProposalsOnHold; diff --git a/CvGameCoreDLL_Expansion2/CvWorldBuilderMapLoader.cpp b/CvGameCoreDLL_Expansion2/CvWorldBuilderMapLoader.cpp index 1848b0f53e..25a78e27d1 100644 --- a/CvGameCoreDLL_Expansion2/CvWorldBuilderMapLoader.cpp +++ b/CvGameCoreDLL_Expansion2/CvWorldBuilderMapLoader.cpp @@ -1126,6 +1126,7 @@ bool CvWorldBuilderMapLoader::InitMap() OutputDebugStringA("Calculating Areas...\n"); kMap.recalculateAreas(); + kMap.updateAdjacency(); OutputDebugStringA("Adding Scenario Elements...\n"); diff --git a/CvGameCoreDLL_Expansion2/Lua/CvLuaCity.cpp b/CvGameCoreDLL_Expansion2/Lua/CvLuaCity.cpp index 7d0ccdc9c0..fdf65dd2c5 100644 --- a/CvGameCoreDLL_Expansion2/Lua/CvLuaCity.cpp +++ b/CvGameCoreDLL_Expansion2/Lua/CvLuaCity.cpp @@ -1638,7 +1638,6 @@ int CvLuaCity::lGetBuildingYieldRateTimes100(lua_State* L) iYieldTimes100 += pkBuildingInfo->GetYieldPerFriendTimes100(eYield) * kPlayer.GetNumCSFriends(); iYieldTimes100 += pkBuildingInfo->GetYieldPerAllyTimes100(eYield) * kPlayer.GetNumCSAllies(); iYieldTimes100 += pkBuildingInfo->GetYieldChangePerMonopoly(eYield) * kPlayer.GetNumGlobalMonopolies() * 100; - iYieldTimes100 += (pkBuildingInfo->GetYieldChangePerBuilding(eYield) * pCity->GetCityBuildings()->GetNumBuildings() * 100).Truncate(); int iYieldPerReligion = pkBuildingInfo->GetYieldChangePerReligion(eYield); if (iYieldPerReligion != 0) diff --git a/CvGameCoreDLL_Expansion2/Lua/CvLuaEnums.cpp b/CvGameCoreDLL_Expansion2/Lua/CvLuaEnums.cpp index e6145a01ab..123bc1f571 100644 --- a/CvGameCoreDLL_Expansion2/Lua/CvLuaEnums.cpp +++ b/CvGameCoreDLL_Expansion2/Lua/CvLuaEnums.cpp @@ -929,14 +929,13 @@ int CvLuaEnums::pRegister(lua_State* L) RegisterEnum(INSTANT_YIELD_TYPE_TR_MOVEMENT_IN_FOREIGN); RegisterEnum(INSTANT_YIELD_TYPE_IMPROVEMENT_BUILD); RegisterEnum(INSTANT_YIELD_TYPE_LUA); - RegisterEnum(INSTANT_YIELD_TYPE_RESEARCH_AGREMEENT); + RegisterEnum(INSTANT_YIELD_TYPE_RESEARCH_AGREEMENT); RegisterEnum(INSTANT_YIELD_TYPE_REFUND); RegisterEnum(INSTANT_YIELD_TYPE_FAITH_REFUND); RegisterEnum(INSTANT_YIELD_TYPE_BIRTH_HOLY_CITY); RegisterEnum(INSTANT_YIELD_TYPE_TECH_RETROACTIVE); RegisterEnum(INSTANT_YIELD_TYPE_PILLAGE_UNIT); RegisterEnum(INSTANT_YIELD_TYPE_COMBAT_EXPERIENCE); - RegisterEnum(INSTANT_YIELD_TYPE_HEALING); RegisterEnum(INSTANT_YIELD_TYPE_SPY_IDENTIFY); RegisterEnum(INSTANT_YIELD_TYPE_SPY_DEFENSE_OR_ID); RegisterEnum(INSTANT_YIELD_TYPE_SPY_RIG_ELECTION); @@ -948,6 +947,8 @@ int CvLuaEnums::pRegister(lua_State* L) RegisterEnum(INSTANT_YIELD_TYPE_WLTKD_START); RegisterEnum(INSTANT_YIELD_TYPE_ANCIENT_RUIN); RegisterEnum(INSTANT_YIELD_TYPE_PLUNDER_TRADE_ROUTE); + RegisterEnum(INSTANT_YIELD_TYPE_HEALING); + RegisterEnum(INSTANT_YIELD_TYPE_SCRAP_OR_UPGRADE); RegisterEnum(NUM_INSTANT_YIELD_TYPES); EnumEnd(L); diff --git a/CvGameCoreDLL_Expansion2/Lua/CvLuaPlayer.cpp b/CvGameCoreDLL_Expansion2/Lua/CvLuaPlayer.cpp index 3ebff30272..550238db49 100644 --- a/CvGameCoreDLL_Expansion2/Lua/CvLuaPlayer.cpp +++ b/CvGameCoreDLL_Expansion2/Lua/CvLuaPlayer.cpp @@ -73,6 +73,7 @@ void CvLuaPlayer::PushMethods(lua_State* L, int t) Method(HasStrategicMonopoly); Method(GetResourcesMisc); Method(GetResourcesFromGP); + Method(GetFreeResourceFromPolicies); Method(GetResourcesFromCorporation); Method(GetResourceFromCSAlliances); Method(GetResourcesFromFranchises); @@ -410,6 +411,7 @@ void CvLuaPlayer::PushMethods(lua_State* L, int t) Method(GetExtraHappinessPerLuxury); Method(GetHappinessFromReligion); Method(GetHappinessFromNaturalWonders); + Method(GetHappinessFromImprovements); Method(GetHappinessFromLeagues); Method(GetHappinessFromMilitaryUnits); @@ -1812,6 +1814,16 @@ int CvLuaPlayer::lGetResourcesFromGP(lua_State* L) return 1; } // ----------------------------------------------------------------------------- +// int CvPlayer::GetFreeResourceFromPolicies(ResourceTypes eResource) +int CvLuaPlayer::lGetFreeResourceFromPolicies(lua_State* L) +{ + CvPlayerAI* pkPlayer = GetInstance(L); + const ResourceTypes eResource = (ResourceTypes)lua_tointeger(L, 2); + const int iResult = pkPlayer->getFreeResourceFromPolicies(eResource); + lua_pushinteger(L, iResult); + return 1; +} +// ----------------------------------------------------------------------------- // int CvPlayer::GetResourcesFromCorporation(ResourceTypes eResource) int CvLuaPlayer::lGetResourcesFromCorporation(lua_State* L) { @@ -4632,6 +4644,13 @@ int CvLuaPlayer::lGetHappinessFromNaturalWonders(lua_State* L) return BasicLuaMethod(L, &CvPlayerAI::GetHappinessFromNaturalWonders); } +//------------------------------------------------------------------------------ +//int GetHappinessFromImprovements() const; +int CvLuaPlayer::lGetHappinessFromImprovements(lua_State* L) +{ + return BasicLuaMethod(L, &CvPlayerAI::GetHappinessFromImprovements); +} + //------------------------------------------------------------------------------ //int GetHappinessFromLeagues() const; int CvLuaPlayer::lGetHappinessFromLeagues(lua_State* L) @@ -14051,21 +14070,6 @@ int CvLuaPlayer::lGetOpinionTable(lua_State* L) aOpinions.push_back(kOpinion); } } - iValue = pDiplo->GetNumSamePolicies(ePlayer); - if (iValue > 0) - { - Opinion kOpinion; - kOpinion.m_iValue = -3; - kOpinion.m_str = Localization::Lookup("TXT_KEY_DIPLO_SAME_POLICIES"); - aOpinions.push_back(kOpinion); - } - else if (iValue < 0) - { - Opinion kOpinion; - kOpinion.m_iValue = bTeammate ? 0 : 3; - kOpinion.m_str = Localization::Lookup("TXT_KEY_DIPLO_DIFFERENT_POLICIES"); - aOpinions.push_back(kOpinion); - } // Same religion? if (pDiplo->IsPlayerSameReligion(ePlayer)) { diff --git a/CvGameCoreDLL_Expansion2/Lua/CvLuaPlayer.h b/CvGameCoreDLL_Expansion2/Lua/CvLuaPlayer.h index 39a3832934..d05947aea9 100644 --- a/CvGameCoreDLL_Expansion2/Lua/CvLuaPlayer.h +++ b/CvGameCoreDLL_Expansion2/Lua/CvLuaPlayer.h @@ -59,6 +59,7 @@ class CvLuaPlayer : public CvLuaScopedInstance static int lHasStrategicMonopoly(lua_State* L); static int lGetResourcesMisc(lua_State* L); static int lGetResourcesFromGP(lua_State* L); + static int lGetFreeResourceFromPolicies(lua_State* L); static int lGetResourcesFromCorporation(lua_State* L); static int lGetResourceFromCSAlliances(lua_State* L); static int lGetResourcesFromFranchises(lua_State* L); @@ -391,6 +392,7 @@ class CvLuaPlayer : public CvLuaScopedInstance static int lGetExtraHappinessPerLuxury(lua_State* L); static int lGetHappinessFromReligion(lua_State* L); static int lGetHappinessFromNaturalWonders(lua_State* L); + static int lGetHappinessFromImprovements(lua_State* L); static int lGetHappinessFromLeagues(lua_State* L); static int lGetHappinessFromMilitaryUnits(lua_State* L); diff --git a/CvGameCoreDLL_Expansion2/Lua/CvLuaUnit.cpp b/CvGameCoreDLL_Expansion2/Lua/CvLuaUnit.cpp index b82460336d..f071643a57 100644 --- a/CvGameCoreDLL_Expansion2/Lua/CvLuaUnit.cpp +++ b/CvGameCoreDLL_Expansion2/Lua/CvLuaUnit.cpp @@ -117,8 +117,8 @@ void CvLuaUnit::PushMethods(lua_State* L, int t) Method(CanCreateGreatWork); Method(CreateGreatWork); Method(greatperson); - Method(GetCombatVersusOtherReligionOwnLands); - Method(GetCombatVersusOtherReligionTheirLands); + Method(GetReligionCombatBonusOwnLands); + Method(GetReligionCombatBonusTheirLands); Method(GetExoticGoodsGoldAmount); Method(GetExoticGoodsXPAmount); @@ -1776,8 +1776,8 @@ int CvLuaUnit::lgreatperson(lua_State* L) return 1; } //------------------------------------------------------------------------------ -//bool GetCombatVersusOtherReligionOwnLands(); -int CvLuaUnit::lGetCombatVersusOtherReligionOwnLands(lua_State* L) +//bool GetReligionCombatBonusOwnLands(); +int CvLuaUnit::lGetReligionCombatBonusOwnLands(lua_State* L) { int iRtnValue = 0; @@ -1795,23 +1795,15 @@ int CvLuaUnit::lGetCombatVersusOtherReligionOwnLands(lua_State* L) if (pReligion) { CvCity* pHolyCity = pReligion->GetHolyCity(); - if (eTheirReligion != eOwnedReligion) - { - int iOtherOwn = pReligion->m_Beliefs.GetCombatVersusOtherReligionOwnLands(pkUnit->getOwner(), pHolyCity); - // Bonus in own land - if((iOtherOwn > 0) && pkUnit->plot()->IsFriendlyTerritory(pkUnit->getOwner())) - { - iRtnValue = iOtherOwn; - } - } - else + + int iOwn = pReligion->m_Beliefs.GetCombatBonusOwnLands(pkUnit->getOwner(), pHolyCity); + int iOtherOwn = pReligion->m_Beliefs.GetCombatBonusVersusOtherReligionOwnLands(pkUnit->getOwner(), pHolyCity); + // Bonus in own land + if((iOwn > 0 || iOtherOwn > 0) && pkUnit->plot()->IsFriendlyTerritory(pkUnit->getOwner())) { - int iOtherOwn = pReligion->m_Beliefs.GetCombatVersusOtherReligionOwnLands(pkUnit->getOwner(), pHolyCity); - // Bonus in own land - if((iOtherOwn > 0) && pkUnit->plot()->IsFriendlyTerritory(pkUnit->getOwner())) - { - iRtnValue = (iOtherOwn / 2); - } + iRtnValue = iOwn; + if (eTheirReligion != eOwnedReligion) + iRtnValue += iOtherOwn; } } } @@ -1821,8 +1813,8 @@ int CvLuaUnit::lGetCombatVersusOtherReligionOwnLands(lua_State* L) return 1; } //------------------------------------------------------------------------------ -//bool GetCombatVersusOtherReligionTheirLands(); -int CvLuaUnit::lGetCombatVersusOtherReligionTheirLands(lua_State* L) +//bool GetReligionCombatBonusTheirLands(); +int CvLuaUnit::lGetReligionCombatBonusTheirLands(lua_State* L) { int iRtnValue = 0; @@ -1840,23 +1832,15 @@ int CvLuaUnit::lGetCombatVersusOtherReligionTheirLands(lua_State* L) if (pReligion) { CvCity* pHolyCity = pReligion->GetHolyCity(); - if (eTheirReligion != eOwnedReligion) - { - int iOtherTheir = pReligion->m_Beliefs.GetCombatVersusOtherReligionTheirLands(pkUnit->getOwner(), pHolyCity); - //Bonus in their land - if ((iOtherTheir > 0) && pkOtherUnit->plot()->IsFriendlyTerritory(pkOtherUnit->getOwner())) - { - iRtnValue = iOtherTheir; - } - } - else + + int iTheir = pReligion->m_Beliefs.GetCombatBonusTheirLands(pkUnit->getOwner(), pHolyCity); + int iOtherTheir = pReligion->m_Beliefs.GetCombatBonusVersusOtherReligionTheirLands(pkUnit->getOwner(), pHolyCity); + // Bonus in their lands + if((iTheir > 0 || iOtherTheir > 0) && pkOtherUnit->plot()->IsFriendlyTerritory(pkOtherUnit->getOwner())) { - int iOtherTheir = pReligion->m_Beliefs.GetCombatVersusOtherReligionTheirLands(pkUnit->getOwner(), pHolyCity); - //Bonus in their land - if ((iOtherTheir > 0) && pkOtherUnit->plot()->IsFriendlyTerritory(pkOtherUnit->getOwner())) - { - iRtnValue = (iOtherTheir / 2); - } + iRtnValue = iTheir; + if (eTheirReligion != eOwnedReligion) + iRtnValue += iOtherTheir; } } } diff --git a/CvGameCoreDLL_Expansion2/Lua/CvLuaUnit.h b/CvGameCoreDLL_Expansion2/Lua/CvLuaUnit.h index 5890f6e0d1..7b47a7d27e 100644 --- a/CvGameCoreDLL_Expansion2/Lua/CvLuaUnit.h +++ b/CvGameCoreDLL_Expansion2/Lua/CvLuaUnit.h @@ -111,8 +111,8 @@ class CvLuaUnit : public CvLuaScopedInstance LUAAPIEXTN(CanCreateGreatWork, bool, pPlot); LUAAPIEXTN(CreateGreatWork, bool); LUAAPIEXTN(greatperson, bool); - static int lGetCombatVersusOtherReligionOwnLands(lua_State* L); - static int lGetCombatVersusOtherReligionTheirLands(lua_State* L); + static int lGetReligionCombatBonusOwnLands(lua_State* L); + static int lGetReligionCombatBonusTheirLands(lua_State* L); static int lGetExoticGoodsGoldAmount(lua_State* L); static int lGetExoticGoodsXPAmount(lua_State* L); diff --git a/LuaCATS/Global.d.lua b/LuaCATS/Global.d.lua index d3f6dabc5e..a2e0314dc4 100644 --- a/LuaCATS/Global.d.lua +++ b/LuaCATS/Global.d.lua @@ -1,9 +1,133 @@ --- @meta +--- Globals that do not have a dedicated LuaCATS class file. +--- Kept in sync with .luacheckrc (see generate_luacats_enums.py). + +--------------------------------------------------------------------------- +-- FLuaVector types (include("FLuaVector")) +--------------------------------------------------------------------------- + +--- @class Vector2 +--- @field x number +--- @field y number + +--- @class Vector3 : Vector2 +--- @field z number + +--- @class Vector4 : Vector3 +--- @field w number + +--------------------------------------------------------------------------- +-- Writable globals (engine + mod code may assign these) +--------------------------------------------------------------------------- --- A super global table that persists across all Lua contexts, useful for sharing data between them ---- @type table +--- @type table MapModData = {} +--- Context state-tracking string (set by UI Lua scripts). +--- @type string? +StateName = nil + +--- Localization shortcut table (populated by include("FLuaVector")). +--- @type table? +R = nil + +--- Type registry table. +--- @type table? +LuaTypes = nil + +--------------------------------------------------------------------------- +-- Core game singleton: Map (CvLuaMap — no dedicated .d.lua yet) +--------------------------------------------------------------------------- + +--- @type table +Map = {} + +--------------------------------------------------------------------------- +-- Event / callback systems +--------------------------------------------------------------------------- + +--- LuaEvents — mod-to-mod custom event bus. +--- @type table +LuaEvents = {} + +--- GameEvents — DLL gameplay hook bus (registered via GameEvents.XXX.Add). +--- @type table +GameEvents = {} + +--- Factory for creating new GameEvents hooks. +--- @type table +GameEventFactory = {} + +--- Factory for creating new LuaEvents hooks. +--- @type table +LuaEventFactory = {} + +--------------------------------------------------------------------------- +-- Engine library tables (ExposeLibraryToThread / lua_setfield) +--------------------------------------------------------------------------- + +--- @type table +ContentManager = {} + +--- Defines from GlobalDefines.xml — accessed as GameDefines.FOO. +--- @type table +GameDefines = {} + +--- @type table +Matchmaking = {} + +--- @type table +Modding = {} + +--- @type table +OptionsManager = {} + +--- @type table +Path = {} + +--- @type table +Steam = {} + +--------------------------------------------------------------------------- +-- Engine globals (lua_setfield LUA_GLOBALSINDEX) +--------------------------------------------------------------------------- + +--- @type table +ExposedMembers = {} + +--- @type table +ExposedTypes = {} + +--- @type table +GameCore = {} + +--- @type table +PreGame = {} + +--- Tooltip manager table. +--- @type table +TTManager = {} + +--------------------------------------------------------------------------- +-- Engine-registered data tables +--------------------------------------------------------------------------- + +--- Fractal noise generator for map scripts. +--- @type table +Fractal = {} + +--- @type table +GameInfoActions = {} + +--- String→integer mapping for database row types (e.g. GameInfoTypes.BUILDING_PALACE → 0). +--- @type table +GameInfoTypes = {} + +--------------------------------------------------------------------------- +-- Standalone engine functions (LuaThunk wrappers) +--------------------------------------------------------------------------- + --- Loads specified `filename` from VFS. It replaces lua's `require` function in Civ5. --- See [Specifics Of Lua Implementation in Civ5](http://modiki.civfanatics.com/index.php?title=Specificities_of_the_Lua_implementation_in_Civ5) --- for more information @@ -13,28 +137,29 @@ MapModData = {} --- @return nil | string[] function include(filename, useRegex) end ---- TODO docs --- @param vector2 Vector2 --- @return Vector2 function ToHexFromGrid(vector2) end ---- TODO docs --- @param x integer --- @param y integer ---- @return integer, integer # +--- @return integer, integer function ToGridFromHex(x, y) end ---- TODO docs --- @param vector2 Vector2 --- @return Vector3 function HexToWorld(vector2) end ---- TODO docs --- @param x integer --- @param y integer --- @return integer, integer, integer function GridToWorld(x, y) end +--- @param x number +--- @param y number +--- @return number, number, number +function HexVertexToWorld(x, y) end + --- Require `include("SupportFunctions")` to work
--- Truncate a given string if necessary so that the resultant string (with ellipsis and the given suffix) fits just within the target size.
--- The resultant string is set as the given control's text. @@ -44,7 +169,184 @@ function GridToWorld(x, y) end --- @param strSuffix string? The optional string to be appended to the truncated text. This counts towards the target size. function TruncateString(control, iTargetSize, strOriginal, strSuffix) end +--- @return boolean +function InStrategicView() end + +--- @return boolean +function IsGameCoreBusy() end + +--- @return boolean +function MouseOverStrategicViewResource() end + +--- @param x integer +--- @param y integer +function ProcessStrategicViewMouseClick(x, y) end + +--- @param show boolean +function StrategicViewShowFeatures(show) end + +--- @param show boolean +function StrategicViewShowFogOfWar(show) end + +function SetStrategicViewOverlay(...) end +function SetStrategicViewIconSetting(...) end + +--- @return table +function GetStrategicViewOverlays() end + +--- @return table +function GetStrategicViewIconSettings() end + +--- @return integer +function GetGameViewRenderType() end + +--- @param renderType integer +function SetGameViewRenderType(renderType) end + +--- @return table +function GetOverlayLegend() end + +function SystemBuildYield(...) end + +--- @param knobID integer +--- @return number +function GetVolumeKnobValue(knobID) end + +--- @param knobID integer +--- @param value number +function SetVolumeKnobValue(knobID, value) end + +--- @param name string +--- @return integer +function GetVolumeKnobIDFromName(name) end + +function ToggleStrategicView() end + +--- @return boolean +function UnitMoving() end + +--- @param path string +--- @return any +function LookUpControl(path) end + +--- @param value any +--- @return boolean +function IsNull(value) end + --- Alias for `debug.traceback` --- that's available in every state except `Main State` --- even if EnableLuaDebugLibrary = 0 in config.ini traceback = debug.traceback + +--------------------------------------------------------------------------- +-- Engine utility globals +--------------------------------------------------------------------------- + +--- Only available in Main State; nil in UI contexts. +--- @type string[]|nil +IncludeFileList = nil + +--- Only available in Main State; nil in UI contexts. +--- @type fun()|nil +RefreshIncludeFileList = nil + +--- @type table +Profiler = {} + +--- @type table +Automation = {} + +--- Firaxis Lua extensions (e.g. FLua.Log). +--- @type table +FLua = {} + +--- @type table +ContentManagerEnums = {} + +--------------------------------------------------------------------------- +-- Vanilla game Lua utility classes/functions (base game includes) +--------------------------------------------------------------------------- + +--- @type table +FractalWorld = {} + +--- @type table +MultilayeredFractal = {} + +--- @type table +TerrainGenerator = {} + +--- @type table +GenerationalInstanceManager = {} + +--- Return a shuffled copy of a sequential table. +--- @param tbl table +--- @return table +function GetShuffledCopyOfTable(tbl) end + +--- @param control any +--- @param entry table +function ApplyGenericEntrySettings(control, entry) end + +--- Print all keys and values of a table (debug). +--- @param tbl table +--- @param name? string +function PrintContentsOfTable(tbl, name) end + +--- @type table +GameplayUtilities = {} + +--------------------------------------------------------------------------- +-- FLuaVector (include("FLuaVector")) — constructors + vector math +--------------------------------------------------------------------------- + +--- @param x number +--- @param y number +--- @return Vector2 +function Vector2(x, y) end + +--- @param x number +--- @param y number +--- @param z number +--- @return Vector3 +function Vector3(x, y, z) end + +--- @param x number +--- @param y number +--- @param z number +--- @param w number +--- @return Vector4 +function Vector4(x, y, z, w) end + +--- Shorthand for `Vector4(r, g, b, a)`. +--- @param r number +--- @param g number +--- @param b number +--- @param a number +--- @return Vector4 +function Color(r, g, b, a) end + +--- Component-wise addition of two vectors. +--- @param v1 Vector2|Vector3|Vector4 +--- @param v2 Vector2|Vector3|Vector4 +--- @return Vector2|Vector3|Vector4 +function VecAdd(v1, v2) end + +--- Component-wise subtraction of two vectors. +--- @param v1 Vector2|Vector3|Vector4 +--- @param v2 Vector2|Vector3|Vector4 +--- @return Vector2|Vector3|Vector4 +function VecSub(v1, v2) end + +--- Compatibility alias for `VecSub`. +VecSubtract = VecSub + +--------------------------------------------------------------------------- +-- Firaxis extensions to standard libraries +--------------------------------------------------------------------------- + +--- @param value number +--- @param min number +--- @param max number +--- @return number +function math.clamp(value, min, max) end diff --git a/LuaCATS/enum/AccomplishmentTypes.d.lua b/LuaCATS/enum/AccomplishmentTypes.d.lua new file mode 100644 index 0000000000..cf4b7ae196 --- /dev/null +++ b/LuaCATS/enum/AccomplishmentTypes.d.lua @@ -0,0 +1,26 @@ +--- @meta + +--- @type table +AccomplishmentTypes = { + ACCOMPLISHMENT_POLICY_BRANCH_ANCIENT = 0, + ACCOMPLISHMENT_POLICY_BRANCH_MEDIEVAL = 1, + ACCOMPLISHMENT_POLICY_BRANCH_INDUSTRIAL = 2, + ACCOMPLISHMENT_CHOOSE_IDEOLOGY = 3, + ACCOMPLISHMENT_BELIEF_PANTHEON = 4, + ACCOMPLISHMENT_BELIEF_FOUNDER = 5, + ACCOMPLISHMENT_BELIEF_FOLLOWER = 6, + ACCOMPLISHMENT_BELIEF_ENHANCER = 7, + ACCOMPLISHMENT_BELIEF_REFORMATION = 8, + ACCOMPLISHMENT_LONGCOUNT_GENERAL = 9, + ACCOMPLISHMENT_LONGCOUNT_ADMIRAL = 10, + ACCOMPLISHMENT_LONGCOUNT_PROPHET = 11, + ACCOMPLISHMENT_LONGCOUNT_WRITER = 12, + ACCOMPLISHMENT_LONGCOUNT_ARTIST = 13, + ACCOMPLISHMENT_LONGCOUNT_MUSICIAN = 14, + ACCOMPLISHMENT_LONGCOUNT_ENGINEER = 15, + ACCOMPLISHMENT_LONGCOUNT_MERCHANT = 16, + ACCOMPLISHMENT_LONGCOUNT_SCIENTIST = 17, + ACCOMPLISHMENT_LONGCOUNT_DIPLOMAT = 18, + ACCOMPLISHMENT_ELIMINATE_PLAYER = 19, + ACCOMPLISHMENT_DIPLOMATIC_MISSION_BOOST = 20, +} diff --git a/LuaCATS/enum/ActivityTypes.d.lua b/LuaCATS/enum/ActivityTypes.d.lua new file mode 100644 index 0000000000..a02e976bbb --- /dev/null +++ b/LuaCATS/enum/ActivityTypes.d.lua @@ -0,0 +1,13 @@ +--- @meta + +--- @enum ActivityType +ActivityTypes = { + NO_ACTIVITY = -1, + ACTIVITY_AWAKE = 0, + ACTIVITY_HOLD = 1, + ACTIVITY_SLEEP = 2, + ACTIVITY_HEAL = 3, + ACTIVITY_SENTRY = 4, + ACTIVITY_INTERCEPT = 5, + ACTIVITY_MISSION = 6, +} diff --git a/LuaCATS/enum/AggressivePostureTypes.d.lua b/LuaCATS/enum/AggressivePostureTypes.d.lua new file mode 100644 index 0000000000..82fe87feec --- /dev/null +++ b/LuaCATS/enum/AggressivePostureTypes.d.lua @@ -0,0 +1,11 @@ +--- @meta + +--- @enum AggressivePostureType +AggressivePostureTypes = { + AGGRESSIVE_POSTURE_NONE = 0, + AGGRESSIVE_POSTURE_LOW = 1, + AGGRESSIVE_POSTURE_MEDIUM = 2, + AGGRESSIVE_POSTURE_HIGH = 3, + AGGRESSIVE_POSTURE_INCREDIBLE = 4, + NUM_AGGRESSIVE_POSTURE_TYPES = 5, +} diff --git a/LuaCATS/enum/BeliefTypes.d.lua b/LuaCATS/enum/BeliefTypes.d.lua new file mode 100644 index 0000000000..0e4dc029ff --- /dev/null +++ b/LuaCATS/enum/BeliefTypes.d.lua @@ -0,0 +1,6 @@ +--- @meta + +--- @type table +BeliefTypes = { + NO_BELIEF = -1, +} diff --git a/LuaCATS/enum/ButtonStates.d.lua b/LuaCATS/enum/ButtonStates.d.lua new file mode 100644 index 0000000000..2987ad5ef7 --- /dev/null +++ b/LuaCATS/enum/ButtonStates.d.lua @@ -0,0 +1,10 @@ +--- @meta + +--- Binary-only enum (registered by engine executable, not the DLL) +--- @enum ButtonState +ButtonStates = { + eNormal = 0, + eMouseOver = 1, + eMouseDown = 2, + eDisabled = 3, +} diff --git a/LuaCATS/enum/ChatTargetTypes.d.lua b/LuaCATS/enum/ChatTargetTypes.d.lua new file mode 100644 index 0000000000..7df6ede2e8 --- /dev/null +++ b/LuaCATS/enum/ChatTargetTypes.d.lua @@ -0,0 +1,9 @@ +--- @meta + +--- @enum ChatTargetType +ChatTargetTypes = { + NO_CHATTARGET = -1, + CHATTARGET_ALL = -2, + CHATTARGET_TEAM = -3, + CHATTARGET_PLAYER = -4, +} diff --git a/LuaCATS/enum/CivApproachTypes.d.lua b/LuaCATS/enum/CivApproachTypes.d.lua new file mode 100644 index 0000000000..daa8d7b6d4 --- /dev/null +++ b/LuaCATS/enum/CivApproachTypes.d.lua @@ -0,0 +1,13 @@ +--- @meta + +--- @enum CivApproachType +CivApproachTypes = { + CIV_APPROACH_WAR = 0, + CIV_APPROACH_HOSTILE = 1, + CIV_APPROACH_DECEPTIVE = 2, + CIV_APPROACH_GUARDED = 3, + CIV_APPROACH_AFRAID = 4, + CIV_APPROACH_NEUTRAL = 5, + CIV_APPROACH_FRIENDLY = 6, + NUM_CIV_APPROACHES = 7, +} diff --git a/LuaCATS/enum/CivOpinionTypes.d.lua b/LuaCATS/enum/CivOpinionTypes.d.lua new file mode 100644 index 0000000000..e3441105af --- /dev/null +++ b/LuaCATS/enum/CivOpinionTypes.d.lua @@ -0,0 +1,12 @@ +--- @meta + +--- @enum CivOpinionType +CivOpinionTypes = { + CIV_OPINION_UNFORGIVABLE = 0, + CIV_OPINION_ENEMY = 1, + CIV_OPINION_COMPETITOR = 2, + CIV_OPINION_NEUTRAL = 3, + CIV_OPINION_FAVORABLE = 4, + CIV_OPINION_FRIEND = 5, + CIV_OPINION_ALLY = 6, +} diff --git a/LuaCATS/enum/CombatPredictionTypes.d.lua b/LuaCATS/enum/CombatPredictionTypes.d.lua new file mode 100644 index 0000000000..002af8243f --- /dev/null +++ b/LuaCATS/enum/CombatPredictionTypes.d.lua @@ -0,0 +1,15 @@ +--- @meta + +--- @enum CombatPredictionType +CombatPredictionTypes = { + NO_COMBAT_PREDICTION = -1, + COMBAT_PREDICTION_RANGED = 0, + COMBAT_PREDICTION_STALEMATE = 1, + COMBAT_PREDICTION_TOTAL_DEFEAT = 2, + COMBAT_PREDICTION_TOTAL_VICTORY = 3, + COMBAT_PREDICTION_MAJOR_VICTORY = 4, + COMBAT_PREDICTION_SMALL_VICTORY = 5, + COMBAT_PREDICTION_MAJOR_DEFEAT = 6, + COMBAT_PREDICTION_SMALL_DEFEAT = 7, + NUM_COMBAT_PREDICTIONS = 8, +} diff --git a/LuaCATS/enum/CommandTypes.d.lua b/LuaCATS/enum/CommandTypes.d.lua new file mode 100644 index 0000000000..942cbcbca2 --- /dev/null +++ b/LuaCATS/enum/CommandTypes.d.lua @@ -0,0 +1,16 @@ +--- @meta + +--- @enum CommandType +CommandTypes = { + COMMAND_PROMOTION = 0, + COMMAND_UPGRADE = 1, + COMMAND_AUTOMATE = 2, + COMMAND_WAKE = 3, + COMMAND_CANCEL = 4, + COMMAND_CANCEL_ALL = 5, + COMMAND_STOP_AUTOMATION = 6, + COMMAND_DELETE = 7, + COMMAND_GIFT = 8, + COMMAND_HOTKEY = 9, + NUM_COMMAND_TYPES = 10, +} diff --git a/LuaCATS/enum/ContentType.d.lua b/LuaCATS/enum/ContentType.d.lua new file mode 100644 index 0000000000..d947129e59 --- /dev/null +++ b/LuaCATS/enum/ContentType.d.lua @@ -0,0 +1,9 @@ +--- @meta + +--- Binary-only enum (registered by engine executable, not the DLL) +--- @enum ContentType +ContentType = { + COMMON = 0, + GAMEPLAY = 1, + UISKIN = 2, +} diff --git a/LuaCATS/enum/ContractTypes.d.lua b/LuaCATS/enum/ContractTypes.d.lua new file mode 100644 index 0000000000..98da784018 --- /dev/null +++ b/LuaCATS/enum/ContractTypes.d.lua @@ -0,0 +1,6 @@ +--- @meta + +--- @enum ContractType +ContractTypes = { + NO_CONTRACT = -1, +} diff --git a/LuaCATS/enum/ControlTypes.d.lua b/LuaCATS/enum/ControlTypes.d.lua new file mode 100644 index 0000000000..f6115230d5 --- /dev/null +++ b/LuaCATS/enum/ControlTypes.d.lua @@ -0,0 +1,53 @@ +--- @meta + +--- @enum ControlType +ControlTypes = { + NO_CONTROL = -1, + CONTROL_CENTERONSELECTION = 0, + CONTROL_SELECTYUNITTYPE = 1, + CONTROL_SELECTYUNITALL = 2, + CONTROL_SELECTCITY = 3, + CONTROL_SELECTCAPITAL = 4, + CONTROL_NEXTCITY = 5, + CONTROL_PREVCITY = 6, + CONTROL_NEXTUNIT = 7, + CONTROL_PREVUNIT = 8, + CONTROL_CYCLEUNIT = 9, + CONTROL_CYCLEUNIT_ALT = 10, + CONTROL_CYCLEWORKER = 11, + CONTROL_LASTUNIT = 12, + CONTROL_ENDTURN = 13, + CONTROL_ENDTURN_ALT = 14, + CONTROL_FORCEENDTURN = 15, + CONTROL_AUTOMOVES = 16, + CONTROL_PING = 17, + CONTROL_YIELDS = 18, + CONTROL_RESOURCE_ALL = 19, + CONTROL_UNIT_ICONS = 20, + CONTROL_SCORES = 21, + CONTROL_LOAD_GAME = 22, + CONTROL_OPTIONS_SCREEN = 23, + CONTROL_RETIRE = 24, + CONTROL_SAVE_GROUP = 25, + CONTROL_SAVE_NORMAL = 26, + CONTROL_QUICK_SAVE = 27, + CONTROL_QUICK_LOAD = 28, + CONTROL_CIVILOPEDIA = 29, + CONTROL_POLICIES_SCREEN = 30, + CONTROL_FOREIGN_SCREEN = 31, + CONTROL_MILITARY_SCREEN = 32, + CONTROL_TECH_CHOOSER = 33, + CONTROL_TURN_LOG = 34, + CONTROL_DOMESTIC_SCREEN = 35, + CONTROL_VICTORY_SCREEN = 36, + CONTROL_INFO = 37, + CONTROL_SELECT_HEALTHY = 38, + CONTROL_TOGGLE_STRATEGIC_VIEW = 39, + CONTROL_ADVISOR_COUNSEL = 40, + CONTROL_ESPIONAGE_OVERVIEW = 41, + CONTROL_RELIGION_OVERVIEW = 42, + CONTROL_RESTART_GAME = 43, + CONTROL_TOGGLE_OBSERVER_MODE = 44, + CONTROL_TOGGLE_AI_TAKEOVER = 45, + CONTROL_SWITCH_TO_NEXT_PLAYER = 46, +} diff --git a/LuaCATS/enum/CoopWarStates.d.lua b/LuaCATS/enum/CoopWarStates.d.lua new file mode 100644 index 0000000000..14bc0d0447 --- /dev/null +++ b/LuaCATS/enum/CoopWarStates.d.lua @@ -0,0 +1,11 @@ +--- @meta + +--- @enum CoopWarState +CoopWarStates = { + NO_COOP_WAR_STATE = -1, + COOP_WAR_STATE_WARNED_TARGET = 0, + COOP_WAR_STATE_REJECTED = 1, + COOP_WAR_STATE_PREPARING = 2, + COOP_WAR_STATE_ONGOING = 3, + NUM_COOP_WAR_STATES = 4, +} diff --git a/LuaCATS/enum/CorporationTypes.d.lua b/LuaCATS/enum/CorporationTypes.d.lua new file mode 100644 index 0000000000..469b745cec --- /dev/null +++ b/LuaCATS/enum/CorporationTypes.d.lua @@ -0,0 +1,6 @@ +--- @meta + +--- @enum CorporationType +CorporationTypes = { + NO_CORPORATION = -1, +} diff --git a/LuaCATS/enum/DiploUIStateTypes.d.lua b/LuaCATS/enum/DiploUIStateTypes.d.lua new file mode 100644 index 0000000000..fd18f5f4ea --- /dev/null +++ b/LuaCATS/enum/DiploUIStateTypes.d.lua @@ -0,0 +1,51 @@ +--- @meta + +--- @enum DiploUIStateType +DiploUIStateTypes = { + NO_DIPLO_UI_STATE = -1, + DIPLO_UI_STATE_DEFAULT_ROOT = 0, + DIPLO_UI_STATE_WAR_DECLARED_BY_HUMAN = 1, + DIPLO_UI_STATE_PEACE_MADE_BY_HUMAN = 2, + DIPLO_UI_STATE_TRADE = 3, + DIPLO_UI_STATE_TRADE_AI_MAKES_OFFER = 4, + DIPLO_UI_STATE_TRADE_AI_ACCEPTS_OFFER = 5, + DIPLO_UI_STATE_TRADE_AI_REJECTS_OFFER = 6, + DIPLO_UI_STATE_TRADE_AI_MAKES_DEMAND = 7, + DIPLO_UI_STATE_TRADE_AI_MAKES_REQUEST = 8, + DIPLO_UI_STATE_TRADE_HUMAN_OFFERS_CONCESSIONS = 9, + DIPLO_UI_STATE_HUMAN_DEMAND = 10, + DIPLO_UI_STATE_BLANK_DISCUSSION_RETURN_TO_ROOT = 11, + DIPLO_UI_STATE_BLANK_DISCUSSION = 12, + DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_HUMAN = 13, + DIPLO_UI_STATE_BLANK_DISCUSSION_MEAN_AI = 14, + DIPLO_UI_STATE_AI_DECLARED_WAR = 15, + DIPLO_UI_STATE_DISCUSS_HUMAN_INVOKED = 16, + DIPLO_UI_STATE_DISCUSS_AGGRESSIVE_MILITARY_WARNING = 17, + DIPLO_UI_STATE_DISCUSS_I_ATTACKED_YOUR_MINOR_CIV = 18, + DIPLO_UI_STATE_DISCUSS_I_BULLIED_YOUR_MINOR_CIV = 19, + DIPLO_UI_STATE_DISCUSS_YOU_ATTACKED_MINOR_CIV = 20, + DIPLO_UI_STATE_DISCUSS_YOU_KILLED_MINOR_CIV = 21, + DIPLO_UI_STATE_DISCUSS_YOU_BULLIED_MINOR_CIV = 22, + DIPLO_UI_STATE_DISCUSS_PROTECT_MINOR_CIV = 23, + DIPLO_UI_STATE_DISCUSS_YOU_EXPANSION_SERIOUS_WARNING = 24, + DIPLO_UI_STATE_DISCUSS_YOU_EXPANSION_WARNING = 25, + DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_SERIOUS_WARNING = 26, + DIPLO_UI_STATE_DISCUSS_YOU_PLOT_BUYING_WARNING = 27, + DIPLO_UI_STATE_DISCUSS_WORK_WITH_US = 28, + DIPLO_UI_STATE_DISCUSS_WORK_AGAINST_SOMEONE = 29, + DIPLO_UI_STATE_DISCUSS_COOP_WAR = 30, + DIPLO_UI_STATE_DISCUSS_COOP_WAR_TIME = 31, + DIPLO_UI_STATE_DISCUSS_PLAN_RESEARCH_AGREEMENT = 32, + DIPLO_UI_STATE_DISCUSS_AI_REQUEST_DENOUNCE = 33, + DIPLO_UI_STATE_CAUGHT_YOUR_SPY = 34, + DIPLO_UI_STATE_KILLED_YOUR_SPY = 35, + DIPLO_UI_STATE_KILLED_MY_SPY = 36, + DIPLO_UI_STATE_CONFRONT_YOU_KILLED_MY_SPY = 37, + DIPLO_UI_STATE_STOP_CONVERSIONS = 38, + DIPLO_UI_STATE_STOP_DIGGING = 39, + DIPLO_UI_STATE_HUMAN_REQUEST = 40, + DIPLO_UI_STATE_TRADE_AI_MAKES_GENEROUS_OFFER = 41, + DIPLO_UI_STATE_DISCUSS_AI_REVOKE_VASSALAGE = 42, + DIPLO_UI_STATE_DISCUSS_END_WORK_WITH_US = 43, + NUM_DIPLO_UI_STATES = 44, +} diff --git a/LuaCATS/enum/DirectionTypes.d.lua b/LuaCATS/enum/DirectionTypes.d.lua new file mode 100644 index 0000000000..5131de2a11 --- /dev/null +++ b/LuaCATS/enum/DirectionTypes.d.lua @@ -0,0 +1,13 @@ +--- @meta + +--- @enum DirectionType +DirectionTypes = { + NO_DIRECTION = 7, + DIRECTION_NORTHEAST = 0, + DIRECTION_EAST = 1, + DIRECTION_SOUTHEAST = 2, + DIRECTION_SOUTHWEST = 3, + DIRECTION_WEST = 4, + DIRECTION_NORTHWEST = 5, + NUM_DIRECTION_TYPES = 6, +} diff --git a/LuaCATS/enum/DisputeLevelTypes.d.lua b/LuaCATS/enum/DisputeLevelTypes.d.lua new file mode 100644 index 0000000000..06d2255ebb --- /dev/null +++ b/LuaCATS/enum/DisputeLevelTypes.d.lua @@ -0,0 +1,10 @@ +--- @meta + +--- @enum DisputeLevelType +DisputeLevelTypes = { + DISPUTE_LEVEL_NONE = 0, + DISPUTE_LEVEL_WEAK = 1, + DISPUTE_LEVEL_STRONG = 2, + DISPUTE_LEVEL_FIERCE = 3, + NUM_DISPUTE_LEVELS = 4, +} diff --git a/LuaCATS/enum/DomainTypes.d.lua b/LuaCATS/enum/DomainTypes.d.lua new file mode 100644 index 0000000000..9f477dc22c --- /dev/null +++ b/LuaCATS/enum/DomainTypes.d.lua @@ -0,0 +1,12 @@ +--- @meta + +--- @enum DomainType +DomainTypes = { + NO_DOMAIN = -1, + DOMAIN_SEA = 0, + DOMAIN_AIR = 1, + DOMAIN_LAND = 2, + DOMAIN_IMMOBILE = 3, + DOMAIN_HOVER = 4, + NUM_DOMAIN_TYPES = 5, +} diff --git a/LuaCATS/enum/EndGameTypes.d.lua b/LuaCATS/enum/EndGameTypes.d.lua new file mode 100644 index 0000000000..26601b4dc2 --- /dev/null +++ b/LuaCATS/enum/EndGameTypes.d.lua @@ -0,0 +1,17 @@ +--- @meta + +--- Binary-only enum (registered by engine executable, not the DLL) +--- @enum EndGameType +EndGameTypes = { + Technology = 0, + Culture = 1, + Domination = 2, + Diplomatic = 3, + Time = 4, + Tutorial1 = 5, + Tutorial2 = 6, + Tutorial3 = 7, + Tutorial4 = 8, + Tutorial5 = 9, + Loss = 10, +} diff --git a/LuaCATS/enum/EndTurnBlockingTypes.d.lua b/LuaCATS/enum/EndTurnBlockingTypes.d.lua new file mode 100644 index 0000000000..791eb99723 --- /dev/null +++ b/LuaCATS/enum/EndTurnBlockingTypes.d.lua @@ -0,0 +1,35 @@ +--- @meta + +--- @enum EndTurnBlockingType +EndTurnBlockingTypes = { + NO_ENDTURN_BLOCKING_TYPE = -1, + ENDTURN_BLOCKING_POLICY = 0, + ENDTURN_BLOCKING_RESEARCH = 1, + ENDTURN_BLOCKING_PRODUCTION = 2, + ENDTURN_BLOCKING_UNITS = 3, + ENDTURN_BLOCKING_DIPLO_VOTE = 4, + ENDTURN_BLOCKING_MINOR_QUEST = 5, + ENDTURN_BLOCKING_FREE_TECH = 6, + ENDTURN_BLOCKING_STACKED_UNITS = 7, + ENDTURN_BLOCKING_UNIT_NEEDS_ORDERS = 8, + ENDTURN_BLOCKING_UNIT_PROMOTION = 9, + ENDTURN_BLOCKING_CITY_RANGE_ATTACK = 10, + ENDTURN_BLOCKING_FREE_POLICY = 11, + ENDTURN_BLOCKING_FREE_ITEMS = 12, + ENDTURN_BLOCKING_FOUND_PANTHEON = 13, + ENDTURN_BLOCKING_FOUND_RELIGION = 14, + ENDTURN_BLOCKING_ENHANCE_RELIGION = 15, + ENDTURN_BLOCKING_STEAL_TECH = 16, + ENDTURN_BLOCKING_MAYA_LONG_COUNT = 17, + ENDTURN_BLOCKING_FAITH_GREAT_PERSON = 18, + ENDTURN_BLOCKING_ADD_REFORMATION_BELIEF = 19, + ENDTURN_BLOCKING_LEAGUE_CALL_FOR_PROPOSALS = 20, + ENDTURN_BLOCKING_CHOOSE_ARCHAEOLOGY = 21, + ENDTURN_BLOCKING_LEAGUE_CALL_FOR_VOTES = 22, + ENDTURN_BLOCKING_CHOOSE_IDEOLOGY = 23, + ENDTURN_BLOCKING_CITY_TILE = 24, + ENDTURN_BLOCKING_PENDING_DEAL = 25, + ENDTURN_BLOCKING_EVENT_CHOICE = 26, + ENDTURN_BLOCKING_CHOOSE_CITY_FATE = 27, + NUM_ENDTURN_BLOCKING_TYPES = 28, +} diff --git a/LuaCATS/enum/FaithPurchaseTypes.d.lua b/LuaCATS/enum/FaithPurchaseTypes.d.lua new file mode 100644 index 0000000000..4e36a9503f --- /dev/null +++ b/LuaCATS/enum/FaithPurchaseTypes.d.lua @@ -0,0 +1,9 @@ +--- @meta + +--- @enum FaithPurchaseType +FaithPurchaseTypes = { + NO_AUTOMATIC_FAITH_PURCHASE = 0, + FAITH_PURCHASE_SAVE_PROPHET = 1, + FAITH_PURCHASE_UNIT = 3, + FAITH_PURCHASE_BUILDING = 2, +} diff --git a/LuaCATS/enum/FeatureTypes.d.lua b/LuaCATS/enum/FeatureTypes.d.lua new file mode 100644 index 0000000000..4230995d5a --- /dev/null +++ b/LuaCATS/enum/FeatureTypes.d.lua @@ -0,0 +1,32 @@ +--- @meta + +--- @type table +FeatureTypes = { + NO_FEATURE = -1, + FEATURE_ICE = 0, + FEATURE_JUNGLE = 1, + FEATURE_MARSH = 2, + FEATURE_OASIS = 3, + FEATURE_FLOOD_PLAINS = 4, + FEATURE_FOREST = 5, + FEATURE_FALLOUT = 6, + FEATURE_CRATER = 7, + FEATURE_FUJI = 8, + FEATURE_MESA = 9, + FEATURE_REEF = 10, + FEATURE_VOLCANO = 11, + FEATURE_GIBRALTAR = 12, + FEATURE_GEYSER = 13, + FEATURE_FOUNTAIN_YOUTH = 14, + FEATURE_POTOSI = 15, + FEATURE_EL_DORADO = 16, + FEATURE_ATOLL = 17, + FEATURE_SRI_PADA = 18, + FEATURE_MT_SINAI = 19, + FEATURE_MT_KAILASH = 20, + FEATURE_ULURU = 21, + FEATURE_LAKE_VICTORIA = 22, + FEATURE_KILIMANJARO = 23, + FEATURE_SOLOMONS_MINES = 24, + NUM_FEATURE_TYPES = 25, +} diff --git a/LuaCATS/enum/FlowDirectionTypes.d.lua b/LuaCATS/enum/FlowDirectionTypes.d.lua new file mode 100644 index 0000000000..4470c0f391 --- /dev/null +++ b/LuaCATS/enum/FlowDirectionTypes.d.lua @@ -0,0 +1,19 @@ +--- @meta + +--- @enum FlowDirectionType +FlowDirectionTypes = { + NO_FLOWDIRECTION = 7, + FLOWDIRECTION_NORTH = 0, + FLOWDIRECTION_NORTHEAST = 1, + FLOWDIRECTION_SOUTHEAST = 2, + FLOWDIRECTION_SOUTH = 3, + FLOWDIRECTION_SOUTHWEST = 4, + FLOWDIRECTION_NORTHWEST = 5, + NUM_FLOWDIRECTION_TYPES = 6, + FLOWDIRECTION_NORTH_MASK = 8, + FLOWDIRECTION_SOUTH_MASK = 9, + FLOWDIRECTION_SOUTHEAST_MASK = 10, + FLOWDIRECTION_NORTHWEST_MASK = 11, + FLOWDIRECTION_SOUTHWEST_MASK = 12, + FLOWDIRECTION_NORTHEAST_MASK = 13, +} diff --git a/LuaCATS/enum/FogOfWarModeTypes.d.lua b/LuaCATS/enum/FogOfWarModeTypes.d.lua new file mode 100644 index 0000000000..0d24a08e04 --- /dev/null +++ b/LuaCATS/enum/FogOfWarModeTypes.d.lua @@ -0,0 +1,8 @@ +--- @meta + +--- @enum FogOfWarModeType +FogOfWarModeTypes = { + FOGOFWARMODE_OFF = 0, + FOGOFWARMODE_NOVIS = 2, + FOGOFWARMODE_UNEXPLORED = 1, +} diff --git a/LuaCATS/enum/FromUIDiploEventTypes.d.lua b/LuaCATS/enum/FromUIDiploEventTypes.d.lua new file mode 100644 index 0000000000..de29c64490 --- /dev/null +++ b/LuaCATS/enum/FromUIDiploEventTypes.d.lua @@ -0,0 +1,48 @@ +--- @meta + +--- @enum FromUIDiploEventType +FromUIDiploEventTypes = { + NO_FROM_UI_DIPLO_EVENT = -1, + FROM_UI_DIPLO_EVENT_HUMAN_DECLARES_WAR = 0, + FROM_UI_DIPLO_EVENT_HUMAN_NEGOTIATE_PEACE = 1, + FROM_UI_DIPLO_EVENT_HUMAN_WANTS_DISCUSSION = 2, + FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_DONT_SETTLE = 3, + FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_WORK_WITH_US = 4, + FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_END_WORK_WITH_US = 5, + FROM_UI_DIPLO_EVENT_DEMAND_HUMAN_REFUSAL = 6, + FROM_UI_DIPLO_EVENT_REQUEST_HUMAN_REFUSAL = 7, + FROM_UI_DIPLO_EVENT_AGGRESSIVE_MILITARY_WARNING_RESPONSE = 8, + FROM_UI_DIPLO_EVENT_I_ATTACKED_YOUR_MINOR_CIV_RESPONSE = 9, + FROM_UI_DIPLO_EVENT_I_BULLIED_YOUR_MINOR_CIV_RESPONSE = 10, + FROM_UI_DIPLO_EVENT_ATTACKED_MINOR_RESPONSE = 11, + FROM_UI_DIPLO_EVENT_KILLED_MINOR_RESPONSE = 12, + FROM_UI_DIPLO_EVENT_BULLIED_MINOR_RESPONSE = 13, + FROM_UI_DIPLO_EVENT_EXPANSION_SERIOUS_WARNING_RESPONSE = 14, + FROM_UI_DIPLO_EVENT_EXPANSION_WARNING_RESPONSE = 15, + FROM_UI_DIPLO_EVENT_PLOT_BUYING_SERIOUS_WARNING_RESPONSE = 16, + FROM_UI_DIPLO_EVENT_PLOT_BUYING_WARNING_RESPONSE = 17, + FROM_UI_DIPLO_EVENT_WORK_WITH_US_RESPONSE = 18, + FROM_UI_DIPLO_EVENT_WORK_AGAINST_SOMEONE_RESPONSE = 19, + FROM_UI_DIPLO_EVENT_DENOUNCE = 20, + FROM_UI_DIPLO_EVENT_COOP_WAR_OFFER = 21, + FROM_UI_DIPLO_EVENT_COOP_WAR_RESPONSE = 22, + FROM_UI_DIPLO_EVENT_COOP_WAR_NOW_RESPONSE = 23, + FROM_UI_DIPLO_EVENT_HUMAN_DEMAND = 24, + FROM_UI_DIPLO_EVENT_PLAN_RA_RESPONSE = 25, + FROM_UI_DIPLO_EVENT_AI_REQUEST_DENOUNCE_RESPONSE = 26, + FROM_UI_DIPLO_EVENT_CAUGHT_YOUR_SPY_RESPONSE = 27, + FROM_UI_DIPLO_EVENT_KILLED_MY_SPY_RESPONSE = 28, + FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_STOP_SPYING = 29, + FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_SHARE_INTRIGUE = 30, + FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_STOP_SPREADING_RELIGION = 31, + FROM_UI_DIPLO_EVENT_STOP_CONVERSIONS = 32, + FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_STOP_DIGGING = 33, + FROM_UI_DIPLO_EVENT_STOP_DIGGING = 34, + FROM_UI_DIPLO_EVENT_HUMAN_DISCUSSION_SHARE_OPINION = 35, + FROM_UI_DIPLO_EVENT_HUMAN_REQUEST = 36, + FROM_UI_DIPLO_EVENT_HUMAN_ENDS_VASSALAGE = 37, + FROM_UI_DIPLO_EVENT_HUMAN_MOVE_TROOPS_RESPONSE = 38, + FROM_UI_DIPLO_EVENT_HUMAN_END_WORK_WITH_US_RESPONSE = 39, + FROM_UI_DIPLO_EVENT_MEAN_RESPONSE = 40, + NUM_FROM_UI_DIPLO_EVENTS = 41, +} diff --git a/LuaCATS/enum/GameOptionTypes.d.lua b/LuaCATS/enum/GameOptionTypes.d.lua new file mode 100644 index 0000000000..bcb697abea --- /dev/null +++ b/LuaCATS/enum/GameOptionTypes.d.lua @@ -0,0 +1,29 @@ +--- @meta + +--- @enum GameOptionType +GameOptionTypes = { + NO_GAMEOPTION = -1, + GAMEOPTION_NO_CITY_RAZING = 0, + GAMEOPTION_NO_BARBARIANS = 1, + GAMEOPTION_RAGING_BARBARIANS = 2, + GAMEOPTION_ALWAYS_WAR = 3, + GAMEOPTION_ALWAYS_PEACE = 4, + GAMEOPTION_ONE_CITY_CHALLENGE = 5, + GAMEOPTION_NO_CHANGING_WAR_PEACE = 6, + GAMEOPTION_NEW_RANDOM_SEED = 7, + GAMEOPTION_LOCK_MODS = 8, + GAMEOPTION_COMPLETE_KILLS = 9, + GAMEOPTION_NO_GOODY_HUTS = 10, + GAMEOPTION_RANDOM_PERSONALITIES = 11, + GAMEOPTION_POLICY_SAVING = 12, + GAMEOPTION_PROMOTION_SAVING = 13, + GAMEOPTION_END_TURN_TIMER_ENABLED = 14, + GAMEOPTION_QUICK_COMBAT = 15, + GAMEOPTION_DISABLE_START_BIAS = 16, + GAMEOPTION_NO_SCIENCE = 17, + GAMEOPTION_NO_POLICIES = 18, + GAMEOPTION_NO_HAPPINESS = 19, + GAMEOPTION_NO_TUTORIAL = 20, + GAMEOPTION_NO_RELIGION = 21, + NUM_GAMEOPTION_TYPES = 22, +} diff --git a/LuaCATS/enum/GameStates.d.lua b/LuaCATS/enum/GameStates.d.lua new file mode 100644 index 0000000000..1b3cc5b398 --- /dev/null +++ b/LuaCATS/enum/GameStates.d.lua @@ -0,0 +1,10 @@ +--- @meta + +--- @enum GameState +GameStates = { + MainMenu = 0, + MainGameView = 1, + GameLoad = 2, + LeaderHead = 3, + NUM_GAME_STATES = 4, +} diff --git a/LuaCATS/enum/GameTypes.d.lua b/LuaCATS/enum/GameTypes.d.lua new file mode 100644 index 0000000000..ff4ea45a09 --- /dev/null +++ b/LuaCATS/enum/GameTypes.d.lua @@ -0,0 +1,11 @@ +--- @meta + +--- Binary-only enum (registered by engine executable, not the DLL) +--- @enum GameType +GameTypes = { + GAME_TYPE_NONE = -1, + GAME_SINGLE_PLAYER = 0, + GAME_NETWORK_MULTIPLAYER = 1, + GAME_HOTSEAT_MULTIPLAYER = 2, + GAME_EMAIL_MULTIPLAYER = 3, +} diff --git a/LuaCATS/enum/GameViewTypes.d.lua b/LuaCATS/enum/GameViewTypes.d.lua new file mode 100644 index 0000000000..65ea89f4a7 --- /dev/null +++ b/LuaCATS/enum/GameViewTypes.d.lua @@ -0,0 +1,9 @@ +--- @meta + +--- Binary-only enum (registered by engine executable, not the DLL) +--- @enum GameViewType +GameViewTypes = { + GAMEVIEW_NONE = 0, + GAMEVIEW_STANDARD = 1, + GAMEVIEW_STRATEGIC = 2, +} diff --git a/LuaCATS/enum/GameplayGameStateTypes.d.lua b/LuaCATS/enum/GameplayGameStateTypes.d.lua new file mode 100644 index 0000000000..435dfdbd23 --- /dev/null +++ b/LuaCATS/enum/GameplayGameStateTypes.d.lua @@ -0,0 +1,8 @@ +--- @meta + +--- @enum GameplayGameStateType +GameplayGameStateTypes = { + GAMESTATE_ON = 0, + GAMESTATE_OVER = 1, + GAMESTATE_EXTENDED = 2, +} diff --git a/LuaCATS/enum/GenericWorldAnchorTypes.d.lua b/LuaCATS/enum/GenericWorldAnchorTypes.d.lua new file mode 100644 index 0000000000..aabfeef503 --- /dev/null +++ b/LuaCATS/enum/GenericWorldAnchorTypes.d.lua @@ -0,0 +1,10 @@ +--- @meta + +--- @enum GenericWorldAnchorType +GenericWorldAnchorTypes = { + NO_WORLD_ANCHOR = -1, + WORLD_ANCHOR_NATURAL_WONDER = 0, + WORLD_ANCHOR_SETTLER = 1, + WORLD_ANCHOR_WORKER = 2, + NUM_WORLD_ANCHORS = 3, +} diff --git a/LuaCATS/enum/InfluenceLevelTrend.d.lua b/LuaCATS/enum/InfluenceLevelTrend.d.lua new file mode 100644 index 0000000000..8c6515b135 --- /dev/null +++ b/LuaCATS/enum/InfluenceLevelTrend.d.lua @@ -0,0 +1,8 @@ +--- @meta + +--- @enum InfluenceLevelTrend +InfluenceLevelTrend = { + INFLUENCE_TREND_FALLING = -1, + INFLUENCE_TREND_STATIC = 0, + INFLUENCE_TREND_RISING = 1, +} diff --git a/LuaCATS/enum/InfluenceLevelTypes.d.lua b/LuaCATS/enum/InfluenceLevelTypes.d.lua new file mode 100644 index 0000000000..cd605988a6 --- /dev/null +++ b/LuaCATS/enum/InfluenceLevelTypes.d.lua @@ -0,0 +1,12 @@ +--- @meta + +--- @enum InfluenceLevelType +InfluenceLevelTypes = { + NO_INFLUENCE_LEVEL = -1, + INFLUENCE_LEVEL_UNKNOWN = 0, + INFLUENCE_LEVEL_EXOTIC = 1, + INFLUENCE_LEVEL_FAMILIAR = 2, + INFLUENCE_LEVEL_POPULAR = 3, + INFLUENCE_LEVEL_INFLUENTIAL = 4, + INFLUENCE_LEVEL_DOMINANT = 5, +} diff --git a/LuaCATS/enum/InstantYieldType.d.lua b/LuaCATS/enum/InstantYieldType.d.lua new file mode 100644 index 0000000000..7f3edbcc2b --- /dev/null +++ b/LuaCATS/enum/InstantYieldType.d.lua @@ -0,0 +1,68 @@ +--- @meta + +--- @enum InstantYieldType +InstantYieldType = { + INSTANT_YIELD_TYPE_BIRTH = 0, + INSTANT_YIELD_TYPE_DEATH = 1, + INSTANT_YIELD_TYPE_PROPOSAL = 2, + INSTANT_YIELD_TYPE_ERA_UNLOCK = 3, + INSTANT_YIELD_TYPE_POLICY_UNLOCK = 4, + INSTANT_YIELD_TYPE_INSTANT = 5, + INSTANT_YIELD_TYPE_TECH = 6, + INSTANT_YIELD_TYPE_CONSTRUCTION = 7, + INSTANT_YIELD_TYPE_BORDERS = 8, + INSTANT_YIELD_TYPE_GP_USE = 9, + INSTANT_YIELD_TYPE_GP_BORN = 10, + INSTANT_YIELD_TYPE_F_SPREAD = 11, + INSTANT_YIELD_TYPE_F_CONQUEST = 12, + INSTANT_YIELD_TYPE_VICTORY = 13, + INSTANT_YIELD_TYPE_U_PROD = 14, + INSTANT_YIELD_TYPE_PURCHASE = 15, + INSTANT_YIELD_TYPE_TILE_PURCHASE = 16, + INSTANT_YIELD_TYPE_FOUND = 17, + INSTANT_YIELD_TYPE_TR_END = 18, + INSTANT_YIELD_TYPE_CONVERSION = 19, + INSTANT_YIELD_TYPE_SPREAD = 20, + INSTANT_YIELD_TYPE_BULLY = 21, + INSTANT_YIELD_TYPE_TR_MOVEMENT = 22, + INSTANT_YIELD_TYPE_SCOUTING = 23, + INSTANT_YIELD_TYPE_LEVEL_UP = 24, + INSTANT_YIELD_TYPE_PILLAGE = 25, + INSTANT_YIELD_TYPE_BIRTH_RETROACTIVE = 26, + INSTANT_YIELD_TYPE_SPY_ATTACK = 27, + INSTANT_YIELD_TYPE_SPY_DEFENSE = 28, + INSTANT_YIELD_TYPE_DELEGATES = 29, + INSTANT_YIELD_TYPE_CONSTRUCTION_WONDER = 30, + INSTANT_YIELD_TYPE_MINOR_QUEST_REWARD = 31, + INSTANT_YIELD_TYPE_CULTURE_BOMB = 32, + INSTANT_YIELD_TYPE_REMOVE_HERESY = 33, + INSTANT_YIELD_TYPE_FAITH_PURCHASE = 34, + INSTANT_YIELD_TYPE_VICTORY_GLOBAL = 35, + INSTANT_YIELD_TYPE_PILLAGE_GLOBAL = 36, + INSTANT_YIELD_TYPE_CONVERSION_EXPO = 37, + INSTANT_YIELD_TYPE_PROMOTION_OBTAINED = 38, + INSTANT_YIELD_TYPE_BARBARIAN_CAMP_CLEARED = 39, + INSTANT_YIELD_TYPE_TR_PRODUCTION_SIPHON = 40, + INSTANT_YIELD_TYPE_TR_MOVEMENT_IN_FOREIGN = 41, + INSTANT_YIELD_TYPE_IMPROVEMENT_BUILD = 42, + INSTANT_YIELD_TYPE_LUA = 43, + INSTANT_YIELD_TYPE_RESEARCH_AGREMEENT = 44, + INSTANT_YIELD_TYPE_REFUND = 45, + INSTANT_YIELD_TYPE_FAITH_REFUND = 46, + INSTANT_YIELD_TYPE_BIRTH_HOLY_CITY = 47, + INSTANT_YIELD_TYPE_TECH_RETROACTIVE = 48, + INSTANT_YIELD_TYPE_PILLAGE_UNIT = 49, + INSTANT_YIELD_TYPE_COMBAT_EXPERIENCE = 50, + INSTANT_YIELD_TYPE_SPY_IDENTIFY = 51, + INSTANT_YIELD_TYPE_SPY_DEFENSE_OR_ID = 52, + INSTANT_YIELD_TYPE_SPY_RIG_ELECTION = 53, + INSTANT_YIELD_TYPE_CITY_DAMAGE = 54, + INSTANT_YIELD_TYPE_LUXURY_RESOURCE_GAIN = 55, + INSTANT_YIELD_TYPE_GOLDEN_AGE_START = 56, + INSTANT_YIELD_TYPE_UNIT_GIFT = 57, + INSTANT_YIELD_TYPE_BAKTUN_END = 58, + INSTANT_YIELD_TYPE_WLTKD_START = 59, + INSTANT_YIELD_TYPE_ANCIENT_RUIN = 60, + INSTANT_YIELD_TYPE_PLUNDER_TRADE_ROUTE = 61, + NUM_INSTANT_YIELD_TYPES = 62, +} diff --git a/LuaCATS/enum/LeaderheadAnimationTypes.d.lua b/LuaCATS/enum/LeaderheadAnimationTypes.d.lua new file mode 100644 index 0000000000..19af04bec9 --- /dev/null +++ b/LuaCATS/enum/LeaderheadAnimationTypes.d.lua @@ -0,0 +1,21 @@ +--- @meta + +--- @enum LeaderheadAnimationType +LeaderheadAnimationTypes = { + NO_LEADERHEAD_ANIM = -1, + LEADERHEAD_ANIM_INTRO = 1, + LEADERHEAD_ANIM_NEUTRAL_HELLO = 2, + LEADERHEAD_ANIM_PEACEFUL = 3, + LEADERHEAD_ANIM_OPENING_GLOAT = 4, + LEADERHEAD_ANIM_DECLARE_WAR = 5, + LEADERHEAD_ANIM_ATTACKED = 6, + LEADERHEAD_ANIM_HATE_HELLO = 7, + LEADERHEAD_ANIM_DEFEATED = 8, + LEADERHEAD_ANIM_REQUEST = 9, + LEADERHEAD_ANIM_DEMAND = 10, + LEADERHEAD_ANIM_NEUTRAL_IDLE = 11, + LEADERHEAD_ANIM_HATE_IDLE = 12, + LEADERHEAD_ANIM_LETS_HEAR_IT = 15, + LEADERHEAD_ANIM_YES = 16, + LEADERHEAD_ANIM_NO = 17, +} diff --git a/LuaCATS/enum/MajorCivApproachTypes.d.lua b/LuaCATS/enum/MajorCivApproachTypes.d.lua new file mode 100644 index 0000000000..d6ad5fec39 --- /dev/null +++ b/LuaCATS/enum/MajorCivApproachTypes.d.lua @@ -0,0 +1,14 @@ +--- @meta + +--- @enum MajorCivApproachType +MajorCivApproachTypes = { + NO_MAJOR_CIV_APPROACH = -1, + MAJOR_CIV_APPROACH_WAR = 0, + MAJOR_CIV_APPROACH_HOSTILE = 1, + MAJOR_CIV_APPROACH_DECEPTIVE = 2, + MAJOR_CIV_APPROACH_GUARDED = 3, + MAJOR_CIV_APPROACH_AFRAID = 4, + MAJOR_CIV_APPROACH_NEUTRAL = 5, + MAJOR_CIV_APPROACH_FRIENDLY = 6, + NUM_MAJOR_CIV_APPROACHES = 7, +} diff --git a/LuaCATS/enum/MinorCivPersonalityTypes.d.lua b/LuaCATS/enum/MinorCivPersonalityTypes.d.lua new file mode 100644 index 0000000000..3ac86a5e29 --- /dev/null +++ b/LuaCATS/enum/MinorCivPersonalityTypes.d.lua @@ -0,0 +1,11 @@ +--- @meta + +--- @enum MinorCivPersonalityType +MinorCivPersonalityTypes = { + NO_MINOR_CIV_PERSONALITY_TYPE = -1, + MINOR_CIV_PERSONALITY_FRIENDLY = 0, + MINOR_CIV_PERSONALITY_NEUTRAL = 1, + MINOR_CIV_PERSONALITY_HOSTILE = 2, + MINOR_CIV_PERSONALITY_IRRATIONAL = 3, + NUM_MINOR_CIV_PERSONALITY_TYPES = 4, +} diff --git a/LuaCATS/enum/MinorCivQuestTypes.d.lua b/LuaCATS/enum/MinorCivQuestTypes.d.lua new file mode 100644 index 0000000000..ac52b50215 --- /dev/null +++ b/LuaCATS/enum/MinorCivQuestTypes.d.lua @@ -0,0 +1,42 @@ +--- @meta + +--- @enum MinorCivQuestType +MinorCivQuestTypes = { + NO_MINOR_CIV_QUEST_TYPE = -1, + MINOR_CIV_QUEST_ROUTE = 0, + MINOR_CIV_QUEST_KILL_CAMP = 1, + MINOR_CIV_QUEST_CONNECT_RESOURCE = 2, + MINOR_CIV_QUEST_CONSTRUCT_WONDER = 3, + MINOR_CIV_QUEST_GREAT_PERSON = 4, + MINOR_CIV_QUEST_KILL_CITY_STATE = 5, + MINOR_CIV_QUEST_FIND_PLAYER = 6, + MINOR_CIV_QUEST_FIND_NATURAL_WONDER = 7, + MINOR_CIV_QUEST_GIVE_GOLD = 8, + MINOR_CIV_QUEST_PLEDGE_TO_PROTECT = 9, + MINOR_CIV_QUEST_CONTEST_CULTURE = 10, + MINOR_CIV_QUEST_CONTEST_FAITH = 11, + MINOR_CIV_QUEST_CONTEST_TECHS = 12, + MINOR_CIV_QUEST_INVEST = 13, + MINOR_CIV_QUEST_BULLY_CITY_STATE = 14, + MINOR_CIV_QUEST_DENOUNCE_MAJOR = 15, + MINOR_CIV_QUEST_SPREAD_RELIGION = 16, + MINOR_CIV_QUEST_TRADE_ROUTE = 17, + MINOR_CIV_QUEST_FIND_CITY = 18, + MINOR_CIV_QUEST_WAR = 19, + MINOR_CIV_QUEST_CONSTRUCT_NATIONAL_WONDER = 20, + MINOR_CIV_QUEST_GIFT_SPECIFIC_UNIT = 21, + MINOR_CIV_QUEST_FIND_CITY_STATE = 22, + MINOR_CIV_QUEST_INFLUENCE = 23, + MINOR_CIV_QUEST_CONTEST_TOURISM = 24, + MINOR_CIV_QUEST_ARCHAEOLOGY = 25, + MINOR_CIV_QUEST_CIRCUMNAVIGATION = 26, + MINOR_CIV_QUEST_LIBERATION = 27, + MINOR_CIV_QUEST_HORDE = 28, + MINOR_CIV_QUEST_REBELLION = 29, + MINOR_CIV_QUEST_EXPLORE_AREA = 30, + MINOR_CIV_QUEST_BUILD_X_BUILDINGS = 31, + MINOR_CIV_QUEST_SPY_ON_MAJOR = 32, + MINOR_CIV_QUEST_COUP = 33, + MINOR_CIV_QUEST_ACQUIRE_CITY = 34, + NUM_MINOR_CIV_QUEST_TYPES = 35, +} diff --git a/LuaCATS/enum/MinorCivTraitTypes.d.lua b/LuaCATS/enum/MinorCivTraitTypes.d.lua new file mode 100644 index 0000000000..7fe06ff49f --- /dev/null +++ b/LuaCATS/enum/MinorCivTraitTypes.d.lua @@ -0,0 +1,12 @@ +--- @meta + +--- @enum MinorCivTraitType +MinorCivTraitTypes = { + NO_MINOR_CIV_TRAIT_TYPE = -1, + MINOR_CIV_TRAIT_CULTURED = 0, + MINOR_CIV_TRAIT_MILITARISTIC = 1, + MINOR_CIV_TRAIT_MARITIME = 2, + MINOR_CIV_TRAIT_MERCANTILE = 3, + MINOR_CIV_TRAIT_RELIGIOUS = 4, + NUM_MINOR_CIV_TRAIT_TYPES = 5, +} diff --git a/LuaCATS/enum/MissionTypes.d.lua b/LuaCATS/enum/MissionTypes.d.lua new file mode 100644 index 0000000000..2564edeecd --- /dev/null +++ b/LuaCATS/enum/MissionTypes.d.lua @@ -0,0 +1,7 @@ +--- @meta + +--- Values are partially data-driven (loaded from database/XML at runtime) +--- @enum MissionType +MissionTypes = { + NO_MISSION = -1, +} diff --git a/LuaCATS/enum/NetErrors.d.lua b/LuaCATS/enum/NetErrors.d.lua new file mode 100644 index 0000000000..1c7c83030b --- /dev/null +++ b/LuaCATS/enum/NetErrors.d.lua @@ -0,0 +1,9 @@ +--- @meta + +--- Binary-only enum (registered by engine executable, not the DLL) +--- @enum NetError +NetErrors = { + NO_ERROR = 0, + MISSING_REQUIRED_DATA = 1, + ROOM_FULL = 2, +} diff --git a/LuaCATS/enum/NetKicked.d.lua b/LuaCATS/enum/NetKicked.d.lua new file mode 100644 index 0000000000..435f137d38 --- /dev/null +++ b/LuaCATS/enum/NetKicked.d.lua @@ -0,0 +1,13 @@ +--- @meta + +--- Binary-only enum (registered by engine executable, not the DLL) +--- @enum NetKickedReason +NetKicked = { + NONE = 0, + NO_HOST = 1, + BY_HOST = 2, + TIMEOUT = 3, + UNRECOGNIZED = 4, + UNAUTHORIZED = 5, + NO_ROOM = 6, +} diff --git a/LuaCATS/enum/NotificationTypes.d.lua b/LuaCATS/enum/NotificationTypes.d.lua new file mode 100644 index 0000000000..5a8e5f747d --- /dev/null +++ b/LuaCATS/enum/NotificationTypes.d.lua @@ -0,0 +1,4 @@ +--- @meta + +--- @type table +NotificationTypes = {} diff --git a/LuaCATS/enum/PeaceTreatyTypes.d.lua b/LuaCATS/enum/PeaceTreatyTypes.d.lua new file mode 100644 index 0000000000..034108b274 --- /dev/null +++ b/LuaCATS/enum/PeaceTreatyTypes.d.lua @@ -0,0 +1,15 @@ +--- @meta + +--- @enum PeaceTreatyType +PeaceTreatyTypes = { + NO_PEACE_TREATY_TYPE = -1, + PEACE_TREATY_WHITE_PEACE = 0, + PEACE_TREATY_ARMISTICE = 1, + PEACE_TREATY_SETTLEMENT = 2, + PEACE_TREATY_BACKDOWN = 3, + PEACE_TREATY_SUBMISSION = 4, + PEACE_TREATY_SURRENDER = 5, + PEACE_TREATY_CESSION = 6, + PEACE_TREATY_CAPITULATION = 7, + PEACE_TREATY_UNCONDITIONAL_SURRENDER = 8, +} diff --git a/LuaCATS/enum/PlotTypes.d.lua b/LuaCATS/enum/PlotTypes.d.lua new file mode 100644 index 0000000000..9577ece249 --- /dev/null +++ b/LuaCATS/enum/PlotTypes.d.lua @@ -0,0 +1,11 @@ +--- @meta + +--- @type table +PlotTypes = { + NO_PLOT = -1, + PLOT_MOUNTAIN = 0, + PLOT_HILLS = 1, + PLOT_LAND = 2, + PLOT_OCEAN = 3, + NUM_PLOT_TYPES = 4, +} diff --git a/LuaCATS/enum/PolicyBranchTypes.d.lua b/LuaCATS/enum/PolicyBranchTypes.d.lua new file mode 100644 index 0000000000..e0226d9c78 --- /dev/null +++ b/LuaCATS/enum/PolicyBranchTypes.d.lua @@ -0,0 +1,6 @@ +--- @meta + +--- @enum PolicyBranchType +PolicyBranchTypes = { + NO_POLICY_BRANCH_TYPE = -1, +} diff --git a/LuaCATS/enum/PopupPriorities.d.lua b/LuaCATS/enum/PopupPriorities.d.lua new file mode 100644 index 0000000000..058aeba3f9 --- /dev/null +++ b/LuaCATS/enum/PopupPriorities.d.lua @@ -0,0 +1,5 @@ +--- @meta + +--- Binary-only enum (engine executable, decompiled values may be incomplete) +--- @type table +PopupPriorities = {} diff --git a/LuaCATS/enum/PublicOpinionTypes.d.lua b/LuaCATS/enum/PublicOpinionTypes.d.lua new file mode 100644 index 0000000000..b3707afa50 --- /dev/null +++ b/LuaCATS/enum/PublicOpinionTypes.d.lua @@ -0,0 +1,10 @@ +--- @meta + +--- @enum PublicOpinionType +PublicOpinionTypes = { + NO_PUBLIC_OPINION = -1, + PUBLIC_OPINION_CONTENT = 0, + PUBLIC_OPINION_DISSIDENTS = 1, + PUBLIC_OPINION_CIVIL_RESISTANCE = 2, + PUBLIC_OPINION_REVOLUTIONARY_WAVE = 3, +} diff --git a/LuaCATS/enum/ReligionTypes.d.lua b/LuaCATS/enum/ReligionTypes.d.lua new file mode 100644 index 0000000000..6a0620fa9d --- /dev/null +++ b/LuaCATS/enum/ReligionTypes.d.lua @@ -0,0 +1,7 @@ +--- @meta + +--- @type table +ReligionTypes = { + NO_RELIGION = -1, + RELIGION_PANTHEON = 0, +} diff --git a/LuaCATS/enum/ResolutionDecisionTypes.d.lua b/LuaCATS/enum/ResolutionDecisionTypes.d.lua new file mode 100644 index 0000000000..27b2ce151f --- /dev/null +++ b/LuaCATS/enum/ResolutionDecisionTypes.d.lua @@ -0,0 +1,18 @@ +--- @meta + +--- @enum ResolutionDecisionType +ResolutionDecisionTypes = { + RESOLUTION_DECISION_NONE = 0, + RESOLUTION_DECISION_YES_OR_NO = 1, + RESOLUTION_DECISION_ANY_MEMBER = 2, + RESOLUTION_DECISION_CITY = 3, + RESOLUTION_DECISION_ANY_LUXURY_RESOURCE = 4, + RESOLUTION_DECISION_REPEAL = 5, + RESOLUTION_DECISION_MAJOR_CIV_MEMBER = 6, + RESOLUTION_DECISION_OTHER_MAJOR_CIV_MEMBER = 7, + RESOLUTION_DECISION_RELIGION = 8, + RESOLUTION_DECISION_IDEOLOGY = 9, + RESOLUTION_DECISION_CITY_CSD = 10, + RESOLUTION_DECISION_MAJOR_CIV_MEMBER_IDEOLOGY = 11, + NUM_RESOLUTION_DECISIONS = 12, +} diff --git a/LuaCATS/enum/ResourceUsageTypes.d.lua b/LuaCATS/enum/ResourceUsageTypes.d.lua new file mode 100644 index 0000000000..1ba1fae69b --- /dev/null +++ b/LuaCATS/enum/ResourceUsageTypes.d.lua @@ -0,0 +1,13 @@ +--- @meta + +--- @enum ResourceUsageType +ResourceUsageTypes = { + RESOURCEUSAGE_BONUS = 0, + RESOURCEUSAGE_STRATEGIC = 1, + RESOURCEUSAGE_LUXURY = 2, + NUM_RESOURCEUSAGE_TYPES = 3, + RESOURCEUSAGE_BONUS = 0, + RESOURCEUSAGE_STRATEGIC = 1, + RESOURCEUSAGE_LUXURY = 2, + NUM_RESOURCEUSAGE_TYPES = 3, +} diff --git a/LuaCATS/enum/RoutePlanTypes.d.lua b/LuaCATS/enum/RoutePlanTypes.d.lua new file mode 100644 index 0000000000..37f211fe08 --- /dev/null +++ b/LuaCATS/enum/RoutePlanTypes.d.lua @@ -0,0 +1,9 @@ +--- @meta + +--- @enum RoutePlanType +RoutePlanTypes = { + NO_PLANNED_ROUTE = -1, + ROAD_PLANNING_EXCLUDE = 0, + ROAD_PLANNING_INCLUDE = 1, + ROAD_PLANNING_PRIORITY_CONSTRUCTION = 2, +} diff --git a/LuaCATS/enum/SlotClaim.d.lua b/LuaCATS/enum/SlotClaim.d.lua new file mode 100644 index 0000000000..93957a2ebc --- /dev/null +++ b/LuaCATS/enum/SlotClaim.d.lua @@ -0,0 +1,10 @@ +--- @meta + +--- Binary-only enum (registered by engine executable, not the DLL) +--- @enum SlotClaim +SlotClaim = { + SLOTCLAIM_UNASSIGNED = 0, + SLOTCLAIM_RESERVED = 1, + SLOTCLAIM_ASSIGNED = 2, + NUM_SLOTCLAIMS = 3, +} diff --git a/LuaCATS/enum/SlotStatus.d.lua b/LuaCATS/enum/SlotStatus.d.lua new file mode 100644 index 0000000000..4d627fbd03 --- /dev/null +++ b/LuaCATS/enum/SlotStatus.d.lua @@ -0,0 +1,12 @@ +--- @meta + +--- Binary-only enum (registered by engine executable, not the DLL) +--- @enum SlotStatus +SlotStatus = { + SS_OPEN = 0, + SS_COMPUTER = 1, + SS_CLOSED = 2, + SS_TAKEN = 3, + SS_OBSERVER = 4, + SS_MAX_SLOT_STATUS = 5, +} diff --git a/LuaCATS/enum/StrengthTypes.d.lua b/LuaCATS/enum/StrengthTypes.d.lua new file mode 100644 index 0000000000..076d53e1ec --- /dev/null +++ b/LuaCATS/enum/StrengthTypes.d.lua @@ -0,0 +1,12 @@ +--- @meta + +--- @enum StrengthType +StrengthTypes = { + STRENGTH_PATHETIC = 0, + STRENGTH_WEAK = 1, + STRENGTH_POOR = 2, + STRENGTH_AVERAGE = 3, + STRENGTH_STRONG = 4, + STRENGTH_POWERFUL = 5, + STRENGTH_IMMENSE = 6, +} diff --git a/LuaCATS/enum/TerraformingEventTypes.d.lua b/LuaCATS/enum/TerraformingEventTypes.d.lua new file mode 100644 index 0000000000..29fa0d9bb0 --- /dev/null +++ b/LuaCATS/enum/TerraformingEventTypes.d.lua @@ -0,0 +1,16 @@ +--- @meta + +--- @enum TerraformingEventType +TerraformingEventTypes = { + NO_TERRAFORMINGEVENT = -1, + TERRAFORMINGEVENT_LOAD = 0, + TERRAFORMINGEVENT_AREA = 1, + TERRAFORMINGEVENT_LANDMASS = 2, + TERRAFORMINGEVENT_CONTINENT = 3, + TERRAFORMINGEVENT_PLOT = 4, + TERRAFORMINGEVENT_TERRAIN = 5, + TERRAFORMINGEVENT_FEATURE = 6, + TERRAFORMINGEVENT_RIVER = 7, + TERRAFORMINGEVENT_CITY = 8, + NUM_TERRAFORMINGEVENT_TYPES = 9, +} diff --git a/LuaCATS/enum/TerrainTypes.d.lua b/LuaCATS/enum/TerrainTypes.d.lua new file mode 100644 index 0000000000..7600a20528 --- /dev/null +++ b/LuaCATS/enum/TerrainTypes.d.lua @@ -0,0 +1,16 @@ +--- @meta + +--- @type table +TerrainTypes = { + NO_TERRAIN = -1, + TERRAIN_GRASS = 0, + TERRAIN_PLAINS = 1, + TERRAIN_DESERT = 2, + TERRAIN_TUNDRA = 3, + TERRAIN_SNOW = 4, + TERRAIN_COAST = 5, + TERRAIN_OCEAN = 6, + TERRAIN_MOUNTAIN = 7, + TERRAIN_HILL = 8, + NUM_TERRAIN_TYPES = 9, +} diff --git a/LuaCATS/enum/ThreatTypes.d.lua b/LuaCATS/enum/ThreatTypes.d.lua new file mode 100644 index 0000000000..f3cdfdd358 --- /dev/null +++ b/LuaCATS/enum/ThreatTypes.d.lua @@ -0,0 +1,11 @@ +--- @meta + +--- @enum ThreatType +ThreatTypes = { + THREAT_NONE = 0, + THREAT_MINOR = 1, + THREAT_MAJOR = 2, + THREAT_SEVERE = 3, + THREAT_CRITICAL = 4, + NUM_THREAT_VALUES = 5, +} diff --git a/LuaCATS/enum/TradeConnectionTypes.d.lua b/LuaCATS/enum/TradeConnectionTypes.d.lua new file mode 100644 index 0000000000..39cc2df3f7 --- /dev/null +++ b/LuaCATS/enum/TradeConnectionTypes.d.lua @@ -0,0 +1,11 @@ +--- @meta + +--- @enum TradeConnectionType +TradeConnectionTypes = { + TRADE_CONNECTION_INTERNATIONAL = 0, + TRADE_CONNECTION_FOOD = 1, + TRADE_CONNECTION_PRODUCTION = 2, + TRADE_CONNECTION_WONDER_RESOURCE = 3, + TRADE_CONNECTION_GOLD_INTERNAL = 4, + NUM_TRADE_CONNECTION_TYPES = 5, +} diff --git a/LuaCATS/enum/TradeableItems.d.lua b/LuaCATS/enum/TradeableItems.d.lua new file mode 100644 index 0000000000..806b9c806c --- /dev/null +++ b/LuaCATS/enum/TradeableItems.d.lua @@ -0,0 +1,24 @@ +--- @meta + +--- @enum TradeableItem +TradeableItems = { + TRADE_ITEM_NONE = -1, + TRADE_ITEM_GOLD = 0, + TRADE_ITEM_GOLD_PER_TURN = 1, + TRADE_ITEM_MAPS = 2, + TRADE_ITEM_RESOURCES = 3, + TRADE_ITEM_CITIES = 4, + TRADE_ITEM_OPEN_BORDERS = 5, + TRADE_ITEM_DEFENSIVE_PACT = 6, + TRADE_ITEM_RESEARCH_AGREEMENT = 7, + TRADE_ITEM_PEACE_TREATY = 8, + TRADE_ITEM_THIRD_PARTY_PEACE = 9, + TRADE_ITEM_THIRD_PARTY_WAR = 10, + TRADE_ITEM_ALLOW_EMBASSY = 11, + TRADE_ITEM_DECLARATION_OF_FRIENDSHIP = 12, + TRADE_ITEM_VOTE_COMMITMENT = 13, + TRADE_ITEM_TECHS = 14, + TRADE_ITEM_VASSALAGE = 15, + TRADE_ITEM_VASSALAGE_REVOKE = 16, + NUM_TRADEABLE_ITEMS = 17, +} diff --git a/LuaCATS/enum/YieldTypes.d.lua b/LuaCATS/enum/YieldTypes.d.lua new file mode 100644 index 0000000000..dd1c9df6be --- /dev/null +++ b/LuaCATS/enum/YieldTypes.d.lua @@ -0,0 +1,19 @@ +--- @meta + +--- @type table +YieldTypes = { + NO_YIELD = -1, + YIELD_FOOD = 0, + YIELD_PRODUCTION = 1, + YIELD_GOLD = 2, + YIELD_SCIENCE = 3, + YIELD_CULTURE = 4, + YIELD_FAITH = 5, + YIELD_TOURISM = 6, + YIELD_GOLDEN_AGE_POINTS = 7, + YIELD_GREAT_ADMIRAL_POINTS = 9, + YIELD_GREAT_GENERAL_POINTS = 8, + YIELD_POPULATION = 10, + YIELD_CULTURE_LOCAL = 11, + NUM_YIELD_TYPES = 17, +} diff --git a/VPSetupData.iss b/VPSetupData.iss index d05e6fed42..c95fe96bc6 100644 --- a/VPSetupData.iss +++ b/VPSetupData.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Vox Populi" -#define MyAppVersion "5.2.6" +#define MyAppVersion "5.2.7" #define MyAppPublisher "Community Patch Project Team" #define MyAppURL "http://forums.civfanatics.com/showthread.php?t=528034"