<commit_before>define([
	'ash',
	'game/GameGlobals',
	'game/GlobalSignals',
	'game/constants/UIConstants',
	'game/constants/CampConstants',
	'game/constants/OccurrenceConstants',
	'game/constants/WorldConstants',
	'game/nodes/sector/CampNode',
	'game/nodes/PlayerPositionNode',
	'game/nodes/player/PlayerStatsNode',
	'game/nodes/tribe/TribeUpgradesNode',
	'game/components/common/PositionComponent',
	'game/components/common/ResourcesComponent',
	'game/components/common/ResourceAccumulationComponent',
	'game/components/player/DeityComponent',
	'game/components/type/LevelComponent',
	'game/components/sector/improvements/SectorImprovementsComponent',
	'game/components/sector/events/RecruitComponent',
	'game/components/sector/events/TraderComponent',
	'game/components/sector/events/RaidComponent',
	'game/components/sector/OutgoingCaravansComponent'
], function (
	Ash, GameGlobals, GlobalSignals, UIConstants, CampConstants, OccurrenceConstants, WorldConstants,
	CampNode, PlayerPositionNode, PlayerStatsNode, TribeUpgradesNode,
	PositionComponent, ResourcesComponent, ResourceAccumulationComponent, DeityComponent, LevelComponent, SectorImprovementsComponent, RecruitComponent, TraderComponent, RaidComponent, OutgoingCaravansComponent
) {
	var UIOutMilestonesSystem = Ash.System.extend({

		engine: null,

		campNodes: null,
		sortedCampNodes: null,
		playerPosNodes: null,
		playerStatsNodes: null,
		tribeUpgradesNodes: null,

		constructor: function () {
			return this;
		},

		addToEngine: function (engine) {
			this.engine = engine;
			this.campNodes = engine.getNodeList(CampNode);
			this.playerPosNodes = engine.getNodeList(PlayerPositionNode);
			this.playerStatsNodes = engine.getNodeList(PlayerStatsNode);
			this.tribeUpgradesNodes = engine.getNodeList(TribeUpgradesNode);
			GlobalSignals.add(this, GlobalSignals.milestoneUnlockedSignal, this.onMilestoneClaimed);
			GlobalSignals.add(this, GlobalSignals.slowUpdateSignal, this.slowUpdate);
			GlobalSignals.add(this, GlobalSignals.tabChangedSignal, this.onTabChanged);
		},

		removeFromEngine: function (engine) {
			this.engine = null;
			this.campNodes = null;
			this.playerPosNodes = null;
			GlobalSignals.removeAll(this);
		},

		update: function () {
			if (GameGlobals.gameState.uiStatus.isHidden) return;
			this.updateBubble();
		},

		slowUpdate: function () {
			if (GameGlobals.gameState.uiStatus.isHidden) return;
		},

		refresh: function () {
			$("#tab-header h2").text("Milestones");
			this.updateMilestones();
		},

		updateBubble: function () {
			let bubbleNumber = 0;
			if (this.canClaimMilestone()) bubbleNumber++;
			
			this.bubbleNumber = bubbleNumber;
			
			$("#switch-world .bubble").text(this.bubbleNumber)
			GameGlobals.uiFunctions.toggle("#switch-milestones .bubble", this.bubbleNumber > 0);
		},
		
		updateMilestones: function () {
			let currentMilestone = GameGlobals.tribeHelper.getCurrentMilestone();
			let nextMilestone = GameGlobals.tribeHelper.getNextMilestone();
			
			$("#milestone-current-name").text("Current tribe level: " + currentMilestone.name + " (" + currentMilestone.index + ")");
			$("#milestone-next-name").text("Next tribe level: " + nextMilestone.name + " (" + nextMilestone.index + ")");
			
			let action = "claim_milestone_" + nextMilestone.index;
			
			let reqs = GameGlobals.playerActionsHelper.getReqs(action);
			let requirementsDiv = "<div style='flex-grow: 2'>";
			if (reqs) {
				if (reqs.tribe && reqs.tribe.population) {
					requirementsDiv += this.getMilestoneReqsListEntry("Population", reqs.tribe.population, GameGlobals.tribeHelper.getTotalPopulation());
				}
				if (reqs.tribe && reqs.tribe.improvements) {
					for (let improvementID in reqs.tribe.improvements) {
						requirementsDiv += this.getMilestoneReqsListEntry(improvementID, reqs.tribe.improvements[improvementID], GameGlobals.playerActionsHelper.getCurrentImprovementCountTotal(improvementID));
					}
				}
				if (reqs.tribe && reqs.tribe.projects) {
					for (let improvementID in reqs.tribe.projects) {
						requirementsDiv += this.getMilestoneReqsListEntry(improvementID, reqs.tribe.projects[improvementID], GameGlobals.playerActionsHelper.getCurrentImprovementCountTotal(improvementID));
					}
				}
			}
			requirementsDiv += "</div>";
			
			$("#milestone-container").empty();
			$("#milestone-container").append(requirementsDiv);
			$("#milestone-container").append("<div><button class='action' action='" + action + "'>Unlock</button></div>");
			
			GameGlobals.uiFunctions.generateButtonOverlays("#milestone-container");
			GameGlobals.uiFunctions.generateCallouts("#milestone-container");
			GameGlobals.uiFunctions.registerActionButtonListeners("#milestone-container");
		},
		
		canClaimMilestone: function () {
			let nextMilestone = GameGlobals.tribeHelper.getNextMilestone();
			let action = "claim_milestone_" + nextMilestone.index;
			return GameGlobals.playerActionsHelper.checkAvailability(action);
		},
		
		getMilestoneReqsListEntry: function (name, reqAmount, currentAmount) {
			let isReady = currentAmount >= reqAmount;
			let spanName = "<span>" + name + "</span>";
			let spanAmount = "<span class='" + (isReady ? "" : "action-cost-blocker") + "'>" + currentAmount + "/" + reqAmount + "</span>";
			let spanIcon = isReady ? "<span class='reqs-checkmark' title='checkmark' />" : "";
			return "<span class='reqs-list-entry'>" + spanName + ": " + spanAmount + spanIcon + "</span>";
		},

		onTabChanged: function () {
			if (GameGlobals.gameState.uiStatus.currentTab === GameGlobals.uiFunctions.elementIDs.tabs.milestones) {
				this.refresh();
			}
		},
		
		onMilestoneClaimed: function () {
			if (GameGlobals.gameState.uiStatus.isHidden) return;
			if (GameGlobals.gameState.uiStatus.currentTab === GameGlobals.uiFunctions.elementIDs.tabs.milestones) {
				this.updateMilestones();
			}
		}

	});

	return UIOutMilestonesSystem;
});
<commit_msg>[qa] fix tribe bubble number wrong<commit_after>define([
	'ash',
	'game/GameGlobals',
	'game/GlobalSignals',
	'game/constants/UIConstants',
	'game/constants/CampConstants',
	'game/constants/OccurrenceConstants',
	'game/constants/WorldConstants',
	'game/nodes/sector/CampNode',
	'game/nodes/PlayerPositionNode',
	'game/nodes/player/PlayerStatsNode',
	'game/nodes/tribe/TribeUpgradesNode',
	'game/components/common/PositionComponent',
	'game/components/common/ResourcesComponent',
	'game/components/common/ResourceAccumulationComponent',
	'game/components/player/DeityComponent',
	'game/components/type/LevelComponent',
	'game/components/sector/improvements/SectorImprovementsComponent',
	'game/components/sector/events/RecruitComponent',
	'game/components/sector/events/TraderComponent',
	'game/components/sector/events/RaidComponent',
	'game/components/sector/OutgoingCaravansComponent'
], function (
	Ash, GameGlobals, GlobalSignals, UIConstants, CampConstants, OccurrenceConstants, WorldConstants,
	CampNode, PlayerPositionNode, PlayerStatsNode, TribeUpgradesNode,
	PositionComponent, ResourcesComponent, ResourceAccumulationComponent, DeityComponent, LevelComponent, SectorImprovementsComponent, RecruitComponent, TraderComponent, RaidComponent, OutgoingCaravansComponent
) {
	var UIOutMilestonesSystem = Ash.System.extend({

		engine: null,

		campNodes: null,
		sortedCampNodes: null,
		playerPosNodes: null,
		playerStatsNodes: null,
		tribeUpgradesNodes: null,

		constructor: function () {
			return this;
		},

		addToEngine: function (engine) {
			this.engine = engine;
			this.campNodes = engine.getNodeList(CampNode);
			this.playerPosNodes = engine.getNodeList(PlayerPositionNode);
			this.playerStatsNodes = engine.getNodeList(PlayerStatsNode);
			this.tribeUpgradesNodes = engine.getNodeList(TribeUpgradesNode);
			GlobalSignals.add(this, GlobalSignals.milestoneUnlockedSignal, this.onMilestoneClaimed);
			GlobalSignals.add(this, GlobalSignals.slowUpdateSignal, this.slowUpdate);
			GlobalSignals.add(this, GlobalSignals.tabChangedSignal, this.onTabChanged);
		},

		removeFromEngine: function (engine) {
			this.engine = null;
			this.campNodes = null;
			this.playerPosNodes = null;
			GlobalSignals.removeAll(this);
		},

		update: function () {
			if (GameGlobals.gameState.uiStatus.isHidden) return;
			this.updateBubble();
		},

		slowUpdate: function () {
			if (GameGlobals.gameState.uiStatus.isHidden) return;
		},

		refresh: function () {
			$("#tab-header h2").text("Milestones");
			this.updateMilestones();
		},

		updateBubble: function () {
			let bubbleNumber = 0;
			if (this.canClaimMilestone()) bubbleNumber++;
			
			this.bubbleNumber = bubbleNumber;
			
			$("#switch-milestones .bubble").text(this.bubbleNumber)
			GameGlobals.uiFunctions.toggle("#switch-milestones .bubble", this.bubbleNumber > 0);
		},
		
		updateMilestones: function () {
			let currentMilestone = GameGlobals.tribeHelper.getCurrentMilestone();
			let nextMilestone = GameGlobals.tribeHelper.getNextMilestone();
			
			$("#milestone-current-name").text("Current tribe level: " + currentMilestone.name + " (" + currentMilestone.index + ")");
			$("#milestone-next-name").text("Next tribe level: " + nextMilestone.name + " (" + nextMilestone.index + ")");
			
			let action = "claim_milestone_" + nextMilestone.index;
			
			let reqs = GameGlobals.playerActionsHelper.getReqs(action);
			let requirementsDiv = "<div style='flex-grow: 2'>";
			if (reqs) {
				if (reqs.tribe && reqs.tribe.population) {
					requirementsDiv += this.getMilestoneReqsListEntry("Population", reqs.tribe.population, GameGlobals.tribeHelper.getTotalPopulation());
				}
				if (reqs.tribe && reqs.tribe.improvements) {
					for (let improvementID in reqs.tribe.improvements) {
						requirementsDiv += this.getMilestoneReqsListEntry(improvementID, reqs.tribe.improvements[improvementID], GameGlobals.playerActionsHelper.getCurrentImprovementCountTotal(improvementID));
					}
				}
				if (reqs.tribe && reqs.tribe.projects) {
					for (let improvementID in reqs.tribe.projects) {
						requirementsDiv += this.getMilestoneReqsListEntry(improvementID, reqs.tribe.projects[improvementID], GameGlobals.playerActionsHelper.getCurrentImprovementCountTotal(improvementID));
					}
				}
			}
			requirementsDiv += "</div>";
			
			$("#milestone-container").empty();
			$("#milestone-container").append(requirementsDiv);
			$("#milestone-container").append("<div><button class='action' action='" + action + "'>Unlock</button></div>");
			
			GameGlobals.uiFunctions.generateButtonOverlays("#milestone-container");
			GameGlobals.uiFunctions.generateCallouts("#milestone-container");
			GameGlobals.uiFunctions.registerActionButtonListeners("#milestone-container");
		},
		
		canClaimMilestone: function () {
			let nextMilestone = GameGlobals.tribeHelper.getNextMilestone();
			let action = "claim_milestone_" + nextMilestone.index;
			return GameGlobals.playerActionsHelper.checkAvailability(action);
		},
		
		getMilestoneReqsListEntry: function (name, reqAmount, currentAmount) {
			let isReady = currentAmount >= reqAmount;
			let spanName = "<span>" + name + "</span>";
			let spanAmount = "<span class='" + (isReady ? "" : "action-cost-blocker") + "'>" + currentAmount + "/" + reqAmount + "</span>";
			let spanIcon = isReady ? "<span class='reqs-checkmark' title='checkmark' />" : "";
			return "<span class='reqs-list-entry'>" + spanName + ": " + spanAmount + spanIcon + "</span>";
		},

		onTabChanged: function () {
			if (GameGlobals.gameState.uiStatus.currentTab === GameGlobals.uiFunctions.elementIDs.tabs.milestones) {
				this.refresh();
			}
		},
		
		onMilestoneClaimed: function () {
			if (GameGlobals.gameState.uiStatus.isHidden) return;
			if (GameGlobals.gameState.uiStatus.currentTab === GameGlobals.uiFunctions.elementIDs.tabs.milestones) {
				this.updateMilestones();
			}
		}

	});

	return UIOutMilestonesSystem;
});

{'fraction_non_alphanumeric': 0.1358714043993232, 'fraction_numerical': 0.0006768189509306261, 'mean_word_length': 5.137590861889928, 'pattern_counts': {'":': 2, '<': 29, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2264010', 'n_tokens_mistral': 4178, 'n_tokens_neox': 3655, 'n_words': 537}