aboutsummaryrefslogtreecommitdiff
path: root/Northstar.Client/mod/scripts/vscripts/ui/menu_stats_maps.nut
blob: eb844af44318086f5f34f600bd28427359e331b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
global function InitViewStatsMapsMenu
global function setit
struct
{
	var menu
	GridMenuData gridData
	bool isGridInitialized = false
	array<string> allMaps
} file

// Why this file is included in Northstar:
// As it turns out, the Respawn developers in all their infinite wisdom decided to add a check to floats for NaN and Inf
// which does not exist in base Squirrel
// The code looks a bit like this:
// 		if ( strcpy_s(fos + 4, 0x16ui64, "1#QNAN") )
//    		invoke_watson(0i64, 0i64, 0i64, 0, 0i64);
// 		goto LABEL_27; 
// As if turns out, there is NO way to check if a float is one of these values in script
// This alone, however, wouldn't be that bad, as it would simply result in a 1#QNAN being output to the screen.
// Unfortunately, Respawns universe sized brains did not stop there.
// One day, a dev at Respawn had to write a function to convert an amount of hours into a timestring.
// Now, you and i dear reader would, being mortals, opt for the O(1) time solution of using the floor() and modulo functions
// You may think this would work perfectly, but you would be wrong. This is not the respawn Way!
// Instead, they opted to write the following piece of O(n) time algorithm:
// 	while ( minutes >= 60 )
//		{
//			minutes -= 60
//			hours++
//		}
// Now you may ask: "Is this horribly inefficient and bug-prone?", but you must understand: This is the Respawn Way
// Passing in a NaN does not simply output a NaN to the screen, for that would be too simple.
// Nay, instead, it hangs the UI thread for all eternity, as it tries to subtract 60 from a NaN
// In fact, i think we should thank that developer for all the fun times we have had tracking and fixing this bug
// However, we mortals cannot possibly wield the greatness of Respawn's code, and we must settle for a lowly O(1) algorithm instead
// P.S: The other part of this fix is menu_stats_utility.nut

void function InitViewStatsMapsMenu()
{
	var menu = GetMenu( "ViewStats_Maps_Menu" )
	file.menu = menu

	Hud_SetText( Hud_GetChild( file.menu, "MenuTitle" ), "#STATS_MAPS" )

	file.gridData.rows = 5
	file.gridData.columns = 1
	//file.gridData.numElements // Set in OnViewStatsWeapons_Open after itemdata exists
	file.gridData.pageType = eGridPageType.VERTICAL
	file.gridData.tileWidth = 224
	file.gridData.tileHeight = 112
	file.gridData.paddingVert = 6
	file.gridData.paddingHorz = 6

	Grid_AutoAspectSettings( menu, file.gridData )

	file.gridData.initCallback = MapButton_Init
	file.gridData.getFocusCallback = MapButton_GetFocus
	file.gridData.clickCallback = MapButton_Activate

	AddMenuEventHandler( file.menu, eUIEvent.MENU_OPEN, OnViewStatsWeapons_Open )

	AddMenuFooterOption( file.menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
}

void function OnViewStatsWeapons_Open()
{
	UI_SetPresentationType( ePresentationType.NO_MODELS )

	file.allMaps = GetPrivateMatchMaps()

	file.gridData.numElements = file.allMaps.len()

	if ( !file.isGridInitialized )
	{
		GridMenuInit( file.menu, file.gridData )
		file.isGridInitialized = true
	}

	file.gridData.currentPage = 0

	Grid_InitPage( file.menu, file.gridData )
	Hud_SetFocused( Grid_GetButtonForElementNumber( file.menu, 0 ) )
	UpdateStatsForMap( file.allMaps[ 0 ] )
}

bool function MapButton_Init( var button, int elemNum )
{
	string mapName = file.allMaps[ elemNum ]

	asset mapImage = GetMapImageForMapName( mapName )

	var rui = Hud_GetRui( button )
	RuiSetImage( rui, "buttonImage", mapImage )

	Hud_SetEnabled( button, true )
	Hud_SetVisible( button, true )

	return true
}

void function MapButton_GetFocus( var button, int elemNum )
{
	if ( IsControllerModeActive() )
		UpdateStatsForMap( file.allMaps[ elemNum ] )
}

void function MapButton_Activate( var button, int elemNum )
{
	if ( !IsControllerModeActive() )
		UpdateStatsForMap( file.allMaps[ elemNum ] )
}

int function GetGameStatForMapInt( string gameStat, string mapName )
{
	array<string> privateMatchModes = GetPrivateMatchModes()

	int totalStat = 0
	int enumCount = PersistenceGetEnumCount( "gameModes" )
	for ( int modeId = 0; modeId < enumCount; modeId++ )
	{
		string modeName = PersistenceGetEnumItemNameForIndex( "gameModes", modeId )

		totalStat += GetGameStatForMapAndModeInt( gameStat, mapName, modeName )
	}

	return totalStat
}

int function GetGameStatForMapAndModeInt( string gameStat, string mapName, string modeName, string difficulty = "1" )
{
	string statString = GetStatVar( "game_stats", gameStat, "" )
	string persistentVar = Stats_GetFixedSaveVar( statString, mapName, modeName, difficulty )

	return GetUIPlayer().GetPersistentVarAsInt( persistentVar )
}

float function GetGameStatForMapFloat( string gameStat, string mapName )
{
	array<string> privateMatchModes = GetPrivateMatchModes()

	float totalStat = 0
	int enumCount = PersistenceGetEnumCount( "gameModes" )
	for ( int modeId = 0; modeId < enumCount; modeId++ )
	{
		string modeName = PersistenceGetEnumItemNameForIndex( "gameModes", modeId )

		if ( (GetGameStatForMapAndModeFloat( gameStat, mapName, modeName ).tostring() ) != "1.#QNAN" )
			totalStat += GetGameStatForMapAndModeFloat( gameStat, mapName, modeName )
		else
			print("Hey buddy, I just saved you from a game freeze. You're welcome :)")
	}

	return totalStat
}

float function GetGameStatForMapAndModeFloat( string gameStat, string mapName, string modeName )
{
	string statString = GetStatVar( "game_stats", gameStat, "" )
	string persistentVar = Stats_GetFixedSaveVar( statString, mapName, modeName, "1" )

	return expect float( GetUIPlayer().GetPersistentVar( persistentVar ) )
}


void function UpdateStatsForMap( string mapName )
{
	entity player = GetUIPlayer()
	if ( player == null )
		return

	Hud_SetText( Hud_GetChild( file.menu, "WeaponName" ), GetMapDisplayName( mapName ) )

	// Image
	var imageElem = Hud_GetRui( Hud_GetChild( file.menu, "WeaponImageLarge" ) )
	RuiSetImage( imageElem, "basicImage", GetMapImageForMapName( mapName ) )
	var hoursplayed = GetGameStatForMapFloat( "hoursPlayed", mapName ) 
	string timePlayed = HoursToTimeString( GetGameStatForMapFloat( "hoursPlayed", mapName ) )
	string gamesPlayed = string( GetGameStatForMapInt( "game_completed", mapName ) )

	SetStatBoxDisplay( Hud_GetChild( file.menu, "Stat0" ), Localize( "#STATS_HEADER_TIME_PLAYED" ), 		timePlayed )
	SetStatBoxDisplay( Hud_GetChild( file.menu, "Stat1" ), Localize( "#STATS_GAMES_PLAYED" ), 				gamesPlayed )
	//SetStatBoxDisplay( Hud_GetChild( file.menu, "Stat2" ), Localize( "#STATS_GAMES_PLAYED" ), 				gamesPlayed )
	//SetStatBoxDisplay( Hud_GetChild( file.menu, "Stat3" ), Localize( "#STATS_GAMES_PLAYED" ), 				gamesPlayed )

	string winPercent = GetPercent( float( GetGameStatForMapInt( "game_won", mapName ) ), float( GetGameStatForMapInt( "game_completed", mapName ) ), 0 )

	SetStatsLabelValue( file.menu, "KillsLabel0", 				"#STATS_GAMES_WIN_PERCENT" )
	SetStatsLabelValue( file.menu, "KillsValue0", 				("" + winPercent + "%") )

	SetStatsLabelValue( file.menu, "KillsLabel1", 				"#STATS_GAMES_WON" )
	SetStatsLabelValue( file.menu, "KillsValue1", 				GetGameStatForMapInt( "game_won", mapName ) )

	SetStatsLabelValue( file.menu, "KillsLabel2", 				"#STATS_GAMES_MVP" )
	SetStatsLabelValue( file.menu, "KillsValue2", 				GetGameStatForMapInt( "mvp", mapName ) )

	SetStatsLabelValue( file.menu, "KillsLabel3", 				"#STATS_GAMES_TOP3" )
	SetStatsLabelValue( file.menu, "KillsValue3", 				GetGameStatForMapInt( "top3OnTeam", mapName ) )

	SetStatsLabelValue( file.menu, "KillsLabel4", 				"--" )
	SetStatsLabelValue( file.menu, "KillsValue4", 				"--" )

	//var anchorElem = Hud_GetChild( file.menu, "WeaponStatsBackground" )
	//printt( Hud_GetX( anchorElem ) )
	//printt( Hud_GetX( anchorElem ) )
	//printt( Hud_GetX( anchorElem ) )
	//printt( Hud_GetX( anchorElem ) )
	//Hud_SetX( anchorElem, 0 )
	//
	array<string> gameModesArray = GetPersistenceEnumAsArray( "gameModes" )

	array<PieChartEntry> modes
	foreach ( modeName in gameModesArray )
	{
		float modePlayedTime = GetGameStatForMapAndModeFloat( "hoursPlayed", mapName, modeName )
		if ( modePlayedTime > 0 )
			AddPieChartEntry( modes, GameMode_GetName( modeName ), modePlayedTime, GetGameModeDisplayColor( modeName ) )
	}

	const MAX_MODE_ROWS = 8

	if ( modes.len() > 0 )
	{
		modes.sort( ComparePieChartEntryValues )

		if ( modes.len() > MAX_MODE_ROWS )
		{
			float otherValue
			for ( int i = MAX_MODE_ROWS-1; i < modes.len() ; i++ )
				otherValue += modes[i].numValue

			modes.resize( MAX_MODE_ROWS-1 )
			AddPieChartEntry( modes, "#GAMEMODE_OTHER", otherValue, [127, 127, 127, 255] )
		}
	}

	PieChartData modesPlayedData
	modesPlayedData.entries = modes
	modesPlayedData.labelColor = [ 255, 255, 255, 255 ]
	SetPieChartData( file.menu, "ModesPieChart", "#GAME_MODES_PLAYED", modesPlayedData )

	array<string> fdMaps = GetPlaylistMaps( "fd" )

	if ( fdMaps.contains( mapName ) )
	{
		array<var> pveElems = GetElementsByClassname( file.menu, "PvEGroup" )
		foreach ( elem in pveElems )
		{
			Hud_Show( elem )
		}

		vector perfectColor = TEAM_COLOR_FRIENDLY / 219.0

		var iconLegendRui = Hud_GetRui( Hud_GetChild( file.menu, "PvEIconLegend" ) )
		RuiSetImage( iconLegendRui, "basicImage", $"rui/menu/gametype_select/playlist_fd_normal" )
		RuiSetFloat3( iconLegendRui, "basicImageColor", perfectColor )

		var icon0Rui = Hud_GetRui( Hud_GetChild( file.menu, "PvEIcon0" ) )
		RuiSetImage( icon0Rui, "basicImage", $"rui/menu/gametype_select/playlist_fd_easy" )
		int easyWins = GetGameStatForMapAndModeInt( "games_completed_fd", mapName, "fd", "0" )
		SetStatsLabelValue( file.menu, "PvELabel0", 				"#FD_DIFFICULTY_EASY" )
		SetStatsLabelValue( file.menu, "PvEValueA0", 				easyWins )
		if ( GetGameStatForMapAndModeInt( "perfectMatches", mapName, "fd", "0" ) )
			RuiSetFloat3( icon0Rui, "basicImageColor", perfectColor )
		else
			RuiSetFloat3( icon0Rui, "basicImageColor", easyWins > 0 ? <1, 1, 1> : <0.15, 0.15, 0.15> )

		var icon1Rui = Hud_GetRui( Hud_GetChild( file.menu, "PvEIcon1" ) )
		RuiSetImage( icon1Rui, "basicImage", $"rui/menu/gametype_select/playlist_fd_normal" )
		int normalWins = GetGameStatForMapAndModeInt( "games_completed_fd", mapName, "fd", "1" )
		SetStatsLabelValue( file.menu, "PvELabel1", 				"#FD_DIFFICULTY_NORMAL" )
		SetStatsLabelValue( file.menu, "PvEValueA1", 				normalWins )
		if ( GetGameStatForMapAndModeInt( "perfectMatches", mapName, "fd", "1" ) )
			RuiSetFloat3( icon1Rui, "basicImageColor", perfectColor )
		else
			RuiSetFloat3( icon1Rui, "basicImageColor", normalWins > 0 ? <1, 1, 1> : <0.15, 0.15, 0.15> )

		var icon2Rui = Hud_GetRui( Hud_GetChild( file.menu, "PvEIcon2" ) )
		RuiSetImage( icon2Rui, "basicImage", $"rui/menu/gametype_select/playlist_fd_hard" )
		int hardWins = GetGameStatForMapAndModeInt( "games_completed_fd", mapName, "fd", "2" )
		SetStatsLabelValue( file.menu, "PvELabel2", 				"#FD_DIFFICULTY_HARD" )
		SetStatsLabelValue( file.menu, "PvEValueA2", 				hardWins )
		if ( GetGameStatForMapAndModeInt( "perfectMatches", mapName, "fd", "2" ) )
			RuiSetFloat3( icon2Rui, "basicImageColor", perfectColor )
		else
			RuiSetFloat3( icon2Rui, "basicImageColor", hardWins > 0 ? <1, 1, 1> : <0.15, 0.15, 0.15> )

		var icon3Rui = Hud_GetRui( Hud_GetChild( file.menu, "PvEIcon3" ) )
		RuiSetImage( icon3Rui, "basicImage", $"rui/menu/gametype_select/playlist_fd_master" )
		int masterWins = GetGameStatForMapAndModeInt( "games_completed_fd", mapName, "fd", "3" )
		SetStatsLabelValue( file.menu, "PvELabel3", 				"#FD_DIFFICULTY_MASTER" )
		SetStatsLabelValue( file.menu, "PvEValueA3", 				masterWins )
		if ( GetGameStatForMapAndModeInt( "perfectMatches", mapName, "fd", "3" ) )
			RuiSetFloat3( icon3Rui, "basicImageColor", perfectColor )
		else
			RuiSetFloat3( icon3Rui, "basicImageColor", masterWins > 0 ? <1, 1, 1> : <0.15, 0.15, 0.15> )

		var icon4Rui = Hud_GetRui( Hud_GetChild( file.menu, "PvEIcon4" ) )
		RuiSetImage( icon4Rui, "basicImage", $"rui/menu/gametype_select/playlist_fd_insane" )
		int insaneWins = GetGameStatForMapAndModeInt( "games_completed_fd", mapName, "fd", "4" )
		SetStatsLabelValue( file.menu, "PvELabel4", 				"#FD_DIFFICULTY_INSANE" )
		SetStatsLabelValue( file.menu, "PvEValueA4", 				insaneWins )
		if ( GetGameStatForMapAndModeInt( "perfectMatches", mapName, "fd", "4" ) )
			RuiSetFloat3( icon4Rui, "basicImageColor", perfectColor )
		else
			RuiSetFloat3( icon4Rui, "basicImageColor", insaneWins > 0 ? <1, 1, 1> : <0.15, 0.15, 0.15> )
	}
	else
	{
		array<var> pveElems = GetElementsByClassname( file.menu, "PvEGroup" )
		foreach ( elem in pveElems )
		{
			Hud_Hide( elem )
		}
	}
}


var function setit( vector color )
{
	var iconLegendRui = Hud_GetRui( Hud_GetChild( file.menu, "PvEIconLegend" ) )
	RuiSetImage( iconLegendRui, "basicImage", $"rui/menu/gametype_select/playlist_fd_normal" )
	RuiSetFloat3( iconLegendRui, "basicImageColor", color )
}