aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/mod/scripts
diff options
context:
space:
mode:
authorGeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com>2024-08-14 17:31:06 +0200
committerGitHub <noreply@github.com>2024-08-14 17:31:06 +0200
commitfa4e319c0b60cd68a3dccaa4322e3c35cfa1e385 (patch)
tree4a78391c5008a4aa50d81fbca5d9f26bb475cd96 /Northstar.CustomServers/mod/scripts
parentbcec5a5e9edd2a2af3a017ea4b250a9ba1112e6f (diff)
parent7aa3958ccd8e32970736654dfae0c7a87f0798bb (diff)
downloadNorthstarMods-fa4e319c0b60cd68a3dccaa4322e3c35cfa1e385.tar.gz
NorthstarMods-fa4e319c0b60cd68a3dccaa4322e3c35cfa1e385.zip
Merge branch 'main' into permanent-amped-weapons-fix-prpermanent-amped-weapons-fix-pr
Diffstat (limited to 'Northstar.CustomServers/mod/scripts')
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/battle_chatter.csv24
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/battle_chatter_voices.csv19
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/burn_meter_rewards.csv21
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/burn_meter_store.csv21
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/caller_ids_mp.csv2
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/calling_cards.csv491
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/callsign_icons.csv193
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/camo_skins.csv161
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/community_entries.csv22
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/death_hints_mp.csv381
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/default_pilot_loadouts.csv11
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/default_titan_loadouts.csv8
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/earn_meter_mp.csv31
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/faction_dialogue.csv273
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/faction_leaders.csv8
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/faction_leaders_dropship_anims.csv23
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/fd_awards.csv15
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/features_mp.csv21
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/flightpath_assets.csv8
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/grunt_chatter_mp.csv47
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/non_loadout_weapons.csv29
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/pain_death_sounds.csv53
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/pilot_abilities.csv16
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/pilot_executions.csv14
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/pilot_passives.csv10
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/pilot_properties.csv8
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_features.csv5
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_mods.csv250
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_mods_common.csv27
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/pilot_weapons.csv33
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/playlist_items.csv27
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/score_events.csv221
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/sp_levels.csv17
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/spectre_chatter_mp.csv8
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/spotlight_images.csv30
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/startpoints.csv313
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_abilities.csv36
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_executions.csv26
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_fd_upgrades.csv50
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_nose_art.csv191
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_os_conversations.csv67
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_passives.csv52
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_primary_mods.csv1
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_primary_mods_common.csv1
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_primary_weapons.csv10
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_properties.csv10
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_skins.csv56
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titan_voices.csv9
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/titans_mp.csv8
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/unlocks_faction_level.csv82
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/unlocks_fd_titan_level.csv26
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/unlocks_player_level.csv996
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/unlocks_random.csv359
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/unlocks_titan_level.csv402
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/unlocks_weapon_level_pilot.csv383
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/weapon_skins.csv35
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/xp_per_faction_level.csv6
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/xp_per_fd_titan_level.csv25
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/xp_per_player_level.csv51
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/xp_per_titan_level.csv21
-rw-r--r--Northstar.CustomServers/mod/scripts/datatable/xp_per_weapon_level.csv21
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_bubble_shield.gnut3
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_chat.gnut8
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_codecallbacks_common.gnut29
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_custom_codecallbacks.gnut9
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_entitystructs.gnut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_harvester.gnut73
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_items.nut26
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut27
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut8
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_northstar_cheatcommands.nut5
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_utility.gnut30
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_utility_shared.nut3
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut55
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_soldiers.gnut13
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut82
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/class/cplayer.nut20
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/conversation/_grunt_chatter_mp.gnut195
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/earn_meter/sv_earn_meter.gnut8
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut236
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut12
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut144
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut162
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut1831
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut58
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut73
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut17
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut33
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut22
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut31
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut49
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/lobby/_lobby.gnut12
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut36
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/lobby/sh_private_lobby_modes_init.gnut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype.gnut29
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut89
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_battery_port.gnut220
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut270
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_changemap.nut4
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut191
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut39
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut217
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_model_viewer.nut180
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut159
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_spectator.gnut10
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut1064
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut3
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut45
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/rodeo/_rodeo_titan.gnut9
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut25
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/sh_northstar_utils.gnut6
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/sh_powerup.gnut294
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/sh_progression.nut1154
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/sh_server_to_client_stringcommands.gnut7
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/sh_store.gnut933
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/sh_utility_all.gnut32
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans.gnut26
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans_drop.gnut17
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_health.gnut2
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/titan/class_titan.gnut5
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut25
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut12
126 files changed, 13504 insertions, 644 deletions
diff --git a/Northstar.CustomServers/mod/scripts/datatable/battle_chatter.csv b/Northstar.CustomServers/mod/scripts/datatable/battle_chatter.csv
new file mode 100644
index 00000000..74d4c1d9
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/battle_chatter.csv
@@ -0,0 +1,24 @@
+conversationname,priority,debounce
+"bc_pReload",500,45.000000
+"bc_pHardcover",500,10.000000
+"bc_pPulse",500,10.000000
+"bc_pGrapple",500,10.000000
+"bc_pHolo",500,10.000000
+"bc_pCloak",500,10.000000
+"bc_pAmp",500,10.000000
+"bc_pStim",500,10.000000
+"bc_pPhase",500,10.000000
+"bc_pFrag",500,10.000000
+"bc_pSmoke",500,10.000000
+"bc_pArc",500,10.000000
+"bc_pGrav",500,10.000000
+"bc_pSatchel",500,10.000000
+"bc_pFirestar",500,10.000000
+"bc_pGravStar",500,10.000000
+"bc_pAmpedWall",500,10.000000
+"bc_fKilledEnemy",500,10.000000
+"bc_pPulseBladeSpotEnemy",500,10.000000
+"bc_fNearEnemyDmg",500,10.000000
+"bc_fCongratsKill",500,10.000000
+"bc_pBatteryOffer",500,5.000000
+"bc_pIntroChat",500,5.000000 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/battle_chatter_voices.csv b/Northstar.CustomServers/mod/scripts/datatable/battle_chatter_voices.csv
new file mode 100644
index 00000000..32450513
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/battle_chatter_voices.csv
@@ -0,0 +1,19 @@
+isMale,isAndroid,ref
+1,0,"M1"
+1,0,"M2"
+1,0,"M3"
+1,0,"M4"
+1,0,"M5"
+1,0,"M6"
+1,0,"M7"
+1,0,"M8"
+1,0,"M9"
+0,0,"F1"
+0,0,"F2"
+0,0,"F3"
+0,0,"F4"
+0,0,"F5"
+1,1,"MA1"
+1,1,"MA2"
+0,1,"FA1"
+0,1,"FA2" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/burn_meter_rewards.csv b/Northstar.CustomServers/mod/scripts/datatable/burn_meter_rewards.csv
new file mode 100644
index 00000000..41e8376d
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/burn_meter_rewards.csv
@@ -0,0 +1,21 @@
+itemRef,selectable,name,description,image,model,skinIndex,activationCost,rewardAvailableFor,weaponName,extraWeaponMod,activationText,cost
+"burnmeter_maphack",1,"#BURNMETER_MAP_HACK","#BURNMETER_MAP_HACK_DESC","rui/menu/boosts/boost_map_hack","models/weapons_r2/burn_card/burn_card.mdl",9,0.700000,"PILOT_AND_TITAN","mp_ability_burncardweapon","burnmeter_maphack","#BURNMETER_MAP_HACK_DESC",125
+"burnmeter_amped_weapons",1,"#BURNMETER_AMPED_WEAPONS","#BURNMETER_AMPED_WEAPONS_DESC","rui/menu/boosts/boost_amped_weapons","models/weapons_r2/burn_card/burn_card.mdl",0,0.800000,"PILOT_AND_TITAN","mp_ability_burncardweapon","burnmeter_amped_weapons","#BURNMETER_AMPED_WEAPONS_DESC",125
+"burnmeter_ticks",1,"#BURNMETER_TICKS","#BURNMETER_TICKS_DESC","rui/menu/boosts/boost_ticks","models/weapons_r2/burn_card/burn_card.mdl",5,0.650000,"PILOT_ONLY","mp_weapon_frag_drone","","#WPN_FRAG_DRONE_LONGDESC",125
+"burnmeter_random_foil",1,"#BURNMETER_RANDOM_FOIL","#BURNMETER_RANDOM_FOIL_DESC","rui/menu/boosts/boost_random","models/weapons_r2/burn_card/burn_card.mdl",11,0.500000,"PILOT_AND_TITAN","mp_ability_burncardweapon","burnmeter_random_foil","#BURNMETER_RANDOM_FOIL_DESC",125
+"burnmeter_ap_turret_weapon",1,"#BURNMETER_AP_TURRETWEAPON","#BURNMETER_AP_TURRETWEAPON_DESC","rui/menu/boosts/boost_antipersonnel_sentry","models/weapons_r2/burn_card/burn_card.mdl",6,0.720000,"PILOT_ONLY","mp_ability_turretweapon","burnmeter_ap_turret_weapon","#BURNMETER_AP_TURRETWEAPON_DESC_BOOST_ACTIVATION_TEXT",125
+"burnmeter_phase_rewind",1,"#WPN_REWIND","#WPN_REWIND_LONGDESC","rui/menu/boosts/boost_phase_rewind","models/weapons_r2/burn_card/burn_card.mdl",8,0.250000,"PILOT_ONLY","mp_ability_burncardweapon","burnmeter_phase_rewind","#WPN_REWIND_LONGDESC",125
+"burnmeter_at_turret_weapon",1,"#BURNMETER_AT_TURRETWEAPON","#BURNMETER_AT_TURRETWEAPON_DESC","rui/menu/boosts/boost_antititan_sentry","models/weapons_r2/burn_card/burn_card.mdl",7,0.350000,"PILOT_ONLY","mp_ability_turretweapon","burnmeter_at_turret_weapon","#BURNMETER_AT_TURRETWEAPON_DESC_BOOST_ACTIVATION_TEXT",125
+"burnmeter_holopilot_nova",1,"#WPN_HOLOPILOT_NOVA","#WPN_HOLOPILOT_NOVA_DESC_BOOST_ACTIVATION_TEXT","rui/menu/boosts/boost_holo_pilots","models/weapons_r2/burn_card/burn_card.mdl",10,0.400000,"PILOT_ONLY","mp_ability_holopilot_nova","","#WPN_HOLOPILOT_NOVA_DESC",125
+"burnmeter_emergency_battery",1,"#BURNMETER_EMERGENCY_BATTERY","#BURNMETER_EMERGENCY_BATTERY_DESC","rui/menu/boosts/boost_battery","models/weapons_r2/burn_card/burn_card.mdl",1,0.800000,"PILOT_ONLY","mp_ability_burncardweapon","burnmeter_emergency_battery","#BURNMETER_AT_BATTERY_BOOST_ACTIVATION_TEXT",125
+"burnmeter_smart_pistol",1,"#WPN_SMART_PISTOL","#WPN_SMART_PISTOL_LONGDESC","rui/menu/boosts/boost_smart_pistol","models/weapons_r2/burn_card/burn_card.mdl",2,0.600000,"PILOT_ONLY","mp_ability_burncardweapon","burnmeter_smart_pistol","#WPN_SMART_PISTOL_BOOST_ACTIVATION_TEXT",125
+"burnmeter_radar_jammer",1,"#BURNMETER_RADAR_JAMMER","#BURNMETER_RADAR_JAMMER_DESC","rui/menu/boosts/boost_radar_jammer","models/weapons_r2/burn_card/burn_card.mdl",3,0.400000,"PILOT_ONLY","mp_ability_burncardweapon","burnmeter_radar_jammer","#BURNMETER_RADAR_JAMMER_DESC",125
+"burnmeter_hard_cover",1,"#WPN_HARD_COVER","#WPN_HARD_COVER_DESC","rui/menu/boosts/boost_shield","models/weapons_r2/burn_card/burn_card.mdl",4,0.200000,"PILOT_ONLY","mp_weapon_hard_cover","","#WPN_HARD_COVER_DESC",125
+"burnmeter_nuke_titan",0,"#WPN_NUKE_TITAN","#WPN_NUKE_TITAN_DESC","rui/menu/boosts/boost_nuke","models/weapons_r2/burn_card/burn_card.mdl",9,0.200000,"SPECIAL_PLAYERS_ONLY","mp_ability_burncardweapon","burnmeter_nuke_titan","#WPN_NUKE_TITAN_DESC",125
+"burnmeter_harvester_shield",0,"#BURNMETER_HARVESTER_SHIELD","#BURNMETER_HARVESTER_SHIELD_DESC","rui/menu/boosts/boost_harvester","models/weapons_r2/burn_card/burn_card.mdl",9,0.200000,"SPECIAL_PLAYERS_ONLY","mp_ability_burncardweapon","burnmeter_harvester_shield","#BURNMETER_HARVESTER_SHIELD_DESC",125
+"burnmeter_arc_trap",0,"#WPN_ARC_TRAP","#WPN_ARC_TRAP_DESC","rui/menu/boosts/boost_arc_trap","models/weapons_r2/burn_card/burn_card.mdl",9,0.200000,"PILOT_ONLY","mp_weapon_arc_trap","","#WPN_ARC_TRAP_DESC",125
+"burnmeter_ap_turret_weapon_infinite",0,"#BURNMETER_AP_TURRETWEAPON_INF","#BURNMETER_AP_TURRETWEAPON_INF_DESC","rui/menu/boosts/boost_antipersonnel_sentry","models/weapons_r2/burn_card/burn_card.mdl",6,0.720000,"PILOT_ONLY","mp_ability_turretweapon","burnmeter_ap_turret_weapon_inf","#BURNMETER_AP_TURRETWEAPON_INF_DESC",125
+"burnmeter_at_turret_weapon_infinite",0,"#BURNMETER_AT_TURRETWEAPON_INF","#BURNMETER_AT_TURRETWEAPON_INF_DESC","rui/menu/boosts/boost_antititan_sentry","models/weapons_r2/burn_card/burn_card.mdl",7,0.350000,"PILOT_ONLY","mp_ability_turretweapon","burnmeter_at_turret_weapon_inf","#BURNMETER_AT_TURRETWEAPON_INF_DESC",125
+"burnmeter_rodeo_grenade",0,"#BURNMETER_SUPER_RODEO","#BURNMETER_SUPER_RODEO_DESC","rui/menu/boosts/boost_core_grenade","models/weapons_r2/burn_card/burn_card.mdl",9,0.350000,"PILOT_ONLY","mp_ability_burncardweapon","burnmeter_rodeo_grenade","#BURNMETER_SUPER_RODEO_DESC",125
+"burnmeter_instant_battery",0,"#BURNMETER_INSTANT_BATTERY","#BURNMETER_INSTANT_BATTERY_DESC","rui/menu/boosts/boost_battery","models/weapons_r2/burn_card/burn_card.mdl",1,0.800000,"PILOT_ONLY","mp_ability_burncardweapon","burnmeter_emergency_battery","#BURNMETER_INSTANT_BATTERY_DESC",125
+"burnmeter_amped_weapons_permanent",0,"#BURNMETER_AMPED_WEAPONS_PERMANENT","#BURNMETER_AMPED_WEAPONS_PERMANENT_DESC","rui/menu/boosts/boost_amped_weapons","models/weapons_r2/burn_card/burn_card.mdl",0,0.800000,"PILOT_AND_TITAN","mp_ability_burncardweapon","burnmeter_amped_weapons","#BURNMETER_AMPED_WEAPONS_PERMANENT_DESC",125 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/burn_meter_store.csv b/Northstar.CustomServers/mod/scripts/datatable/burn_meter_store.csv
new file mode 100644
index 00000000..2f05bfc8
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/burn_meter_store.csv
@@ -0,0 +1,21 @@
+itemRef,modes,cost,extraDesc,autoActivate,storeIcon,lockedStoreIcon,extraDescFail
+"burnmeter_maphack","",0,"",0,"rui/menu/boosts/boost_icon_map_hack","rui/menu/boosts/boost_icon_map_hack",""
+"burnmeter_amped_weapons","",100,"#BOOST_STORE_INSTANT",1,"rui/menu/boosts/boost_icon_amped","rui/menu/boosts/boost_icon_amped","#BOOST_STORE_AMPED_FAIL"
+"burnmeter_ticks","",50,"",0,"rui/menu/boosts/boost_icon_tick","rui/menu/boosts/boost_icon_tick",""
+"burnmeter_random_foil","",0,"",0,"rui/menu/boosts/boost_icon_random","rui/menu/boosts/boost_icon_random",""
+"burnmeter_ap_turret_weapon","",0,"",0,"rui/menu/boosts/boost_icon_personel_sentry","rui/menu/boosts/boost_icon_personel_sentry",""
+"burnmeter_phase_rewind","",0,"",0,"rui/menu/boosts/boost_icon_phase_rewind","rui/menu/boosts/boost_icon_phase_rewind",""
+"burnmeter_at_turret_weapon","",0,"",0,"rui/menu/boosts/boost_icon_titan_sentry","rui/menu/boosts/boost_icon_titan_sentry",""
+"burnmeter_holopilot_nova","",0,"",0,"rui/menu/boosts/boost_icon_holopilot","rui/menu/boosts/boost_icon_holopilot",""
+"burnmeter_emergency_battery","",0,"",0,"rui/menu/boosts/boost_icon_battery","rui/menu/boosts/boost_icon_battery",""
+"burnmeter_smart_pistol","",0,"",1,"rui/menu/boosts/boost_icon_smart_pistol","rui/menu/boosts/boost_icon_smart_pistol",""
+"burnmeter_radar_jammer","",0,"",0,"rui/menu/boosts/boost_icon_radar_jam","rui/menu/boosts/boost_icon_radar_jam",""
+"burnmeter_hard_cover","",0,"",0,"rui/menu/boosts/boost_icon_shield","rui/menu/boosts/boost_icon_shield",""
+"burnmeter_nuke_titan","",0,"",0,"rui/menu/boosts/boost_icon_nuke","rui/menu/boosts/boost_icon_nuke",""
+"burnmeter_harvester_shield","fd",1200,"",1,"rui/menu/boosts/boost_icon_harvester_shield","rui/menu/boosts/locked/boost_icon_harvester_shield","#BOOST_STORE_HARVESTER_SHIELD_FAIL"
+"burnmeter_arc_trap","fd",650,"",0,"rui/menu/boosts/boost_icon_arc_trap","rui/menu/boosts/locked/boost_icon_arc_trap","#BOOST_STORE_ARC_TRAP_FAIL"
+"burnmeter_at_turret_weapon_infinite","",1200,"#BOOST_STORE_TURRET_LIMIT",0,"rui/menu/boosts/boost_icon_titan_sentry","rui/menu/boosts/boost_icon_titan_sentry",""
+"burnmeter_ap_turret_weapon_infinite","fd",1200,"#BOOST_STORE_TURRET_LIMIT",0,"rui/menu/boosts/boost_icon_personel_sentry","rui/menu/boosts/locked/boost_icon_personel_sentry","#BOOST_STORE_TURRET_FAIL"
+"burnmeter_rodeo_grenade","fd",500,"",1,"rui/menu/boosts/boost_icon_core_overload","rui/menu/boosts/locked/boost_icon_core_overload","#BOOST_STORE_RODEO_GRENADE_FAIL"
+"burnmeter_instant_battery","fd",400,"#BOOST_STORE_INSTANT",1,"rui/menu/boosts/boost_icon_battery_amped","rui/menu/boosts/locked/boost_icon_battery_amped","#BOOST_STORE_BATTERY_FAIL"
+"burnmeter_amped_weapons_permanent","fd",100,"#BOOST_STORE_INSTANT",1,"rui/menu/boosts/boost_icon_amped","rui/menu/boosts/locked/boost_icon_amped","#BOOST_STORE_AMPED_FAIL" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/caller_ids_mp.csv b/Northstar.CustomServers/mod/scripts/datatable/caller_ids_mp.csv
new file mode 100644
index 00000000..dcb3f5ab
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/caller_ids_mp.csv
@@ -0,0 +1,2 @@
+title,image
+"#FACTION_LEADER_NAME_MARVIN","rui/hud/caller_ids/caller_id_36" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/calling_cards.csv b/Northstar.CustomServers/mod/scripts/datatable/calling_cards.csv
new file mode 100644
index 00000000..998e644e
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/calling_cards.csv
@@ -0,0 +1,491 @@
+itemRef,name,layoutType,image,cost
+"callsign_01_col","#BANNER_CALLSIGN1",0,"rui/callsigns/callsign_01_col",0
+"callsign_02_col","#BANNER_CALLSIGN2",0,"rui/callsigns/callsign_02_col",100
+"callsign_03_col","#BANNER_CALLSIGN3",0,"rui/callsigns/callsign_03_col",0
+"callsign_04_col","#BANNER_CALLSIGN4",0,"rui/callsigns/callsign_04_col",0
+"callsign_05_col","#BANNER_CALLSIGN5",0,"rui/callsigns/callsign_05_col",0
+"callsign_06_col","#BANNER_CALLSIGN6",0,"rui/callsigns/callsign_06_col",0
+"callsign_07_col","#BANNER_CALLSIGN7",0,"rui/callsigns/callsign_07_col",0
+"callsign_08_col","#BANNER_CALLSIGN8",0,"rui/callsigns/callsign_08_col",100
+"callsign_09_col","#BANNER_CALLSIGN9",0,"rui/callsigns/callsign_09_col",0
+"callsign_10_col","#BANNER_CALLSIGN10",0,"rui/callsigns/callsign_10_col",100
+"callsign_11_col","#BANNER_CALLSIGN11",0,"rui/callsigns/callsign_11_col",0
+"callsign_12_col","#BANNER_CALLSIGN12",0,"rui/callsigns/callsign_12_col",100
+"callsign_13_col","#BANNER_CALLSIGN13",0,"rui/callsigns/callsign_13_col",0
+"callsign_14_col","#BANNER_CALLSIGN14",0,"rui/callsigns/callsign_14_col",0
+"callsign_15_col","#BANNER_CALLSIGN15",0,"rui/callsigns/callsign_15_col",0
+"callsign_16_col","#BANNER_CALLSIGN16",0,"rui/callsigns/callsign_16_col",0
+"callsign_17_col","#BANNER_CALLSIGN17",0,"rui/callsigns/callsign_17_col",100
+"callsign_18_col","#BANNER_CALLSIGN18",0,"rui/callsigns/callsign_18_col",0
+"callsign_19_col","#BANNER_CALLSIGN19",0,"rui/callsigns/callsign_19_col",0
+"callsign_20_col","#BANNER_CALLSIGN20",0,"rui/callsigns/callsign_20_col",100
+"callsign_21_col","#BANNER_CALLSIGN21",0,"rui/callsigns/callsign_21_col",0
+"callsign_22_col","#BANNER_CALLSIGN22",0,"rui/callsigns/callsign_22_col",0
+"callsign_23_col","#BANNER_CALLSIGN23",0,"rui/callsigns/callsign_23_col",0
+"callsign_24_col","#BANNER_CALLSIGN24",0,"rui/callsigns/callsign_24_col",0
+"callsign_25_col","#BANNER_CALLSIGN25",0,"rui/callsigns/callsign_25_col",100
+"callsign_26_col","#BANNER_CALLSIGN26",0,"rui/callsigns/callsign_26_col",0
+"callsign_27_col","#BANNER_CALLSIGN27",0,"rui/callsigns/callsign_27_col",0
+"callsign_28_col","#BANNER_CALLSIGN28",0,"rui/callsigns/callsign_28_col",100
+"callsign_29_col","#BANNER_CALLSIGN29",0,"rui/callsigns/callsign_29_col",0
+"callsign_30_col","#BANNER_CALLSIGN30",0,"rui/callsigns/callsign_30_col",0
+"callsign_31_col","#BANNER_CALLSIGN31",0,"rui/callsigns/callsign_31_col",0
+"callsign_32_col","#BANNER_CALLSIGN32",0,"rui/callsigns/callsign_32_col",100
+"callsign_34_col","#BANNER_CALLSIGN34",0,"rui/callsigns/callsign_34_col",0
+"callsign_35_col","#BANNER_CALLSIGN35",0,"rui/callsigns/callsign_35_col",0
+"callsign_36_col","#BANNER_CALLSIGN36",0,"rui/callsigns/callsign_36_col",0
+"callsign_37_col","#BANNER_CALLSIGN37",0,"rui/callsigns/callsign_37_col",0
+"callsign_39_col","#BANNER_CALLSIGN39",0,"rui/callsigns/callsign_39_col",0
+"callsign_40_col","#BANNER_CALLSIGN40",0,"rui/callsigns/callsign_40_col",0
+"callsign_41_col","#BANNER_CALLSIGN41",0,"rui/callsigns/callsign_41_col",0
+"callsign_42_col","#BANNER_CALLSIGN42",0,"rui/callsigns/callsign_42_col",0
+"callsign_43_col","#BANNER_CALLSIGN43",0,"rui/callsigns/callsign_43_col",100
+"callsign_44_col","#BANNER_CALLSIGN44",0,"rui/callsigns/callsign_44_col",100
+"callsign_45_col","#BANNER_CALLSIGN45",0,"rui/callsigns/callsign_45_col",0
+"callsign_46_col","#BANNER_CALLSIGN46",0,"rui/callsigns/callsign_46_col",0
+"callsign_47_col","#BANNER_CALLSIGN47",0,"rui/callsigns/callsign_47_col",0
+"callsign_48_col","#BANNER_CALLSIGN48",0,"rui/callsigns/callsign_48_col",0
+"callsign_49_col","#BANNER_CALLSIGN49",0,"rui/callsigns/callsign_49_col",100
+"callsign_50_col","#BANNER_CALLSIGN50",0,"rui/callsigns/callsign_50_col",100
+"callsign_51_col","#BANNER_CALLSIGN51",0,"rui/callsigns/callsign_51_col",0
+"callsign_52_col","#BANNER_CALLSIGN52",0,"rui/callsigns/callsign_52_col",100
+"callsign_53_col","#BANNER_CALLSIGN53",0,"rui/callsigns/callsign_53_col",0
+"callsign_54_col","#BANNER_CALLSIGN54",0,"rui/callsigns/callsign_54_col",100
+"callsign_55_col","#BANNER_CALLSIGN55",0,"rui/callsigns/callsign_55_col",0
+"callsign_56_col","#BANNER_CALLSIGN56",0,"rui/callsigns/callsign_56_col",100
+"callsign_57_col","#BANNER_CALLSIGN57",0,"rui/callsigns/callsign_57_col",0
+"callsign_58_col","#BANNER_CALLSIGN58",0,"rui/callsigns/callsign_58_col",100
+"callsign_59_col","#BANNER_CALLSIGN59",0,"rui/callsigns/callsign_59_col",0
+"callsign_60_col","#BANNER_CALLSIGN60",0,"rui/callsigns/callsign_60_col",100
+"callsign_61_col","#BANNER_CALLSIGN61",0,"rui/callsigns/callsign_61_col",100
+"callsign_62_col","#BANNER_CALLSIGN62",0,"rui/callsigns/callsign_62_col",100
+"callsign_63_col","#BANNER_CALLSIGN63",0,"rui/callsigns/callsign_63_col",100
+"callsign_64_col","#BANNER_CALLSIGN64",0,"rui/callsigns/callsign_64_col",100
+"callsign_66_col","#BANNER_CALLSIGN66",0,"rui/callsigns/callsign_66_col",0
+"callsign_67_col","#BANNER_CALLSIGN67",0,"rui/callsigns/callsign_67_col",0
+"callsign_68_col","#BANNER_CALLSIGN68",0,"rui/callsigns/callsign_68_col",0
+"callsign_69_col","#BANNER_CALLSIGN69",0,"rui/callsigns/callsign_69_col",0
+"callsign_70_col","#BANNER_CALLSIGN70",0,"rui/callsigns/callsign_70_col",0
+"callsign_71_col","#BANNER_CALLSIGN71",0,"rui/callsigns/callsign_71_col",100
+"callsign_72_col","#BANNER_CALLSIGN72",0,"rui/callsigns/callsign_72_col",100
+"callsign_73_col","#BANNER_CALLSIGN73",0,"rui/callsigns/callsign_73_col",100
+"callsign_74_col","#BANNER_CALLSIGN74",0,"rui/callsigns/callsign_74_col",100
+"callsign_75_col","#BANNER_CALLSIGN75",0,"rui/callsigns/callsign_75_col",0
+"callsign_76_col","#BANNER_CALLSIGN76",0,"rui/callsigns/callsign_76_col",0
+"callsign_77_col","#BANNER_CALLSIGN77",0,"rui/callsigns/callsign_77_col",100
+"callsign_78_col","#BANNER_CALLSIGN78",0,"rui/callsigns/callsign_78_col",0
+"callsign_79_col","#BANNER_CALLSIGN79",0,"rui/callsigns/callsign_79_col",0
+"callsign_80_col","#BANNER_CALLSIGN80",0,"rui/callsigns/callsign_80_col",100
+"callsign_81_col","#BANNER_CALLSIGN81",0,"rui/callsigns/callsign_81_col",100
+"callsign_82_col","#BANNER_CALLSIGN82",0,"rui/callsigns/callsign_82_col",100
+"callsign_83_col","#BANNER_CALLSIGN83",0,"rui/callsigns/callsign_83_col",100
+"callsign_84_col","#BANNER_CALLSIGN84",0,"rui/callsigns/callsign_84_col",100
+"callsign_85_col","#BANNER_CALLSIGN85",0,"rui/callsigns/callsign_85_col",100
+"callsign_86_col","#BANNER_CALLSIGN86",0,"rui/callsigns/callsign_86_col",100
+"callsign_87_col","#BANNER_CALLSIGN87",0,"rui/callsigns/callsign_87_col",100
+"callsign_88_col","#BANNER_CALLSIGN88",0,"rui/callsigns/callsign_88_col",100
+"callsign_89_col","#BANNER_CALLSIGN89",0,"rui/callsigns/callsign_89_col",100
+"callsign_90_col","#BANNER_CALLSIGN90",0,"rui/callsigns/callsign_90_col",100
+"callsign_91_col","#BANNER_CALLSIGN91",0,"rui/callsigns/callsign_91_col",100
+"callsign_92_col","#BANNER_CALLSIGN92",0,"rui/callsigns/callsign_92_col",0
+"callsign_93_col","#BANNER_CALLSIGN93",0,"rui/callsigns/callsign_93_col",100
+"callsign_94_col","#BANNER_CALLSIGN94",0,"rui/callsigns/callsign_94_col",0
+"callsign_95_col","#BANNER_CALLSIGN95",0,"rui/callsigns/callsign_95_col",100
+"callsign_96_col","#BANNER_CALLSIGN96",0,"rui/callsigns/callsign_96_col",0
+"callsign_97_col","#BANNER_CALLSIGN97",0,"rui/callsigns/callsign_97_col",0
+"callsign_98_col","#BANNER_CALLSIGN98",0,"rui/callsigns/callsign_98_col",100
+"callsign_99_col","#BANNER_CALLSIGN99",0,"rui/callsigns/callsign_99_col",0
+"callsign_100_col","#BANNER_CALLSIGN100",0,"rui/callsigns/callsign_100_col",0
+"callsign_101_col","#BANNER_CALLSIGN101",0,"rui/callsigns/callsign_101_col",100
+"callsign_102_col","#BANNER_CALLSIGN102",0,"rui/callsigns/callsign_102_col",100
+"callsign_103_col","#BANNER_CALLSIGN103",0,"rui/callsigns/callsign_103_col",0
+"callsign_104_col","#BANNER_CALLSIGN104",0,"rui/callsigns/callsign_104_col",0
+"callsign_02_col_prism","#BANNER_PRISM_CALLSIGN2",1,"rui/callsigns/callsign_02_col",250
+"callsign_08_col_prism","#BANNER_PRISM_CALLSIGN8",1,"rui/callsigns/callsign_08_col",250
+"callsign_10_col_prism","#BANNER_PRISM_CALLSIGN10",1,"rui/callsigns/callsign_10_col",250
+"callsign_12_col_prism","#BANNER_PRISM_CALLSIGN12",1,"rui/callsigns/callsign_12_col",250
+"callsign_17_col_prism","#BANNER_PRISM_CALLSIGN17",1,"rui/callsigns/callsign_17_col",250
+"callsign_20_col_prism","#BANNER_PRISM_CALLSIGN20",1,"rui/callsigns/callsign_20_col",250
+"callsign_25_col_prism","#BANNER_PRISM_CALLSIGN25",1,"rui/callsigns/callsign_25_col",250
+"callsign_28_col_prism","#BANNER_PRISM_CALLSIGN28",1,"rui/callsigns/callsign_28_col",250
+"callsign_32_col_prism","#BANNER_PRISM_CALLSIGN32",1,"rui/callsigns/callsign_32_col",250
+"callsign_43_col_prism","#BANNER_PRISM_CALLSIGN43",1,"rui/callsigns/callsign_43_col",250
+"callsign_44_col_prism","#BANNER_PRISM_CALLSIGN44",1,"rui/callsigns/callsign_44_col",250
+"callsign_49_col_prism","#BANNER_PRISM_CALLSIGN49",1,"rui/callsigns/callsign_49_col",250
+"callsign_50_col_prism","#BANNER_PRISM_CALLSIGN50",1,"rui/callsigns/callsign_50_col",250
+"callsign_52_col_prism","#BANNER_PRISM_CALLSIGN52",1,"rui/callsigns/callsign_52_col",250
+"callsign_54_col_prism","#BANNER_PRISM_CALLSIGN54",1,"rui/callsigns/callsign_54_col",250
+"callsign_56_col_prism","#BANNER_PRISM_CALLSIGN56",1,"rui/callsigns/callsign_56_col",250
+"callsign_58_col_prism","#BANNER_PRISM_CALLSIGN58",1,"rui/callsigns/callsign_58_col",250
+"callsign_60_col_prism","#BANNER_PRISM_CALLSIGN60",1,"rui/callsigns/callsign_60_col",250
+"callsign_61_col_prism","#BANNER_PRISM_CALLSIGN61",1,"rui/callsigns/callsign_61_col",250
+"callsign_62_col_prism","#BANNER_PRISM_CALLSIGN62",1,"rui/callsigns/callsign_62_col",250
+"callsign_63_col_prism","#BANNER_PRISM_CALLSIGN63",1,"rui/callsigns/callsign_63_col",250
+"callsign_64_col_prism","#BANNER_PRISM_CALLSIGN64",1,"rui/callsigns/callsign_64_col",250
+"callsign_71_col_prism","#BANNER_PRISM_CALLSIGN71",1,"rui/callsigns/callsign_71_col",250
+"callsign_72_col_prism","#BANNER_PRISM_CALLSIGN72",1,"rui/callsigns/callsign_72_col",250
+"callsign_73_col_prism","#BANNER_PRISM_CALLSIGN73",1,"rui/callsigns/callsign_73_col",250
+"callsign_74_col_prism","#BANNER_PRISM_CALLSIGN74",1,"rui/callsigns/callsign_74_col",250
+"callsign_77_col_prism","#BANNER_PRISM_CALLSIGN77",1,"rui/callsigns/callsign_77_col",250
+"callsign_80_col_prism","#BANNER_PRISM_CALLSIGN80",1,"rui/callsigns/callsign_80_col",250
+"callsign_81_col_prism","#BANNER_PRISM_CALLSIGN81",1,"rui/callsigns/callsign_81_col",250
+"callsign_82_col_prism","#BANNER_PRISM_CALLSIGN82",1,"rui/callsigns/callsign_82_col",250
+"callsign_83_col_prism","#BANNER_PRISM_CALLSIGN83",1,"rui/callsigns/callsign_83_col",250
+"callsign_84_col_prism","#BANNER_PRISM_CALLSIGN84",1,"rui/callsigns/callsign_84_col",250
+"callsign_85_col_prism","#BANNER_PRISM_CALLSIGN85",1,"rui/callsigns/callsign_85_col",250
+"callsign_86_col_prism","#BANNER_PRISM_CALLSIGN86",1,"rui/callsigns/callsign_86_col",250
+"callsign_87_col_prism","#BANNER_PRISM_CALLSIGN87",1,"rui/callsigns/callsign_87_col",250
+"callsign_88_col_prism","#BANNER_PRISM_CALLSIGN88",1,"rui/callsigns/callsign_88_col",250
+"callsign_89_col_prism","#BANNER_PRISM_CALLSIGN89",1,"rui/callsigns/callsign_89_col",250
+"callsign_90_col_prism","#BANNER_PRISM_CALLSIGN90",1,"rui/callsigns/callsign_90_col",250
+"callsign_91_col_prism","#BANNER_PRISM_CALLSIGN91",1,"rui/callsigns/callsign_91_col",250
+"callsign_93_col_prism","#BANNER_PRISM_CALLSIGN93",1,"rui/callsigns/callsign_93_col",250
+"callsign_95_col_prism","#BANNER_PRISM_CALLSIGN95",1,"rui/callsigns/callsign_95_col",250
+"callsign_98_col_prism","#BANNER_PRISM_CALLSIGN98",1,"rui/callsigns/callsign_98_col",250
+"callsign_101_col_prism","#BANNER_PRISM_CALLSIGN101",1,"rui/callsigns/callsign_101_col",250
+"callsign_102_col_prism","#BANNER_PRISM_CALLSIGN102",1,"rui/callsigns/callsign_102_col",250
+"callsign_16_col_gold","#BANNER_GOLD_CALLSIGN16",2,"rui/callsigns/callsign_16_col",0
+"callsign_01_col_gold","#BANNER_GOLD_CALLSIGN1",2,"rui/callsigns/callsign_01_col",0
+"callsign_03_col_gold","#BANNER_GOLD_CALLSIGN3",2,"rui/callsigns/callsign_03_col",0
+"callsign_04_col_gold","#BANNER_GOLD_CALLSIGN4",2,"rui/callsigns/callsign_04_col",0
+"callsign_05_col_gold","#BANNER_GOLD_CALLSIGN5",2,"rui/callsigns/callsign_05_col",0
+"callsign_06_col_gold","#BANNER_GOLD_CALLSIGN6",2,"rui/callsigns/callsign_06_col",0
+"callsign_07_col_gold","#BANNER_GOLD_CALLSIGN7",2,"rui/callsigns/callsign_07_col",0
+"callsign_09_col_gold","#BANNER_GOLD_CALLSIGN9",2,"rui/callsigns/callsign_09_col",0
+"callsign_11_col_gold","#BANNER_GOLD_CALLSIGN11",2,"rui/callsigns/callsign_11_col",0
+"callsign_13_col_gold","#BANNER_GOLD_CALLSIGN13",2,"rui/callsigns/callsign_13_col",0
+"callsign_14_col_gold","#BANNER_GOLD_CALLSIGN14",2,"rui/callsigns/callsign_14_col",0
+"callsign_15_col_gold","#BANNER_GOLD_CALLSIGN15",2,"rui/callsigns/callsign_15_col",0
+"callsign_18_col_gold","#BANNER_GOLD_CALLSIGN18",2,"rui/callsigns/callsign_18_col",0
+"callsign_19_col_gold","#BANNER_GOLD_CALLSIGN19",2,"rui/callsigns/callsign_19_col",0
+"callsign_21_col_gold","#BANNER_GOLD_CALLSIGN21",2,"rui/callsigns/callsign_21_col",0
+"callsign_22_col_gold","#BANNER_GOLD_CALLSIGN22",2,"rui/callsigns/callsign_22_col",0
+"callsign_23_col_gold","#BANNER_GOLD_CALLSIGN23",2,"rui/callsigns/callsign_23_col",0
+"callsign_24_col_gold","#BANNER_GOLD_CALLSIGN24",2,"rui/callsigns/callsign_24_col",0
+"callsign_26_col_gold","#BANNER_GOLD_CALLSIGN26",2,"rui/callsigns/callsign_26_col",0
+"callsign_27_col_gold","#BANNER_GOLD_CALLSIGN27",2,"rui/callsigns/callsign_27_col",0
+"callsign_29_col_gold","#BANNER_GOLD_CALLSIGN29",2,"rui/callsigns/callsign_29_col",0
+"callsign_30_col_gold","#BANNER_GOLD_CALLSIGN30",2,"rui/callsigns/callsign_30_col",0
+"callsign_31_col_gold","#BANNER_GOLD_CALLSIGN31",2,"rui/callsigns/callsign_31_col",0
+"callsign_33_col_gold","#BANNER_GOLD_CALLSIGN33",2,"rui/callsigns/callsign_33_col",0
+"callsign_34_col_gold","#BANNER_GOLD_CALLSIGN34",2,"rui/callsigns/callsign_34_col",0
+"callsign_35_col_gold","#BANNER_GOLD_CALLSIGN35",2,"rui/callsigns/callsign_35_col",0
+"callsign_36_col_gold","#BANNER_GOLD_CALLSIGN36",2,"rui/callsigns/callsign_36_col",0
+"callsign_37_col_gold","#BANNER_GOLD_CALLSIGN37",2,"rui/callsigns/callsign_37_col",0
+"callsign_38_col_gold","#BANNER_GOLD_CALLSIGN38",2,"rui/callsigns/callsign_38_col",0
+"callsign_39_col_gold","#BANNER_GOLD_CALLSIGN39",2,"rui/callsigns/callsign_39_col",0
+"callsign_40_col_gold","#BANNER_GOLD_CALLSIGN40",2,"rui/callsigns/callsign_40_col",0
+"callsign_41_col_gold","#BANNER_GOLD_CALLSIGN41",2,"rui/callsigns/callsign_41_col",0
+"callsign_42_col_gold","#BANNER_GOLD_CALLSIGN42",2,"rui/callsigns/callsign_42_col",0
+"callsign_45_col_gold","#BANNER_GOLD_CALLSIGN45",2,"rui/callsigns/callsign_45_col",0
+"callsign_46_col_gold","#BANNER_GOLD_CALLSIGN46",2,"rui/callsigns/callsign_46_col",0
+"callsign_47_col_gold","#BANNER_GOLD_CALLSIGN47",2,"rui/callsigns/callsign_47_col",0
+"callsign_48_col_gold","#BANNER_GOLD_CALLSIGN48",2,"rui/callsigns/callsign_48_col",0
+"callsign_51_col_gold","#BANNER_GOLD_CALLSIGN51",2,"rui/callsigns/callsign_51_col",0
+"callsign_53_col_gold","#BANNER_GOLD_CALLSIGN53",2,"rui/callsigns/callsign_53_col",0
+"callsign_55_col_gold","#BANNER_GOLD_CALLSIGN55",2,"rui/callsigns/callsign_55_col",0
+"callsign_57_col_gold","#BANNER_GOLD_CALLSIGN57",2,"rui/callsigns/callsign_57_col",0
+"callsign_59_col_gold","#BANNER_GOLD_CALLSIGN59",2,"rui/callsigns/callsign_59_col",0
+"callsign_65_col_gold","#BANNER_GOLD_CALLSIGN65",2,"rui/callsigns/callsign_65_col",0
+"callsign_66_col_gold","#BANNER_GOLD_CALLSIGN66",2,"rui/callsigns/callsign_66_col",0
+"callsign_67_col_gold","#BANNER_GOLD_CALLSIGN67",2,"rui/callsigns/callsign_67_col",0
+"callsign_68_col_gold","#BANNER_GOLD_CALLSIGN68",2,"rui/callsigns/callsign_68_col",0
+"callsign_69_col_gold","#BANNER_GOLD_CALLSIGN69",2,"rui/callsigns/callsign_69_col",0
+"callsign_70_col_gold","#BANNER_GOLD_CALLSIGN70",2,"rui/callsigns/callsign_70_col",0
+"callsign_71_col_gold","#BANNER_GOLD_CALLSIGN71",2,"rui/callsigns/callsign_71_col",0
+"callsign_75_col_gold","#BANNER_GOLD_CALLSIGN75",2,"rui/callsigns/callsign_75_col",0
+"callsign_76_col_gold","#BANNER_GOLD_CALLSIGN76",2,"rui/callsigns/callsign_76_col",0
+"callsign_78_col_gold","#BANNER_GOLD_CALLSIGN78",2,"rui/callsigns/callsign_78_col",0
+"callsign_79_col_gold","#BANNER_GOLD_CALLSIGN79",2,"rui/callsigns/callsign_79_col",0
+"callsign_92_col_gold","#BANNER_GOLD_CALLSIGN92",2,"rui/callsigns/callsign_92_col",0
+"callsign_94_col_gold","#BANNER_GOLD_CALLSIGN94",2,"rui/callsigns/callsign_94_col",0
+"callsign_96_col_gold","#BANNER_GOLD_CALLSIGN96",2,"rui/callsigns/callsign_96_col",0
+"callsign_97_col_gold","#BANNER_GOLD_CALLSIGN97",2,"rui/callsigns/callsign_97_col",0
+"callsign_99_col_gold","#BANNER_GOLD_CALLSIGN99",2,"rui/callsigns/callsign_99_col",0
+"callsign_100_col_gold","#BANNER_GOLD_CALLSIGN100",2,"rui/callsigns/callsign_100_col",0
+"callsign_103_col_gold","#BANNER_GOLD_CALLSIGN103",2,"rui/callsigns/callsign_103_col",0
+"callsign_104_col_gold","#BANNER_GOLD_CALLSIGN104",2,"rui/callsigns/callsign_104_col",0
+"callsign_105_col_gold","#BANNER_GOLD_CALLSIGN105",2,"rui/callsigns/callsign_105_col",0
+"callsign_16_col_fire","#BANNER_FIRE_CALLSIGN16",3,"rui/callsigns/callsign_16_col",0
+"callsign_01_col_fire","#BANNER_FIRE_CALLSIGN1",3,"rui/callsigns/callsign_01_col",0
+"callsign_03_col_fire","#BANNER_FIRE_CALLSIGN3",3,"rui/callsigns/callsign_03_col",0
+"callsign_04_col_fire","#BANNER_FIRE_CALLSIGN4",3,"rui/callsigns/callsign_04_col",0
+"callsign_05_col_fire","#BANNER_FIRE_CALLSIGN5",3,"rui/callsigns/callsign_05_col",0
+"callsign_06_col_fire","#BANNER_FIRE_CALLSIGN6",3,"rui/callsigns/callsign_06_col",0
+"callsign_07_col_fire","#BANNER_FIRE_CALLSIGN7",3,"rui/callsigns/callsign_07_col",0
+"callsign_09_col_fire","#BANNER_FIRE_CALLSIGN9",3,"rui/callsigns/callsign_09_col",0
+"callsign_11_col_fire","#BANNER_FIRE_CALLSIGN11",3,"rui/callsigns/callsign_11_col",0
+"callsign_13_col_fire","#BANNER_FIRE_CALLSIGN13",3,"rui/callsigns/callsign_13_col",0
+"callsign_14_col_fire","#BANNER_FIRE_CALLSIGN14",3,"rui/callsigns/callsign_14_col",0
+"callsign_15_col_fire","#BANNER_FIRE_CALLSIGN15",3,"rui/callsigns/callsign_15_col",0
+"callsign_18_col_fire","#BANNER_FIRE_CALLSIGN18",3,"rui/callsigns/callsign_18_col",0
+"callsign_19_col_fire","#BANNER_FIRE_CALLSIGN19",3,"rui/callsigns/callsign_19_col",0
+"callsign_21_col_fire","#BANNER_FIRE_CALLSIGN21",3,"rui/callsigns/callsign_21_col",0
+"callsign_22_col_fire","#BANNER_FIRE_CALLSIGN22",3,"rui/callsigns/callsign_22_col",0
+"callsign_23_col_fire","#BANNER_FIRE_CALLSIGN23",3,"rui/callsigns/callsign_23_col",0
+"callsign_24_col_fire","#BANNER_FIRE_CALLSIGN24",3,"rui/callsigns/callsign_24_col",0
+"callsign_26_col_fire","#BANNER_FIRE_CALLSIGN26",3,"rui/callsigns/callsign_26_col",0
+"callsign_27_col_fire","#BANNER_FIRE_CALLSIGN27",3,"rui/callsigns/callsign_27_col",0
+"callsign_29_col_fire","#BANNER_FIRE_CALLSIGN29",3,"rui/callsigns/callsign_29_col",0
+"callsign_30_col_fire","#BANNER_FIRE_CALLSIGN30",3,"rui/callsigns/callsign_30_col",0
+"callsign_31_col_fire","#BANNER_FIRE_CALLSIGN31",3,"rui/callsigns/callsign_31_col",0
+"callsign_34_col_fire","#BANNER_FIRE_CALLSIGN34",3,"rui/callsigns/callsign_34_col",0
+"callsign_35_col_fire","#BANNER_FIRE_CALLSIGN35",3,"rui/callsigns/callsign_35_col",0
+"callsign_36_col_fire","#BANNER_FIRE_CALLSIGN36",3,"rui/callsigns/callsign_36_col",0
+"callsign_37_col_fire","#BANNER_FIRE_CALLSIGN37",3,"rui/callsigns/callsign_37_col",0
+"callsign_39_col_fire","#BANNER_FIRE_CALLSIGN39",3,"rui/callsigns/callsign_39_col",0
+"callsign_40_col_fire","#BANNER_FIRE_CALLSIGN40",3,"rui/callsigns/callsign_40_col",0
+"callsign_41_col_fire","#BANNER_FIRE_CALLSIGN41",3,"rui/callsigns/callsign_41_col",0
+"callsign_42_col_fire","#BANNER_FIRE_CALLSIGN42",3,"rui/callsigns/callsign_42_col",0
+"callsign_45_col_fire","#BANNER_FIRE_CALLSIGN45",3,"rui/callsigns/callsign_45_col",0
+"callsign_46_col_fire","#BANNER_FIRE_CALLSIGN46",3,"rui/callsigns/callsign_46_col",0
+"callsign_47_col_fire","#BANNER_FIRE_CALLSIGN47",3,"rui/callsigns/callsign_47_col",0
+"callsign_48_col_fire","#BANNER_FIRE_CALLSIGN48",3,"rui/callsigns/callsign_48_col",0
+"callsign_51_col_fire","#BANNER_FIRE_CALLSIGN51",3,"rui/callsigns/callsign_51_col",0
+"callsign_53_col_fire","#BANNER_FIRE_CALLSIGN53",3,"rui/callsigns/callsign_53_col",0
+"callsign_55_col_fire","#BANNER_FIRE_CALLSIGN55",3,"rui/callsigns/callsign_55_col",0
+"callsign_57_col_fire","#BANNER_FIRE_CALLSIGN57",3,"rui/callsigns/callsign_57_col",0
+"callsign_59_col_fire","#BANNER_FIRE_CALLSIGN59",3,"rui/callsigns/callsign_59_col",0
+"callsign_66_col_fire","#BANNER_FIRE_CALLSIGN66",3,"rui/callsigns/callsign_66_col",0
+"callsign_67_col_fire","#BANNER_FIRE_CALLSIGN67",3,"rui/callsigns/callsign_67_col",0
+"callsign_68_col_fire","#BANNER_FIRE_CALLSIGN68",3,"rui/callsigns/callsign_68_col",0
+"callsign_69_col_fire","#BANNER_FIRE_CALLSIGN69",3,"rui/callsigns/callsign_69_col",0
+"callsign_70_col_fire","#BANNER_FIRE_CALLSIGN70",3,"rui/callsigns/callsign_70_col",0
+"callsign_75_col_fire","#BANNER_FIRE_CALLSIGN75",3,"rui/callsigns/callsign_75_col",0
+"callsign_76_col_fire","#BANNER_FIRE_CALLSIGN76",3,"rui/callsigns/callsign_76_col",0
+"callsign_78_col_fire","#BANNER_FIRE_CALLSIGN78",3,"rui/callsigns/callsign_78_col",0
+"callsign_79_col_fire","#BANNER_FIRE_CALLSIGN79",3,"rui/callsigns/callsign_79_col",0
+"callsign_92_col_fire","#BANNER_FIRE_CALLSIGN92",3,"rui/callsigns/callsign_92_col",0
+"callsign_94_col_fire","#BANNER_FIRE_CALLSIGN94",3,"rui/callsigns/callsign_94_col",0
+"callsign_96_col_fire","#BANNER_FIRE_CALLSIGN96",3,"rui/callsigns/callsign_96_col",0
+"callsign_97_col_fire","#BANNER_FIRE_CALLSIGN97",3,"rui/callsigns/callsign_97_col",0
+"callsign_99_col_fire","#BANNER_FIRE_CALLSIGN99",3,"rui/callsigns/callsign_99_col",0
+"callsign_100_col_fire","#BANNER_FIRE_CALLSIGN100",3,"rui/callsigns/callsign_100_col",0
+"callsign_103_col_fire","#BANNER_FIRE_CALLSIGN103",3,"rui/callsigns/callsign_103_col",0
+"callsign_104_col_fire","#BANNER_FIRE_CALLSIGN104",3,"rui/callsigns/callsign_104_col",0
+"callsign_106_col","#BANNER_CALLSIGN106",0,"rui/callsigns/callsign_106_col",0
+"callsign_107_col","#BANNER_CALLSIGN107",0,"rui/callsigns/callsign_107_col",0
+"callsign_108_col","#BANNER_CALLSIGN108",0,"rui/callsigns/callsign_108_col",0
+"callsign_109_col","#BANNER_CALLSIGN109",0,"rui/callsigns/callsign_109_col",0
+"callsign_110_col","#BANNER_CALLSIGN110",0,"rui/callsigns/callsign_110_col",0
+"callsign_111_col","#BANNER_CALLSIGN111",0,"rui/callsigns/callsign_111_col",0
+"callsign_112_col","#BANNER_CALLSIGN112",0,"rui/callsigns/callsign_112_col",0
+"callsign_113_col","#BANNER_CALLSIGN113",0,"rui/callsigns/callsign_113_col",0
+"callsign_114_col","#BANNER_CALLSIGN114",0,"rui/callsigns/callsign_114_col",0
+"callsign_115_col","#BANNER_CALLSIGN115",0,"rui/callsigns/callsign_115_col",0
+"callsign_116_col","#BANNER_CALLSIGN116",0,"rui/callsigns/callsign_116_col",0
+"callsign_117_col","#BANNER_CALLSIGN117",0,"rui/callsigns/callsign_117_col",0
+"callsign_118_col","#BANNER_CALLSIGN118",0,"rui/callsigns/callsign_118_col",0
+"callsign_119_col","#BANNER_CALLSIGN119",0,"rui/callsigns/callsign_119_col",0
+"callsign_120_col","#BANNER_CALLSIGN120",0,"rui/callsigns/callsign_120_col",0
+"callsign_121_col","#BANNER_CALLSIGN121",0,"rui/callsigns/callsign_121_col",0
+"callsign_122_col","#BANNER_CALLSIGN122",0,"rui/callsigns/callsign_122_col",0
+"callsign_123_col","#BANNER_CALLSIGN123",0,"rui/callsigns/callsign_123_col",0
+"callsign_124_col","#BANNER_CALLSIGN124",0,"rui/callsigns/callsign_124_col",0
+"callsign_125_col","#BANNER_CALLSIGN125",0,"rui/callsigns/callsign_125_col",0
+"callsign_139_col","#BANNER_CALLSIGN139",0,"rui/callsigns/callsign_139_col",0
+"callsign_139_col_fire","#BANNER_FIRE_CALLSIGN139",3,"rui/callsigns/callsign_139_col",0
+"callsign_139_col_gold","#BANNER_GOLD_CALLSIGN139",2,"rui/callsigns/callsign_139_col",0
+"callsign_126_col","#BANNER_CALLSIGN126",0,"rui/callsigns/callsign_126_col",0
+"callsign_127_col","#BANNER_CALLSIGN127",0,"rui/callsigns/callsign_127_col",0
+"callsign_128_col","#BANNER_CALLSIGN128",0,"rui/callsigns/callsign_128_col",0
+"callsign_129_col","#BANNER_CALLSIGN129",0,"rui/callsigns/callsign_129_col",0
+"callsign_130_col","#BANNER_CALLSIGN130",0,"rui/callsigns/callsign_130_col",0
+"callsign_131_col","#BANNER_CALLSIGN131",0,"rui/callsigns/callsign_131_col",0
+"callsign_132_col","#BANNER_CALLSIGN132",0,"rui/callsigns/callsign_132_col",0
+"callsign_133_col","#BANNER_CALLSIGN133",0,"rui/callsigns/callsign_133_col",0
+"callsign_134_col","#BANNER_CALLSIGN134",0,"rui/callsigns/callsign_134_col",0
+"callsign_135_col","#BANNER_CALLSIGN135",0,"rui/callsigns/callsign_135_col",0
+"callsign_136_col","#BANNER_CALLSIGN136",0,"rui/callsigns/callsign_136_col",0
+"callsign_137_col","#BANNER_CALLSIGN137",0,"rui/callsigns/callsign_137_col",0
+"callsign_138_col","#BANNER_CALLSIGN138",0,"rui/callsigns/callsign_138_col",0
+"callsign_125_col_fire","#BANNER_FIRE_CALLSIGN125",3,"rui/callsigns/callsign_125_col",0
+"callsign_126_col_fire","#BANNER_FIRE_CALLSIGN126",3,"rui/callsigns/callsign_126_col",0
+"callsign_127_col_fire","#BANNER_FIRE_CALLSIGN127",3,"rui/callsigns/callsign_127_col",0
+"callsign_128_col_fire","#BANNER_FIRE_CALLSIGN128",3,"rui/callsigns/callsign_128_col",0
+"callsign_129_col_fire","#BANNER_FIRE_CALLSIGN129",3,"rui/callsigns/callsign_129_col",0
+"callsign_130_col_fire","#BANNER_FIRE_CALLSIGN130",3,"rui/callsigns/callsign_130_col",0
+"callsign_131_col_fire","#BANNER_FIRE_CALLSIGN131",3,"rui/callsigns/callsign_131_col",0
+"callsign_132_col_fire","#BANNER_FIRE_CALLSIGN132",3,"rui/callsigns/callsign_132_col",0
+"callsign_133_col_fire","#BANNER_FIRE_CALLSIGN133",3,"rui/callsigns/callsign_133_col",0
+"callsign_134_col_fire","#BANNER_FIRE_CALLSIGN134",3,"rui/callsigns/callsign_134_col",0
+"callsign_135_col_fire","#BANNER_FIRE_CALLSIGN135",3,"rui/callsigns/callsign_135_col",0
+"callsign_136_col_fire","#BANNER_FIRE_CALLSIGN136",3,"rui/callsigns/callsign_136_col",0
+"callsign_137_col_fire","#BANNER_FIRE_CALLSIGN137",3,"rui/callsigns/callsign_137_col",0
+"callsign_138_col_fire","#BANNER_FIRE_CALLSIGN138",3,"rui/callsigns/callsign_138_col",0
+"callsign_125_col_gold","#BANNER_GOLD_CALLSIGN125",2,"rui/callsigns/callsign_125_col",0
+"callsign_126_col_gold","#BANNER_GOLD_CALLSIGN126",2,"rui/callsigns/callsign_126_col",0
+"callsign_127_col_gold","#BANNER_GOLD_CALLSIGN127",2,"rui/callsigns/callsign_127_col",0
+"callsign_128_col_gold","#BANNER_GOLD_CALLSIGN128",2,"rui/callsigns/callsign_128_col",0
+"callsign_129_col_gold","#BANNER_GOLD_CALLSIGN129",2,"rui/callsigns/callsign_129_col",0
+"callsign_130_col_gold","#BANNER_GOLD_CALLSIGN130",2,"rui/callsigns/callsign_130_col",0
+"callsign_131_col_gold","#BANNER_GOLD_CALLSIGN131",2,"rui/callsigns/callsign_131_col",0
+"callsign_132_col_gold","#BANNER_GOLD_CALLSIGN132",2,"rui/callsigns/callsign_132_col",0
+"callsign_133_col_gold","#BANNER_GOLD_CALLSIGN133",2,"rui/callsigns/callsign_133_col",0
+"callsign_134_col_gold","#BANNER_GOLD_CALLSIGN134",2,"rui/callsigns/callsign_134_col",0
+"callsign_135_col_gold","#BANNER_GOLD_CALLSIGN135",2,"rui/callsigns/callsign_135_col",0
+"callsign_136_col_gold","#BANNER_GOLD_CALLSIGN136",2,"rui/callsigns/callsign_136_col",0
+"callsign_137_col_gold","#BANNER_GOLD_CALLSIGN137",2,"rui/callsigns/callsign_137_col",0
+"callsign_138_col_gold","#BANNER_GOLD_CALLSIGN138",2,"rui/callsigns/callsign_138_col",0
+"callsign_125_col_prism","#BANNER_PRISM_CALLSIGN125",1,"rui/callsigns/callsign_125_col",0
+"callsign_126_col_prism","#BANNER_PRISM_CALLSIGN126",1,"rui/callsigns/callsign_126_col",1500
+"callsign_127_col_prism","#BANNER_PRISM_CALLSIGN127",1,"rui/callsigns/callsign_127_col",1500
+"callsign_128_col_prism","#BANNER_PRISM_CALLSIGN128",1,"rui/callsigns/callsign_128_col",0
+"callsign_129_col_prism","#BANNER_PRISM_CALLSIGN129",1,"rui/callsigns/callsign_129_col",0
+"callsign_130_col_prism","#BANNER_PRISM_CALLSIGN130",1,"rui/callsigns/callsign_130_col",1500
+"callsign_131_col_prism","#BANNER_PRISM_CALLSIGN131",1,"rui/callsigns/callsign_131_col",1500
+"callsign_132_col_prism","#BANNER_PRISM_CALLSIGN132",1,"rui/callsigns/callsign_132_col",0
+"callsign_133_col_prism","#BANNER_PRISM_CALLSIGN133",1,"rui/callsigns/callsign_133_col",0
+"callsign_134_col_prism","#BANNER_PRISM_CALLSIGN134",1,"rui/callsigns/callsign_134_col",0
+"callsign_135_col_prism","#BANNER_PRISM_CALLSIGN135",1,"rui/callsigns/callsign_135_col",0
+"callsign_136_col_prism","#BANNER_PRISM_CALLSIGN136",1,"rui/callsigns/callsign_136_col",0
+"callsign_137_col_prism","#BANNER_PRISM_CALLSIGN137",1,"rui/callsigns/callsign_137_col",0
+"callsign_138_col_prism","#BANNER_PRISM_CALLSIGN138",1,"rui/callsigns/callsign_138_col",0
+"callsign_140_col","#BANNER_CALLSIGN140",0,"rui/callsigns/callsign_140_col",0
+"callsign_141_col","#BANNER_CALLSIGN141",0,"rui/callsigns/callsign_141_col",0
+"callsign_142_col","#BANNER_CALLSIGN142",0,"rui/callsigns/callsign_142_col",0
+"callsign_140_col_fire","#BANNER_FIRE_CALLSIGN140",3,"rui/callsigns/callsign_140_col",0
+"callsign_141_col_fire","#BANNER_FIRE_CALLSIGN141",3,"rui/callsigns/callsign_141_col",0
+"callsign_142_col_fire","#BANNER_FIRE_CALLSIGN142",3,"rui/callsigns/callsign_142_col",0
+"callsign_140_col_gold","#BANNER_GOLD_CALLSIGN140",2,"rui/callsigns/callsign_140_col",0
+"callsign_141_col_gold","#BANNER_GOLD_CALLSIGN141",2,"rui/callsigns/callsign_141_col",0
+"callsign_142_col_gold","#BANNER_GOLD_CALLSIGN142",2,"rui/callsigns/callsign_142_col",0
+"callsign_143_col","#BANNER_CALLSIGN143",0,"rui/callsigns/callsign_143_col",0
+"callsign_144_col","#BANNER_CALLSIGN144",0,"rui/callsigns/callsign_144_col",0
+"callsign_145_col","#BANNER_CALLSIGN145",0,"rui/callsigns/callsign_145_col",0
+"callsign_146_col","#BANNER_CALLSIGN146",0,"rui/callsigns/callsign_146_col",0
+"callsign_147_col","#BANNER_CALLSIGN147",0,"rui/callsigns/callsign_147_col",0
+"callsign_148_col","#BANNER_CALLSIGN148",0,"rui/callsigns/callsign_148_col",0
+"callsign_149_col","#BANNER_CALLSIGN149",0,"rui/callsigns/callsign_149_col",0
+"callsign_150_col","#BANNER_CALLSIGN150",0,"rui/callsigns/callsign_150_col",0
+"callsign_151_col","#BANNER_CALLSIGN151",0,"rui/callsigns/callsign_151_col",0
+"callsign_152_col","#BANNER_CALLSIGN152",0,"rui/callsigns/callsign_152_col",0
+"callsign_153_col","#BANNER_CALLSIGN153",0,"rui/callsigns/callsign_153_col",0
+"callsign_154_col","#BANNER_CALLSIGN154",0,"rui/callsigns/callsign_154_col",0
+"callsign_155_col","#BANNER_CALLSIGN155",0,"rui/callsigns/callsign_155_col",0
+"callsign_156_col","#BANNER_CALLSIGN156",0,"rui/callsigns/callsign_156_col",0
+"callsign_157_col","#BANNER_CALLSIGN157",0,"rui/callsigns/callsign_157_col",0
+"callsign_158_col","#BANNER_CALLSIGN158",0,"rui/callsigns/callsign_158_col",0
+"callsign_159_col","#BANNER_CALLSIGN159",0,"rui/callsigns/callsign_159_col",0
+"callsign_160_col","#BANNER_CALLSIGN160",0,"rui/callsigns/callsign_160_col",0
+"callsign_161_col","#BANNER_CALLSIGN161",0,"rui/callsigns/callsign_161_col",0
+"callsign_162_col","#BANNER_CALLSIGN162",0,"rui/callsigns/callsign_162_col",0
+"callsign_163_col","#BANNER_CALLSIGN163",0,"rui/callsigns/callsign_163_col",0
+"callsign_164_col","#BANNER_CALLSIGN164",0,"rui/callsigns/callsign_164_col",0
+"callsign_163_col_prism","#BANNER_PRISM_CALLSIGN163",1,"rui/callsigns/callsign_163_col",0
+"callsign_164_col_prism","#BANNER_PRISM_CALLSIGN164",1,"rui/callsigns/callsign_164_col",0
+"callsign_163_col_gold","#BANNER_GOLD_CALLSIGN163",2,"rui/callsigns/callsign_163_col",0
+"callsign_164_col_gold","#BANNER_GOLD_CALLSIGN164",2,"rui/callsigns/callsign_164_col",0
+"callsign_163_col_fire","#BANNER_FIRE_CALLSIGN163",3,"rui/callsigns/callsign_163_col",0
+"callsign_164_col_fire","#BANNER_FIRE_CALLSIGN164",3,"rui/callsigns/callsign_164_col",0
+"callsign_regen_10_col","#BANNER_REGEN_10",0,"rui/callsigns/callsign_regen_10_col",0
+"callsign_regen_20_col","#BANNER_REGEN_20",0,"rui/callsigns/callsign_regen_20_col",0
+"callsign_regen_30_col","#BANNER_REGEN_30",0,"rui/callsigns/callsign_regen_30_col",0
+"callsign_regen_40_col","#BANNER_REGEN_40",0,"rui/callsigns/callsign_regen_40_col",0
+"callsign_regen_50_col","#BANNER_REGEN_50",0,"rui/callsigns/callsign_regen_50_col",0
+"callsign_regen_60_col","#BANNER_REGEN_60",0,"rui/callsigns/callsign_regen_60_col",0
+"callsign_regen_70_col","#BANNER_REGEN_70",0,"rui/callsigns/callsign_regen_70_col",0
+"callsign_regen_80_col","#BANNER_REGEN_80",0,"rui/callsigns/callsign_regen_80_col",0
+"callsign_regen_90_col","#BANNER_REGEN_90",0,"rui/callsigns/callsign_regen_90_col",0
+"callsign_regen_100_col","#BANNER_REGEN_100",0,"rui/callsigns/callsign_regen_100_col",0
+"callsign_regen_10_col_prism","#BANNER_PRISM_REGEN_10",1,"rui/callsigns/callsign_regen_10_col",0
+"callsign_regen_20_col_prism","#BANNER_PRISM_REGEN_20",1,"rui/callsigns/callsign_regen_20_col",0
+"callsign_regen_30_col_prism","#BANNER_PRISM_REGEN_30",1,"rui/callsigns/callsign_regen_30_col",0
+"callsign_regen_40_col_prism","#BANNER_PRISM_REGEN_40",1,"rui/callsigns/callsign_regen_40_col",0
+"callsign_regen_50_col_prism","#BANNER_PRISM_REGEN_50",1,"rui/callsigns/callsign_regen_50_col",0
+"callsign_regen_60_col_prism","#BANNER_PRISM_REGEN_60",1,"rui/callsigns/callsign_regen_60_col",0
+"callsign_regen_70_col_prism","#BANNER_PRISM_REGEN_70",1,"rui/callsigns/callsign_regen_70_col",0
+"callsign_regen_80_col_prism","#BANNER_PRISM_REGEN_80",1,"rui/callsigns/callsign_regen_80_col",0
+"callsign_regen_90_col_prism","#BANNER_PRISM_REGEN_90",1,"rui/callsigns/callsign_regen_90_col",0
+"callsign_regen_100_col_prism","#BANNER_PRISM_REGEN_100",1,"rui/callsigns/callsign_regen_100_col",0
+"callsign_165_col","#BANNER_CALLSIGN165",0,"rui/callsigns/callsign_165_col",0
+"callsign_166_col","#BANNER_CALLSIGN166",0,"rui/callsigns/callsign_166_col",0
+"callsign_167_col","#BANNER_CALLSIGN167",0,"rui/callsigns/callsign_167_col",0
+"callsign_168_col","#BANNER_CALLSIGN168",0,"rui/callsigns/callsign_168_col",0
+"callsign_169_col","#BANNER_CALLSIGN169",0,"rui/callsigns/callsign_169_col",0
+"callsign_170_col","#BANNER_CALLSIGN170",0,"rui/callsigns/callsign_170_col",0
+"callsign_171_col","#BANNER_CALLSIGN171",0,"rui/callsigns/callsign_171_col",0
+"callsign_172_col","#BANNER_CALLSIGN172",0,"rui/callsigns/callsign_172_col",0
+"callsign_173_col","#BANNER_CALLSIGN173",0,"rui/callsigns/callsign_173_col",0
+"callsign_174_col","#BANNER_CALLSIGN174",0,"rui/callsigns/callsign_174_col",0
+"callsign_175_col","#BANNER_CALLSIGN175",0,"rui/callsigns/callsign_175_col",0
+"callsign_176_col","#BANNER_CALLSIGN176",0,"rui/callsigns/callsign_176_col",0
+"callsign_177_col","#BANNER_CALLSIGN177",0,"rui/callsigns/callsign_177_col",0
+"callsign_178_col","#BANNER_CALLSIGN178",0,"rui/callsigns/callsign_178_col",0
+"callsign_179_col","#BANNER_CALLSIGN179",0,"rui/callsigns/callsign_179_col",0
+"callsign_180_col","#BANNER_CALLSIGN180",0,"rui/callsigns/callsign_180_col",0
+"callsign_181_col","#BANNER_CALLSIGN181",0,"rui/callsigns/callsign_181_col",0
+"callsign_182_col","#BANNER_CALLSIGN182",0,"rui/callsigns/callsign_182_col",0
+"callsign_183_col","#BANNER_CALLSIGN183",0,"rui/callsigns/callsign_183_col",0
+"callsign_184_col","#BANNER_CALLSIGN184",0,"rui/callsigns/callsign_184_col",0
+"callsign_185_col","#BANNER_CALLSIGN185",0,"rui/callsigns/callsign_185_col",0
+"callsign_165_col_fire","#BANNER_FIRE_CALLSIGN165",3,"rui/callsigns/callsign_165_col",0
+"callsign_165_col_gold","#BANNER_GOLD_CALLSIGN165",2,"rui/callsigns/callsign_165_col",0
+"callsign_24_col_prism","#BANNER_PRISM_CALLSIGN24",1,"rui/callsigns/callsign_24_col",0
+"callsign_47_col_prism","#BANNER_PRISM_CALLSIGN47",1,"rui/callsigns/callsign_47_col",0
+"callsign_36_col_prism","#BANNER_PRISM_CALLSIGN36",1,"rui/callsigns/callsign_36_col",0
+"callsign_45_col_prism","#BANNER_PRISM_CALLSIGN45",1,"rui/callsigns/callsign_45_col",0
+"callsign_68_col_prism","#BANNER_PRISM_CALLSIGN68",1,"rui/callsigns/callsign_68_col",0
+"callsign_26_col_prism","#BANNER_PRISM_CALLSIGN26",1,"rui/callsigns/callsign_26_col",0
+"callsign_165_col_prism","#BANNER_PRISM_CALLSIGN165",1,"rui/callsigns/callsign_165_col",0
+"callsign_fd_ion_dynamic","#BANNER_CALLSIGN_FD_ION",0,"rui/callsigns/callsign_fd_ion_dynamic",0
+"callsign_fd_tone_dynamic","#BANNER_CALLSIGN_FD_TONE",0,"rui/callsigns/callsign_fd_tone_dynamic",0
+"callsign_fd_scorch_dynamic","#BANNER_CALLSIGN_FD_SCORCH",0,"rui/callsigns/callsign_fd_scorch_dynamic",0
+"callsign_fd_legion_dynamic","#BANNER_CALLSIGN_FD_LEGION",0,"rui/callsigns/callsign_fd_legion_dynamic",0
+"callsign_fd_ronin_dynamic","#BANNER_CALLSIGN_FD_RONIN",0,"rui/callsigns/callsign_fd_ronin_dynamic",0
+"callsign_fd_northstar_dynamic","#BANNER_CALLSIGN_FD_NORTHSTAR",0,"rui/callsigns/callsign_fd_northstar_dynamic",0
+"callsign_fd_monarch_dynamic","#BANNER_CALLSIGN_FD_MONARCH",0,"rui/callsigns/callsign_fd_monarch_dynamic",0
+"callsign_fd_ion_hard","#BANNER_CALLSIGN_FD_ION",1,"rui/callsigns/callsign_fd_ion_hard",0
+"callsign_fd_ion_master","#BANNER_CALLSIGN_FD_ION",1,"rui/callsigns/callsign_fd_ion_master",0
+"callsign_fd_ion_insane","#BANNER_CALLSIGN_FD_ION",2,"rui/callsigns/callsign_fd_ion_insane",0
+"callsign_fd_tone_hard","#BANNER_CALLSIGN_FD_TONE",1,"rui/callsigns/callsign_fd_tone_hard",0
+"callsign_fd_tone_master","#BANNER_CALLSIGN_FD_TONE",1,"rui/callsigns/callsign_fd_tone_master",0
+"callsign_fd_tone_insane","#BANNER_CALLSIGN_FD_TONE",2,"rui/callsigns/callsign_fd_tone_insane",0
+"callsign_fd_scorch_hard","#BANNER_CALLSIGN_FD_SCORCH",1,"rui/callsigns/callsign_fd_scorch_hard",0
+"callsign_fd_scorch_master","#BANNER_CALLSIGN_FD_SCORCH",1,"rui/callsigns/callsign_fd_scorch_master",0
+"callsign_fd_scorch_insane","#BANNER_CALLSIGN_FD_SCORCH",2,"rui/callsigns/callsign_fd_scorch_insane",0
+"callsign_fd_legion_hard","#BANNER_CALLSIGN_FD_LEGION",1,"rui/callsigns/callsign_fd_legion_hard",0
+"callsign_fd_legion_master","#BANNER_CALLSIGN_FD_LEGION",1,"rui/callsigns/callsign_fd_legion_master",0
+"callsign_fd_legion_insane","#BANNER_CALLSIGN_FD_LEGION",2,"rui/callsigns/callsign_fd_legion_insane",0
+"callsign_fd_ronin_hard","#BANNER_CALLSIGN_FD_RONIN",1,"rui/callsigns/callsign_fd_ronin_hard",0
+"callsign_fd_ronin_master","#BANNER_CALLSIGN_FD_RONIN",1,"rui/callsigns/callsign_fd_ronin_master",0
+"callsign_fd_ronin_insane","#BANNER_CALLSIGN_FD_RONIN",2,"rui/callsigns/callsign_fd_ronin_insane",0
+"callsign_fd_northstar_hard","#BANNER_CALLSIGN_FD_NORTHSTAR",1,"rui/callsigns/callsign_fd_northstar_hard",0
+"callsign_fd_northstar_master","#BANNER_CALLSIGN_FD_NORTHSTAR",1,"rui/callsigns/callsign_fd_northstar_master",0
+"callsign_fd_northstar_insane","#BANNER_CALLSIGN_FD_NORTHSTAR",2,"rui/callsigns/callsign_fd_northstar_insane",0
+"callsign_fd_monarch_hard","#BANNER_CALLSIGN_FD_MONARCH",1,"rui/callsigns/callsign_fd_monarch_hard",0
+"callsign_fd_monarch_master","#BANNER_CALLSIGN_FD_MONARCH",1,"rui/callsigns/callsign_fd_monarch_master",0
+"callsign_fd_monarch_insane","#BANNER_CALLSIGN_FD_MONARCH",2,"rui/callsigns/callsign_fd_monarch_insane",0
+"callsign_tt_gameover","#BANNER_CALLSIGN_TT_GAMEOVER",0,"rui/callsigns/callsign_tt_gameover",1500
+"callsign_tt_gameover_prism","#BANNER_PRISM_TT_GAMEOVER",1,"rui/callsigns/callsign_tt_gameover",3000
+"callsign_tt_guardtheflag","#BANNER_CALLSIGN_TT_GUARDTHEFLAG",0,"rui/callsigns/callsign_tt_guardtheflag",1500
+"callsign_tt_guardtheflag_prism","#BANNER_PRISM_TT_GUARDTHEFLAG",1,"rui/callsigns/callsign_tt_guardtheflag",3000
+"callsign_tt_megamarvin","#BANNER_CALLSIGN_TT_MEGAMARVIN",0,"rui/callsigns/callsign_tt_megamarvin",1500
+"callsign_tt_megamarvin_prism","#BANNER_PRISM_TT_MEGAMARVIN",1,"rui/callsigns/callsign_tt_megamarvin",3000
+"callsign_tt_nessievault","#BANNER_CALLSIGN_TT_NESSIEVAULT",0,"rui/callsigns/callsign_tt_nessievault",1500
+"callsign_tt_nessievault_prism","#BANNER_PRISM_TT_NESSIEVAULT",1,"rui/callsigns/callsign_tt_nessievault",3000
+"callsign_tt_nsbt","#BANNER_CALLSIGN_TT_NSBT",0,"rui/callsigns/callsign_tt_nsbt",1500
+"callsign_tt_nsbt_prism","#BANNER_PRISM_TT_NSBT",1,"rui/callsigns/callsign_tt_nsbt",3000
+"callsign_tt_protocol2","#BANNER_CALLSIGN_TT_PROTOCOL2",0,"rui/callsigns/callsign_tt_protocol2",1500
+"callsign_tt_protocol2_prism","#BANNER_PRISM_TT_PROTOCOL2",1,"rui/callsigns/callsign_tt_protocol2",3000
+"callsign_tt_rekt","#BANNER_CALLSIGN_TT_REKT",0,"rui/callsigns/callsign_tt_rekt",1500
+"callsign_tt_rekt_prism","#BANNER_PRISM_TT_REKT",1,"rui/callsigns/callsign_tt_rekt",3000
+"callsign_tt_titantoons","#BANNER_CALLSIGN_TT_TITANTOONS",0,"rui/callsigns/callsign_tt_titantoons",1500
+"callsign_tt_titantoons_prism","#BANNER_PRISM_TT_TITANTOONS",1,"rui/callsigns/callsign_tt_titantoons",3000
+"callsign_eat_ion","#BANNER_EAT_ION",0,"rui/callsigns/callsign_eat_ion",1500
+"callsign_eat_legion","#BANNER_EAT_LEGION",0,"rui/callsigns/callsign_eat_legion",1500
+"callsign_eat_northstar","#BANNER_EAT_NORTHSTAR",0,"rui/callsigns/callsign_eat_northstar",1500
+"callsign_eat_ronin","#BANNER_EAT_RONIN",0,"rui/callsigns/callsign_eat_ronin",1500
+"callsign_eat_scorch","#BANNER_EAT_SCORCH",0,"rui/callsigns/callsign_eat_scorch",1500
+"callsign_eat_tone","#BANNER_EAT_TONE",0,"rui/callsigns/callsign_eat_tone",1500
+"callsign_goodboy","#BANNER_GOODBOY",0,"rui/callsigns/callsign_goodboy",0
+"callsign_contest_01","#BANNER_CONTEST_01",0,"rui/callsigns/callsign_contest_01",0
+"callsign_contest_02","#BANNER_CONTEST_02",0,"rui/callsigns/callsign_contest_02",0
+"callsign_contest_03","#BANNER_CONTEST_03",0,"rui/callsigns/callsign_contest_03",0
+"callsign_contest_04","#BANNER_CONTEST_04",0,"rui/callsigns/callsign_contest_04",0
+"callsign_contest_05","#BANNER_CONTEST_05",0,"rui/callsigns/callsign_contest_05",0
+"callsign_contest_06","#BANNER_CONTEST_06",0,"rui/callsigns/callsign_contest_06",0
+"callsign_contest_07","#BANNER_CONTEST_07",0,"rui/callsigns/callsign_contest_07",0
+"callsign_contest_08","#BANNER_CONTEST_08",0,"rui/callsigns/callsign_contest_08",0
+"callsign_contest_09","#BANNER_CONTEST_09",0,"rui/callsigns/callsign_contest_09",0
+"callsign_contest_10","#BANNER_CONTEST_10",0,"rui/callsigns/callsign_contest_10",0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/callsign_icons.csv b/Northstar.CustomServers/mod/scripts/datatable/callsign_icons.csv
new file mode 100644
index 00000000..61840c68
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/callsign_icons.csv
@@ -0,0 +1,193 @@
+itemRef,name,image,smallImage,cost
+"gc_icon_5star","#PATCH_GC_ICON_5STAR","rui/gencard_icons/gc_icon_5star","rui/gencard_icons/gc_icon_5star_small",50
+"gc_icon_8ball","#PATCH_8BALL","rui/gencard_icons/gc_icon_8ball","rui/gencard_icons/gc_icon_8ball_small",0
+"gc_icon_ace","#PATCH_ACE","rui/gencard_icons/gc_icon_ace","rui/gencard_icons/gc_icon_ace_small",0
+"gc_icon_angryface","#PATCH_ANGRYFACE","rui/gencard_icons/gc_icon_angryface","rui/gencard_icons/gc_icon_angryface_small",50
+"gc_icon_apostrophe","#PATCH_APOSTROPHE","rui/gencard_icons/gc_icon_apostrophe","rui/gencard_icons/gc_icon_apostrophe_small",0
+"gc_icon_atom","#PATCH_ATOM","rui/gencard_icons/gc_icon_atom","rui/gencard_icons/gc_icon_atom_small",0
+"gc_icon_balloon","#PATCH_BALLOON","rui/gencard_icons/gc_icon_balloon","rui/gencard_icons/gc_icon_balloon_small",0
+"gc_icon_bear","#PATCH_BEAR","rui/gencard_icons/gc_icon_bear","rui/gencard_icons/gc_icon_bear_small",50
+"gc_icon_bird","#PATCH_BIRD","rui/gencard_icons/gc_icon_bird","rui/gencard_icons/gc_icon_bird_small",50
+"gc_icon_bomb_01","#PATCH_BOMB_01","rui/gencard_icons/gc_icon_bomb_01","rui/gencard_icons/gc_icon_bomb_01_small",0
+"gc_icon_bomb_02","#PATCH_BOMB_02","rui/gencard_icons/gc_icon_bomb_02","rui/gencard_icons/gc_icon_bomb_02_small",0
+"gc_icon_bullet","#PATCH_BULLET","rui/gencard_icons/gc_icon_bullet","rui/gencard_icons/gc_icon_bullet_small",0
+"gc_icon_bullseye","#PATCH_BULLSEYE","rui/gencard_icons/gc_icon_bullseye","rui/gencard_icons/gc_icon_bullseye_small",0
+"gc_icon_cateye","#PATCH_CATEYE","rui/gencard_icons/gc_icon_cateye","rui/gencard_icons/gc_icon_cateye_small",0
+"gc_icon_chicken","#PATCH_CHICKEN","rui/gencard_icons/gc_icon_chicken","rui/gencard_icons/gc_icon_chicken_small",50
+"gc_icon_clawmark","#PATCH_CLAWMARK","rui/gencard_icons/gc_icon_clawmark","rui/gencard_icons/gc_icon_clawmark_small",0
+"gc_icon_club","#PATCH_CLUB","rui/gencard_icons/gc_icon_club","rui/gencard_icons/gc_icon_club_small",50
+"gc_icon_comet","#PATCH_COMET","rui/gencard_icons/gc_icon_comet","rui/gencard_icons/gc_icon_comet_small",0
+"gc_icon_cow","#PATCH_COW","rui/gencard_icons/gc_icon_cow","rui/gencard_icons/gc_icon_cow_small",50
+"gc_icon_cowboy_hat","#PATCH_COWBOY_HAT","rui/gencard_icons/gc_icon_cowboy_hat","rui/gencard_icons/gc_icon_cowboy_hat_small",0
+"gc_icon_crosshair","#PATCH_CROSSHAIR","rui/gencard_icons/gc_icon_crosshair","rui/gencard_icons/gc_icon_crosshair_small",0
+"gc_icon_cupcake","#PATCH_CUPCAKE","rui/gencard_icons/gc_icon_cupcake","rui/gencard_icons/gc_icon_cupcake_small",50
+"gc_icon_diamond","#PATCH_DIAMOND","rui/gencard_icons/gc_icon_diamond","rui/gencard_icons/gc_icon_diamond_small",0
+"gc_icon_dice","#PATCH_DICE","rui/gencard_icons/gc_icon_dice","rui/gencard_icons/gc_icon_dice_small",0
+"gc_icon_dollarsign","#PATCH_DOLLARSIGN","rui/gencard_icons/gc_icon_dollarsign","rui/gencard_icons/gc_icon_dollarsign_small",0
+"gc_icon_doublerainbow","#PATCH_DOUBLERAINBOW","rui/gencard_icons/gc_icon_doublerainbow","rui/gencard_icons/gc_icon_doublerainbow_small",50
+"gc_icon_fingerprint","#PATCH_FINGERPRINT","rui/gencard_icons/gc_icon_fingerprint","rui/gencard_icons/gc_icon_fingerprint_small",50
+"gc_icon_fireball","#PATCH_FIREBALL","rui/gencard_icons/gc_icon_fireball","rui/gencard_icons/gc_icon_fireball_small",0
+"gc_icon_frag","#PATCH_FRAG","rui/gencard_icons/gc_icon_frag","rui/gencard_icons/gc_icon_frag_small",0
+"gc_icon_gear","#PATCH_GEAR","rui/gencard_icons/gc_icon_gear","rui/gencard_icons/gc_icon_gear_small",0
+"gc_icon_ghostface","#PATCH_GHOSTFACE","rui/gencard_icons/gc_icon_ghostface","rui/gencard_icons/gc_icon_ghostface_small",50
+"gc_icon_hamburger","#PATCH_HAMBURGER","rui/gencard_icons/gc_icon_hamburger","rui/gencard_icons/gc_icon_hamburger_small",50
+"gc_icon_handprint","#PATCH_HANDPRINT","rui/gencard_icons/gc_icon_handprint","rui/gencard_icons/gc_icon_handprint_small",50
+"gc_icon_happyface","#PATCH_HAPPYFACE","rui/gencard_icons/gc_icon_happyface","rui/gencard_icons/gc_icon_happyface_small",0
+"gc_icon_heart","#PATCH_HEART","rui/gencard_icons/gc_icon_heart","rui/gencard_icons/gc_icon_heart_small",0
+"gc_icon_hvt","#PATCH_HVT","rui/gencard_icons/gc_icon_hvt","rui/gencard_icons/gc_icon_hvt_small",50
+"gc_icon_jollyrgr","#PATCH_JOLLYRGR","rui/gencard_icons/gc_icon_jollyrgr","rui/gencard_icons/gc_icon_jollyrgr_small",50
+"gc_icon_lightning","#PATCH_LIGHTNING","rui/gencard_icons/gc_icon_lightning","rui/gencard_icons/gc_icon_lightning_small",0
+"gc_icon_lol","#PATCH_LOL","rui/gencard_icons/gc_icon_lol","rui/gencard_icons/gc_icon_lol_small",50
+"gc_icon_mad_hat","#PATCH_MAD_HAT","rui/gencard_icons/gc_icon_mad_hat","rui/gencard_icons/gc_icon_mad_hat_small",50
+"gc_icon_medic","#PATCH_MEDIC","rui/gencard_icons/gc_icon_medic","rui/gencard_icons/gc_icon_medic_small",0
+"gc_icon_moon","#PATCH_MOON","rui/gencard_icons/gc_icon_moon","rui/gencard_icons/gc_icon_moon_small",0
+"gc_icon_omg","#PATCH_OMG","rui/gencard_icons/gc_icon_omg","rui/gencard_icons/gc_icon_omg_small",50
+"gc_icon_paw","#PATCH_PAW","rui/gencard_icons/gc_icon_paw","rui/gencard_icons/gc_icon_paw_small",0
+"gc_icon_pizza","#PATCH_PIZZA","rui/gencard_icons/gc_icon_pizza","rui/gencard_icons/gc_icon_pizza_small",50
+"gc_icon_pro","#PATCH_PRO","rui/gencard_icons/gc_icon_pro","rui/gencard_icons/gc_icon_pro_small",0
+"gc_icon_question","#PATCH_QUESTION","rui/gencard_icons/gc_icon_question","rui/gencard_icons/gc_icon_question_small",50
+"gc_icon_rainbow","#PATCH_RAINBOW","rui/gencard_icons/gc_icon_rainbow","rui/gencard_icons/gc_icon_rainbow_small",50
+"gc_icon_raincloud","#PATCH_RAINCLOUD","rui/gencard_icons/gc_icon_raincloud","rui/gencard_icons/gc_icon_raincloud_small",0
+"gc_icon_rocket","#PATCH_ROCKET","rui/gencard_icons/gc_icon_rocket","rui/gencard_icons/gc_icon_rocket_small",0
+"gc_icon_saturn","#PATCH_SATURN","rui/gencard_icons/gc_icon_saturn","rui/gencard_icons/gc_icon_saturn_small",50
+"gc_icon_skull","#PATCH_SKULL","rui/gencard_icons/gc_icon_skull","rui/gencard_icons/gc_icon_skull_small",50
+"gc_icon_spade","#PATCH_SPADE","rui/gencard_icons/gc_icon_spade","rui/gencard_icons/gc_icon_spade_small",0
+"gc_icon_star","#PATCH_STAR","rui/gencard_icons/gc_icon_star","rui/gencard_icons/gc_icon_star_small",0
+"gc_icon_sword","#PATCH_SWORD","rui/gencard_icons/gc_icon_sword","rui/gencard_icons/gc_icon_sword_small",0
+"gc_icon_teabage","#PATCH_TEABAGE","rui/gencard_icons/gc_icon_teabage","rui/gencard_icons/gc_icon_teabage_small",50
+"gc_icon_titanfall","#PATCH_TITANFALL","rui/gencard_icons/gc_icon_titanfall","rui/gencard_icons/gc_icon_titanfall_small",0
+"gc_icon_vip","#PATCH_VIP","rui/gencard_icons/gc_icon_vip","rui/gencard_icons/gc_icon_vip_small",0
+"gc_icon_witch_hat","#PATCH_WITCH_HAT","rui/gencard_icons/gc_icon_witch_hat","rui/gencard_icons/gc_icon_witch_hat_small",50
+"gc_icon_wizard_hat","#PATCH_WIZARD_HAT","rui/gencard_icons/gc_icon_wizard_hat","rui/gencard_icons/gc_icon_wizard_hat_small",50
+"gc_icon_wtf","#PATCH_WTF","rui/gencard_icons/gc_icon_wtf","rui/gencard_icons/gc_icon_wtf_small",50
+"gc_icon_yuckface","#PATCH_YUCKFACE","rui/gencard_icons/gc_icon_yuckface","rui/gencard_icons/gc_icon_yuckface_small",0
+"gc_icon_respawn","#PATCH_RESPAWN","rui/gencard_icons/gc_icon_respawn","rui/gencard_icons/gc_icon_respawn_small",50
+"gc_icon_fair_warning","#PATCH_FAIR_WARNING","rui/gencard_icons/gc_icon_fair_warning","rui/gencard_icons/gc_icon_fair_warning_small",50
+"gc_icon_hawk","#PATCH_HAWK","rui/gencard_icons/gc_icon_hawk","rui/gencard_icons/gc_icon_hawk_small",0
+"gc_icon_ram","#PATCH_RAM","rui/gencard_icons/gc_icon_ram","rui/gencard_icons/gc_icon_ram_small",50
+"gc_icon_stab","#PATCH_STAB","rui/gencard_icons/gc_icon_stab","rui/gencard_icons/gc_icon_stab_small",50
+"gc_icon_bigcat","#PATCH_BIGCAT","rui/gencard_icons/gc_icon_bigcat","rui/gencard_icons/gc_icon_bigcat_small",50
+"gc_icon_wraith","#PATCH_WRAITH","rui/gencard_icons/gc_icon_wraith","rui/gencard_icons/gc_icon_wraith_small",50
+"gc_icon_stinger","#PATCH_STINGER","rui/gencard_icons/gc_icon_stinger","rui/gencard_icons/gc_icon_stinger_small",0
+"gc_icon_heartless","#PATCH_HEARTLESS","rui/gencard_icons/gc_icon_heartless","rui/gencard_icons/gc_icon_heartless_small",50
+"gc_icon_earthworm","#PATCH_EARTHWORM","rui/gencard_icons/gc_icon_earthworm","rui/gencard_icons/gc_icon_earthworm_small",50
+"gc_icon_prowler","#PATCH_PROWLER","rui/gencard_icons/gc_icon_prowler","rui/gencard_icons/gc_icon_prowler_small",0
+"gc_icon_ordnance","#PATCH_ORDNANCE","rui/gencard_icons/gc_icon_ordnance","rui/gencard_icons/gc_icon_ordnance_small",50
+"gc_icon_radar","#PATCH_RADAR","rui/gencard_icons/gc_icon_radar","rui/gencard_icons/gc_icon_radar_small",0
+"gc_icon_ramskull","#PATCH_RAMSKULL","rui/gencard_icons/gc_icon_ramskull","rui/gencard_icons/gc_icon_ramskull_small",0
+"gc_icon_hawkmoth","#PATCH_HAWKMOTH","rui/gencard_icons/gc_icon_hawkmoth","rui/gencard_icons/gc_icon_hawkmoth_small",50
+"gc_icon_fox","#PATCH_FOX","rui/gencard_icons/gc_icon_fox","rui/gencard_icons/gc_icon_fox_small",0
+"gc_icon_marksman","#PATCH_MARKSMAN","rui/gencard_icons/gc_icon_marksman","rui/gencard_icons/gc_icon_marksman_small",50
+"gc_icon_bunnyskull","#PATCH_BUNNYSKULL","rui/gencard_icons/gc_icon_bunnyskull","rui/gencard_icons/gc_icon_bunnyskull_small",50
+"gc_icon_falcon","#PATCH_FALCON","rui/gencard_icons/gc_icon_falcon","rui/gencard_icons/gc_icon_falcon_small",50
+"gc_icon_assault","#PATCH_ASSAULT","rui/gencard_icons/gc_icon_assault","rui/gencard_icons/gc_icon_assault_small",0
+"gc_icon_flying_skull","#PATCH_FLYING_SKULL","rui/gencard_icons/gc_icon_flying_skull","rui/gencard_icons/gc_icon_flying_skull_small",50
+"gc_icon_hammer","#PATCH_HAMMER","rui/gencard_icons/gc_icon_hammer","rui/gencard_icons/gc_icon_hammer_small",50
+"gc_icon_dragonfly","#PATCH_DRAGONFLY","rui/gencard_icons/gc_icon_dragonfly","rui/gencard_icons/gc_icon_dragonfly_small",0
+"gc_icon_striketwice","#PATCH_STRIKETWICE","rui/gencard_icons/gc_icon_striketwice","rui/gencard_icons/gc_icon_striketwice_small",0
+"gc_icon_waves","#PATCH_WAVES","rui/gencard_icons/gc_icon_waves","rui/gencard_icons/gc_icon_waves_small",0
+"gc_icon_fin","#PATCH_FIN","rui/gencard_icons/gc_icon_fin","rui/gencard_icons/gc_icon_fin_small",0
+"gc_icon_bee","#PATCH_BEE","rui/gencard_icons/gc_icon_bee","rui/gencard_icons/gc_icon_bee_small",50
+"gc_icon_wasp","#PATCH_WASP","rui/gencard_icons/gc_icon_wasp","rui/gencard_icons/gc_icon_wasp_small",0
+"gc_icon_bat","#PATCH_BAT","rui/gencard_icons/gc_icon_bat","rui/gencard_icons/gc_icon_bat_small",0
+"gc_icon_dataknife","#PATCH_DATAKNIFE","rui/gencard_icons/gc_icon_dataknife","rui/gencard_icons/gc_icon_dataknife_small",50
+"gc_icon_knife","#PATCH_KNIFE","rui/gencard_icons/gc_icon_knife","rui/gencard_icons/gc_icon_knife_small",50
+"gc_icon_widow","#PATCH_WIDOW","rui/gencard_icons/gc_icon_widow","rui/gencard_icons/gc_icon_widow_small",50
+"gc_icon_snake","#PATCH_SNAKE","rui/gencard_icons/gc_icon_snake","rui/gencard_icons/gc_icon_snake_small",50
+"gc_icon_scorpion","#PATCH_SCORPION","rui/gencard_icons/gc_icon_scorpion","rui/gencard_icons/gc_icon_scorpion_small",50
+"gc_icon_dragon","#PATCH_DRAGON","rui/gencard_icons/gc_icon_dragon","rui/gencard_icons/gc_icon_dragon_small",50
+"gc_icon_sgt_major","#PATCH_SGT_MAJOR","rui/gencard_icons/gc_icon_sgt_major","rui/gencard_icons/gc_icon_sgt_major_small",0
+"gc_icon_senior_sgt_e6","#PATCH_SENIOR_SGT_E6","rui/gencard_icons/gc_icon_senior_sgt_e6","rui/gencard_icons/gc_icon_senior_sgt_e6_small",50
+"gc_icon_senior_sgt","#PATCH_SENIOR_SGT","rui/gencard_icons/gc_icon_senior_sgt","rui/gencard_icons/gc_icon_senior_sgt_small",50
+"gc_icon_sgt","#PATCH_SGT","rui/gencard_icons/gc_icon_sgt","rui/gencard_icons/gc_icon_sgt_small",50
+"gc_icon_corporal","#PATCH_CORPORAL","rui/gencard_icons/gc_icon_corporal","rui/gencard_icons/gc_icon_corporal_small",50
+"gc_icon_pvt","#PATCH_PVT","rui/gencard_icons/gc_icon_private","rui/gencard_icons/gc_icon_private_small",50
+"gc_icon_gen0","#PATCH_GEN0","rui/gencard_icons/gc_icon_gen0","rui/gencard_icons/gc_icon_gen0_small",0
+"gc_icon_gen1","#PATCH_GEN1","rui/gencard_icons/gc_icon_gen1","rui/gencard_icons/gc_icon_gen1_small",0
+"gc_icon_gen2","#PATCH_GEN2","rui/gencard_icons/gc_icon_gen2","rui/gencard_icons/gc_icon_gen2_small",0
+"gc_icon_gen3","#PATCH_GEN3","rui/gencard_icons/gc_icon_gen3","rui/gencard_icons/gc_icon_gen3_small",0
+"gc_icon_gen4","#PATCH_GEN4","rui/gencard_icons/gc_icon_gen4","rui/gencard_icons/gc_icon_gen4_small",0
+"gc_icon_gen5","#PATCH_GEN5","rui/gencard_icons/gc_icon_gen5","rui/gencard_icons/gc_icon_gen5_small",0
+"gc_icon_gen6","#PATCH_GEN6","rui/gencard_icons/gc_icon_gen6","rui/gencard_icons/gc_icon_gen6_small",0
+"gc_icon_gen7","#PATCH_GEN7","rui/gencard_icons/gc_icon_gen7","rui/gencard_icons/gc_icon_gen7_small",0
+"gc_icon_gen8","#PATCH_GEN8","rui/gencard_icons/gc_icon_gen8","rui/gencard_icons/gc_icon_gen8_small",0
+"gc_icon_gen9","#PATCH_GEN9","rui/gencard_icons/gc_icon_gen9","rui/gencard_icons/gc_icon_gen9_small",0
+"gc_icon_respawn_dev","#PATCH_DEV","rui/gencard_icons/gc_icon_respawn_dev","rui/gencard_icons/gc_icon_respawn_dev_small",0
+"gc_icon_64","#PATCH_64","rui/gencard_icons/dlc1/gc_icon_64","rui/gencard_icons/dlc1/gc_icon_64_small",0
+"gc_icon_aces","#PATCH_ACES","rui/gencard_icons/dlc1/gc_icon_aces","rui/gencard_icons/dlc1/gc_icon_aces_small",0
+"gc_icon_alien","#PATCH_ALIEN","rui/gencard_icons/dlc1/gc_icon_alien","rui/gencard_icons/dlc1/gc_icon_alien_small",0
+"gc_icon_apex","#PATCH_APEX","rui/gencard_icons/dlc1/gc_icon_apex","rui/gencard_icons/dlc1/gc_icon_apex_small",0
+"gc_icon_ares","#PATCH_ARES","rui/gencard_icons/dlc1/gc_icon_ares","rui/gencard_icons/dlc1/gc_icon_ares_small",0
+"gc_icon_controller","#PATCH_CONTROLLER","rui/gencard_icons/dlc1/gc_icon_controller","rui/gencard_icons/dlc1/gc_icon_controller_small",0
+"gc_icon_drone","#PATCH_DRONE","rui/gencard_icons/dlc1/gc_icon_drone","rui/gencard_icons/dlc1/gc_icon_drone_small",0
+"gc_icon_heartbreaker","#PATCH_HEARTBREAKER","rui/gencard_icons/dlc1/gc_icon_heartbreaker","rui/gencard_icons/dlc1/gc_icon_heartbreaker_small",0
+"gc_icon_hexes","#PATCH_HEXES","rui/gencard_icons/dlc1/gc_icon_hexes","rui/gencard_icons/dlc1/gc_icon_hexes_small",0
+"gc_icon_kodai","#PATCH_KODAI","rui/gencard_icons/dlc1/gc_icon_kodai","rui/gencard_icons/dlc1/gc_icon_kodai_small",0
+"gc_icon_lastimosa","#PATCH_LASTIMOSA","rui/gencard_icons/dlc1/gc_icon_lastimosa","rui/gencard_icons/dlc1/gc_icon_lastimosa_small",0
+"gc_icon_lawai","#PATCH_LAWAI","rui/gencard_icons/dlc1/gc_icon_lawai","rui/gencard_icons/dlc1/gc_icon_lawai_small",0
+"gc_icon_mcor","#PATCH_MCOR","rui/gencard_icons/dlc1/gc_icon_mcor","rui/gencard_icons/dlc1/gc_icon_mcor_small",0
+"gc_icon_phoenix","#PATCH_PHOENIX","rui/gencard_icons/dlc1/gc_icon_phoenix","rui/gencard_icons/dlc1/gc_icon_phoenix_small",0
+"gc_icon_pilot","#PATCH_PILOT","rui/gencard_icons/dlc1/gc_icon_pilot","rui/gencard_icons/dlc1/gc_icon_pilot_small",0
+"gc_icon_robot","#PATCH_ROBOT","rui/gencard_icons/dlc1/gc_icon_robot","rui/gencard_icons/dlc1/gc_icon_robot_small",0
+"gc_icon_sentry","#PATCH_SENTRY","rui/gencard_icons/dlc1/gc_icon_sentry","rui/gencard_icons/dlc1/gc_icon_sentry_small",0
+"gc_icon_super_spectre","#PATCH_SUPER_SPECTRE","rui/gencard_icons/dlc1/gc_icon_super_spectre","rui/gencard_icons/dlc1/gc_icon_super_spectre_small",0
+"gc_icon_vinson","#PATCH_VINSON","rui/gencard_icons/dlc1/gc_icon_vinson","rui/gencard_icons/dlc1/gc_icon_vinson_small",0
+"gc_icon_wonyeon","#PATCH_WONYEON","rui/gencard_icons/dlc1/gc_icon_wonyeon","rui/gencard_icons/dlc1/gc_icon_wonyeon_small",0
+"gc_icon_b3_wing","#PATCH_B3_WING","rui/gencard_icons/gc_icon_b3_wing","rui/gencard_icons/gc_icon_b3_wing_small",0
+"gc_icon_balance","#PATCH_BALANCE","rui/gencard_icons/dlc3/gc_icon_balance","rui/gencard_icons/dlc3/gc_icon_balance_small",0
+"gc_icon_boot","#PATCH_BOOT","rui/gencard_icons/dlc3/gc_icon_boot","rui/gencard_icons/dlc3/gc_icon_boot_small",0
+"gc_icon_bt_eye","#PATCH_BT_EYE","rui/gencard_icons/dlc3/gc_icon_bt_eye","rui/gencard_icons/dlc3/gc_icon_bt_eye_small",0
+"gc_icon_peace","#PATCH_PEACE","rui/gencard_icons/dlc3/gc_icon_peace","rui/gencard_icons/dlc3/gc_icon_peace_small",0
+"gc_icon_pilot2","#PATCH_PILOT2","rui/gencard_icons/dlc3/gc_icon_pilot","rui/gencard_icons/dlc3/gc_icon_pilot_small",0
+"gc_icon_srs","#PATCH_SRS","rui/gencard_icons/dlc3/gc_icon_srs","rui/gencard_icons/dlc3/gc_icon_srs_small",0
+"gc_icon_starline","#PATCH_STARLINE","rui/gencard_icons/dlc3/gc_icon_starline","rui/gencard_icons/dlc3/gc_icon_starline_small",0
+"gc_icon_thumbdown","#PATCH_THUMBDOWN","rui/gencard_icons/dlc3/gc_icon_thumbdown","rui/gencard_icons/dlc3/gc_icon_thumbdown_small",0
+"gc_icon_thumbup","#PATCH_THUMBUP","rui/gencard_icons/dlc3/gc_icon_thumbup","rui/gencard_icons/dlc3/gc_icon_thumbup_small",0
+"gc_icon_vanguard","#PATCH_VANGUARD","rui/gencard_icons/dlc3/gc_icon_vanguard","rui/gencard_icons/dlc3/gc_icon_vanguard_small",0
+"gc_icon_deuce","#PATCH_DEUCE","rui/gencard_icons/dlc2/gc_icon_deuce","rui/gencard_icons/dlc2/gc_icon_deuce_small",0
+"gc_icon_down","#PATCH_DOWN","rui/gencard_icons/dlc2/gc_icon_down","rui/gencard_icons/dlc2/gc_icon_down_small",50
+"gc_icon_joy","#PATCH_JOY","rui/gencard_icons/dlc2/gc_icon_joy","rui/gencard_icons/dlc2/gc_icon_joy_small",50
+"gc_icon_mushroom","#PATCH_MUSHROOM","rui/gencard_icons/dlc2/gc_icon_mushroom","rui/gencard_icons/dlc2/gc_icon_mushroom_small",0
+"gc_icon_prowlerhead","#PATCH_PROWLERHEAD","rui/gencard_icons/dlc2/gc_icon_prowlerhead","rui/gencard_icons/dlc2/gc_icon_prowlerhead_small",0
+"gc_icon_scythe","#PATCH_SCYTHE","rui/gencard_icons/dlc2/gc_icon_scythe","rui/gencard_icons/dlc2/gc_icon_scythe_small",0
+"gc_icon_shuriken","#PATCH_SHURIKEN","rui/gencard_icons/dlc2/gc_icon_shuriken","rui/gencard_icons/dlc2/gc_icon_shuriken_small",0
+"gc_icon_squid","#PATCH_SQUID","rui/gencard_icons/dlc2/gc_icon_squid","rui/gencard_icons/dlc2/gc_icon_squid_small",0
+"gc_icon_threebullets","#PATCH_THREEBULLETS","rui/gencard_icons/dlc2/gc_icon_threebullets","rui/gencard_icons/dlc2/gc_icon_threebullets_small",0
+"gc_icon_tick","#PATCH_TICK","rui/gencard_icons/dlc2/gc_icon_tick","rui/gencard_icons/dlc2/gc_icon_tick_small",0
+"gc_icon_buzzsaw","#PATCH_BUZZSAW","rui/gencard_icons/dlc3/gc_icon_buzzsaw","rui/gencard_icons/dlc3/gc_icon_buzzsaw_small",0
+"gc_icon_crossed_lighting","#PATCH_CROSSED_LIGHTING","rui/gencard_icons/dlc3/gc_icon_crossed_lighting","rui/gencard_icons/dlc3/gc_icon_crossed_lighting_small",0
+"gc_icon_flying_bullet","#PATCH_FLYING_BULLET","rui/gencard_icons/dlc3/gc_icon_flying_bullet","rui/gencard_icons/dlc3/gc_icon_flying_bullet_small",0
+"gc_icon_hammer2","#PATCH_HAMMER2","rui/gencard_icons/dlc3/gc_icon_hammer","rui/gencard_icons/dlc3/gc_icon_hammer_small",0
+"gc_icon_keyboard","#PATCH_KEYBOARD","rui/gencard_icons/dlc3/gc_icon_keyboard","rui/gencard_icons/dlc3/gc_icon_keyboard_small",0
+"gc_icon_lightbulb","#PATCH_LIGHTBULB","rui/gencard_icons/dlc3/gc_icon_lightbulb","rui/gencard_icons/dlc3/gc_icon_lightbulb_small",0
+"gc_icon_narwhal","#PATCH_NARWHAL","rui/gencard_icons/dlc3/gc_icon_narwhal","rui/gencard_icons/dlc3/gc_icon_narwhal_small",0
+"gc_icon_robot_eye","#PATCH_ROBOT_EYE","rui/gencard_icons/dlc3/gc_icon_robot_eye","rui/gencard_icons/dlc3/gc_icon_robot_eye_small",0
+"gc_icon_taco","#PATCH_TACO","rui/gencard_icons/dlc3/gc_icon_taco","rui/gencard_icons/dlc3/gc_icon_taco_small",0
+"gc_icon_treble","#PATCH_TREBLE","rui/gencard_icons/dlc3/gc_icon_treble","rui/gencard_icons/dlc3/gc_icon_treble_small",0
+"gc_icon_monarch","#PATCH_MONARCH","rui/gencard_icons/dlc4/gc_icon_monarch","rui/gencard_icons/dlc4/gc_icon_monarch_small",0
+"gc_icon_mrvn","#PATCH_MRVN","rui/gencard_icons/dlc4/gc_icon_mrvn","rui/gencard_icons/dlc4/gc_icon_mrvn_small",0
+"gc_icon_blank","#PATCH_BLANK","rui/gencard_icons/gc_icon_blank","rui/gencard_icons/gc_icon_blank_small",0
+"gc_icon_monarch_dlc5","#PATCH_MONARCH_DLC5","rui/gencard_icons/dlc5/gc_icon_monarch","rui/gencard_icons/dlc5/gc_icon_monarch_small",0
+"gc_icon_militia","#PATCH_MILITIA","rui/gencard_icons/dlc5/gc_icon_militia","rui/gencard_icons/dlc5/gc_icon_militia_small",0
+"gc_icon_militia_alt","#PATCH_MILITIA_ALT","rui/gencard_icons/dlc5/gc_icon_militia_alt","rui/gencard_icons/dlc5/gc_icon_militia_alt_small",0
+"gc_icon_imc","#PATCH_IMC","rui/gencard_icons/dlc5/gc_icon_imc","rui/gencard_icons/dlc5/gc_icon_imc_small",0
+"gc_icon_hammond","#PATCH_HAMMOND","rui/gencard_icons/dlc5/gc_icon_hammond","rui/gencard_icons/dlc5/gc_icon_hammond_small",0
+"gc_icon_tri_chevron","#PATCH_TRI_CHEVRON","rui/gencard_icons/dlc5/gc_icon_tri_chevron","rui/gencard_icons/dlc5/gc_icon_tri_chevron_small",0
+"gc_icon_pilot_circle","#PATCH_PILOT_CIRCLE","rui/gencard_icons/dlc5/gc_icon_pilot_circle","rui/gencard_icons/dlc5/gc_icon_pilot_circle_small",0
+"gc_icon_x","#PATCH_X","rui/gencard_icons/dlc5/gc_icon_x","rui/gencard_icons/dlc5/gc_icon_x_small",0
+"gc_icon_nessie","#PATCH_NESSIE","rui/gencard_icons/dlc5/gc_icon_nessie","rui/gencard_icons/dlc5/gc_icon_nessie_small",0
+"gc_icon_spicy","#PATCH_SPICY","rui/gencard_icons/dlc5/gc_icon_spicy","rui/gencard_icons/dlc5/gc_icon_spicy_small",0
+"gc_icon_crown","#PATCH_CROWN","rui/gencard_icons/dlc5/gc_icon_crown","rui/gencard_icons/dlc5/gc_icon_crown_small",0
+"gc_icon_pawn","#PATCH_PAWN","rui/gencard_icons/dlc5/gc_icon_pawn","rui/gencard_icons/dlc5/gc_icon_pawn_small",0
+"gc_icon_excite","#PATCH_EXCITE","rui/gencard_icons/dlc5/gc_icon_excite","rui/gencard_icons/dlc5/gc_icon_excite_small",0
+"gc_icon_duck","#PATCH_DUCK","rui/gencard_icons/dlc5/gc_icon_duck","rui/gencard_icons/dlc5/gc_icon_duck_small",0
+"gc_icon_sock","#PATCH_SOCK","rui/gencard_icons/dlc5/gc_icon_sock","rui/gencard_icons/dlc5/gc_icon_sock_small",0
+"gc_icon_rabbit","#PATCH_RABBIT","rui/gencard_icons/dlc5/gc_icon_rabbit","rui/gencard_icons/dlc5/gc_icon_rabbit_small",0
+"gc_icon_peanut","#PATCH_PEANUT","rui/gencard_icons/dlc5/gc_icon_peanut","rui/gencard_icons/dlc5/gc_icon_peanut_small",0
+"gc_icon_clock","#PATCH_CLOCK","rui/gencard_icons/dlc5/gc_icon_clock","rui/gencard_icons/dlc5/gc_icon_clock_small",0
+"gc_icon_shamrock","#PATCH_SHAMROCK","rui/gencard_icons/dlc5/gc_icon_shamrock","rui/gencard_icons/dlc5/gc_icon_shamrock_small",0
+"gc_icon_trident","#PATCH_TRIDENT","rui/gencard_icons/dlc5/gc_icon_trident","rui/gencard_icons/dlc5/gc_icon_trident_small",0
+"gc_icon_fd_normal","#PATCH_FD_NORMAL","rui/gencard_icons/fd/gc_icon_fd_normal","rui/gencard_icons/fd/gc_icon_fd_normal_small",0
+"gc_icon_fd_hard","#PATCH_FD_HARD","rui/gencard_icons/fd/gc_icon_fd_hard","rui/gencard_icons/fd/gc_icon_fd_hard_small",0
+"gc_icon_fd_master","#PATCH_FD_MASTER","rui/gencard_icons/fd/gc_icon_fd_master","rui/gencard_icons/fd/gc_icon_fd_master_small",0
+"gc_icon_fd_insane","#PATCH_FD_INSANE","rui/gencard_icons/fd/gc_icon_fd_insane","rui/gencard_icons/fd/gc_icon_fd_insane_small",0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/camo_skins.csv b/Northstar.CustomServers/mod/scripts/datatable/camo_skins.csv
new file mode 100644
index 00000000..2773f4b6
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/camo_skins.csv
@@ -0,0 +1,161 @@
+itemRef,pilotRef,titanRef,type,image,name,description,pilotCost,titanCost,pilotWeaponCost,titanWeaponCost,category
+"camo_skin00","pilot_camo_skin00","titan_camo_skin00","CAMO","rui/menu/common/no_art","#CAMO_DEFAULT","#DEFAULT_DESC",0,0,0,0,1
+"camo_skin01","pilot_camo_skin01","titan_camo_skin01","CAMO","models/camo_skins/camo_skin01_col","#CAMO_SKIN_01","#CAMO_SKIN_01_DESC",0,0,0,0,1
+"camo_skin02","pilot_camo_skin02","titan_camo_skin02","CAMO","models/camo_skins/camo_skin02_col","#CAMO_SKIN_02","#CAMO_SKIN_02_DESC",0,0,0,0,1
+"camo_skin03","pilot_camo_skin03","titan_camo_skin03","CAMO","models/camo_skins/camo_skin03_col","#CAMO_SKIN_03","#CAMO_SKIN_03_DESC",0,0,0,0,1
+"camo_skin04","pilot_camo_skin04","titan_camo_skin04","CAMO","models/camo_skins/camo_skin04_col","#CAMO_SKIN_04","#CAMO_SKIN_04_DESC",150,100,75,50,3
+"camo_skin05","pilot_camo_skin05","titan_camo_skin05","CAMO","models/camo_skins/camo_skin05_col","#CAMO_SKIN_05","#CAMO_SKIN_05_DESC",150,100,75,50,3
+"camo_skin06","pilot_camo_skin06","titan_camo_skin06","CAMO","models/camo_skins/camo_skin06_col","#CAMO_SKIN_06","#CAMO_SKIN_06_DESC",150,100,75,50,4
+"camo_skin07","pilot_camo_skin07","titan_camo_skin07","CAMO","models/camo_skins/camo_skin07_col","#CAMO_SKIN_07","#CAMO_SKIN_07_DESC",0,0,0,0,2
+"camo_skin08","pilot_camo_skin08","titan_camo_skin08","CAMO","models/camo_skins/camo_skin08_col","#CAMO_SKIN_08","#CAMO_SKIN_08_DESC",150,100,75,50,4
+"camo_skin09","pilot_camo_skin09","titan_camo_skin09","CAMO","models/camo_skins/camo_skin09_col","#CAMO_SKIN_09","#CAMO_SKIN_09_DESC",0,0,0,0,1
+"camo_skin10","pilot_camo_skin10","titan_camo_skin10","CAMO","models/camo_skins/camo_skin10_col","#CAMO_SKIN_10","#CAMO_SKIN_10_DESC",0,0,0,0,1
+"camo_skin11","pilot_camo_skin11","titan_camo_skin11","CAMO","models/camo_skins/camo_skin11_col","#CAMO_SKIN_11","#CAMO_SKIN_11_DESC",150,100,75,50,4
+"camo_skin12","pilot_camo_skin12","titan_camo_skin12","CAMO","models/camo_skins/camo_skin12_col","#CAMO_SKIN_12","#CAMO_SKIN_12_DESC",150,100,75,50,4
+"camo_skin13","pilot_camo_skin13","titan_camo_skin13","CAMO","models/camo_skins/camo_skin13_col","#CAMO_SKIN_13","#CAMO_SKIN_13_DESC",0,0,0,0,2
+"camo_skin14","pilot_camo_skin14","titan_camo_skin14","CAMO","models/camo_skins/camo_skin14_col","#CAMO_SKIN_14","#CAMO_SKIN_14_DESC",0,0,0,0,1
+"camo_skin15","pilot_camo_skin15","titan_camo_skin15","CAMO","models/camo_skins/camo_skin15_col","#CAMO_SKIN_15","#CAMO_SKIN_15_DESC",0,0,0,0,1
+"camo_skin16","pilot_camo_skin16","titan_camo_skin16","CAMO","models/camo_skins/camo_skin16_col","#CAMO_SKIN_16","#CAMO_SKIN_16_DESC",0,0,0,0,1
+"camo_skin17","pilot_camo_skin17","titan_camo_skin17","CAMO","models/camo_skins/camo_skin17_col","#CAMO_SKIN_17","#CAMO_SKIN_17_DESC",0,0,0,0,1
+"camo_skin18","pilot_camo_skin18","titan_camo_skin18","CAMO","models/camo_skins/camo_skin18_col","#CAMO_SKIN_18","#CAMO_SKIN_18_DESC",0,0,0,0,1
+"camo_skin19","pilot_camo_skin19","titan_camo_skin19","CAMO","models/camo_skins/camo_skin19_col","#CAMO_SKIN_19","#CAMO_SKIN_19_DESC",0,0,0,0,1
+"camo_skin20","pilot_camo_skin20","titan_camo_skin20","CAMO","models/camo_skins/camo_skin20_col","#CAMO_SKIN_20","#CAMO_SKIN_20_DESC",150,100,75,50,3
+"camo_skin21","pilot_camo_skin21","titan_camo_skin21","CAMO","models/camo_skins/camo_skin21_col","#CAMO_SKIN_21","#CAMO_SKIN_21_DESC",150,100,75,50,3
+"camo_skin22","pilot_camo_skin22","titan_camo_skin22","CAMO","models/camo_skins/camo_skin22_col","#CAMO_SKIN_22","#CAMO_SKIN_22_DESC",150,100,75,50,3
+"camo_skin23","pilot_camo_skin23","titan_camo_skin23","CAMO","models/camo_skins/camo_skin23_col","#CAMO_SKIN_23","#CAMO_SKIN_23_DESC",150,100,75,50,3
+"camo_skin24","pilot_camo_skin24","titan_camo_skin24","CAMO","models/camo_skins/camo_skin24_col","#CAMO_SKIN_24","#CAMO_SKIN_24_DESC",0,0,0,0,1
+"camo_skin25","pilot_camo_skin25","titan_camo_skin25","CAMO","models/camo_skins/camo_skin25_col","#CAMO_SKIN_25","#CAMO_SKIN_25_DESC",0,0,0,0,1
+"camo_skin26","pilot_camo_skin26","titan_camo_skin26","CAMO","models/camo_skins/camo_skin26_col","#CAMO_SKIN_26","#CAMO_SKIN_26_DESC",0,0,0,0,1
+"camo_skin27","pilot_camo_skin27","titan_camo_skin27","CAMO","models/camo_skins/camo_skin27_col","#CAMO_SKIN_27","#CAMO_SKIN_27_DESC",0,0,0,0,1
+"camo_skin28","pilot_camo_skin28","titan_camo_skin28","CAMO","models/camo_skins/camo_skin28_col","#CAMO_SKIN_28","#CAMO_SKIN_28_DESC",150,100,75,50,4
+"camo_skin29","pilot_camo_skin29","titan_camo_skin29","CAMO","models/camo_skins/camo_skin29_col","#CAMO_SKIN_29","#CAMO_SKIN_29_DESC",150,100,75,50,4
+"camo_skin30","pilot_camo_skin30","titan_camo_skin30","CAMO","models/camo_skins/camo_skin30_col","#CAMO_SKIN_30","#CAMO_SKIN_30_DESC",0,0,0,0,1
+"camo_skin31","pilot_camo_skin31","titan_camo_skin31","CAMO","models/camo_skins/camo_skin31_col","#CAMO_SKIN_31","#CAMO_SKIN_31_DESC",0,0,0,0,1
+"camo_skin32","pilot_camo_skin32","titan_camo_skin32","CAMO","models/camo_skins/camo_skin32_col","#CAMO_SKIN_32","#CAMO_SKIN_32_DESC",150,100,75,50,4
+"camo_skin33","pilot_camo_skin33","titan_camo_skin33","CAMO","models/camo_skins/camo_skin33_col","#CAMO_SKIN_33","#CAMO_SKIN_33_DESC",150,100,75,50,4
+"camo_skin34","pilot_camo_skin34","titan_camo_skin34","CAMO","models/camo_skins/camo_skin34_col","#CAMO_SKIN_34","#CAMO_SKIN_34_DESC",150,100,75,50,4
+"camo_skin35","pilot_camo_skin35","titan_camo_skin35","CAMO","models/camo_skins/camo_skin35_col","#CAMO_SKIN_35","#CAMO_SKIN_35_DESC",150,100,75,50,3
+"camo_skin36","pilot_camo_skin36","titan_camo_skin36","CAMO","models/camo_skins/camo_skin36_col","#CAMO_SKIN_36","#CAMO_SKIN_36_DESC",150,100,75,50,3
+"camo_skin37","pilot_camo_skin37","titan_camo_skin37","CAMO","models/camo_skins/camo_skin37_col","#CAMO_SKIN_37","#CAMO_SKIN_37_DESC",150,100,75,50,4
+"camo_skin38","pilot_camo_skin38","titan_camo_skin38","CAMO","models/camo_skins/camo_skin38_col","#CAMO_SKIN_38","#CAMO_SKIN_38_DESC",150,100,75,50,4
+"camo_skin39","pilot_camo_skin39","titan_camo_skin39","CAMO","models/camo_skins/camo_skin39_col","#CAMO_SKIN_39","#CAMO_SKIN_39_DESC",150,100,75,50,4
+"camo_skin40","pilot_camo_skin40","titan_camo_skin40","CAMO","models/camo_skins/camo_skin40_col","#CAMO_SKIN_40","#CAMO_SKIN_40_DESC",150,100,75,50,3
+"camo_skin41","pilot_camo_skin41","titan_camo_skin41","CAMO","models/camo_skins/camo_skin41_col","#CAMO_SKIN_41","#CAMO_SKIN_41_DESC",150,100,75,50,3
+"camo_skin42","pilot_camo_skin42","titan_camo_skin42","CAMO","models/camo_skins/camo_skin42_col","#CAMO_SKIN_42","#CAMO_SKIN_42_DESC",150,100,75,50,3
+"camo_skin43","pilot_camo_skin43","titan_camo_skin43","CAMO","models/camo_skins/camo_skin43_col","#CAMO_SKIN_43","#CAMO_SKIN_43_DESC",150,100,75,50,3
+"camo_skin44","pilot_camo_skin44","titan_camo_skin44","CAMO","models/camo_skins/camo_skin44_col","#CAMO_SKIN_44","#CAMO_SKIN_44_DESC",150,100,75,50,3
+"camo_skin45","pilot_camo_skin45","titan_camo_skin45","CAMO","models/camo_skins/camo_skin45_col","#CAMO_SKIN_45","#CAMO_SKIN_45_DESC",150,100,75,50,3
+"camo_skin46","pilot_camo_skin46","titan_camo_skin46","CAMO","models/camo_skins/camo_skin46_col","#CAMO_SKIN_46","#CAMO_SKIN_46_DESC",150,100,75,50,3
+"camo_skin47","pilot_camo_skin47","titan_camo_skin47","CAMO","models/camo_skins/camo_skin47_col","#CAMO_SKIN_47","#CAMO_SKIN_47_DESC",150,100,75,50,4
+"camo_skin48","pilot_camo_skin48","titan_camo_skin48","CAMO","models/camo_skins/camo_skin48_col","#CAMO_SKIN_48","#CAMO_SKIN_48_DESC",150,100,75,50,4
+"camo_skin49","pilot_camo_skin49","titan_camo_skin49","CAMO","models/camo_skins/camo_skin49_col","#CAMO_SKIN_49","#CAMO_SKIN_49_DESC",150,100,75,50,4
+"camo_skin50","pilot_camo_skin50","titan_camo_skin50","CAMO","models/camo_skins/camo_skin50_col","#CAMO_SKIN_50","#CAMO_SKIN_50_DESC",150,100,75,50,4
+"camo_skin51","pilot_camo_skin51","titan_camo_skin51","CAMO","models/camo_skins/camo_skin51_col","#CAMO_SKIN_51","#CAMO_SKIN_51_DESC",150,100,75,50,4
+"camo_skin52","pilot_camo_skin52","titan_camo_skin52","CAMO","models/camo_skins/camo_skin52_col","#CAMO_SKIN_52","#CAMO_SKIN_52_DESC",150,100,75,50,4
+"camo_skin53","pilot_camo_skin53","titan_camo_skin53","CAMO","models/camo_skins/camo_skin53_col","#CAMO_SKIN_53","#CAMO_SKIN_53_DESC",150,100,75,50,4
+"camo_skin54","pilot_camo_skin54","titan_camo_skin54","CAMO","models/camo_skins/camo_skin54_col","#CAMO_SKIN_54","#CAMO_SKIN_54_DESC",150,100,75,50,4
+"camo_skin55","pilot_camo_skin55","titan_camo_skin55","CAMO","models/camo_skins/camo_skin55_col","#CAMO_SKIN_55","#CAMO_SKIN_55_DESC",0,0,0,0,2
+"camo_skin56","pilot_camo_skin56","titan_camo_skin56","CAMO","models/camo_skins/camo_skin56_col","#CAMO_SKIN_56","#CAMO_SKIN_56_DESC",0,0,0,0,2
+"camo_skin57","pilot_camo_skin57","titan_camo_skin57","CAMO","models/camo_skins/camo_skin57_col","#CAMO_SKIN_57","#CAMO_SKIN_57_DESC",0,0,0,0,2
+"camo_skin58","pilot_camo_skin58","titan_camo_skin58","CAMO","models/camo_skins/camo_skin58_col","#CAMO_SKIN_58","#CAMO_SKIN_58_DESC",0,0,0,0,2
+"camo_skin59","pilot_camo_skin59","titan_camo_skin59","CAMO","models/camo_skins/camo_skin59_col","#CAMO_SKIN_59","#CAMO_SKIN_59_DESC",0,0,0,0,2
+"camo_skin60","pilot_camo_skin60","titan_camo_skin60","CAMO","models/camo_skins/camo_skin60_col","#CAMO_SKIN_60","#CAMO_SKIN_60_DESC",0,0,0,0,2
+"camo_skin61","pilot_camo_skin61","titan_camo_skin61","CAMO","models/camo_skins/camo_skin61_col","#CAMO_SKIN_61","#CAMO_SKIN_61_DESC",0,0,0,0,2
+"camo_skin62","pilot_camo_skin62","titan_camo_skin62","CAMO","models/camo_skins/camo_skin62_col","#CAMO_SKIN_62","#CAMO_SKIN_62_DESC",0,0,0,0,2
+"camo_skin63","pilot_camo_skin63","titan_camo_skin63","CAMO","models/camo_skins/camo_skin63_col","#CAMO_SKIN_63","#CAMO_SKIN_63_DESC",0,0,0,0,2
+"camo_skin64","pilot_camo_skin64","titan_camo_skin64","CAMO","models/camo_skins/camo_skin64_col","#CAMO_SKIN_64","#CAMO_SKIN_64_DESC",0,0,0,0,2
+"camo_skin65","pilot_camo_skin65","titan_camo_skin65","CAMO","models/camo_skins/camo_skin65_col","#CAMO_SKIN_65","#CAMO_SKIN_65_DESC",0,0,0,0,2
+"camo_skin66","pilot_camo_skin66","titan_camo_skin66","CAMO","models/camo_skins/camo_skin66_col","#CAMO_SKIN_66","#CAMO_SKIN_66_DESC",0,0,0,0,2
+"camo_skin67","pilot_camo_skin67","titan_camo_skin67","CAMO","models/camo_skins/camo_skin67_col","#CAMO_SKIN_67","#CAMO_SKIN_67_DESC",0,0,0,0,2
+"camo_skin68","pilot_camo_skin68","titan_camo_skin68","CAMO","models/camo_skins/camo_skin68_col","#CAMO_SKIN_68","#CAMO_SKIN_68_DESC",150,100,75,50,4
+"camo_skin69","pilot_camo_skin69","titan_camo_skin69","CAMO","models/camo_skins/camo_skin69_col","#CAMO_SKIN_69","#CAMO_SKIN_69_DESC",150,100,75,50,4
+"camo_skin70","pilot_camo_skin70","titan_camo_skin70","CAMO","models/camo_skins/camo_skin70_col","#CAMO_SKIN_70","#CAMO_SKIN_70_DESC",150,100,75,50,4
+"camo_skin71","pilot_camo_skin71","titan_camo_skin71","CAMO","models/camo_skins/camo_skin71_col","#CAMO_SKIN_71","#CAMO_SKIN_71_DESC",150,100,75,50,4
+"camo_skin72","pilot_camo_skin72","titan_camo_skin72","CAMO","models/camo_skins/camo_skin72_col","#CAMO_SKIN_72","#CAMO_SKIN_72_DESC",150,100,75,50,4
+"camo_skin73","pilot_camo_skin73","titan_camo_skin73","CAMO","models/camo_skins/camo_skin73_col","#CAMO_SKIN_73","#CAMO_SKIN_73_DESC",150,100,75,50,4
+"camo_skin74","pilot_camo_skin74","titan_camo_skin74","CAMO","models/camo_skins/camo_skin74_col","#CAMO_SKIN_74","#CAMO_SKIN_74_DESC",150,100,75,50,4
+"camo_skin75","pilot_camo_skin75","titan_camo_skin75","CAMO","models/camo_skins/camo_skin75_col","#CAMO_SKIN_75","#CAMO_SKIN_75_DESC",150,100,75,50,4
+"camo_skin76","pilot_camo_skin76","titan_camo_skin76","CAMO","models/camo_skins/camo_skin76_col","#CAMO_SKIN_76","#CAMO_SKIN_76_DESC",150,100,75,50,3
+"camo_skin77","pilot_camo_skin77","titan_camo_skin77","CAMO","models/camo_skins/camo_skin77_col","#CAMO_SKIN_77","#CAMO_SKIN_77_DESC",150,100,75,50,3
+"camo_skin78","pilot_camo_skin78","titan_camo_skin78","CAMO","models/camo_skins/camo_skin78_col","#CAMO_SKIN_78","#CAMO_SKIN_78_DESC",150,100,75,50,3
+"camo_skin79","pilot_camo_skin79","titan_camo_skin79","CAMO","models/camo_skins/camo_skin79_col","#CAMO_SKIN_79","#CAMO_SKIN_79_DESC",150,100,75,50,3
+"camo_skin80","pilot_camo_skin80","titan_camo_skin80","CAMO","models/camo_skins/camo_skin80_col","#CAMO_SKIN_80","#CAMO_SKIN_80_DESC",150,100,75,50,3
+"camo_skin81","pilot_camo_skin81","titan_camo_skin81","CAMO","models/camo_skins/camo_skin81_col","#CAMO_SKIN_81","#CAMO_SKIN_81_DESC",0,0,0,0,1
+"camo_skin82","pilot_camo_skin82","titan_camo_skin82","CAMO","models/camo_skins/camo_skin82_col","#CAMO_SKIN_82","#CAMO_SKIN_82_DESC",0,0,0,0,1
+"camo_skin83","pilot_camo_skin83","titan_camo_skin83","CAMO","models/camo_skins/camo_skin83_col","#CAMO_SKIN_83","#CAMO_SKIN_83_DESC",0,0,0,0,1
+"camo_skin84","pilot_camo_skin84","titan_camo_skin84","CAMO","models/camo_skins/camo_skin84_col","#CAMO_SKIN_84","#CAMO_SKIN_84_DESC",150,100,75,50,4
+"camo_skin85","pilot_camo_skin85","titan_camo_skin85","CAMO","models/camo_skins/camo_skin85_col","#CAMO_SKIN_85","#CAMO_SKIN_85_DESC",0,0,0,0,2
+"camo_skin86","pilot_camo_skin86","titan_camo_skin86","CAMO","models/camo_skins/camo_skin86_col","#CAMO_SKIN_86","#CAMO_SKIN_86_DESC",150,100,75,50,4
+"camo_skin87","pilot_camo_skin87","titan_camo_skin87","CAMO","models/camo_skins/camo_skin87_col","#CAMO_SKIN_87","#CAMO_SKIN_87_DESC",0,0,0,0,2
+"camo_skin88","pilot_camo_skin88","titan_camo_skin88","CAMO","models/camo_skins/camo_skin88_col","#CAMO_SKIN_88","#CAMO_SKIN_88_DESC",150,100,75,50,4
+"camo_skin89","pilot_camo_skin89","titan_camo_skin89","CAMO","models/camo_skins/camo_skin89_col","#CAMO_SKIN_89","#CAMO_SKIN_89_DESC",150,100,75,50,4
+"camo_skin90","pilot_camo_skin90","titan_camo_skin90","CAMO","models/camo_skins/camo_skin90_col","#CAMO_SKIN_90","#CAMO_SKIN_90_DESC",150,100,75,50,4
+"camo_skin91","pilot_camo_skin91","titan_camo_skin91","CAMO","models/camo_skins/camo_skin91_col","#CAMO_SKIN_91","#CAMO_SKIN_91_DESC",0,0,0,0,2
+"camo_skin92","pilot_camo_skin92","titan_camo_skin92","CAMO","models/camo_skins/camo_skin92_col","#CAMO_SKIN_92","#CAMO_SKIN_92_DESC",0,0,0,0,1
+"camo_skin93","pilot_camo_skin93","titan_camo_skin93","CAMO","models/camo_skins/camo_skin93_col","#CAMO_SKIN_93","#CAMO_SKIN_93_DESC",0,0,0,0,2
+"camo_skin94","pilot_camo_skin94","titan_camo_skin94","CAMO","models/camo_skins/camo_skin94_col","#CAMO_SKIN_94","#CAMO_SKIN_94_DESC",0,0,0,0,2
+"camo_skin95","pilot_camo_skin95","titan_camo_skin95","CAMO","models/camo_skins/camo_skin95_col","#CAMO_SKIN_95","#CAMO_SKIN_95_DESC",150,100,75,50,4
+"camo_skin96","pilot_camo_skin96","titan_camo_skin96","CAMO","models/camo_skins/camo_skin96_col","#CAMO_SKIN_96","#CAMO_SKIN_96_DESC",150,100,75,50,4
+"camo_skin97","pilot_camo_skin97","titan_camo_skin97","CAMO","models/camo_skins/camo_skin97_col","#CAMO_SKIN_97","#CAMO_SKIN_97_DESC",0,0,0,0,1
+"camo_skin98","pilot_camo_skin98","titan_camo_skin98","CAMO","models/camo_skins/camo_skin98_col","#CAMO_SKIN_98","#CAMO_SKIN_98_DESC",150,100,75,50,4
+"camo_skin99","pilot_camo_skin99","titan_camo_skin99","CAMO","models/camo_skins/camo_skin99_col","#CAMO_SKIN_99","#CAMO_SKIN_99_DESC",150,100,75,50,4
+"camo_skin101","pilot_camo_skin101","titan_camo_skin101","CAMO","models/camo_skins/camo_skin101_col","#CAMO_SKIN_101","#CAMO_SKIN_101_DESC",0,0,0,0,5
+"camo_skin102","pilot_camo_skin102","titan_camo_skin102","CAMO","models/camo_skins/camo_skin102_col","#CAMO_SKIN_102","#CAMO_SKIN_102_DESC",0,0,0,0,5
+"camo_skin103","pilot_camo_skin103","titan_camo_skin103","CAMO","models/camo_skins/camo_skin103_col","#CAMO_SKIN_103","#CAMO_SKIN_103_DESC",0,0,0,0,5
+"camo_skin104","pilot_camo_skin104","titan_camo_skin104","CAMO","models/camo_skins/camo_skin104_col","#CAMO_SKIN_104","#CAMO_SKIN_104_DESC",0,0,0,0,5
+"camo_skin105","pilot_camo_skin105","titan_camo_skin105","CAMO","models/camo_skins/camo_skin105_col","#CAMO_SKIN_105","#CAMO_SKIN_105_DESC",0,0,0,0,5
+"camo_skin106","pilot_camo_skin106","titan_camo_skin106","CAMO","models/camo_skins/camo_skin106_col","#CAMO_SKIN_106","#CAMO_SKIN_106_DESC",0,0,0,0,5
+"camo_skin107","pilot_camo_skin107","titan_camo_skin107","CAMO","models/camo_skins/camo_skin107_col","#CAMO_SKIN_107","#CAMO_SKIN_107_DESC",0,0,0,0,5
+"camo_skin108","pilot_camo_skin108","titan_camo_skin108","CAMO","models/camo_skins/camo_skin108_col","#CAMO_SKIN_108","#CAMO_SKIN_108_DESC",0,0,0,0,5
+"camo_skin109","pilot_camo_skin109","titan_camo_skin109","CAMO","models/camo_skins/camo_skin109_col","#CAMO_SKIN_109","#CAMO_SKIN_109_DESC",0,0,0,0,5
+"camo_skin110","pilot_camo_skin110","titan_camo_skin110","CAMO","models/camo_skins/camo_skin110_col","#CAMO_SKIN_110","#CAMO_SKIN_110_DESC",0,0,0,0,5
+"camo_skin111","pilot_camo_skin111","titan_camo_skin111","CAMO","models/camo_skins/camo_skin111_col","#CAMO_SKIN_111","#CAMO_SKIN_111_DESC",0,0,0,0,5
+"camo_skin112","pilot_camo_skin112","titan_camo_skin112","CAMO","models/camo_skins/camo_skin112_col","#CAMO_SKIN_112","#CAMO_SKIN_112_DESC",0,0,0,0,5
+"camo_skin113","pilot_camo_skin113","titan_camo_skin113","CAMO","models/camo_skins/camo_skin113_col","#CAMO_SKIN_113","#CAMO_SKIN_113_DESC",0,0,0,0,5
+"camo_skin114","pilot_camo_skin114","titan_camo_skin114","CAMO","models/camo_skins/camo_skin114_col","#CAMO_SKIN_114","#CAMO_SKIN_114_DESC",0,0,0,0,5
+"camo_skin115","pilot_camo_skin115","titan_camo_skin115","CAMO","models/camo_skins/camo_skin115_col","#CAMO_SKIN_115","#CAMO_SKIN_115_DESC",0,0,0,0,5
+"camo_skin116","pilot_camo_skin116","titan_camo_skin116","CAMO","models/camo_skins/camo_skin116_col","#CAMO_SKIN_116","#CAMO_SKIN_116_DESC",0,0,0,0,5
+"camo_skin117","pilot_camo_skin117","titan_camo_skin117","CAMO","models/camo_skins/camo_skin117_col","#CAMO_SKIN_117","#CAMO_SKIN_117_DESC",0,0,0,0,5
+"camo_skin118","pilot_camo_skin118","titan_camo_skin118","CAMO","models/camo_skins/camo_skin118_col","#CAMO_SKIN_118","#CAMO_SKIN_118_DESC",0,0,0,0,5
+"camo_skin119","pilot_camo_skin119","titan_camo_skin119","CAMO","models/camo_skins/camo_skin119_col","#CAMO_SKIN_119","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin120","pilot_camo_skin120","titan_camo_skin120","CAMO","models/camo_skins/camo_skin120_col","#CAMO_SKIN_120","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin121","pilot_camo_skin121","titan_camo_skin121","CAMO","models/camo_skins/camo_skin121_col","#CAMO_SKIN_121","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin122","pilot_camo_skin122","titan_camo_skin122","CAMO","models/camo_skins/camo_skin122_col","#CAMO_SKIN_122","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin123","pilot_camo_skin123","titan_camo_skin123","CAMO","models/camo_skins/camo_skin123_col","#CAMO_SKIN_123","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin124","pilot_camo_skin124","titan_camo_skin124","CAMO","models/camo_skins/camo_skin124_col","#CAMO_SKIN_124","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin125","pilot_camo_skin125","titan_camo_skin125","CAMO","models/camo_skins/camo_skin125_col","#CAMO_SKIN_125","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin126","pilot_camo_skin126","titan_camo_skin126","CAMO","models/camo_skins/camo_skin126_col","#CAMO_SKIN_126","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin127","pilot_camo_skin127","titan_camo_skin127","CAMO","models/camo_skins/camo_skin127_col","#CAMO_SKIN_127","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin128","pilot_camo_skin128","titan_camo_skin128","CAMO","models/camo_skins/camo_skin128_col","#CAMO_SKIN_128","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin129","pilot_camo_skin129","titan_camo_skin129","CAMO","models/camo_skins/camo_skin129_col","#CAMO_SKIN_129","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin130","pilot_camo_skin130","titan_camo_skin130","CAMO","models/camo_skins/camo_skin130_col","#CAMO_SKIN_130","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin131","pilot_camo_skin131","titan_camo_skin131","CAMO","models/camo_skins/camo_skin131_col","#CAMO_SKIN_131","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin132","pilot_camo_skin132","titan_camo_skin132","CAMO","models/camo_skins/camo_skin132_col","#CAMO_SKIN_132","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin133","pilot_camo_skin133","titan_camo_skin133","CAMO","models/camo_skins/camo_skin133_col","#CAMO_SKIN_133","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin134","pilot_camo_skin134","titan_camo_skin134","CAMO","models/camo_skins/camo_skin134_col","#CAMO_SKIN_134","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin135","pilot_camo_skin135","titan_camo_skin135","CAMO","models/camo_skins/camo_skin135_col","#CAMO_SKIN_135","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin136","pilot_camo_skin136","titan_camo_skin136","CAMO","models/camo_skins/camo_skin136_col","#CAMO_SKIN_136","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin137","pilot_camo_skin137","titan_camo_skin137","CAMO","models/camo_skins/camo_skin137_col","#CAMO_SKIN_137","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin138","pilot_camo_skin138","titan_camo_skin138","CAMO","models/camo_skins/camo_skin138_col","#CAMO_SKIN_138","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin139","pilot_camo_skin139","titan_camo_skin139","CAMO","models/camo_skins/camo_skin139_col","#CAMO_SKIN_139","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin140","pilot_camo_skin140","titan_camo_skin140","CAMO","models/camo_skins/camo_skin140_col","#CAMO_SKIN_140","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin141","pilot_camo_skin141","titan_camo_skin141","CAMO","models/camo_skins/camo_skin141_col","#CAMO_SKIN_141","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin142","pilot_camo_skin142","titan_camo_skin142","CAMO","models/camo_skins/camo_skin142_col","#CAMO_SKIN_142","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin143","pilot_camo_skin143","titan_camo_skin143","CAMO","models/camo_skins/camo_skin143_col","#CAMO_SKIN_143","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin144","pilot_camo_skin144","titan_camo_skin144","CAMO","models/camo_skins/camo_skin144_col","#CAMO_SKIN_144","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin145","pilot_camo_skin145","titan_camo_skin145","CAMO","models/camo_skins/camo_skin145_col","#CAMO_SKIN_145","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin146","pilot_camo_skin146","titan_camo_skin146","CAMO","models/camo_skins/camo_skin146_col","#CAMO_SKIN_146","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin147","pilot_camo_skin147","titan_camo_skin147","CAMO","models/camo_skins/camo_skin147_col","#CAMO_SKIN_147","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin148","pilot_camo_skin148","titan_camo_skin148","CAMO","models/camo_skins/camo_skin148_col","#CAMO_SKIN_148","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin149","pilot_camo_skin149","titan_camo_skin149","CAMO","models/camo_skins/camo_skin149_col","#CAMO_SKIN_149","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin150","pilot_camo_skin150","titan_camo_skin150","CAMO","models/camo_skins/camo_skin150_col","#CAMO_SKIN_150","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin151","pilot_camo_skin151","titan_camo_skin151","CAMO","models/camo_skins/camo_skin151_col","#CAMO_SKIN_151","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin152","pilot_camo_skin152","titan_camo_skin152","CAMO","models/camo_skins/camo_skin152_col","#CAMO_SKIN_152","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin153","pilot_camo_skin153","titan_camo_skin153","CAMO","models/camo_skins/camo_skin153_col","#CAMO_SKIN_153","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin154","pilot_camo_skin154","titan_camo_skin154","CAMO","models/camo_skins/camo_skin154_col","#CAMO_SKIN_154","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin155","pilot_camo_skin155","titan_camo_skin155","CAMO","models/camo_skins/camo_skin155_col","#CAMO_SKIN_155","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin156","pilot_camo_skin156","titan_camo_skin156","CAMO","models/camo_skins/camo_skin156_col","#CAMO_SKIN_156","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin157","pilot_camo_skin157","titan_camo_skin157","CAMO","models/camo_skins/camo_skin157_col","#CAMO_SKIN_157","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin158","pilot_camo_skin158","titan_camo_skin158","CAMO","models/camo_skins/camo_skin158_col","#CAMO_SKIN_158","#CAMO_SKIN_120_DESC",0,0,0,0,5
+"camo_skin159","pilot_camo_skin159","titan_camo_skin159","CAMO","models/camo_skins/camo_skin159_col","#CAMO_SKIN_159","#CAMO_SKIN_119_DESC",0,0,0,0,5
+"camo_skin160","pilot_camo_skin160","titan_camo_skin160","CAMO","models/camo_skins/camo_skin160_col","#CAMO_SKIN_160","#CAMO_SKIN_120_DESC",0,0,0,0,5 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/community_entries.csv b/Northstar.CustomServers/mod/scripts/datatable/community_entries.csv
new file mode 100644
index 00000000..0616f131
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/community_entries.csv
@@ -0,0 +1,22 @@
+category,key,locString
+"categories","gaming","#COMMUNITY_CATEGORY_GAMING"
+"categories","lifestyle","#COMMUNITY_CATEGORY_LIFESTYLE"
+"categories","geography","#COMMUNITY_CATEGORY_GEOGRAPHY"
+"categories","tech","#COMMUNITY_CATEGORY_TECH"
+"categories","other","#COMMUNITY_CATEGORY_OTHER"
+"regions","North America","#COMMUNITY_REGION_NORTHAMERICA"
+"regions","Europe","#COMMUNITY_REGION_EUROPE"
+"regions","South America","#COMMUNITY_REGION_SOUTHAMERICA"
+"regions","Asia","#COMMUNITY_REGION_ASIA"
+"regions","Oceania","#COMMUNITY_REGION_AUSTRALIA"
+"languages","English","#COMMUNITY_LANGUAGE_ENGLISH"
+"languages","French","#COMMUNITY_LANGUAGE_FRENCH"
+"languages","Italian","#COMMUNITY_LANGUAGE_ITALIAN"
+"languages","German","#COMMUNITY_LANGUAGE_GERMAN"
+"languages","Spanish","#COMMUNITY_LANGUAGE_SPANISH"
+"languages","MSpanish","#COMMUNITY_LANGUAGE_MSPANISH"
+"languages","Japanese","#COMMUNITY_LANGUAGE_JAPANESE"
+"languages","TChinese","#COMMUNITY_LANGUAGE_TCHINESE"
+"languages","Russian","#COMMUNITY_LANGUAGE_RUSSIAN"
+"languages","Portuguese","#COMMUNITY_LANGUAGE_PORTUGUESE"
+"languages","Polish","#COMMUNITY_LANGUAGE_POLISH" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/death_hints_mp.csv b/Northstar.CustomServers/mod/scripts/datatable/death_hints_mp.csv
new file mode 100644
index 00000000..4388ab0f
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/death_hints_mp.csv
@@ -0,0 +1,381 @@
+source,className,locString,mapName,gameMode
+"titan_class","ion","#DEATH_HINT_ION_001","",""
+"titan_class","ion","#DEATH_HINT_ION_002","",""
+"titan_class","ion","#DEATH_HINT_ION_003","",""
+"titan_class","ion","#DEATH_HINT_ION_004","",""
+"titan_class","ion","#DEATH_HINT_ION_005","",""
+"titan_class","ion","#DEATH_HINT_ION_006","",""
+"titan_class","legion","#DEATH_HINT_LEGION_001","",""
+"titan_class","legion","#DEATH_HINT_LEGION_002","",""
+"titan_class","legion","#DEATH_HINT_LEGION_003","",""
+"titan_class","legion","#DEATH_HINT_LEGION_004","",""
+"titan_class","legion","#DEATH_HINT_LEGION_005","",""
+"titan_class","legion","#DEATH_HINT_LEGION_006","",""
+"titan_class","northstar","#DEATH_HINT_NORTHSTAR_001","",""
+"titan_class","northstar","#DEATH_HINT_NORTHSTAR_002","",""
+"titan_class","northstar","#DEATH_HINT_NORTHSTAR_003","",""
+"titan_class","northstar","#DEATH_HINT_NORTHSTAR_004","",""
+"titan_class","northstar","#DEATH_HINT_NORTHSTAR_005","",""
+"titan_class","northstar","#DEATH_HINT_NORTHSTAR_006","",""
+"titan_class","ronin","#DEATH_HINT_RONIN_001","",""
+"titan_class","ronin","#DEATH_HINT_RONIN_002","",""
+"titan_class","ronin","#DEATH_HINT_RONIN_003","",""
+"titan_class","ronin","#DEATH_HINT_RONIN_004","",""
+"titan_class","ronin","#DEATH_HINT_RONIN_005","",""
+"titan_class","ronin","#DEATH_HINT_RONIN_006","",""
+"titan_class","ronin","#DEATH_HINT_RONIN_007","",""
+"titan_class","ronin","#DEATH_HINT_RONIN_008","",""
+"titan_class","ronin","#DEATH_HINT_RONIN_009","",""
+"titan_class","scorch","#DEATH_HINT_SCORCH_001","",""
+"titan_class","scorch","#DEATH_HINT_SCORCH_002","",""
+"titan_class","scorch","#DEATH_HINT_SCORCH_003","",""
+"titan_class","scorch","#DEATH_HINT_SCORCH_004","",""
+"titan_class","scorch","#DEATH_HINT_SCORCH_005","",""
+"titan_class","scorch","#DEATH_HINT_SCORCH_006","",""
+"titan_class","tone","#DEATH_HINT_TONE_001","",""
+"titan_class","tone","#DEATH_HINT_TONE_002","",""
+"titan_class","tone","#DEATH_HINT_TONE_003","",""
+"titan_class","tone","#DEATH_HINT_TONE_004","",""
+"titan_class","tone","#DEATH_HINT_TONE_005","",""
+"titan_class","tone","#DEATH_HINT_TONE_006","",""
+"weapon","mp_ability_cloak","#DEATH_HINT_CLOAK_001","",""
+"weapon","mp_ability_cloak","#DEATH_HINT_CLOAK_002","",""
+"weapon","mp_ability_cloak","#DEATH_HINT_CLOAK_003","",""
+"weapon","mp_ability_grapple","#DEATH_HINT_GRAPPLE_001","",""
+"weapon","mp_ability_heal","#DEATH_HINT_STIM_001","",""
+"weapon","mp_ability_heal","#DEATH_HINT_STIM_002","",""
+"weapon","mp_ability_heal","#DEATH_HINT_STIM_003","",""
+"weapon","mp_ability_holopilot","#DEATH_HINT_HOLOPILOT_001","",""
+"weapon","mp_ability_holopilot","#DEATH_HINT_HOLOPILOT_002","",""
+"weapon","mp_ability_holopilot","#DEATH_HINT_HOLOPILOT_003","",""
+"weapon","mp_ability_holopilot","#DEATH_HINT_HOLOPILOT_004","",""
+"weapon","mp_ability_holopilot","#DEATH_HINT_HOLOPILOT_005","",""
+"weapon","mp_ability_holopilot","#DEATH_HINT_HOLOPILOT_006","",""
+"weapon","mp_ability_phase_rewind","#DEATH_HINT_PHASE_REWIND_001","",""
+"weapon","mp_ability_shifter","#DEATH_HINT_PHASESHIFT_001","",""
+"weapon","mp_ability_shifter","#DEATH_HINT_PHASESHIFT_002","",""
+"weapon","mp_ability_shifter","#DEATH_HINT_PHASESHIFT_003","",""
+"weapon","mp_ability_shifter","#DEATH_HINT_PHASESHIFT_004","",""
+"weapon","mp_ability_shifter","#DEATH_HINT_PHASESHIFT_005","",""
+"weapon","mp_ability_shifter","#DEATH_HINT_PHASESHIFT_006","",""
+"weapon","mp_ability_shifter","#DEATH_HINT_PHASESHIFT_007","",""
+"weapon","mp_ability_shifter","#DEATH_HINT_PHASESHIFT_008","",""
+"weapon","mp_ability_sonar","#DEATH_HINT_SONAR_001","",""
+"weapon","mp_titanability_ammo_swap","#DEATH_HINT_AMMO_SWAP_001_MP","",""
+"weapon","mp_titanability_basic_block","#DEATH_HINT_SWORD_BLOCK_001_MP","",""
+"weapon","mp_titanability_gun_shield","#DEATH_HINT_GUN_SHIELD_001_MP","",""
+"weapon","mp_titanability_hover","#DEATH_HINT_TITAN_HOVER_001","",""
+"weapon","mp_titanability_laser_trip","#DEATH_HINT_LASER_TRIPWIRE_001","",""
+"weapon","mp_titanability_particle_wall","#DEATH_HINT_PARTICLE_WALL_001_MP","",""
+"weapon","mp_titanability_particle_wall","#DEATH_HINT_PARTICLE_WALL_002_MP","",""
+"weapon","mp_titanability_particle_wall","#DEATH_HINT_PARTICLE_WALL_003_MP","",""
+"weapon","mp_titanability_phase_dash","#DEATH_HINT_PHASE_DASH_001_MP","",""
+"weapon","mp_titanability_phase_dash","#DEATH_HINT_PHASE_DASH_002_MP","",""
+"weapon","mp_titanability_power_shot","#DEATH_HINT_POWER_SHOT_001_MP","",""
+"weapon","mp_titanability_power_shot","#DEATH_HINT_POWER_SHOT_002_MP","",""
+"weapon","mp_titanability_slow_trap","#DEATH_HINT_INCENDIARY_TRAP_001","",""
+"weapon","mp_titanability_tether_trap","#DEATH_HINT_TETHER_TRAP_001_MP","",""
+"weapon","mp_titancore_flame_wave","#DEATH_HINT_FLAME_CORE_001_MP","",""
+"weapon","mp_titancore_flight_core","#DEATH_HINT_FLIGHT_CORE_001_MP","",""
+"weapon","mp_titancore_laser_cannon","#DEATH_HINT_LASER_CANNON_001_MP","",""
+"weapon","mp_titancore_salvo_core","#DEATH_HINT_SALVO_CORE_001_MP","",""
+"weapon","mp_titancore_shift_core","#DEATH_HINT_SHIFT_CORE_001","",""
+"weapon","mp_titancore_siege_mode","#DEATH_HINT_SMART_CORE_001_MP","",""
+"weapon","mp_titanweapon_arc_wave","#DEATH_HINT_ARC_WAVE_001","",""
+"weapon","mp_titanweapon_flame_wall","#DEATH_HINT_FIREWALL_001_MP","",""
+"weapon","mp_titanweapon_heat_shield","#DEATH_HINT_HEAT_SHIELD_001_MP","",""
+"weapon","mp_titanweapon_laser_lite","#DEATH_HINT_LASER_SHOT_001_MP","",""
+"weapon","mp_titanweapon_leadwall","#DEATH_HINT_LEADWALL_001","",""
+"weapon","mp_titanweapon_leadwall","#DEATH_HINT_LEADWALL_002","",""
+"weapon","mp_titanweapon_leadwall","#DEATH_HINT_LEADWALL_003","",""
+"weapon","mp_titanweapon_leadwall","#DEATH_HINT_LEADWALL_004","",""
+"weapon","mp_titanweapon_meteor","#DEATH_HINT_THERMITE_LAUNCHER_001","",""
+"weapon","mp_titanweapon_meteor","#DEATH_HINT_THERMITE_LAUNCHER_002","",""
+"weapon","mp_titanweapon_meteor","#DEATH_HINT_THERMITE_LAUNCHER_003","",""
+"weapon","mp_titanweapon_meteor","#DEATH_HINT_THERMITE_LAUNCHER_004","",""
+"weapon","mp_titanweapon_meteor","#DEATH_HINT_THERMITE_LAUNCHER_005","",""
+"weapon","mp_titanweapon_particle_accelerator","#DEATH_HINT_SPLITTER_RIFLE_001","",""
+"weapon","mp_titanweapon_particle_accelerator","#DEATH_HINT_SPLITTER_RIFLE_002","",""
+"weapon","mp_titanweapon_particle_accelerator","#DEATH_HINT_SPLITTER_RIFLE_003","",""
+"weapon","mp_titanweapon_particle_accelerator","#DEATH_HINT_SPLITTER_RIFLE_004","",""
+"weapon","mp_titanweapon_predator_cannon","#DEATH_HINT_PREDATOR_CANNON_001","",""
+"weapon","mp_titanweapon_predator_cannon","#DEATH_HINT_PREDATOR_CANNON_002","",""
+"weapon","mp_titanweapon_predator_cannon","#DEATH_HINT_PREDATOR_CANNON_003","",""
+"weapon","mp_titanweapon_predator_cannon","#DEATH_HINT_PREDATOR_CANNON_004","",""
+"weapon","mp_titanweapon_predator_cannon","#DEATH_HINT_PREDATOR_CANNON_005","",""
+"weapon","mp_titanweapon_sniper","#DEATH_HINT_PLASMA_RAILGUN_001","",""
+"weapon","mp_titanweapon_sniper","#DEATH_HINT_PLASMA_RAILGUN_002","",""
+"weapon","mp_titanweapon_sniper","#DEATH_HINT_PLASMA_RAILGUN_003","",""
+"weapon","mp_titanweapon_sniper","#DEATH_HINT_PLASMA_RAILGUN_004","",""
+"weapon","mp_titanweapon_sniper","#DEATH_HINT_PLASMA_RAILGUN_005","",""
+"weapon","mp_titanweapon_sticky_40mm","#DEATH_HINT_40MM_TRACKER_001","",""
+"weapon","mp_titanweapon_sticky_40mm","#DEATH_HINT_40MM_TRACKER_002","",""
+"weapon","mp_titanweapon_sticky_40mm","#DEATH_HINT_40MM_TRACKER_003","",""
+"weapon","mp_titanweapon_sticky_40mm","#DEATH_HINT_40MM_TRACKER_004","",""
+"weapon","mp_titanweapon_sticky_40mm","#DEATH_HINT_40MM_TRACKER_005","",""
+"weapon","mp_titanweapon_sticky_40mm","#DEATH_HINT_40MM_TRACKER_006","",""
+"weapon","mp_titanweapon_tracker_rockets","#DEATH_HINT_TRACKING_ROCKETS_001_MP","",""
+"weapon","mp_titanweapon_tracker_rockets","#DEATH_HINT_TRACKING_ROCKETS_002_MP","",""
+"weapon","mp_titanweapon_tracker_rockets","#DEATH_HINT_TRACKING_ROCKETS_003","",""
+"weapon","mp_titanweapon_vortex_shield","#DEATH_HINT_VORTEX_SHIELD_001","",""
+"weapon","mp_titanweapon_vortex_shield","#DEATH_HINT_VORTEX_SHIELD_002_MP","",""
+"weapon","mp_weapon_alternator_smg","#DEATH_HINT_ALTERNATOR_001","",""
+"weapon","mp_weapon_alternator_smg","#DEATH_HINT_ALTERNATOR_002","",""
+"weapon","mp_weapon_arc_launcher","#DEATH_HINT_ARCLAUNCHER_001","",""
+"weapon","mp_weapon_arc_launcher","#DEATH_HINT_ARCLAUNCHER_002","",""
+"weapon","mp_weapon_arc_launcher","#DEATH_HINT_ARCLAUNCHER_003","",""
+"weapon","mp_weapon_arc_launcher","#DEATH_HINT_ARCLAUNCHER_004","",""
+"weapon","mp_weapon_autopistol","#DEATH_HINT_RE45_001","",""
+"weapon","mp_weapon_autopistol","#DEATH_HINT_RE45_002","",""
+"weapon","mp_weapon_autopistol","#DEATH_HINT_RE45_003","",""
+"weapon","mp_weapon_car","#DEATH_HINT_CAR_001","",""
+"weapon","mp_weapon_car","#DEATH_HINT_CAR_002","",""
+"weapon","mp_weapon_defender","#DEATH_HINT_CHARGERIFLE_001","",""
+"weapon","mp_weapon_defender","#DEATH_HINT_CHARGERIFLE_002","",""
+"weapon","mp_weapon_defender","#DEATH_HINT_CHARGERIFLE_003","",""
+"weapon","mp_weapon_defender","#DEATH_HINT_CHARGERIFLE_004","",""
+"weapon","mp_weapon_defender","#DEATH_HINT_CHARGERIFLE_005","",""
+"weapon","mp_weapon_defender","#DEATH_HINT_CHARGERIFLE_006","",""
+"weapon","mp_weapon_deployable_cover","#DEATH_HINT_AWALL_001","",""
+"weapon","mp_weapon_deployable_cover","#DEATH_HINT_AWALL_002","",""
+"weapon","mp_weapon_deployable_cover","#DEATH_HINT_AWALL_003","",""
+"weapon","mp_weapon_deployable_cover","#DEATH_HINT_AWALL_004","",""
+"weapon","mp_weapon_deployable_cover","#DEATH_HINT_AWALL_005","",""
+"weapon","mp_weapon_deployable_cover","#DEATH_HINT_AWALL_006","",""
+"weapon","mp_weapon_dmr","#DEATH_HINT_DMR_001","",""
+"weapon","mp_weapon_dmr","#DEATH_HINT_DMR_002","",""
+"weapon","mp_weapon_dmr","#DEATH_HINT_DMR_003","",""
+"weapon","mp_weapon_dmr","#DEATH_HINT_DMR_004","",""
+"weapon","mp_weapon_dmr","#DEATH_HINT_DMR_005","",""
+"weapon","mp_weapon_dmr","#DEATH_HINT_DMR_006","",""
+"weapon","mp_weapon_doubletake","#DEATH_HINT_DOUBLETAKE_001","",""
+"weapon","mp_weapon_doubletake","#DEATH_HINT_DOUBLETAKE_002","",""
+"weapon","mp_weapon_doubletake","#DEATH_HINT_DOUBLETAKE_003","",""
+"weapon","mp_weapon_doubletake","#DEATH_HINT_DOUBLETAKE_004","",""
+"weapon","mp_weapon_doubletake","#DEATH_HINT_DOUBLETAKE_005","",""
+"weapon","mp_weapon_doubletake","#DEATH_HINT_DOUBLETAKE_006","",""
+"weapon","mp_weapon_doubletake","#DEATH_HINT_DOUBLETAKE_007","",""
+"weapon","mp_weapon_epg","#DEATH_HINT_EPG1_001","",""
+"weapon","mp_weapon_epg","#DEATH_HINT_EPG1_002","",""
+"weapon","mp_weapon_epg","#DEATH_HINT_EPG1_003","",""
+"weapon","mp_weapon_epg","#DEATH_HINT_EPG1_004","",""
+"weapon","mp_weapon_epg","#DEATH_HINT_EPG1_005","",""
+"weapon","mp_weapon_epg","#DEATH_HINT_EPG1_006","",""
+"weapon","mp_weapon_epg","#DEATH_HINT_EPG1_007","",""
+"weapon","mp_weapon_epg","#DEATH_HINT_EPG1_008","",""
+"weapon","mp_weapon_esaw","#DEATH_HINT_DEVOTION_001","",""
+"weapon","mp_weapon_esaw","#DEATH_HINT_DEVOTION_002","",""
+"weapon","mp_weapon_esaw","#DEATH_HINT_DEVOTION_003","",""
+"weapon","mp_weapon_esaw","#DEATH_HINT_DEVOTION_004","",""
+"weapon","mp_weapon_frag_drone","#DEATH_HINT_FRAG_DRONE_001","",""
+"weapon","mp_weapon_frag_grenade","#DEATH_HINT_GRENADE_FRAG_001","",""
+"weapon","mp_weapon_frag_grenade","#DEATH_HINT_GRENADE_FRAG_002","",""
+"weapon","mp_weapon_frag_grenade","#DEATH_HINT_GRENADE_FRAG_003","",""
+"weapon","mp_weapon_frag_grenade","#DEATH_HINT_GRENADE_FRAG_004","",""
+"weapon","mp_weapon_frag_grenade","#DEATH_HINT_GRENADE_FRAG_005","",""
+"weapon","mp_weapon_frag_grenade","#DEATH_HINT_GRENADE_FRAG_006","",""
+"weapon","mp_weapon_frag_grenade","#DEATH_HINT_GRENADE_FRAG_007","",""
+"weapon","mp_weapon_g2","#DEATH_HINT_G2_001","",""
+"weapon","mp_weapon_g3","#DEATH_HINT_G2_002","",""
+"weapon","mp_weapon_g4","#DEATH_HINT_G2_003","",""
+"weapon","mp_weapon_g5","#DEATH_HINT_G2_004","",""
+"weapon","mp_weapon_grenade_electric_smoke","#DEATH_HINT_GRENADE_ELECTRIC_SMOKE_001","",""
+"weapon","mp_weapon_grenade_electric_smoke","#DEATH_HINT_GRENADE_ELECTRIC_SMOKE_002","",""
+"weapon","mp_weapon_grenade_electric_smoke","#DEATH_HINT_GRENADE_ELECTRIC_SMOKE_003","",""
+"weapon","mp_weapon_grenade_electric_smoke","#DEATH_HINT_GRENADE_ELECTRIC_SMOKE_004","",""
+"weapon","mp_weapon_grenade_emp","#DEATH_HINT_GRENADE_EMP_001","",""
+"weapon","mp_weapon_grenade_emp","#DEATH_HINT_GRENADE_EMP_002","",""
+"weapon","mp_weapon_grenade_emp","#DEATH_HINT_GRENADE_EMP_003","",""
+"weapon","mp_weapon_grenade_emp","#DEATH_HINT_GRENADE_EMP_004","",""
+"weapon","mp_weapon_grenade_emp","#DEATH_HINT_GRENADE_EMP_005","",""
+"weapon","mp_weapon_grenade_gravity","#DEATH_HINT_GRENADE_GRAVITY_001","",""
+"weapon","mp_weapon_grenade_gravity","#DEATH_HINT_GRENADE_GRAVITY_002","",""
+"weapon","mp_weapon_grenade_gravity","#DEATH_HINT_GRENADE_GRAVITY_003","",""
+"weapon","mp_weapon_grenade_gravity","#DEATH_HINT_GRENADE_GRAVITY_004","",""
+"weapon","mp_weapon_grenade_gravity","#DEATH_HINT_GRENADE_GRAVITY_005","",""
+"weapon","mp_weapon_grenade_sonar","#DEATH_HINT_GRENADE_SONAR_001","",""
+"weapon","mp_weapon_grenade_sonar","#DEATH_HINT_GRENADE_SONAR_002","",""
+"weapon","mp_weapon_grenade_sonar","#DEATH_HINT_GRENADE_SONAR_003","",""
+"weapon","mp_weapon_grenade_sonar","#DEATH_HINT_GRENADE_SONAR_004","",""
+"weapon","mp_weapon_hemlok","#DEATH_HINT_HEMLOK_001","",""
+"weapon","mp_weapon_hemlok","#DEATH_HINT_HEMLOK_002","",""
+"weapon","mp_weapon_hemlok_smg","#DEATH_HINT_VOLT_001","",""
+"weapon","mp_weapon_hemlok_smg","#DEATH_HINT_VOLT_002","",""
+"weapon","mp_weapon_lmg","#DEATH_HINT_SPITFIRE_001","",""
+"weapon","mp_weapon_lmg","#DEATH_HINT_SPITFIRE_002","",""
+"weapon","mp_weapon_lmg","#DEATH_HINT_SPITFIRE_003","",""
+"weapon","mp_weapon_lmg","#DEATH_HINT_SPITFIRE_004","",""
+"weapon","mp_weapon_lmg","#DEATH_HINT_SPITFIRE_005","",""
+"weapon","mp_weapon_lmg","#DEATH_HINT_SPITFIRE_006","",""
+"weapon","mp_weapon_lmg","#DEATH_HINT_SPITFIRE_007","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_001","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_002","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_003","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_004","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_005","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_006","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_007","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_008","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_009","",""
+"weapon","mp_weapon_lstar","#DEATH_HINT_LSTAR_010","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_001","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_002","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_003","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_004","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_005","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_006","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_007","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_008","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_009","",""
+"weapon","mp_weapon_mastiff","#DEATH_HINT_MASTIFF_010","",""
+"weapon","mp_weapon_mgl","#DEATH_HINT_MGL_001","",""
+"weapon","mp_weapon_mgl","#DEATH_HINT_MGL_002","",""
+"weapon","mp_weapon_mgl","#DEATH_HINT_MGL_003","",""
+"weapon","mp_weapon_mgl","#DEATH_HINT_MGL_004","",""
+"weapon","mp_weapon_mgl","#DEATH_HINT_MGL_005","",""
+"weapon","mp_weapon_mgl","#DEATH_HINT_MGL_006","",""
+"weapon","mp_weapon_mgl","#DEATH_HINT_MGL_007","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_001","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_002","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_003","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_004","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_005","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_006","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_007","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_008","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_009","",""
+"weapon","mp_weapon_pulse_lmg","#DEATH_HINT_COLDWAR_010","",""
+"weapon","mp_weapon_r97","#DEATH_HINT_97_001","",""
+"weapon","mp_weapon_r97","#DEATH_HINT_97_002","",""
+"weapon","mp_weapon_r97","#DEATH_HINT_97_003","",""
+"weapon","mp_weapon_r97","#DEATH_HINT_97_004","",""
+"weapon","mp_weapon_rocket_launcher","#DEATH_HINT_ARCHER_001","",""
+"weapon","mp_weapon_rocket_launcher","#DEATH_HINT_ARCHER_002","",""
+"weapon","mp_weapon_rocket_launcher","#DEATH_HINT_ARCHER_003","",""
+"weapon","mp_weapon_rocket_launcher","#DEATH_HINT_ARCHER_004","",""
+"weapon","mp_weapon_rocket_launcher","#DEATH_HINT_ARCHER_005","",""
+"weapon","mp_weapon_rspn101","#DEATH_HINT_102_001","",""
+"weapon","mp_weapon_rspn102","#DEATH_HINT_102_002","",""
+"weapon","mp_weapon_satchel","#DEATH_HINT_SATCHEL_001","",""
+"weapon","mp_weapon_satchel","#DEATH_HINT_SATCHEL_002","",""
+"weapon","mp_weapon_satchel","#DEATH_HINT_SATCHEL_003","",""
+"weapon","mp_weapon_semipistol","#DEATH_HINT_P2011_001","",""
+"weapon","mp_weapon_semipistol","#DEATH_HINT_P2011_002","",""
+"weapon","mp_weapon_semipistol","#DEATH_HINT_P2011_003","",""
+"weapon","mp_weapon_shotgun","#DEATH_HINT_EVA8_001","",""
+"weapon","mp_weapon_shotgun","#DEATH_HINT_EVA8_002","",""
+"weapon","mp_weapon_shotgun","#DEATH_HINT_EVA8_003","",""
+"weapon","mp_weapon_shotgun","#DEATH_HINT_EVA8_004","",""
+"weapon","mp_weapon_shotgun","#DEATH_HINT_EVA8_005","",""
+"weapon","mp_weapon_shotgun","#DEATH_HINT_EVA8_006","",""
+"weapon","mp_weapon_shotgun","#DEATH_HINT_EVA8_007","",""
+"weapon","mp_weapon_shotgun_pistol","#DEATH_HINT_MOZAMBIQUE_001","",""
+"weapon","mp_weapon_shotgun_pistol","#DEATH_HINT_MOZAMBIQUE_002","",""
+"weapon","mp_weapon_shotgun_pistol","#DEATH_HINT_MOZAMBIQUE_003","",""
+"weapon","mp_weapon_smart_pistol","#DEATH_HINT_SMARTPISTOL_001","",""
+"weapon","mp_weapon_smart_pistol","#DEATH_HINT_SMARTPISTOL_002","",""
+"weapon","mp_weapon_smart_pistol","#DEATH_HINT_SMARTPISTOL_003","",""
+"weapon","mp_weapon_smart_pistol","#DEATH_HINT_SMARTPISTOL_004","",""
+"weapon","mp_weapon_smart_pistol","#DEATH_HINT_SMARTPISTOL_005","",""
+"weapon","mp_weapon_smart_pistol","#DEATH_HINT_SMARTPISTOL_006","",""
+"weapon","mp_weapon_smr","#DEATH_HINT_SMR_001","",""
+"weapon","mp_weapon_smr","#DEATH_HINT_SMR_002","",""
+"weapon","mp_weapon_smr","#DEATH_HINT_SMR_003","",""
+"weapon","mp_weapon_smr","#DEATH_HINT_SMR_004","",""
+"weapon","mp_weapon_smr","#DEATH_HINT_SMR_005","",""
+"weapon","mp_weapon_smr","#DEATH_HINT_SMR_006","",""
+"weapon","mp_weapon_smr","#DEATH_HINT_SMR_007","",""
+"weapon","mp_weapon_smr","#DEATH_HINT_SMR_008","",""
+"weapon","mp_weapon_sniper","#DEATH_HINT_KRABER_001","",""
+"weapon","mp_weapon_sniper","#DEATH_HINT_KRABER_002","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_001","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_002","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_003","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_004","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_005","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_006","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_007","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_008","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_009","",""
+"weapon","mp_weapon_softball","#DEATH_HINT_SOFTBALL_010","",""
+"weapon","mp_weapon_thermite_grenade","#DEATH_HINT_GRENADE_THERMITE_001","",""
+"weapon","mp_weapon_thermite_grenade","#DEATH_HINT_GRENADE_THERMITE_002","",""
+"weapon","mp_weapon_thermite_grenade","#DEATH_HINT_GRENADE_THERMITE_003","",""
+"weapon","mp_weapon_thermite_grenade","#DEATH_HINT_GRENADE_THERMITE_004","",""
+"weapon","mp_weapon_vinson","#DEATH_HINT_VINSON_001","",""
+"weapon","mp_weapon_vinson","#DEATH_HINT_VINSON_002","",""
+"weapon","mp_weapon_vinson","#DEATH_HINT_VINSON_003","",""
+"weapon","mp_weapon_wingman","#DEATH_HINT_WINGMAN_001","",""
+"weapon","mp_weapon_wingman","#DEATH_HINT_WINGMAN_002","",""
+"weapon","mp_weapon_wingman","#DEATH_HINT_WINGMAN_003","",""
+"weapon","mp_weapon_wingman","#DEATH_HINT_WINGMAN_004","",""
+"weapon","mp_weapon_wingman_n","#DEATH_HINT_WINGMAN_N_001","",""
+"weapon","pas_power_cell","#DEATH_HINT_POWER_CELL_001","",""
+"weapon","pas_fast_health_regen","#DEATH_HINT_FAST_REGEN_001","",""
+"weapon","pas_ordnance_pack","#DEATH_HINT_ORDNANCE_EXPERT_001","",""
+"weapon","pas_ordnance_pack","#DEATH_HINT_ORDNANCE_EXPERT_002","",""
+"weapon","pas_ordnance_pack","#DEATH_HINT_ORDNANCE_EXPERT_003","",""
+"weapon","pas_ordnance_pack","#DEATH_HINT_ORDNANCE_EXPERT_004","",""
+"weapon","pas_fast_embark","#DEATH_HINT_PHASE_EMBARK_001","",""
+"weapon","pas_fast_embark","#DEATH_HINT_PHASE_EMBARK_002","",""
+"weapon","pas_fast_embark","#DEATH_HINT_PHASE_EMBARK_003","",""
+"weapon","pas_enemy_death_icons","#DEATH_HINT_KILL_REPORT_001","",""
+"weapon","pas_enemy_death_icons","#DEATH_HINT_KILL_REPORT_002","",""
+"weapon","pas_enemy_death_icons","#DEATH_HINT_KILL_REPORT_003","",""
+"weapon","pas_wallhang","#DEATH_HINT_WALLHANG_001","",""
+"weapon","pas_wallhang","#DEATH_HINT_WALLHANG_002","",""
+"weapon","pas_ads_hover","#DEATH_HINT_HOVER_001","",""
+"weapon","pas_ads_hover","#DEATH_HINT_HOVER_002","",""
+"weapon","pas_ads_hover","#DEATH_HINT_HOVER_003","",""
+"weapon","pas_ads_hover","#DEATH_HINT_HOVER_004","",""
+"weapon","pas_ads_hover","#DEATH_HINT_HOVER_005","",""
+"weapon","pas_stealth_movement","#DEATH_HINT_LOW_PROFILE_001","",""
+"weapon","pas_enhanced_titan_ai","#DEATH_HINT_ASSAULT_CHIP_001","",""
+"weapon","pas_enhanced_titan_ai","#DEATH_HINT_ASSAULT_CHIP_002","",""
+"weapon","pas_auto_eject","#DEATH_HINT_AUTO_EJECT_001","",""
+"weapon","pas_auto_eject","#DEATH_HINT_AUTO_EJECT_002","",""
+"weapon","pas_auto_eject","#DEATH_HINT_AUTO_EJECT_003","",""
+"weapon","pas_auto_eject","#DEATH_HINT_AUTO_EJECT_004","",""
+"weapon","pas_mobility_dash_capacity","#DEATH_HINT_TURBO_ENGINE_001","",""
+"weapon","pas_mobility_dash_capacity","#DEATH_HINT_TURBO_ENGINE_002","",""
+"weapon","pas_mobility_dash_capacity","#DEATH_HINT_TURBO_ENGINE_003","",""
+"weapon","pas_hyper_core","#DEATH_HINT_OVERCORE_001","",""
+"weapon","pas_build_up_nuclear_core","#DEATH_HINT_NUKE_EJECT_001","",""
+"weapon","pas_build_up_nuclear_core","#DEATH_HINT_NUKE_EJECT_002","",""
+"weapon","pas_anti_rodeo","#DEATH_HINT_COUNTER_READY_001","",""
+"weapon","pas_anti_rodeo","#DEATH_HINT_COUNTER_READY_002","",""
+"weapon","pas_warpfall","#DEATH_HINT_WARPFALL_001","",""
+"weapon","pas_bubbleshield","#DEATH_HINT_DOME_SHIELD_001","",""
+"weapon","pas_bubbleshield","#DEATH_HINT_DOME_SHIELD_002","",""
+"npc_title","#NPC_TITAN_AUTO_NUKE","#HINT_FD_TITAN_AUTO_NUKE","","fd"
+"npc_title","#NPC_TITAN_AUTO_NUKE","#HINT_FD_TITAN_AUTO_NUKE2","","fd"
+"npc_title","#NPC_TITAN_ARC","#HINT_FD_TITAN_ARC","","fd"
+"npc_title","#NPC_TITAN_ARC","#HINT_FD_TITAN_ARC2","","fd"
+"npc_title","#NPC_TITAN_MORTAR","#HINT_FD_TITAN_MORTAR","","fd"
+"npc_title","#NPC_TITAN_MORTAR","#HINT_FD_TITAN_MORTAR2","","fd"
+"npc_title","#NPC_SOLDIER","#HINT_FD_SOLDIER","","fd"
+"npc_title","#NPC_SPECTRE","#HINT_FD_SPECTRE","","fd"
+"npc_title","#NPC_SPECTRE_MORTAR","#HINT_FD_SPECTRE_MORTAR","","fd"
+"npc_title","#NPC_SPECTRE_MORTAR","#HINT_FD_SPECTRE_MORTAR2","","fd"
+"npc_title","#NPC_STALKER","#HINT_FD_STALKER","","fd"
+"npc_title","#NPC_SUPER_SPECTRE","#HINT_FD_SUPER_SPECTRE","","fd"
+"npc_title","#NPC_SUPER_SPECTRE","#HINT_FD_SUPER_SPECTRE2","","fd"
+"npc_title","#NPC_SPECTRE_SUICIDE","#HINT_FD_SPECTRE_SUICIDE","","fd"
+"npc_title","#NPC_DRONE_PLASMA","#HINT_FD_DRONE_PLASMA","","fd"
+"npc_title","#NPC_DRONE_CLOAKED","#HINT_FD_DRONE_CLOAKED","","fd"
+"npc_title","#NPC_TITAN_STRYDER_LEADWALL","#HINT_FD_TITAN_STRYDER_LEADWALL","","fd"
+"npc_title","#NPC_TITAN_STRYDER_SNIPER","#HINT_FD_TITAN_STRYDER_SNIPER","","fd"
+"npc_title","#NPC_TITAN_OGRE_METEOR","#HINT_FD_TITAN_OGRE_METEOR","","fd"
+"npc_title","#NPC_TITAN_OGRE_MINIGUN","#HINT_FD_TITAN_OGRE_MINIGUN","","fd"
+"npc_title","#NPC_TITAN_ATLAS_TRACKER","#HINT_FD_TITAN_ATLAS_TRACKER","","fd"
+"npc_title","#NPC_TITAN_ATLAS_STICKYBOMB","#HINT_FD_TITAN_ATLAS_STICKYBOMB","","fd"
+"npc_title","#NPC_TITAN_ATLAS_VANGUARD","#HINT_FD_TITAN_ATLAS_VANGUARD","","fd"
+"npc_title","#NPC_TITAN_SNIPER_FD","#HINT_FD_TITAN_STRYDER_SNIPER","","fd"
+"npc_title","#NPC_TITAN_SNIPER_FD","#HINT_FD_TITAN_STRYDER_SNIPER2","","fd"
+"npc_title","#NPC_TITAN_SNIPER_FD","#HINT_FD_TITAN_STRYDER_SNIPER3","","fd"
+"titan_class","vanguard","#DEATH_HINT_VANGUARD_001","",""
+"titan_class","vanguard","#DEATH_HINT_VANGUARD_002","",""
+"titan_class","vanguard","#DEATH_HINT_VANGUARD_003","",""
+"titan_class","vanguard","#DEATH_HINT_VANGUARD_004","",""
+"titan_class","vanguard","#DEATH_HINT_VANGUARD_005","",""
+"titan_class","vanguard","#DEATH_HINT_VANGUARD_006","","" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/default_pilot_loadouts.csv b/Northstar.CustomServers/mod/scripts/datatable/default_pilot_loadouts.csv
new file mode 100644
index 00000000..e0fbbcd9
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/default_pilot_loadouts.csv
@@ -0,0 +1,11 @@
+name,suit,race,primary,primaryAttachment,primaryMod1,primaryMod2,primaryMod3,secondary,secondaryMod1,secondaryMod2,secondaryMod3,weapon3,weapon3Mod1,weapon3Mod2,weapon3Mod3,ordnance,passive1,passive2
+"#DEFAULT_PILOT_1","grapple","race_human_male","mp_weapon_rspn101","","","","","mp_weapon_defender","","","","mp_weapon_autopistol","","","","mp_weapon_frag_grenade","pas_power_cell","pas_enemy_death_icons"
+"#DEFAULT_PILOT_2","medium","race_human_female","mp_weapon_car","","","","","mp_weapon_mgl","","","","mp_weapon_autopistol","","","","mp_weapon_grenade_emp","pas_fast_health_regen","pas_wallhang"
+"#DEFAULT_PILOT_3","grapple","race_human_female","mp_weapon_lmg","","","","","mp_weapon_defender","","","","mp_weapon_autopistol","","","","mp_weapon_thermite_grenade","pas_fast_health_regen","pas_enemy_death_icons"
+"#DEFAULT_PILOT_4","medium","race_human_male","mp_weapon_shotgun","","","","","mp_weapon_defender","","","","mp_weapon_autopistol","","","","mp_weapon_frag_grenade","pas_power_cell","pas_wallhang"
+"#DEFAULT_PILOT_5","geist","race_human_female","mp_weapon_sniper","","","","","mp_weapon_defender","","","","mp_weapon_autopistol","","","","mp_weapon_grenade_emp","pas_power_cell","pas_enemy_death_icons"
+"#DEFAULT_PILOT_6","grapple","race_human_female","mp_weapon_smr","","","","","mp_weapon_defender","","","","mp_weapon_semipistol","","","","mp_weapon_thermite_grenade","pas_fast_health_regen","pas_wallhang"
+"#DEFAULT_PILOT_7","medium","race_human_female","mp_weapon_rspn101","","","","","mp_weapon_mgl","","","","mp_weapon_autopistol","","","","mp_weapon_grenade_emp","pas_power_cell","pas_enemy_death_icons"
+"#DEFAULT_PILOT_8","grapple","race_human_male","mp_weapon_car","","","","","mp_weapon_defender","","","","mp_weapon_autopistol","","","","mp_weapon_thermite_grenade","pas_fast_health_regen","pas_wallhang"
+"#DEFAULT_PILOT_9","grapple","race_human_male","mp_weapon_sniper","","","","","mp_weapon_defender","","","","mp_weapon_semipistol","","","","mp_weapon_frag_grenade","pas_power_cell","pas_enemy_death_icons"
+"#DEFAULT_PILOT_10","geist","race_human_male","mp_weapon_smr","","","","","mp_weapon_defender","","","","mp_weapon_autopistol","","","","mp_weapon_grenade_emp","pas_fast_health_regen","pas_wallhang" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/default_titan_loadouts.csv b/Northstar.CustomServers/mod/scripts/datatable/default_titan_loadouts.csv
new file mode 100644
index 00000000..b19ef636
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/default_titan_loadouts.csv
@@ -0,0 +1,8 @@
+name,primeName,setFile,titanRef,primaryMod,special,antirodeo,passive1,passive2,passive3,passive4,passive5,passive6,voice
+"#DEFAULT_TITAN_5","#DEFAULT_TITAN_5_PRIME","titan_atlas_stickybomb","ion","","mp_titanweapon_vortex_shield_ion","mp_titanability_laser_trip","pas_enhanced_titan_ai","pas_ion_weapon","pas_bubbleshield","pas_vanguard_core1","pas_vanguard_core4","pas_vanguard_core7","titanos_ion"
+"#DEFAULT_TITAN_7","#DEFAULT_TITAN_7_PRIME","titan_ogre_meteor","scorch","","mp_titanweapon_heat_shield","mp_titanability_slow_trap","pas_enhanced_titan_ai","pas_scorch_weapon","pas_bubbleshield","pas_vanguard_core1","pas_vanguard_core4","pas_vanguard_core7","titanos_scorch"
+"#DEFAULT_TITAN_3","#DEFAULT_TITAN_3_PRIME","titan_stryder_sniper","northstar","","mp_titanability_tether_trap","mp_titanability_hover","pas_enhanced_titan_ai","pas_northstar_weapon","pas_bubbleshield","pas_vanguard_core1","pas_vanguard_core4","pas_vanguard_core7","titanos_northstar"
+"#DEFAULT_TITAN_2","#DEFAULT_TITAN_2_PRIME","titan_stryder_leadwall","ronin","","mp_titanability_basic_block","mp_titanability_phase_dash","pas_enhanced_titan_ai","pas_ronin_weapon","pas_bubbleshield","pas_vanguard_core1","pas_vanguard_core4","pas_vanguard_core7","titanos_ronin"
+"#DEFAULT_TITAN_4","#DEFAULT_TITAN_4_PRIME","titan_atlas_tracker","tone","","mp_titanability_particle_wall","mp_titanability_sonar_pulse","pas_enhanced_titan_ai","pas_tone_weapon","pas_bubbleshield","pas_vanguard_core1","pas_vanguard_core4","pas_vanguard_core7","titanos_tone"
+"#DEFAULT_TITAN_8","#DEFAULT_TITAN_8_PRIME","titan_ogre_minigun","legion","","mp_titanability_gun_shield","mp_titanability_ammo_swap","pas_enhanced_titan_ai","pas_legion_weapon","pas_bubbleshield","pas_vanguard_core1","pas_vanguard_core4","pas_vanguard_core7","titanos_legion"
+"#DEFAULT_TITAN_10","#DEFAULT_TITAN_10_PRIME","titan_atlas_vanguard","vanguard","","mp_titanweapon_stun_laser","mp_titanability_rearm","pas_enhanced_titan_ai","pas_vanguard_shield","pas_bubbleshield","pas_vanguard_core1","pas_vanguard_core4","pas_vanguard_core7","titanos_vanguard" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/earn_meter_mp.csv b/Northstar.CustomServers/mod/scripts/datatable/earn_meter_mp.csv
new file mode 100644
index 00000000..1c766181
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/earn_meter_mp.csv
@@ -0,0 +1,31 @@
+itemRef,earnType,buildingImage,readyImage,nameText
+"titan_ronin","GOAL","rui/hud/earn_meter/ajax_building","rui/hud/earn_meter/titan_ready","#RONIN"
+"titan","GOAL","rui/hud/earn_meter/ajax_building","rui/hud/earn_meter/titan_ready","#TITAN"
+"ion","GOAL","rui/hud/earn_meter/ajax_building","rui/hud/earn_meter/titan_ready","#ION"
+"tone","GOAL","rui/hud/earn_meter/ajax_building","rui/hud/earn_meter/titan_ready","#TONE"
+"scorch","GOAL","rui/hud/earn_meter/ogre_building","rui/hud/earn_meter/titan_ready","#SCORCH"
+"legion","GOAL","rui/hud/earn_meter/ogre_building","rui/hud/earn_meter/titan_ready","#LEGION"
+"ronin","GOAL","rui/hud/earn_meter/stryder_building","rui/hud/earn_meter/titan_ready","#RONIN"
+"northstar","GOAL","rui/hud/earn_meter/stryder_building","rui/hud/earn_meter/titan_ready","#NORTHSTAR"
+"vanguard","GOAL","rui/hud/earn_meter/ajax_building","rui/hud/earn_meter/titan_ready","#VANGUARD"
+"core_electric_smoke","REWARD","rui/menu/boosts/boost_icon_electric_smoke","rui/menu/boosts/boost_icon_electric_smoke","#WPN_TITAN_ELECTRIC_SMOKE"
+"burnmeter_maphack","REWARD","rui/menu/boosts/boost_icon_map_hack","rui/menu/boosts/boost_icon_map_hack","#BURNMETER_MAP_HACK"
+"burnmeter_amped_weapons","REWARD","rui/menu/boosts/boost_icon_amped","rui/menu/boosts/boost_icon_amped","#BURNMETER_AMPED_WEAPONS"
+"burnmeter_ticks","REWARD","rui/menu/boosts/boost_icon_tick","rui/menu/boosts/boost_icon_tick","#BURNMETER_TICKS"
+"burnmeter_emergency_titan","REWARD","ui/temp","ui/temp","#BURNMETER_EMERGENCY_TITAN"
+"burnmeter_random_foil","REWARD","rui/menu/boosts/boost_icon_random","rui/menu/boosts/boost_icon_random","#BURNMETER_RANDOM_FOIL"
+"burnmeter_double_agent","REWARD","rui/menu/boosts/burncard_icon","rui/menu/boosts/burncard_icon","#BURNMETER_DOUBLE_AGENT"
+"burnmeter_phase_rewind","REWARD","rui/menu/boosts/boost_icon_phase_rewind","rui/menu/boosts/boost_icon_phase_rewind","#WPN_REWIND"
+"burnmeter_at_turret_weapon","REWARD","rui/menu/boosts/boost_icon_titan_sentry","rui/menu/boosts/boost_icon_titan_sentry","#BURNMETER_AT_TURRETWEAPON"
+"burnmeter_ap_turret_weapon","REWARD","rui/menu/boosts/boost_icon_personel_sentry","rui/menu/boosts/boost_icon_personel_sentry","#BURNMETER_AP_TURRETWEAPON"
+"burnmeter_holopilot_nova","REWARD","rui/menu/boosts/boost_icon_holopilot","rui/menu/boosts/boost_icon_holopilot","#WPN_HOLOPILOT_NOVA"
+"burnmeter_emergency_battery","REWARD","rui/menu/boosts/boost_icon_battery","rui/menu/boosts/boost_icon_battery","#BURNMETER_EMERGENCY_BATTERY"
+"burnmeter_smart_pistol","REWARD","rui/menu/boosts/boost_icon_smart_pistol","rui/menu/boosts/boost_icon_smart_pistol","#WPN_SMART_PISTOL"
+"burnmeter_radar_jammer","REWARD","rui/menu/boosts/boost_icon_radar_jam","rui/menu/boosts/boost_icon_radar_jam","#BURNMETER_RADAR_JAMMER"
+"burnmeter_hard_cover","REWARD","rui/menu/boosts/boost_icon_shield","rui/menu/boosts/boost_icon_shield","#WPN_HARD_COVER"
+"burnmeter_nuke_titan","REWARD","rui/menu/boosts/boost_icon_nuke","rui/menu/boosts/boost_icon_nuke","#WPN_NUKE_TITAN"
+"burnmeter_harvester_shield","REWARD","rui/menu/boosts/boost_icon_harvester_shield","rui/menu/boosts/boost_icon_harvester_shield","#BURNMETER_HARVESTER_SHIELD"
+"burnmeter_arc_trap","REWARD","rui/menu/boosts/boost_icon_arc_trap","rui/menu/boosts/boost_icon_arc_trap","#WPN_ARC_TRAP"
+"burnmeter_at_turret_weapon_infinite","REWARD","rui/menu/boosts/boost_icon_titan_sentry","rui/menu/boosts/boost_icon_titan_sentry","#BURNMETER_AT_TURRETWEAPON"
+"burnmeter_ap_turret_weapon_infinite","REWARD","rui/menu/boosts/boost_icon_personel_sentry","rui/menu/boosts/boost_icon_personel_sentry","#BURNMETER_AP_TURRETWEAPON"
+"burnmeter_rodeo_grenade","REWARD","rui/menu/boosts/boost_icon_core_overload","rui/menu/boosts/boost_icon_core_overload","#BURNMETER_SUPER_RODEO" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/faction_dialogue.csv b/Northstar.CustomServers/mod/scripts/datatable/faction_dialogue.csv
new file mode 100644
index 00000000..8e9673d1
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/faction_dialogue.csv
@@ -0,0 +1,273 @@
+conversationname,priority,debounce,disabledForFaction,inheritedDebounceConversations
+"scoring_won",3000,10.000000,"",""
+"scoring_lost",3000,10.000000,"",""
+"scoring_tied",3000,10.000000,"",""
+"scoring_wonClose",3000,10.000000,"",""
+"scoring_lostClose",3000,10.000000,"",""
+"scoring_winning",3000,10.000000,"faction_marvin",""
+"scoring_losing",3000,10.000000,"faction_marvin",""
+"scoring_winningClose",3000,10.000000,"faction_marvin",""
+"scoring_losingClose",3000,10.000000,"faction_marvin",""
+"scoring_winningLarge",3000,10.000000,"faction_marvin",""
+"scoring_losingLarge",3000,10.000000,"faction_marvin",""
+"scoring_lostMercy",3000,10.000000,"",""
+"scoring_wonMercy",3000,10.000000,"",""
+"fortwar_matchLoss",3000,10.000000,"",""
+"scoring_flavor",1500,10.000000,"",""
+"scoring_gotMailAlert",1500,10.000000,"",""
+"amphp_modeName",1500,10.000000,"",""
+"mtitan_modeName",1500,10.000000,"",""
+"ffa_modeName",2500,10.000000,"",""
+"freea_modeName",2500,10.000000,"",""
+"at_modeName",2500,10.000000,"",""
+"hp_modeName",2500,10.000000,"",""
+"cp_modeName",2500,10.000000,"",""
+"phunt_modeName",2500,10.000000,"",""
+"fortwar_modeName",2500,10.000000,"",""
+"tw_modeName",2500,10.000000,"",""
+"ctf_modeName",2500,10.000000,"",""
+"bh_modeName",2500,10.000000,"",""
+"front_modeName",2500,10.000000,"",""
+"frontdef_modeName",2500,10.000000,"",""
+"frontatk_modeName",2500,10.000000,"",""
+"lts_modeName",2500,10.000000,"",""
+"mfd_modeName",2500,10.000000,"",""
+"tmfd_modeName",2500,10.000000,"",""
+"extract_modeName",2500,10.000000,"",""
+"mw_modeName",2500,10.000000,"",""
+"pvp_modeName",2500,10.000000,"",""
+"raid_modeName",2500,10.000000,"",""
+"aslt_modeName",2500,10.000000,"",""
+"gnrc_modeDesc",2500,10.000000,"",""
+"ffa_modeDesc",2500,10.000000,"",""
+"ctf_modeDesc",2500,10.000000,"",""
+"lts_modeDesc",2500,10.000000,"",""
+"mfd_modeDesc",2500,10.000000,"",""
+"ltsd_modeDesc",2500,10.000000,"",""
+"ltsa_modeDesc",2500,10.000000,"",""
+"hp_modeDesc",2500,10.000000,"",""
+"mtitan_modeDesc",2500,10.000000,"",""
+"pvp_modeDesc",2500,10.000000,"",""
+"phunt_modeDesc",2500,10.000000,"",""
+"freea_modeDesc",2500,10.000000,"",""
+"grnc_modeDesc",2500,10.000000,"",""
+"mp_titanReady",1200,30.000000,"",""
+"mp_titanSoon",1200,45.000000,"faction_marvin",""
+"mp_titanInbound",1200,10.000000,"",""
+"mp_titanEmergency",1200,10.000000,"faction_marvin",""
+"mp_evacGo",1500,10.000000,"faction_marvin",""
+"mp_evacStop",1500,10.000000,"faction_marvin",""
+"mp_evacGoNag",1500,10.000000,"faction_marvin",""
+"mp_evacStopNag",1500,10.000000,"faction_marvin",""
+"mp_halftime",1500,10.000000,"",""
+"mp_sideSwitching",1500,10.000000,"",""
+"bh_modeDesc",1500,10.000000,"",""
+"bh_incoming",1500,10.000000,"",""
+"bh_collect",1500,10.000000,"faction_marvin",""
+"bh_decrypt",1500,10.000000,"faction_marvin",""
+"bh_collectSuccess",1500,10.000000,"faction_marvin",""
+"bh_collectFail",1500,10.000000,"faction_marvin",""
+"bh_mvp",1500,10.000000,"faction_marvin",""
+"bh_clearedA",1500,10.000000,"",""
+"bh_clearedB",1500,10.000000,"",""
+"bh_clearedC",1500,10.000000,"",""
+"bh_newWave",1500,10.000000,"",""
+"bh_bountyClaimedByEnemy",1500,10.000000,"",""
+"bh_bountyClaimedByFriendly",1500,10.000000,"",""
+"bh_playerKilledBounty",1500,10.000000,"faction_marvin",""
+"lts_atk60",1500,10.000000,"faction_marvin",""
+"lts_atk30",1500,10.000000,"faction_marvin",""
+"lts_def60",1500,10.000000,"faction_marvin",""
+"lts_def30",1500,10.000000,"faction_marvin",""
+"lts_bombDown",1500,10.000000,"faction_marvin",""
+"lts_bombDownAtk",1500,10.000000,"faction_marvin",""
+"lts_bombDownDef",1500,10.000000,"faction_marvin",""
+"lts_bombPickup",1500,10.000000,"faction_marvin",""
+"lts_bombPlanted",1500,10.000000,"faction_marvin",""
+"lts_bombDefusedAtk",1500,10.000000,"faction_marvin",""
+"lts_bombDefusedDef",1500,10.000000,"faction_marvin",""
+"lts_bombPlantedAtk",1500,10.000000,"faction_marvin",""
+"lts_bombPlantedDef",1500,10.000000,"faction_marvin",""
+"fortwar_turretDeployFriendly",1500,10.000000,"faction_marvin",""
+"fortwar_turretDestroyedFriendly",1500,10.000000,"faction_marvin",""
+"fortwar_baseShieldDownFriendly",1500,10.000000,"faction_marvin",""
+"fortwar_baseShieldDownEnemy",1500,10.000000,"faction_marvin",""
+"fortwar_baseShieldUpFriendly",1500,10.000000,"faction_marvin",""
+"fortwar_baseDmgFriendly",1500,10.000000,"faction_marvin",""
+"fortwar_baseDmgFriendly75",1500,10.000000,"faction_marvin",""
+"fortwar_baseDmgFriendly50",1500,10.000000,"faction_marvin",""
+"fortwar_baseDmgFriendly25",1500,10.000000,"faction_marvin",""
+"fortwar_baseDmgEnemy75",1500,10.000000,"faction_marvin",""
+"fortwar_baseDmgEnemy50",1500,10.000000,"faction_marvin",""
+"fortwar_baseDmgEnemy25",1500,10.000000,"faction_marvin",""
+"fortwar_baseEnemyAllyAttacking",1500,30.000000,"faction_marvin",""
+"fortwar_awayTurretsUnderAttack",1450,30.000000,"faction_marvin",""
+"fortwar_baseTurretsUnderAttack",1470,30.000000,"faction_marvin",""
+"fortwar_turretShieldedByFriendlyPilot",1400,10.000000,"faction_marvin",""
+"tw_territoryNag",1500,10.000000,"faction_marvin",""
+"fortwar_terEnemyExpelled",1500,60.000000,"faction_marvin",""
+"fortwar_terFriendlyExpelled",1450,60.000000,"faction_marvin",""
+"fortwar_terEnteredEnemyPilot",1400,60.000000,"faction_marvin",""
+"fortwar_terEnteredEnemyTitan",1450,60.000000,"faction_marvin",""
+"fortwar_terPresentEnemyTitans",1470,60.000000,"faction_marvin",""
+"fortwar_terEnteredEnemyForce",1500,60.000000,"faction_marvin",""
+"amphp_friendlyCappingA",1500,30.000000,"",""
+"amphp_friendlyCappingB",1500,30.000000,"",""
+"amphp_friendlyCappingC",1500,30.000000,"",""
+"amphp_friendlyCappedA",1500,10.000000,"faction_marvin",""
+"amphp_friendlyCappedB",1500,10.000000,"faction_marvin",""
+"amphp_friendlyCappedC",1500,10.000000,"faction_marvin",""
+"amphp_friendlyAmpedA",1500,10.000000,"faction_marvin",""
+"amphp_friendlyAmpedB",1500,10.000000,"faction_marvin",""
+"amphp_friendlyAmpedC",1500,10.000000,"faction_marvin",""
+"amphp_enemyCappedA",1500,10.000000,"",""
+"amphp_enemyCappedB",1500,10.000000,"",""
+"amphp_enemyCappedC",1500,10.000000,"",""
+"amphp_enemyAmpedA",1500,10.000000,"",""
+"amphp_enemyAmpedB",1500,10.000000,"",""
+"amphp_enemyAmpedC",1500,10.000000,"",""
+"amphp_friendlyCapAll",2000,10.000000,"",""
+"amphp_enemyCapAll",2000,10.000000,"",""
+"amphp_youAmpedA",1500,10.000000,"",""
+"amphp_youAmpedB",1500,10.000000,"",""
+"amphp_youAmpedC",1500,10.000000,"",""
+"ctf_flagPickupFriendly",1500,10.000000,"",""
+"ctf_flagPickupYou",1500,10.000000,"",""
+"ctf_flagReturnedFriendly",1500,10.000000,"",""
+"ctf_flagReturnedEnemy",1500,10.000000,"",""
+"ctf_notifyWin1more",1500,10.000000,"faction_marvin",""
+"ctf_notifyLose1more",1500,10.000000,"faction_marvin",""
+"kc_pilotkillLegion",1400,0.100000,"faction_marvin",""
+"kc_pilotkillScorch",1400,0.100000,"faction_marvin",""
+"kc_pilotkillTone",1400,0.100000,"faction_marvin",""
+"kc_pilotkillIon",1400,0.100000,"faction_marvin",""
+"kc_pilotkillRonin",1400,0.100000,"faction_marvin",""
+"kc_pilotkillNorthstar",1400,0.100000,"faction_marvin",""
+"kc_bullseye",1350,0.100000,"faction_marvin",""
+"kc_rodeo",1350,10.000000,"faction_marvin",""
+"kc_rakerodeoguy",1400,10.000000,"faction_marvin",""
+"kc_pilotkilltitan",1400,0.100000,"faction_marvin",""
+"kc_hitandrun",1350,0.100000,"faction_marvin",""
+"kc_firstblood",2100,0.100000,"faction_marvin",""
+"kc_megakill",2100,0.100000,"",""
+"kc_triplekill",2100,0.100000,"",""
+"kc_doublekill",1350,0.100000,"faction_marvin",""
+"kc_iced",1350,0.100000,"faction_marvin",""
+"kc_rampage",1350,0.100000,"faction_marvin",""
+"kc_killingspree",1350,0.100000,"faction_marvin",""
+"kc_dominating",1350,0.100000,"faction_marvin",""
+"kc_retribution",1350,0.100000,"faction_marvin",""
+"kc_comeback",1350,0.100000,"faction_marvin",""
+"mfd_youAreMarked",1500,0.100000,"",""
+"mfd_youKilledMark",1500,0.100000,"",""
+"mfd_markDownEnemy",1500,0.100000,"",""
+"mfd_markDownFriendly",1500,0.100000,"",""
+"mfd_targetsMarkedLong",1500,0.100000,"",""
+"mfd_targetsMarkedShort",1500,0.100000,"",""
+"mfd_youOutlastedEnemy",1500,0.100000,"",""
+"mfd_markCountdown",1500,0.100000,"",""
+"lts_playerLastTitanOnTeam",1500,0.100000,"",""
+"fd_modeDesc",2500,10.000000,"",""
+"fd_firstWaveStartPrefix",2500,10.000000,"",""
+"fd_newWaveStartPrefix",2500,10.000000,"",""
+"fd_finalWaveStartPrefix",2500,10.000000,"",""
+"fd_waveVictory",3000,10.000000,"",""
+"fd_waveRestart",3000,10.000000,"",""
+"fd_waveRedoTwo",3000,10.000000,"",""
+"fd_waveRedoFinal",3000,10.000000,"",""
+"fd_titanReadyNag",1500,10.000000,"",""
+"fd_minimapTip",1500,10.000000,"",""
+"fd_waveNoTitanDrops",1500,10.000000,"",""
+"fd_waveTypeInfantry",1500,10.000000,"",""
+"fd_waveTypeCloakDrone",1500,10.000000,"",""
+"fd_waveTypeTitanReg",1500,10.000000,"",""
+"fd_waveTypeTitanMortar",1500,10.000000,"",""
+"fd_waveTypeTitanNuke",1500,10.000000,"",""
+"fd_waveTypeTitanArc",1500,10.000000,"",""
+"fd_waveComboNukeMortar",1500,10.000000,"",""
+"fd_waveComboArcMortar",1500,10.000000,"",""
+"fd_waveComboArcNuke",1500,10.000000,"",""
+"fd_waveComboNukeCloak",1500,10.000000,"",""
+"fd_waveComboNukeTrain",1500,10.000000,"",""
+"fd_waveComboMultiMix",1500,10.000000,"",""
+"fd_waveTypeReapers",1500,10.000000,"",""
+"fd_waveTypeTicks",1500,10.000000,"",""
+"fd_waveTypeStalkers",1500,10.000000,"",""
+"fd_waveTypeMortarSpectre",1500,10.000000,"",""
+"fd_waveTypeReaperTicks",1500,10.000000,"",""
+"fd_waveTypeEliteTitan",1500,10.000000,"",""
+"fd_waveTypeFlyers",1500,10.000000,"",""
+"fd_baseDeath",3000,10.000000,"",""
+"fd_waveRecapLowHealth",25000,10.000000,"",""
+"fd_waveRecapNearPerfect",2500,10.000000,"",""
+"fd_waveRecapPerfect",2500,10.000000,"",""
+"fd_bigWaveInc",1500,10.000000,"",""
+"fd_finalWaveStartGeneric",1500,10.000000,"",""
+"fd_baseHealthRecharge",2500,10.000000,"",""
+"fd_matchVictory",3000,10.000000,"",""
+"fd_matchDefeat",3000,10.000000,"",""
+"fd_waveCleanup",1500,10.000000,"",""
+"fd_singlePilotDown",2000,10.000000,"",""
+"fd_multiPilotDown",2000,10.000000,"",""
+"fd_onlyPlayerIsAlive",2000,10.000000,"",""
+"fd_pilotRespawn",2000,10.000000,"",""
+"fd_nukeTitanNearBase",1750,10.000000,"",""
+"fd_waveCleanup5",1500,20.000000,"",""
+"fd_waveCleanup4",1500,20.000000,"",""
+"fd_waveCleanup3",1500,20.000000,"",""
+"fd_waveCleanup2",1500,20.000000,"",""
+"fd_waveCleanup1",1500,20.000000,"",""
+"fd_baseShieldTakingDmg",1900,30.000000,"",""
+"fd_baseShieldLow",1910,10.000000,"","fd_baseShieldTakingDmg"
+"fd_baseShieldDown",1930,10.000000,"",""
+"fd_baseShieldUp",1810,8.000000,"","fd_baseShieldTakingDmg fd_baseShieldRecharging"
+"fd_baseShieldRecharging",1800,30.000000,"","fd_baseShieldTakingDmg"
+"fd_baseShieldRechargingShort",1800,30.000000,"",""
+"fd_baseBatteryNagLow",1920,10.000000,"",""
+"fd_baseBatteryNagDown",2000,10.000000,"",""
+"fd_baseShieldLowHolding",2000,10.000000,"",""
+"fd_waveNoTitans",1500,10.000000,"",""
+"fd_incTitansNukeClump",1500,10.000000,"",""
+"fd_incTitansMortarClump",1500,10.000000,"",""
+"fd_incArcTitanClump",1500,10.000000,"",""
+"fd_incCloakDroneClump",1500,10.000000,"",""
+"fd_incReaperClump",1500,10.000000,"",""
+"fd_nagKillTitansMortar",2020,45.000000,"",""
+"fd_nagKillInfantry",1500,45.000000,"",""
+"fd_nagKillStalkers",1000,45.000000,"",""
+"fd_nagKillMortarSpectres",2020,45.000000,"",""
+"fd_nagKillTitanEMP",1500,45.000000,"",""
+"fd_nagTitanArcAtBase",1500,10.000000,"",""
+"fd_baseHealth75",2000,15.000000,"",""
+"fd_baseHealth50",2010,15.000000,"","fd_baseHealth75"
+"fd_baseHealth25",2020,15.000000,"","fd_baseHealth75 fd_baseHealth50"
+"fd_baseLowHealth",2030,15.000000,"","fd_baseHealth75 fd_baseHealth50 fd_baseHealth25"
+"fd_baseHealth50nag",2010,10.000000,"",""
+"fd_baseHealth25nag",2020,10.000000,"",""
+"fd_playerCashNagSurplus",1500,10.000000,"",""
+"fd_playerCashNagReg",1500,10.000000,"",""
+"fd_wavePayoutAddtnl",1500,10.000000,"",""
+"fd_wavePayoutFirst",1500,10.000000,"",""
+"fd_introEasy",1500,10.000000,"",""
+"fd_introMedium",1500,10.000000,"",""
+"fd_introHard",1500,10.000000,"",""
+"fd_turretOffline",1500,10.000000,"",""
+"fd_turretOnline",1500,10.000000,"",""
+"turretKillsLow",1500,10.000000,"",""
+"turretKillsMedium",1500,10.000000,"",""
+"turretKillsHigh",1500,10.000000,"",""
+"turretKillsMega",1500,10.000000,"",""
+"fd_boughtSentryTurret",1500,10.000000,"",""
+"fd_boughtArcTrap",1500,10.000000,"",""
+"fd_boughtCoreOverload",1500,10.000000,"",""
+"fd_playerNeedsToReadyUp",1500,10.000000,"",""
+"fd_boughtAmpedWeapons",1500,10.000000,"",""
+"fd_boughtHarvesterShield",1500,10.000000,"",""
+"fd_boughtBattery",1500,10.000000,"",""
+"matchRecapNoDeaths",1500,10.000000,"",""
+"matchRecapNoPlayerDeath",1500,10.000000,"",""
+"matchRecapPlayerMVP",1500,10.000000,"",""
+"fd_soonNukeTitans",1500,10.000000,"",""
+"fd_soonArcTitans",1500,10.000000,"",""
+"fd_soonMortarTitans",1500,10.000000,"",""
+"fd_stalkerExploNag",1000,300.000000,"","" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/faction_leaders.csv b/Northstar.CustomServers/mod/scripts/datatable/faction_leaders.csv
new file mode 100644
index 00000000..22fb3e45
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/faction_leaders.csv
@@ -0,0 +1,8 @@
+persistenceRef,factionDialoguePrefix,logo,image,name,description,factionName,usesWaveform,modelName,skinIndex,menuIdleAnim,propModelName,propAttachment,cost
+"faction_marauder","mcor_sarah","rui/faction/faction_logo_marauder","rui/faction/faction_button_marauder","#FACTION_LEADER_NAME_SARAH","#FACTION_LEADER_DESC_SARAH","#FACTION_MARAUDER",0,"models/humans/heroes/mlt_hero_sarah.mdl",0,"Sarah_menu_pose","models/Weapons/p2011/w_p2011.mdl","KNIFE",100
+"faction_apex","imc_blisk","rui/faction/faction_logo_apex","rui/faction/faction_button_apex","#FACTION_LEADER_NAME_BLISK","#FACTION_LEADER_DESC_BLISK","#FACTION_APEX",0,"models/humans/heroes/imc_hero_blisk.mdl",0,"Blisk_menu_pose","models/Weapons/combat_knife/w_combat_knife.mdl","KNIFE",100
+"faction_vinson","imc_ash","rui/faction/faction_logo_vinson","rui/faction/faction_button_vinson","#FACTION_LEADER_NAME_ASH","#FACTION_LEADER_DESC_ASH","#FACTION_VINSON",0,"models/humans/heroes/imc_hero_ash.mdl",0,"Ash_menu_pose","","",100
+"faction_aces","mcor_barker","rui/faction/faction_logo_aces","rui/faction/faction_button_aces","#FACTION_LEADER_NAME_BARKER","#FACTION_LEADER_DESC_BARKER","#FACTION_ACES",0,"models/humans/heroes/mlt_hero_barker.mdl",0,"Barker_menu_pose","models/props/flask/prop_flask_animated.mdl","PROPGUN",100
+"faction_64","mcor_gates","rui/faction/faction_logo_64","rui/faction/faction_button_64","#FACTION_LEADER_NAME_GATES","#FACTION_LEADER_DESC_GATES","#FACTION_64",0,"models/humans/pilots/sp_medium_geist_f.mdl",0,"Gates_menu_pose","models/Weapons/p2011/w_p2011.mdl","KNIFE",100
+"faction_ares","imc_marder","rui/faction/faction_logo_ares","rui/faction/faction_button_ares","#FACTION_LEADER_NAME_MARDER","#FACTION_LEADER_DESC_MARDER","#FACTION_ARES",0,"models/humans/heroes/imc_hero_marder.mdl",0,"Marder_menu_pose","","",100
+"faction_marvin","mcor_marvin","rui/faction/faction_logo_mrvn","rui/faction/faction_button_mrvn","#FACTION_LEADER_NAME_MARVIN","#FACTION_LEADER_DESC_MARVIN","#FACTION_MARVIN",1,"models/Robots/marvin/marvin.mdl",1,"commander_MP_flyin_marvin_idle","","",100 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/faction_leaders_dropship_anims.csv b/Northstar.CustomServers/mod/scripts/datatable/faction_leaders_dropship_anims.csv
new file mode 100644
index 00000000..0d759d93
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/faction_leaders_dropship_anims.csv
@@ -0,0 +1,23 @@
+persistenceRef,dropshipAnimName,isEasterEgg
+"faction_marauder","commander_MP_flyin_sarah",0
+"faction_marauder","commander_MP_flyin_sarah_alt",0
+"faction_marauder","commander_MP_flyin_sarah_silent",0
+"faction_apex","commander_MP_flyin_blisk_betta",0
+"faction_apex","commander_MP_flyin_blisk_born",0
+"faction_apex","commander_MP_flyin_blisk_silent",0
+"faction_vinson","commander_MP_flyin_ash_focus",0
+"faction_vinson","commander_MP_flyin_ash_grateful",0
+"faction_vinson","commander_MP_flyin_ash_silent",0
+"faction_aces","commander_MP_flyin_barker",0
+"faction_aces","commander_MP_flyin_barker_victory",0
+"faction_aces","commander_MP_flyin_barker_conductor",0
+"faction_64","commander_MP_flyin_gates_family",0
+"faction_64","commander_MP_flyin_gates_sixfour",0
+"faction_64","commander_MP_flyin_gates_silent",0
+"faction_ares","commander_MP_flyin_marder",0
+"faction_ares","commander_MP_flyin_marder_alt1",0
+"faction_ares","commander_MP_flyin_marder_alt2",0
+"faction_marvin","commander_MP_flyin_marvin_salute",0
+"faction_marvin","commander_MP_flyin_marvin_highfive",0
+"faction_marvin","commander_MP_flyin_marvin_greeter",0
+"faction_marvin","commander_MP_flyin_marvin_freestyle",1 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/fd_awards.csv b/Northstar.CustomServers/mod/scripts/datatable/fd_awards.csv
new file mode 100644
index 00000000..01c6254b
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/fd_awards.csv
@@ -0,0 +1,15 @@
+ref,priority,displayString,subText,awardDisplayString,displayStyle,image,validityCheck,validityCheckValue,comparisonCheck,needsToBeBest
+"harvesterHeals",5,"#FD_AWARD_0","#FD_AWARD_SUBTEXT_0","#FD_AWARD_VALUE_DISPLAY_BLANK","FD_DISPLAY_STYLE_NUMBER","rui/medals/shielded_harvester","greater_than",1.000000,"highest",1
+"mortarUnitsKilled",3,"#FD_AWARD_1","#FD_AWARD_SUBTEXT_1","#FD_AWARD_VALUE_DISPLAY_BLANK","FD_DISPLAY_STYLE_NUMBER","rui/medals/mortar_units","greater_than",1.000000,"highest",1
+"moneySpent",4,"#FD_AWARD_2","#FD_AWARD_SUBTEXT_2","#FD_AWARD_VALUE_DISPLAY_MONEY","FD_DISPLAY_STYLE_NUMBER","rui/medals/cash_spent","greater_than",2000.000000,"highest",1
+"coresUsed",8,"#FD_AWARD_3","#FD_AWARD_SUBTEXT_3","#FD_AWARD_VALUE_DISPLAY_BLANK","FD_DISPLAY_STYLE_NUMBER","rui/medals/titan_core","greater_than",1.000000,"highest",1
+"longestTitanLife",9,"#FD_AWARD_4","#FD_AWARD_SUBTEXT_4","#FD_AWARD_VALUE_DISPLAY_SECONDS","FD_DISPLAY_STYLE_TIME","rui/medals/least_lost","greater_than",100.000000,"highest",1
+"turretsRepaired",7,"#FD_AWARD_5","#FD_AWARD_SUBTEXT_5","#FD_AWARD_VALUE_DISPLAY_BLANK","FD_DISPLAY_STYLE_NUMBER","rui/medals/turrets_healed","greater_than",5.000000,"highest",1
+"moneyShared",6,"#FD_AWARD_6","#FD_AWARD_SUBTEXT_6","#FD_AWARD_VALUE_DISPLAY_MONEY","FD_DISPLAY_STYLE_NUMBER","rui/medals/cash_shared","greater_than",100.000000,"highest",1
+"damageDealt",1,"#FD_AWARD_7","#FD_AWARD_SUBTEXT_7","#FD_AWARD_VALUE_DISPLAY_POINTS","FD_DISPLAY_STYLE_NUMBER","rui/medals/most_damage","greater_than",-1.000000,"highest",0
+"timeNearHarvester",2,"#FD_AWARD_8","#FD_AWARD_SUBTEXT_8","#FD_AWARD_VALUE_DISPLAY_SECONDS","FD_DISPLAY_STYLE_TIME","rui/medals/harvester_protect","greater_than",200.000000,"highest",1
+"longestLife",0,"#FD_AWARD_9","#FD_AWARD_SUBTEXT_9","#FD_AWARD_VALUE_DISPLAY_SECONDS","FD_DISPLAY_STYLE_TIME","rui/medals/long_life","greater_than",200.000000,"highest",1
+"heals",11,"#FD_AWARD_10","#FD_AWARD_SUBTEXT_10","#FD_AWARD_VALUE_DISPLAY_POINTS","FD_DISPLAY_STYLE_NUMBER","rui/medals/heal","greater_than",5000.000000,"highest",1
+"turretKills",10,"#FD_AWARD_11","#FD_AWARD_SUBTEXT_11","#FD_AWARD_VALUE_DISPLAY_BLANK","FD_DISPLAY_STYLE_NUMBER","rui/medals/turret_damage","greater_than",10.000000,"highest",1
+"mvp",13,"#FD_AWARD_12","#FD_AWARD_SUBTEXT_12","#FD_AWARD_VALUE_DISPLAY_BLANK","FD_DISPLAY_STYLE_NUMBER","rui/medals/mvp","greater_than",2.000000,"highest",1
+"titanKills",12,"#FD_AWARD_13","#FD_AWARD_SUBTEXT_13","#FD_AWARD_VALUE_DISPLAY_BLANK","FD_DISPLAY_STYLE_NUMBER","rui/medals/titans_killed","greater_than",30.000000,"highest",1 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/features_mp.csv b/Northstar.CustomServers/mod/scripts/datatable/features_mp.csv
new file mode 100644
index 00000000..8f1b39b3
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/features_mp.csv
@@ -0,0 +1,21 @@
+featureRef,featureName,featureDesc,featureIcon,specificType,cost
+"communities","#FEATURE_COMMUNITIES","","rui/hud/common/feature_icon","#ITEM_TYPE_FEATURE",0
+"happy_hour","#FEATURE_HAPPY_HOUR","","rui/hud/common/feature_icon","#ITEM_TYPE_FEATURE",0
+"pilot_loadout_1","#DEFAULT_PILOT_1","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",15
+"pilot_loadout_2","#DEFAULT_PILOT_2","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",15
+"pilot_loadout_3","#DEFAULT_PILOT_3","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",15
+"pilot_loadout_4","#DEFAULT_PILOT_4","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",15
+"pilot_loadout_5","#DEFAULT_PILOT_5","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",15
+"pilot_loadout_6","#DEFAULT_PILOT_6","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",15
+"pilot_loadout_7","#DEFAULT_PILOT_7","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",100
+"pilot_loadout_8","#DEFAULT_PILOT_8","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",100
+"pilot_loadout_9","#DEFAULT_PILOT_9","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",100
+"pilot_loadout_10","#DEFAULT_PILOT_10","","rui/hud/common/feature_icon","#ITEM_TYPE_LOADOUT",100
+"coliseum_ticket","#ITEM_COLISEUM","#ITEM_COLISEUM_DESC","rui/menu/common/ticket_icon","#ITEM_TYPE_PLAYLIST",10
+"double_xp","#DOUBLE_XP","#DOUBLE_XP_DESC","rui/menu/common/dbl_xp_icon","#ITEM_TYPE_DOUBLEXP",0
+"credit_award","#CREDIT_AWARD","#CREDIT_AWARD_DESC","rui/menu/common/credit_symbol_large_color","#ITEM_TYPE_CREDITS",0
+"credit_award_5x","#CREDIT_AWARD_5X","","rui/menu/common/credit_symbol_large_color","#ITEM_TYPE_CREDITS",0
+"advocate_gift","#RANDOM","","rui\menu\common\unlock_random","#UNLOCK_RANDOM",0
+"mp_angel_city","#MP_ANGEL_CITY","","rui/hud/common/feature_icon","#UNLOCK_RANDOM",0
+"random","#RANDOM","","rui\menu\common\unlock_random","#UNLOCK_RANDOM",0
+"classic_music","#CLASSIC_MUSIC","","rui/hud/common/feature_icon","#UNLOCK_CLASSIC_MUSIC",0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/flightpath_assets.csv b/Northstar.CustomServers/mod/scripts/datatable/flightpath_assets.csv
new file mode 100644
index 00000000..d14566f1
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/flightpath_assets.csv
@@ -0,0 +1,8 @@
+name,mp_model,sp_model
+"fp_dropship_model","models/vehicle/goblin_dropship/goblin_dropship.mdl","models/vehicle/goblin_dropship/goblin_dropship.mdl"
+"fp_dropship_hero_model","models/vehicle/goblin_dropship/goblin_dropship_hero.mdl","models/vehicle/goblin_dropship/goblin_dropship_hero.mdl"
+"fp_crow_model","models/vehicle/crow_dropship/crow_dropship.mdl","models/vehicle/crow_dropship/crow_dropship.mdl"
+"fp_crow_hero_model","models/vehicle/crow_dropship/crow_dropship_hero.mdl","models/vehicle/crow_dropship/crow_dropship_hero.mdl"
+"fp_titan_model","models/titans/medium/titan_medium_ajax.mdl","models/titans/medium/sp_titan_medium_ajax.mdl"
+"fp_straton_model","models/vehicle/straton/straton_imc_gunship_01.mdl","models/vehicle/straton/straton_imc_gunship_01.mdl"
+"fp_hornet_model","models/vehicle/hornet/hornet_fighter.mdl","models/vehicle/hornet/hornet_fighter.mdl" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/grunt_chatter_mp.csv b/Northstar.CustomServers/mod/scripts/datatable/grunt_chatter_mp.csv
new file mode 100644
index 00000000..c3c15f19
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/grunt_chatter_mp.csv
@@ -0,0 +1,47 @@
+conversationname,alias,priority,debounce
+"bc_allygruntdown","bc_allygruntdown",250,20.000000
+"bc_enemytitandown","bc_enemytitandown",250,20.000000
+"bc_enemytitanspotcall","bc_enemytitanspotcall",250,20.000000
+"bc_engageenemycloakedpilot","bc_engageenemycloakedpilot",250,20.000000
+"bc_engagepilotenemy","bc_engagepilotenemy",250,20.000000
+"bc_grenadecall","bc_grenadecall",250,20.000000
+"bc_gruntkillstitan","bc_gruntkillstitan",250,20.000000
+"bc_killenemypilot","bc_killenemypilot",250,20.000000
+"bc_spotenemypilot","bc_spotenemypilot",250,20.000000
+"bc_squaddeplete","bc_squaddeplete",250,20.000000
+"bc_reactGrenadeArc","bc_reactGrenadeArc",250,20.000000
+"bc_reactGrenadeThermite","bc_reactGrenadeThermite",250,20.000000
+"bc_reactGrenadeGravity","bc_reactGrenadeGravity",250,20.000000
+"bc_reactGrenadeElecSmoke","bc_reactGrenadeElecSmoke",250,20.000000
+"bc_grenadeOutCall","bc_grenadeOutCall",250,20.000000
+"bc_fleePlayerTitanCall","bc_spotclosetitancall_01",250,20.000000
+"bc_fleePlayerTitanCall","bc_fleePlayerTitanCall",250,20.000000
+"bc_reactTickSpawnFriendly","bc_reactTickSpawnFriendly_01",250,20.000000
+"bc_reactTickSpawnFriendly","bc_reactTickSpawnFriendly_02",250,20.000000
+"bc_reactTickSpawnFriendly","bc_reactTickSpawnFriendly_03",250,20.000000
+"bc_reactTitanfallFriendlyArrives","bc_reactTitanfallFriendlyArrives_01",250,40.000000
+"bc_reactTitanfallFriendlyArrives","bc_titancheer_01",250,40.000000
+"bc_reactEnemySpotted","bc_engagepilotenemy_01",250,20.000000
+"bc_reactEnemySpotted","bc_engagepilotenemy_02",250,20.000000
+"bc_reactEnemySpotted","bc_engagepilotenemy_06",250,20.000000
+"bc_reactEnemyReaper","diag_sp_ReaperTown_BM102_15_01_mcor_grunt3",250,20.000000
+"bc_reactEnemyReaper","diag_sp_ReaperTown_BM102_16_01_mcor_grunt2",250,20.000000
+"bc_reactReaperFriendlyArrives","bc_reactReaperFriendlyArrives_01",250,40.000000
+"bc_reactFriendlyPilot","diag_sp_ReaperTown_BM103_01a_01_mcor_grunt2",250,180.000000
+"TEMP_bc_reactFriendlyPilot","diag_sp_corkscrew_SE131_02_01_mcor_grunt1",250,180.000000
+"bc_reactFriendlyPilot","diag_sp_corkscrew_SE131_19_01_mcor_grunt1",250,180.000000
+"bc_generalCombat","diag_sp_corkscrew_SE131_21_01_mcor_grunt1",250,20.000000
+"bc_generalCombat","diag_sp_corkscrew_SE131_12_01_mcor_grunt1",250,20.000000
+"bc_generalCombat","diag_sp_intro_WD104_29_01_mcor_grunt6",250,20.000000
+"bc_generalCombat","diag_sp_intro_WD104_24_01_mcor_grunt1",250,20.000000
+"bc_generalCombat","diag_sp_intro_WD103_02_01_mcor_grunt2",250,20.000000
+"bc_generalCombat","diag_sp_intro_WD104_25_01_mcor_grunt2",250,20.000000
+"bc_generalCombat","diag_sp_intro_WD104_26_01_mcor_grunt3",250,20.000000
+"bc_generalCombat","diag_sp_intro_WD104_27_01_mcor_grunt4",250,20.000000
+"bc_generalCombat","diag_sp_intro_WD104_28_01_mcor_grunt5",250,20.000000
+"TEMP_bc_generalNonCombat","diag_sp_ReaperTown_BM102_29a_01_imc_gcaptain",250,100.000000
+"bc_generalNonCombat","diag_sp_ReaperTown_BM103_02a_01_mcor_grunt1",250,100.000000
+"bc_generalCombatTitan","diag_sp_sewerArena_SE151_09_01_imc_grunt1",250,20.000000
+"bc_generalCombatTitan","diag_sp_sewerArena_SE152_01_01_imc_grunt1",250,20.000000
+"bc_generalCombatTitan","diag_sp_intro_WD103_07_01_mcor_grunt1",250,20.000000
+"bc_generalCombatTitan","diag_sp_bigCharge_TD111_06_01_mcor_pilot3",250,20.000000 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/non_loadout_weapons.csv b/Northstar.CustomServers/mod/scripts/datatable/non_loadout_weapons.csv
new file mode 100644
index 00000000..8816fd8f
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/non_loadout_weapons.csv
@@ -0,0 +1,29 @@
+weapon
+"melee_titan_punch"
+"melee_titan_punch_fighter"
+"melee_titan_punch_ion"
+"melee_titan_punch_tone"
+"melee_titan_punch_northstar"
+"melee_titan_punch_scorch"
+"melee_titan_punch_legion"
+"melee_titan_sword"
+"melee_titan_sword_aoe"
+"mp_titanweapon_flightcore_rockets"
+"mp_titanweapon_orbital_strike"
+"mp_titanweapon_predator_cannon_siege"
+"mp_weapon_spectre_spawner"
+"proto_viewmodel_test"
+"mp_turretweapon_blaster"
+"mp_turretweapon_plasma"
+"mp_turretweapon_sentry"
+"mp_ability_arc_blast"
+"mp_ability_burncardweapon"
+"mp_ability_holopilot_nova"
+"mp_weapon_smart_pistol"
+"mp_weapon_hard_cover"
+"melee_titan_punch_vanguard"
+"mp_titanweapon_rocketeer_rocketstream"
+"mp_titanweapon_shoulder_rockets"
+"mp_ability_swordblock"
+"mp_titanability_nuke_eject"
+"mp_weapon_arc_trap" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/pain_death_sounds.csv b/Northstar.CustomServers/mod/scripts/datatable/pain_death_sounds.csv
new file mode 100644
index 00000000..00d5e595
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/pain_death_sounds.csv
@@ -0,0 +1,53 @@
+event,priority,blocksNextPriority,method,bodyType,alias_1p_victim_only,alias_3p_except_victim,alias_3p_attacker_only,alias_3p_except_attacker,spmp
+"death",105,0,"SE_GIB","NPC_GRUNT","","death.pinkmist","","","spmp"
+"death",105,0,"SE_GIB","NPC_MARVIN","","","","","spmp"
+"death",105,0,"SE_GIB","NPC_PROWLER","","","","","spmp"
+"death",105,0,"SE_GIB","NPC_SPECTRE","","","","","spmp"
+"death",105,0,"SE_GIB","NPC_STALKER","","","","","spmp"
+"death",110,1,"SE_HEADSHOT_BULLET","NPC_GRUNT","","","","Flesh.Light.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_BULLET","NPC_MARVIN","","","","Android.Light.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_BULLET","NPC_SPECTRE","","","","Android.Light.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_BULLET","NPC_STALKER","","","","Android.Light.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_SHOTGUN","NPC_GRUNT","","","","Flesh.Shotgun.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_SHOTGUN","NPC_MARVIN","","","","Android.Heavy.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_SHOTGUN","NPC_SPECTRE","","","","Android.Heavy.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_SHOTGUN","NPC_STALKER","","","","Android.Heavy.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_TITAN","NPC_GRUNT","","","","Flesh.Heavy.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_TITAN","NPC_MARVIN","","","","Android.Heavy.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_TITAN","NPC_SPECTRE","","","","Android.Heavy.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",110,1,"SE_HEADSHOT_TITAN","NPC_STALKER","","","","Android.Heavy.BulletImpact_Headshot_3P_vs_3P","spmp"
+"death",120,1,"SE_NECK_SNAP","NPC_GRUNT","","diag_efforts_DeathNeck_gl_grunt_3p","","","spmp"
+"death",140,1,"SE_DISSOLVE","NPC_GRUNT","","diag_efforts_DeathBurn_gl_grunt_3p","","","spmp"
+"death",160,1,"SE_ELECTRICAL","NPC_GRUNT","","diag_efforts_DeathElec_gl_grunt_3p","","","spmp"
+"death",180,1,"SE_EXPLOSION","NPC_GRUNT","","diag_efforts_DeathExplo_gl_grunt_3p","","","spmp"
+"death",200,1,"SE_FALL","NPC_GRUNT","","diag_efforts_DeathFall_gl_grunt_3p","","","spmp"
+"death",220,1,"SE_PROWLER","NPC_GRUNT","","diag_efforts_DeathProwler_gl_grunt_3p","","","spmp"
+"death",290,1,"SE_TITAN_STEP","NPC_GRUNT","","titan_grunt_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","NPC_MARVIN","","titan_spectre_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","NPC_SPECTRE","","titan_spectre_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","NPC_STALKER","","titan_spectre_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","NPC_PROWLER","","titan_grunt_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","PLAYER_HUMAN_MALE","","titan_grunt_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","PLAYER_HUMAN_MALE","","titan_grunt_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","PLAYER_ANDROID_MALE","","titan_spectre_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","PLAYER_HUMAN_FEMALE","","titan_grunt_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","PLAYER_ANDROID_FEMALE","","titan_spectre_squish","","","spmp"
+"death",290,1,"SE_TITAN_STEP","NPC_GRUNT","","diag_efforts_DeathCrush_gl_grunt_3p","","","spmp"
+"death",290,1,"SE_TITAN_STEP","PLAYER_HUMAN_MALE","diag_efforts_DeathCrush_sp_cooper_3p_vs_1p","diag_efforts_DeathCrush_mp_maleHuman_3p","","","sp"
+"death",290,1,"SE_TITAN_STEP","PLAYER_HUMAN_MALE","diag_efforts_DeathCrush_mp_maleHuman_3p_vs_1p","diag_efforts_DeathCrush_mp_maleHuman_3p","","","mp"
+"death",290,1,"SE_TITAN_STEP","PLAYER_ANDROID_MALE","diag_efforts_DeathCrush_mp_maleRobot_3p_vs_1p","diag_efforts_DeathCrush_mp_maleRobot_3p","","","mp"
+"death",290,1,"SE_TITAN_STEP","PLAYER_HUMAN_FEMALE","diag_efforts_DeathCrush_mp_femaleHuman_3p_vs_1p","diag_efforts_DeathCrush_mp_femaleHuman_3p","","","mp"
+"death",400,1,"SE_ANY","NPC_MARVIN","","marvin_death","","","spmp"
+"death",400,1,"SE_TITAN_STEP","PLAYER_ANDROID_FEMALE","diag_efforts_DeathCrush_mp_femaleRobot_3p_vs_1p","diag_efforts_DeathCrush_mp_femaleRobot_3p","","","mp"
+"death",500,1,"SE_ANY","NPC_GRUNT","","diag_efforts_DeathQuick_gl_grunt_3p","","","spmp"
+"death",500,1,"SE_ANY","TITAN","titan_death_explode","titan_death_explode","","","spmp"
+"pain",300,1,"SE_ELECTRICAL","NPC_GRUNT","","diag_efforts_hitByArc_gl_grunt_3p","","","spmp"
+"pain",300,1,"SE_THERMITE_GRENADE","NPC_GRUNT","","diag_efforts_burns_gl_grunt_3p","","","spmp"
+"pain",400,0,"SE_PROWLER","NPC_GRUNT","","diag_efforts_hitByProwler_gl_grunt_3p","","","spmp"
+"pain",400,0,"SE_SMOKE","NPC_GRUNT","","diag_efforts_cough_gl_grunt_3p","","","spmp"
+"pain",400,0,"SE_ANY","NPC_GRUNT","","diag_efforts_hits_gl_grunt_3p","","","spmp"
+"pain",400,0,"SE_ANY","PLAYER_ANDROID_FEMALE","diag_efforts_hits_mp_femaleRobot_3p_vs_1p","","","","mp"
+"pain",400,0,"SE_ANY","PLAYER_ANDROID_MALE","diag_efforts_hits_mp_maleRobot_3p_vs_1p","","","","mp"
+"pain",400,0,"SE_ANY","PLAYER_HUMAN_FEMALE","diag_efforts_hits_mp_femaleHuman_3p_vs_1p","","","","mp"
+"pain",400,0,"SE_ANY","PLAYER_HUMAN_MALE","diag_efforts_hits_mp_maleHuman_3p_vs_1p","","","","mp"
+"pain",400,0,"SE_ANY","PLAYER_HUMAN_MALE","diag_efforts_hits_sp_cooper_3p_vs_1p","","","","sp" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/pilot_abilities.csv b/Northstar.CustomServers/mod/scripts/datatable/pilot_abilities.csv
new file mode 100644
index 00000000..e9949479
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/pilot_abilities.csv
@@ -0,0 +1,16 @@
+itemRef,type,damageSource,hidden,cost
+"mp_ability_grapple","PILOT_SPECIAL",0,0,180
+"mp_ability_heal","PILOT_SPECIAL",0,0,180
+"mp_ability_holopilot","PILOT_SPECIAL",0,0,180
+"mp_weapon_grenade_sonar","PILOT_SPECIAL",1,0,180
+"mp_ability_shifter","PILOT_SPECIAL",0,0,180
+"mp_weapon_deployable_cover","PILOT_SPECIAL",0,0,180
+"mp_ability_cloak","PILOT_SPECIAL",0,0,180
+"mp_weapon_frag_grenade","PILOT_ORDNANCE",1,0,220
+"mp_weapon_grenade_emp","PILOT_ORDNANCE",1,0,220
+"mp_weapon_grenade_gravity","PILOT_ORDNANCE",1,0,220
+"mp_weapon_grenade_electric_smoke","PILOT_ORDNANCE",1,0,220
+"mp_weapon_thermite_grenade","PILOT_ORDNANCE",1,0,220
+"mp_weapon_satchel","PILOT_ORDNANCE",1,0,220
+"melee_pilot_sword","NOT_LOADOUT",1,1,220
+"mp_ability_shifter_super","PILOT_SPECIAL",0,1,220 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/pilot_executions.csv b/Northstar.CustomServers/mod/scripts/datatable/pilot_executions.csv
new file mode 100644
index 00000000..faae9db7
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/pilot_executions.csv
@@ -0,0 +1,14 @@
+ref,name,description,image,hidden,cost
+"execution_neck_snap","#PILOT_EXECUTION_NECK_SNAP","#PILOT_EXECUTION_NECK_SNAP_DESC","rui/pilot_loadout/execution/execution_neck_snap",0,0
+"execution_face_stab","#PILOT_EXECUTION_FACE_STAB","#PILOT_EXECUTION_FACE_STAB_DESC","rui/pilot_loadout/execution/execution_face_stab",0,300
+"execution_backshot","#PILOT_EXECUTION_BACK_SHOT","#PILOT_EXECUTION_BACK_SHOT_DESC","rui/pilot_loadout/execution/execution_backshot",0,300
+"execution_combo","#PILOT_EXECUTION_COMBO","#PILOT_EXECUTION_COMBO_DESC","rui/pilot_loadout/execution/execution_combo",0,300
+"execution_knockout","#PILOT_EXECUTION_KNOCKOUT","#PILOT_EXECUTION_KNOCKOUT_DESC","rui/pilot_loadout/execution/execution_knockout",0,300
+"execution_telefrag","#PILOT_EXECUTION_TELEFRAG","#PILOT_EXECUTION_TELEFRAG_DESC","rui/pilot_loadout/execution/execution_inner_pieces",0,0
+"execution_stim","#PILOT_EXECUTION_STIM","#PILOT_EXECUTION_STIM_DESC","rui/pilot_loadout/execution/execution_straight_blast",0,0
+"execution_grapple","#PILOT_EXECUTION_GRAPPLE","#PILOT_EXECUTION_GRAPPLE_DESC","rui/pilot_loadout/execution/execution_grapple",0,0
+"execution_pulseblade","#PILOT_EXECUTION_PULSEBLADE","#PILOT_EXECUTION_PULSEBLADE_DESC","rui/pilot_loadout/execution/execution_pulseblade",0,0
+"execution_random","#PILOT_EXECUTION_RANDOM","#PILOT_EXECUTION_RANDOM_DESC","rui/pilot_loadout/execution/execution_random",0,0
+"execution_cloak","#PILOT_EXECUTION_CLOAK","#PILOT_EXECUTION_CLOAK_DESC","rui/pilot_loadout/execution/execution_now_you_see_me",0,0
+"execution_holopilot","#PILOT_EXECUTION_HOLOPILOT","#PILOT_EXECUTION_HOLOPILOT_DESC","rui/pilot_loadout/execution/execution_holopilot",0,0
+"execution_ampedwall","#PILOT_EXECUTION_AMPEDWALL","#PILOT_EXECUTION_AMPEDWALL_DESC","rui/pilot_loadout/execution/execution_amped_wall",0,0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/pilot_passives.csv b/Northstar.CustomServers/mod/scripts/datatable/pilot_passives.csv
new file mode 100644
index 00000000..a13a98f2
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/pilot_passives.csv
@@ -0,0 +1,10 @@
+passive,type,name,description,image,hidden,cost
+"pas_enemy_death_icons","PILOT_PASSIVE2","#GEAR_ENEMY_DEATH_ICONS","#GEAR_ENEMY_DEATH_ICONS_DESC","rui/pilot_loadout/kit/kill_report_menu",0,25
+"pas_ordnance_pack","PILOT_PASSIVE1","#GEAR_EXPLOSIVES_PACK","#GEAR_EXPLOSIVES_PACK_DESC","rui/pilot_loadout/kit/ordnance_expert_menu",0,125
+"pas_power_cell","PILOT_PASSIVE1","#GEAR_POWER_CELL","#GEAR_POWER_CELL_DESC","rui/pilot_loadout/kit/power_cell_menu",0,25
+"pas_fast_embark","PILOT_PASSIVE1","#GEAR_FAST_EMBARK","#GEAR_FAST_EMBARK_DESC","rui/pilot_loadout/kit/phase_embark_menu",0,125
+"pas_ads_hover","PILOT_PASSIVE2","#GEAR_ADS_HOVER","#GEAR_ADS_HOVER_DESC","rui/pilot_loadout/kit/hover_menu",0,225
+"pas_wallhang","PILOT_PASSIVE2","#GEAR_WALLHANG","#GEAR_WALLHANG_DESC","rui/pilot_loadout/kit/wall_hang_menu",0,25
+"pas_fast_health_regen","PILOT_PASSIVE1","#GEAR_FAST_HEALTH_REGEN","#GEAR_FAST_HEALTH_REGEN_DESC","rui/pilot_loadout/kit/quick_regen_menu",0,25
+"pas_stealth_movement","PILOT_PASSIVE2","#GEAR_STEALTH_KIT","#GEAR_STEALTH_KIT_DESC","rui/pilot_loadout/kit/stealth_movement_menu",0,225
+"pas_at_hunter","PILOT_PASSIVE2","#GEAR_AT_HUNTER_KIT","#GEAR_AT_HUNTER_KIT_DESC","rui/pilot_loadout/kit/titan_hunter_menu",0,225 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/pilot_properties.csv b/Northstar.CustomServers/mod/scripts/datatable/pilot_properties.csv
new file mode 100644
index 00000000..6e40e332
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/pilot_properties.csv
@@ -0,0 +1,8 @@
+type,image,tactical,defaultPassive1,defaultPassive2,melee,cost
+"nomad","rui/pilot_loadout/suit/nomad","mp_ability_heal","pas_power_cell","pas_enemy_death_icons","melee_pilot_emptyhanded",180
+"light","rui/pilot_loadout/suit/light","mp_ability_shifter","pas_fast_health_regen","pas_wallhang","melee_pilot_emptyhanded",180
+"geist","rui/pilot_loadout/suit/geist","mp_ability_cloak","pas_power_cell","pas_enemy_death_icons","melee_pilot_emptyhanded",180
+"medium","rui/pilot_loadout/suit/medium","mp_weapon_grenade_sonar","pas_fast_health_regen","pas_wallhang","melee_pilot_emptyhanded",180
+"grapple","rui/pilot_loadout/suit/grapple","mp_ability_grapple","pas_power_cell","pas_wallhang","melee_pilot_emptyhanded",180
+"heavy","rui/pilot_loadout/suit/heavy","mp_weapon_deployable_cover","pas_fast_health_regen","pas_enemy_death_icons","melee_pilot_emptyhanded",180
+"stalker","rui/pilot_loadout/suit/stalker","mp_ability_holopilot","pas_power_cell","pas_enemy_death_icons","melee_pilot_emptyhanded",180 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_features.csv b/Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_features.csv
new file mode 100644
index 00000000..2226eb79
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_features.csv
@@ -0,0 +1,5 @@
+featureRef,featureName,featureDesc,featureIcon,cost
+"primarymod2","#EXTRA_MOD","","rui/hud/common/feature_icon",0
+"secondarymod2","#EXTRA_MOD","","rui/hud/common/feature_icon",0
+"primarymod3","#MOD_PRO_SCREEN_NAME","","rui/hud/common/feature_icon",0
+"secondarymod3","#MOD_PRO_SCREEN_NAME","","rui/hud/common/feature_icon",0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_mods.csv b/Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_mods.csv
new file mode 100644
index 00000000..b59acb18
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_mods.csv
@@ -0,0 +1,250 @@
+mod,weapon,statDamage,statAccuracy,statRange,statFireRate,statClipSize,hidden
+"extended_ammo","mp_weapon_r97",0,0,0,0,10,0
+"extended_ammo","mp_weapon_shotgun",0,0,0,0,3,0
+"extended_ammo","mp_weapon_mastiff",0,0,0,0,3,0
+"extended_ammo","mp_weapon_car",0,0,0,0,10,0
+"extended_ammo","mp_weapon_rspn101",0,0,0,0,6,0
+"extended_ammo","mp_weapon_rspn101_og",0,0,0,0,6,0
+"extended_ammo","mp_weapon_hemlok",0,0,0,0,6,0
+"extended_ammo","mp_weapon_g2",0,0,0,0,4,0
+"extended_ammo","mp_weapon_vinson",0,0,0,0,6,0
+"extended_ammo","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"extended_ammo","mp_weapon_alternator_smg",0,0,0,0,0,0
+"extended_ammo","mp_weapon_wingman",0,0,0,0,0,0
+"extended_ammo","mp_weapon_wingman_n",0,0,0,0,0,0
+"extended_ammo","mp_weapon_shotgun_pistol",0,0,0,0,0,0
+"extended_ammo","mp_weapon_sniper",0,0,0,0,0,0
+"extended_ammo","mp_weapon_defender",0,0,0,0,0,0
+"extended_ammo","mp_weapon_dmr",0,0,0,0,0,0
+"extended_ammo","mp_weapon_doubletake",0,0,0,0,0,0
+"extended_ammo","mp_weapon_softball",0,0,0,0,0,0
+"extended_ammo","mp_weapon_epg",0,0,0,0,0,0
+"extended_ammo","mp_weapon_pulse_lmg",0,0,0,0,0,0
+"extended_ammo","mp_weapon_smr",0,0,0,0,0,0
+"extended_ammo","mp_weapon_rocket_launcher",0,0,0,0,0,0
+"extended_ammo","mp_weapon_mgl",0,0,0,0,0,0
+"extended_ammo","mp_weapon_arc_launcher",0,0,0,0,0,0
+"extended_ammo","mp_weapon_semipistol",0,0,0,0,0,0
+"extended_ammo","mp_weapon_autopistol",0,0,0,0,0,0
+"extended_ammo","mp_weapon_lmg",0,0,0,0,0,0
+"extended_ammo","mp_weapon_lstar",0,0,0,0,0,0
+"extended_ammo","mp_weapon_esaw",0,0,0,0,0,0
+"silencer","mp_weapon_wingman",0,0,0,0,0,0
+"silencer","mp_weapon_shotgun_pistol",0,0,0,0,0,0
+"silencer","mp_weapon_autopistol",0,0,0,0,0,0
+"silencer","mp_weapon_semipistol",0,0,0,0,0,0
+"hcog","mp_weapon_rspn101",0,0,0,0,0,0
+"hcog","mp_weapon_rspn101_og",0,0,0,0,0,0
+"hcog","mp_weapon_hemlok",0,0,0,0,0,0
+"hcog","mp_weapon_g2",0,0,0,0,0,0
+"hcog","mp_weapon_vinson",0,0,0,0,0,0
+"hcog","mp_weapon_alternator_smg",0,0,0,0,0,0
+"redline_sight","mp_weapon_r97",0,0,0,0,0,0
+"redline_sight","mp_weapon_car",0,0,0,0,0,0
+"redline_sight","mp_weapon_rspn101",0,0,0,0,0,0
+"redline_sight","mp_weapon_rspn101_og",0,0,0,0,0,0
+"redline_sight","mp_weapon_hemlok",0,0,0,0,0,0
+"redline_sight","mp_weapon_g2",0,0,0,0,0,0
+"redline_sight","mp_weapon_lmg",0,0,0,0,0,0
+"redline_sight","mp_weapon_vinson",0,0,0,0,0,0
+"redline_sight","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"redline_sight","mp_weapon_alternator_smg",0,0,0,0,0,0
+"redline_sight","mp_weapon_lstar",0,0,0,0,0,0
+"redline_sight","mp_weapon_esaw",0,0,0,0,0,0
+"redline_sight","mp_weapon_shotgun",0,0,0,0,0,0
+"redline_sight","mp_weapon_mastiff",0,0,0,0,0,0
+"aog","mp_weapon_lstar",0,0,0,0,0,0
+"aog","mp_weapon_lmg",0,0,0,0,0,0
+"aog","mp_weapon_esaw",0,0,0,0,0,0
+"holosight","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"holosight","mp_weapon_car",0,0,0,0,0,0
+"holosight","mp_weapon_r97",0,0,0,0,0,0
+"holosight","mp_weapon_shotgun",0,0,0,0,0,0
+"holosight","mp_weapon_mastiff",0,0,0,0,0,0
+"scope_4x","mp_weapon_dmr",0,0,0,0,0,0
+"scope_4x","mp_weapon_sniper",0,0,0,0,0,0
+"scope_4x","mp_weapon_doubletake",0,0,0,0,0,0
+"ricochet","mp_weapon_sniper",0,0,0,0,0,0
+"ricochet","mp_weapon_doubletake",0,0,0,0,0,0
+"ricochet","mp_weapon_wingman_n",0,0,0,0,0,0
+"threat_scope","mp_weapon_rspn101",0,0,0,0,0,0
+"threat_scope","mp_weapon_rspn101_og",0,0,0,0,0,0
+"threat_scope","mp_weapon_vinson",0,0,0,0,0,0
+"threat_scope","mp_weapon_hemlok",0,0,0,0,0,0
+"threat_scope","mp_weapon_g2",0,0,0,0,0,0
+"threat_scope","mp_weapon_car",0,0,0,0,0,0
+"threat_scope","mp_weapon_r97",0,0,0,0,0,0
+"threat_scope","mp_weapon_alternator_smg",0,0,0,0,0,0
+"threat_scope","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"threat_scope","mp_weapon_sniper",0,0,0,0,0,0
+"threat_scope","mp_weapon_dmr",0,0,0,0,0,0
+"threat_scope","mp_weapon_doubletake",0,0,0,0,0,0
+"threat_scope","mp_weapon_lmg",0,0,0,0,0,0
+"threat_scope","mp_weapon_lstar",0,0,0,0,0,0
+"threat_scope","mp_weapon_esaw",0,0,0,0,0,0
+"threat_scope","mp_weapon_shotgun",0,0,0,0,0,0
+"threat_scope","mp_weapon_mastiff",0,0,0,0,0,0
+"quick_charge","mp_weapon_defender",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_rspn101",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_rspn101_og",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_vinson",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_hemlok",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_g2",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_car",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_r97",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_alternator_smg",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_lmg",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_lstar",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_esaw",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_sniper",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_dmr",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_doubletake",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_wingman",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_wingman_n",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_shotgun_pistol",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_autopistol",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_semipistol",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_shotgun",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_mastiff",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_softball",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_epg",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_pulse_lmg",0,0,0,0,0,0
+"tactical_cdr_on_kill","mp_weapon_smr",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_rspn101",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_rspn101_og",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_vinson",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_hemlok",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_g2",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_car",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_r97",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_alternator_smg",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_lmg",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_lstar",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_esaw",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_sniper",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_dmr",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_doubletake",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_wingman",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_wingman_n",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_shotgun_pistol",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_autopistol",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_semipistol",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_shotgun",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_mastiff",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_softball",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_epg",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_pulse_lmg",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_smr",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_defender",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_rocket_launcher",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_mgl",0,0,0,0,0,0
+"pas_fast_ads","mp_weapon_arc_launcher",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_rspn101",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_rspn101_og",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_vinson",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_hemlok",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_g2",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_car",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_r97",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_alternator_smg",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_lmg",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_lstar",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_esaw",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_sniper",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_dmr",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_doubletake",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_shotgun",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_mastiff",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_softball",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_epg",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_pulse_lmg",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_smr",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_defender",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_rocket_launcher",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_mgl",0,0,0,0,0,0
+"pas_fast_swap","mp_weapon_arc_launcher",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_rspn101",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_rspn101_og",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_vinson",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_hemlok",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_g2",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_car",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_r97",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_alternator_smg",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_lmg",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_lstar",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_esaw",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_sniper",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_dmr",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_doubletake",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_wingman",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_wingman_n",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_shotgun_pistol",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_autopistol",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_semipistol",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_shotgun",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_mastiff",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_softball",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_epg",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_pulse_lmg",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_smr",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_rocket_launcher",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_mgl",0,0,0,0,0,0
+"pas_fast_reload","mp_weapon_arc_launcher",0,0,0,0,0,0
+"pro_screen","mp_weapon_r97",0,0,0,0,0,0
+"pro_screen","mp_weapon_shotgun",0,0,0,0,0,0
+"pro_screen","mp_weapon_mastiff",0,0,0,0,0,0
+"pro_screen","mp_weapon_car",0,0,0,0,0,0
+"pro_screen","mp_weapon_rspn101",0,0,0,0,0,0
+"pro_screen","mp_weapon_rspn101_og",0,0,0,0,0,0
+"pro_screen","mp_weapon_hemlok",0,0,0,0,0,0
+"pro_screen","mp_weapon_g2",0,0,0,0,0,0
+"pro_screen","mp_weapon_vinson",0,0,0,0,0,0
+"pro_screen","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"pro_screen","mp_weapon_alternator_smg",0,0,0,0,0,0
+"pro_screen","mp_weapon_wingman",0,0,0,0,0,0
+"pro_screen","mp_weapon_wingman_n",0,0,0,0,0,0
+"pro_screen","mp_weapon_shotgun_pistol",0,0,0,0,0,0
+"pro_screen","mp_weapon_sniper",0,0,0,0,0,0
+"pro_screen","mp_weapon_defender",0,0,0,0,0,0
+"pro_screen","mp_weapon_dmr",0,0,0,0,0,0
+"pro_screen","mp_weapon_doubletake",0,0,0,0,0,0
+"pro_screen","mp_weapon_softball",0,0,0,0,0,0
+"pro_screen","mp_weapon_epg",0,0,0,0,0,0
+"pro_screen","mp_weapon_pulse_lmg",0,0,0,0,0,0
+"pro_screen","mp_weapon_smr",0,0,0,0,0,0
+"pro_screen","mp_weapon_rocket_launcher",0,0,0,0,0,0
+"pro_screen","mp_weapon_mgl",0,0,0,0,0,0
+"pro_screen","mp_weapon_arc_launcher",0,0,0,0,0,0
+"pro_screen","mp_weapon_semipistol",0,0,0,0,0,0
+"pro_screen","mp_weapon_autopistol",0,0,0,0,0,0
+"pro_screen","mp_weapon_lmg",0,0,0,0,0,0
+"pro_screen","mp_weapon_lstar",0,0,0,0,0,0
+"pro_screen","mp_weapon_esaw",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_r97",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_shotgun",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_mastiff",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_car",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_rspn101",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_rspn101_og",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_hemlok",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_g2",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_vinson",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_hemlok_smg",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_alternator_smg",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_wingman",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_wingman_n",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_shotgun_pistol",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_softball",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_epg",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_pulse_lmg",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_smr",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_semipistol",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_autopistol",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_lmg",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_lstar",0,0,0,0,0,0
+"pas_run_and_gun","mp_weapon_esaw",0,0,0,0,0,0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_mods_common.csv b/Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_mods_common.csv
new file mode 100644
index 00000000..4718c126
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/pilot_weapon_mods_common.csv
@@ -0,0 +1,27 @@
+mod,type,name,shortName,description,image,cost,costSniper,costPistol,costAT
+"iron_sights","attachment","#FACTORY_ISSUE_NAME","","#FACTORY_ISSUE_SIGHT_DESC","r2_ui/menus/loadout_icons/attachments/iron_sights",0,0,0,0
+"holosight","attachment","#MOD_HOLOSIGHT_NAME","","#MOD_HOLOSIGHT_DESC","r2_ui/menus/loadout_icons/attachments/holosight",0,0,0,0
+"hcog","attachment","#MOD_HCOG_NAME","HCOG","#MOD_HCOG_DESC","r2_ui/menus/loadout_icons/attachments/hcog_ranger",0,0,0,0
+"aog","attachment","#MOD_AOG_NAME","AOG","#MOD_AOG_DESC","r2_ui/menus/loadout_icons/attachments/aog",0,0,0,0
+"scope_4x","attachment","#MOD_SCOPE_4X_NAME","","#MOD_SCOPE_4X_DESC","r2_ui/menus/loadout_icons/attachments/variable_zoom",0,0,0,0
+"scope_6x","attachment","4x Zoom Scope","4x Zoom","#MOD_SCOPE_4X_DESC","ui/menu/items/attachment_icons/scope_6x",0,0,0,0
+"redline_sight","attachment","#MOD_ZOOM_SIGHT_NAME","#MOD_ZOOM_SIGHT_NAME","#MOD_ZOOM_SIGHT_DESC","r2_ui/menus/loadout_icons/attachments/hcog",0,0,0,0
+"threat_scope","attachment","#MOD_THREAT_SIGHT_NAME","T-Scope","#MOD_THREAT_SIGHT_DESC","r2_ui/menus/loadout_icons/attachments/threat_scope",0,0,0,0
+"extended_ammo","mod","#MOD_EXTENDED_AMMO_NAME","Ext. Mag","#MOD_EXTENDED_AMMO_DESC","rui/pilot_loadout/mods/extended_ammo",0,0,0,0
+"silencer","mod","#MOD_SILENCER_NAME","Suppressor","#MOD_SILENCER_DESC","r2_ui/menus/loadout_icons/attachments/suppressor",0,0,0,0
+"stabilizer","mod","Stabilizer","Stabilizer","Reduces sway when looking down scope","ui/menu/items/mod_icons/stabilizer",0,0,0,0
+"slammer","mod","Slammer","Slammer","Increases rodeo damage","ui/menu/items/mod_icons/slammer",0,0,0,0
+"enhanced_targeting","mod","Enhanced Targeting","E. Targeting","Enables multiple lock-on capability","ui/menu/items/mod_icons/enhanced_targeting",0,0,0,0
+"single_lock","mod","Single Lock","Single Lock","Single Lock on Pilots","ui/menu/items/mod_icons/enhanced_targeting",0,0,0,0
+"ricochet","mod","#MOD_RICOCHET_NAME","Ricochet","#MOD_RICOCHET_DESC","rui/pilot_loadout/mods/ricochet",0,0,0,0
+"ar_trajectory","mod","Ar Trajectory","Ar Trajectory","Displays estimated trajectory","rui/pilot_loadout/mods/ar_trajectory",0,0,0,0
+"alt_spread","mod","Alt Spread","Alt Spread","Alternate spread pattern","ui/temp",0,0,0,0
+"delayed_shot","mod","Soft Launch","Soft Launch","Delayed ignition with fast rockets","ui/temp",0,0,0,0
+"quick_charge","mod","#MOD_CHARGE_HACK_NAME","Charge Hack","#MOD_CHARGE_HACK_DESC","rui/pilot_loadout/mods/charge_hack",0,0,0,0
+"pas_run_and_gun","mod","#MOD_GUNRUNNER_NAME","Gunrunner","#MOD_GUNRUNNER_DESC","rui/pilot_loadout/mods/gunrunner",0,0,0,0
+"tactical_cdr_on_kill","mod","#MOD_TACTIKILL_NAME","Tactikill","#MOD_TACTIKILL_DESC","rui/pilot_loadout/mods/tactikill",0,0,0,0
+"pas_fast_ads","mod","#MOD_GUN_READY_NAME","Fast ADS","#MOD_GUN_READY_DESC","rui/pilot_loadout/mods/gun_ready",0,0,0,0
+"pas_fast_swap","mod","#MOD_SPEED_TRANSITION_NAME","Fast Swap","#MOD_SPEED_TRANSITION_DESC","rui/pilot_loadout/mods/speed_transition",0,0,0,0
+"pas_fast_reload","mod","#GEAR_QUICK_RELOAD","#GEAR_QUICK_RELOAD","#GEAR_QUICK_RELOAD_DESC","rui/pilot_loadout/kit/speed_loader",0,0,0,0
+"pro_screen","mod3","#MOD_PRO_SCREEN_NAME","Pro Screen","#MOD_PRO_SCREEN_DESC","rui/pilot_loadout/mods/pro_screen",0,0,0,0
+"rocket_arena","mod","#MOD_PRO_SCREEN_NAME","Pro Screen","#MOD_PRO_SCREEN_DESC","rui/pilot_loadout/mods/pro_screen",0,0,0,0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/pilot_weapons.csv b/Northstar.CustomServers/mod/scripts/datatable/pilot_weapons.csv
new file mode 100644
index 00000000..d8bbfb0a
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/pilot_weapons.csv
@@ -0,0 +1,33 @@
+itemRef,type,defaultAttachment,defaultMod1,defaultMod2,defaultMod3,hidden,xpPerLevelType,cost
+"mp_weapon_r97","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_alternator_smg","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_car","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_hemlok_smg","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_lmg","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_lstar","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_esaw","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_rspn101","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_vinson","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_hemlok","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_g2","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_shotgun","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_mastiff","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_dmr","PILOT_PRIMARY","","","","",0,"sniper",100
+"mp_weapon_doubletake","PILOT_PRIMARY","","","","",0,"sniper",100
+"mp_weapon_sniper","PILOT_PRIMARY","","","","",0,"sniper",100
+"mp_weapon_epg","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_smr","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_pulse_lmg","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_softball","PILOT_PRIMARY","","","","",0,"default",100
+"mp_weapon_autopistol","PILOT_SECONDARY","","","","",0,"pistol",50
+"mp_weapon_semipistol","PILOT_SECONDARY","","","","",0,"pistol",50
+"mp_weapon_wingman","PILOT_SECONDARY","","","","",0,"pistol",50
+"mp_weapon_shotgun_pistol","PILOT_PRIMARY","","","","",0,"default",50
+"mp_weapon_rocket_launcher","PILOT_SECONDARY","","","","",0,"antititan",50
+"mp_weapon_arc_launcher","PILOT_SECONDARY","","","","",0,"antititan",50
+"mp_weapon_defender","PILOT_SECONDARY","","","","",0,"antititan",50
+"mp_weapon_mgl","PILOT_SECONDARY","","","","",0,"antititan",50
+"melee_pilot_emptyhanded","PILOT_MELEE","","","","",0,"default",100
+"melee_pilot_arena","PILOT_MELEE","","","","",0,"default",100
+"mp_weapon_wingman_n","PILOT_PRIMARY","","","","",0,"default",50
+"mp_weapon_rspn101_og","PILOT_PRIMARY","","","","",0,"default",100 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/playlist_items.csv b/Northstar.CustomServers/mod/scripts/datatable/playlist_items.csv
new file mode 100644
index 00000000..f485fe70
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/playlist_items.csv
@@ -0,0 +1,27 @@
+playlist,name,image,thumbnail,cost
+"at","#PL_attrition","rui/menu/gametype_select/bounty_hunt","rui/menu/gametype_select/playlist_bounty_hunt",5
+"aitdm","#PL_aitdm","rui/menu/gametype_select/attrition","rui/menu/gametype_select/playlist_attrition",5
+"cp","#PL_hardpoint","rui/menu/gametype_select/hardpoint","rui/menu/gametype_select/playlist_amped_hardpoint",5
+"ctf","#PL_capture_the_flag","rui/menu/gametype_select/capture_the_flag","rui/menu/gametype_select/playlist_ctf",5
+"lts","#PL_last_titan_standing","rui/menu/gametype_select/last_titan_standing","rui/menu/gametype_select/playlist_lts",5
+"tdm","#PL_pilot_hunter","rui/menu/gametype_select/pilot_hunter","rui/menu/gametype_select/playlist_skirmish",5
+"varietypack","Variety Pack","rui/menu/gametype_select/variety_pack","rui/menu/gametype_select/playlist_gen_weapon",5
+"coliseum","#PL_coliseum","rui/menu/gametype_select/colosseum","rui/menu/gametype_select/playlist_coliseum",0
+"fnf","Friday Night Fights","rui/menu/gametype_select/friday_night_fights","rui/menu/gametype_select/playlist_gen_star",5
+"ffa","#PL_ffa","rui/menu/gametype_select/free_for_all","rui/menu/gametype_select/playlist_free_for_all",5
+"ps","#PL_pilot_skirmish","rui/menu/gametype_select/pilot_v_pilot","rui/menu/gametype_select/playlist_pilot_vs_pilot",5
+"default","Default","rui/menu/gametype_select/bounty_hunt","rui/menu/gametype_select/playlist_gen_star",5
+"fw","#PL_titan_war","rui/menu/gametype_select/titan_war","rui/menu/gametype_select/playlist_gen_arrow",5
+"private_match","#PL_private_match","rui/menu/gametype_select/private_match","rui/menu/gametype_select/playlist_gen_star",0
+"lf","#PL_speedball","rui/menu/gametype_select/hardcore_pilot_hunter","rui/menu/gametype_select/playlist_live_fire",5
+"angel_city_247","#PL_angel_city","rui/menu/gametype_select/angel_city","rui/menu/gametype_select/playlist_gen_star",5
+"hunted","#PL_hunted","rui/menu/gametype_select/variety_pack","rui/menu/gametype_select/playlist_apex",5
+"mfd","#PL_mfd","rui/menu/gametype_select/pilot_hunter","rui/menu/gametype_select/playlist_marked_for_death",5
+"speedball","#PL_speedball","rui/menu/gametype_select/hardcore_pilot_hunter","rui/menu/gametype_select/playlist_live_fire",0
+"fd_easy","#PL_fd_easy","rui/menu/gametype_select/last_titan_standing","rui/menu/gametype_select/playlist_fd_easy",0
+"fd","#PL_fd_normal","rui/menu/gametype_select/last_titan_standing","rui/menu/gametype_select/playlist_fd_normal",0
+"fd_normal","#PL_fd_normal","rui/menu/gametype_select/last_titan_standing","rui/menu/gametype_select/playlist_fd_normal",0
+"fd_hard","#PL_fd_hard","rui/menu/gametype_select/last_titan_standing","rui/menu/gametype_select/playlist_fd_hard",0
+"fd_master","#PL_fd_master","rui/menu/gametype_select/last_titan_standing","rui/menu/gametype_select/playlist_fd_master",0
+"fd_insane","#PL_fd_insane","rui/menu/gametype_select/last_titan_standing","rui/menu/gametype_select/playlist_fd_insane",0
+"ttdm","#PL_titan_brawl","rui/menu/gametype_select/last_titan_standing","rui/menu/gametype_select/playlist_titan_brawl",5 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/score_events.csv b/Northstar.CustomServers/mod/scripts/datatable/score_events.csv
new file mode 100644
index 00000000..d71d3ac9
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/score_events.csv
@@ -0,0 +1,221 @@
+name,splashText,medalText,pointValue,burnPointValue,pointType,xpValue,xpValueTitan,xpValueWeapon,xpValueFaction,xpType,displayType,medalIcon,conversation,scaleScoreForAutoTitan
+"AttritionAirDroneKilled","#SCORE_EVENT_AT_AIR_DRONE_KILLED","#SCORE_EVENT_AT_AIR_DRONE_KILLED","ATTRITION_SCORE_AIR_DRONE","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"AttritionBossKilled","#SCORE_EVENT_AT_BOSS_KILLED","#SCORE_EVENT_AT_BOSS_KILLED","ATTRITION_SCORE_BOSS","","ASSAULT","0","0","0","0","","CALLINGCARD MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"AttritionBountySurvived","#SCORE_EVENT_AT_BOUNTY_SURVIVED","#SCORE_EVENT_AT_BOUNTY_SURVIVED","ATTRITION_SCORE_BOUNTY_SURVIVAL","","","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/victory","",0
+"AttritionGruntKilled","#SCORE_EVENT_AT_GRUNT_KILLED","#SCORE_EVENT_AT_GRUNT_KILLED","ATTRITION_SCORE_GRUNT","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"AttritionMegaTurretKilled","#SCORE_EVENT_AT_MEGA_TURRET_KILLED","#SCORE_EVENT_AT_MEGA_TURRET_KILLED","ATTRITION_SCORE_MEGATURRET","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"AttritionSentryTurretKilled","#SCORE_EVENT_AT_SENTRY_TURRET_KILLED","#SCORE_EVENT_AT_SENTRY_TURRET_KILLED","ATTRITION_SCORE_SENTRYTURRET","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"AttritionPilotKilled","#SCORE_EVENT_AT_PILOT_KILLED","#SCORE_EVENT_AT_PILOT_KILLED","ATTRITION_SCORE_PILOT","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"AttritionProwlerKilled","#SCORE_EVENT_AT_PROWLER_KILLED","#SCORE_EVENT_AT_PROWLER_KILLED","ATTRITION_SCORE_PROWLER","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"AttritionSpectreKilled","#SCORE_EVENT_AT_SPECTRE_KILLED","#SCORE_EVENT_AT_SPECTRE_KILLED","ATTRITION_SCORE_SPECTRE","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"AttritionStalkerKilled","#SCORE_EVENT_AT_STALKER_KILLED","#SCORE_EVENT_AT_STALKER_KILLED","ATTRITION_SCORE_STALKER","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"AttritionSuperSpectreKilled","#SCORE_EVENT_AT_SUPER_SPECTRE_KILLED","#SCORE_EVENT_AT_SUPER_SPECTRE_KILLED","ATTRITION_SCORE_SUPER_SPECTRE","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"AttritionTitanDoomed","#SCORE_EVENT_AT_TITAN_DOOMED","#SCORE_EVENT_AT_TITAN_DOOMED","ATTRITION_SCORE_TITAN","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"AttritionTitanKilled","#SCORE_EVENT_AT_TITAN_DOOMED","#SCORE_EVENT_AT_TITAN_DOOMED","ATTRITION_SCORE_TITAN","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"AttritionCashedBonus","#SCORE_EVENT_AT_CASHED_BONUS","#SCORE_EVENT_AT_CASHED_BONUS","ATTRITION_SCORE_BONUS","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"AttritionBonusStolen","#SCORE_EVENT_AT_BONUS_STOLEN","#SCORE_EVENT_AT_BONUS_STOLEN","ATTRITION_SCORE_BONUS_STOLEN","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"BombCarry","#SCORE_EVENT_BOMB_CARRY_POINTS","#SCORE_EVENT_BOMB_CARRY_POINTS","GAMEMODE_BOMB_SCORE_CARRY","","DISTANCE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/bomb","",0
+"BombPlant","#SCORE_EVENT_BOMB_PLANT_POINTS","#SCORE_EVENT_BOMB_PLANT_POINTS","GAMEMODE_BOMB_SCORE_PLANT","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/bomb","",0
+"BombDefuse","#SCORE_EVENT_BOMB_DEFUSE_POINTS","#SCORE_EVENT_BOMB_DEFUSE_POINTS","GAMEMODE_BOMB_SCORE_DEFUSE","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/bomb","",0
+"BombExplode","#SCORE_EVENT_BOMB_EXPLODE_POINTS","#SCORE_EVENT_BOMB_EXPLODE_POINTS","GAMEMODE_BOMB_SCORE_EXPLODE","","DETONATION","0","0","0","0","","MEDAL GAMEMODE","rui/medals/bomb","",0
+"ChallengeCompleted","#SCORE_EVENT_CHALLENGE_COMPLETED","#SCORE_EVENT_CHALLENGE_COMPLETED","0","","","0","0","0","0","","MEDAL","rui/medals/victory","",0
+"ChallengePVPKillCount","#SCORE_EVENT_CHALLENGE_PVP_KILLS","#SCORE_EVENT_CHALLENGE_PVP_KILLS","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeATAssault","#SCORE_EVENT_CHALLENGE_AT_ASSAULT","#SCORE_EVENT_CHALLENGE_AT_ASSAULT","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeCPAssault","#SCORE_EVENT_CHALLENGE_CP_ASSAULT","#SCORE_EVENT_CHALLENGE_CP_ASSAULT","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeCPDefense","#SCORE_EVENT_CHALLENGE_CP_DEFENSE","#SCORE_EVENT_CHALLENGE_CP_DEFENSE","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeCTFCapAssist","#SCORE_EVENT_ASSIST_WITH_FLAG_CAP","#SCORE_EVENT_ASSIST_WITH_FLAG_CAP","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeCTFRetAssist","#SCORE_EVENT_ASSIST_WITH_FLAG_RET","#SCORE_EVENT_ASSIST_WITH_FLAG_RET","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeFW","#SCORE_EVENT_USE_N_BATTERIES","#SCORE_EVENT_USE_N_BATTERIES","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeFFA","#SCORE_EVENT_KILL_N_PILOTS","#SCORE_EVENT_KILL_N_PILOTS","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeTDM","#SCORE_EVENT_KILL_N_IN_A_ROW","#SCORE_EVENT_KILL_N_IN_A_ROW","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeTTDM","#SCORE_EVENT_CHALLENGE_COMPLETED","#SCORE_EVENT_CHALLENGE_COMPLETED","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeLTS","#SCORE_EVENT_KILL_N_TITANS","#SCORE_EVENT_KILL_N_TITANS","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeSPEEDBALL","#SCORE_EVENT_KILL_N_PILOTS","#SCORE_EVENT_KILL_N_PILOTS","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeMFD","#SCORE_EVENT_CHALLENGE_COMPLETED","#SCORE_EVENT_CHALLENGE_COMPLETED","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"ChallengeFD","#SCORE_EVENT_CHALLENGE_FD","#SCORE_EVENT_CHALLENGE_FD","","","","1","0","0","0","SCORE_MILESTONE","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"Comeback","#SCORE_EVENT_COMEBACK","#SCORE_EVENT_COMEBACK","POINTVALUE_COMEBACK","","","0","0","0","0","","MEDAL","rui/medals/kill","kc_comeback",0
+"ControlPanelHeavyTurretActivate","#SCORE_EVENT_HEAVY_TURRET_ACTIVATED","#SCORE_EVENT_HEAVY_TURRET_ACTIVATED","POINTVALUE_CONTROL_PANEL_ACTIVATE","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"ControlPanelLightTurretActivate","#SCORE_EVENT_LIGHT_TURRETS_ACTIVATED","#SCORE_EVENT_LIGHT_TURRETS_ACTIVATED","POINTVALUE_CONTROL_PANEL_ACTIVATE_LIGHT","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"ControlPointCapture","#SCORE_EVENT_HARDPOINT_CAPTURED","#SCORE_EVENT_HARDPOINT_CAPTURED","POINTVALUE_HARDPOINT_CAPTURE","","ASSAULT","0","0","0","0","","CALLINGCARD MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"ControlPointCaptureAssist","#SCORE_EVENT_HARDPOINT_CAPTURE_ASSIST","#SCORE_EVENT_HARDPOINT_CAPTURE_ASSIST","POINTVALUE_HARDPOINT_CAPTURE_ASSIST","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"ControlPointHold","#SCORE_EVENT_HARDPOINT_HOLD","#SCORE_EVENT_HARDPOINT_HOLD","POINTVALUE_HARDPOINT_HOLD","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"ControlPointTake","#SCORE_EVENT_HARDPOINT_TAKE","#SCORE_EVENT_HARDPOINT_TAKE","POINTVALUE_HARDPOINT_HOLD","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"ControlPointAmped","#SCORE_EVENT_HARDPOINT_AMPED","#SCORE_EVENT_HARDPOINT_AMPED","POINTVALUE_HARDPOINT_AMPED","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"ControlPointAmpedHold","#SCORE_EVENT_HARDPOINT_AMPED_HOLD","#SCORE_EVENT_HARDPOINT_AMPED_HOLD","POINTVALUE_HARDPOINT_AMPED_HOLD","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"ControlPointNeutralize","#SCORE_EVENT_HARDPOINT_NEUTRALIZED","#SCORE_EVENT_HARDPOINT_NEUTRALIZED","POINTVALUE_HARDPOINT_NEUTRALIZE","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"ControlPointNeutralizeAssist","#SCORE_EVENT_HARDPOINT_NEUTRALIZE_ASSIST","#SCORE_EVENT_HARDPOINT_NEUTRALIZE_ASSIST","POINTVALUE_HARDPOINT_NEUTRALIZE_ASSIST","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"Damage","#SCORE_EVENT_DAMAGE_GENERIC","#SCORE_EVENT_DAMAGE_GENERIC","0","","","0","0","0","0","","CENTER","rui/medals/kill","",0
+"DamageTitan","#SCORE_EVENT_DAMAGE_TITAN","#SCORE_EVENT_DAMAGE_TITAN","0","","","0","0","0","0","","CENTER","rui/medals/kill","",0
+"StealMeter","#SCORE_EVENT_STEAL_METER","#SCORE_EVENT_STEAL_METER","0","","","0","0","0","0","","CENTER","rui/medals/kill","",0
+"Destored_Proximity_Mine","#SCORE_EVENT_DESTROYED_PROXIMITY_CHARGE","#SCORE_EVENT_DESTROYED_PROXIMITY_CHARGE","POINTVALUE_DESTROYED_PROXIMITY_MINE","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"Destroyed_Satchel","#SCORE_EVENT_DESTROYED_SATCHEL_CHARGE","#SCORE_EVENT_DESTROYED_SATCHEL_CHARGE","POINTVALUE_DESTROYED_SATCHEL","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"Dominating","#SCORE_EVENT_DOMINATING","#SCORE_EVENT_DOMINATING","POINTVALUE_DOMINATING","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","kc_dominating",0
+"DoomAutoTitan","#MEDAL_DOOMED_TITAN","#MEDAL_DOOMED_TITAN","100","BURN_METER_SMALL_POINT_VALUE","","0","0","0","0","","MEDAL","rui/medals/kill_robot","",0
+"DoomTitan","#MEDAL_DOOMED_TITAN","#MEDAL_DOOMED_TITAN","100","BURN_METER_SMALL_POINT_VALUE","","0","0","0","0","","BIG MEDAL","rui/medals/kill_robot","",0
+"DoubleKill","#SCORE_EVENT_DOUBLE_KILL","#SCORE_EVENT_DOUBLE_KILL","POINTVALUE_DOUBLEKILL","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","kc_doublekill",0
+"EliminatePilot","#SCORE_EVENT_ELIMINATED_PILOT","#MEDAL_ELIMINATED_PILOT","POINTVALUE_ELIMINATE_PILOT","BURN_METER_SMALL_POINT_VALUE","","0","0","1","0","KILL","BIG MEDAL CENTER","rui/medals/kill","",0
+"EliminateTitan","#MEDAL_ELIMINATED_TITAN","#MEDAL_ELIMINATED_TITAN","1","BURN_METER_SMALL_POINT_VALUE","","0","0","1","0","","CALLINGCARD MEDAL","rui/medals/kill_robot","",0
+"EliminateAutoTitan","#MEDAL_ELIMINATED_TITAN","#MEDAL_ELIMINATED_TITAN","1","BURN_METER_SMALL_POINT_VALUE","","0","0","1","0","","CALLINGCARD MEDAL","rui/medals/kill_robot","",0
+"FirstStrike","#SCORE_EVENT_FIRST_STRIKE","#SCORE_EVENT_FIRST_STRIKE","POINTVALUE_FIRST_STRIKE","","","0","0","0","0","","CALLINGCARD MEDAL","rui/medals/first_strike","kc_firstblood",0
+"FirstTitanfall","#SCORE_EVENT_FIRST_TITANFALL","#SCORE_EVENT_FIRST_TITANFALL","POINTVALUE_FIRST_TITANFALL","","","0","1","0","0","TITAN_FALL","CALLINGCARD MEDAL","rui/medals/titanfall","",0
+"FishInBarrel","#SCORE_EVENT_FISH_IN_A_BARREL","#SCORE_EVENT_FISH_IN_A_BARREL","POINTVALUE_FISHINBARREL","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"FlagCapture","#SCORE_EVENT_FLAG_CAPTURE","#SCORE_EVENT_FLAG_CAPTURE","POINTVALUE_FLAG_CAPTURE","","","0","0","0","0","","CALLINGCARD MEDAL GAMEMODE","rui/medals/ctf","",0
+"FlagTaken","#SCORE_EVENT_FLAG_TAKEN","#SCORE_EVENT_FLAG_TAKEN","POINTVALUE_FLAG_TAKEN","","","0","0","0","0","","CALLINGCARD MEDAL GAMEMODE","rui/medals/ctf","",0
+"FlagCaptureAssist","#SCORE_EVENT_FLAG_CAPTURE_ASSIST","#SCORE_EVENT_FLAG_CAPTURE_ASSIST","POINTVALUE_FLAG_CAPTURE_ASSIST","","","0","0","0","0","","MEDAL","rui/medals/ctf","",0
+"FlagCarrierKill","#SCORE_EVENT_KILLED_FLAG_CARRIER","#SCORE_EVENT_KILLED_FLAG_CARRIER","POINTVALUE_FLAG_CARRIER_KILL","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/ctf","",0
+"FlagReturn","#SCORE_EVENT_FLAG_RETURN","#SCORE_EVENT_FLAG_RETURN","POINTVALUE_FLAG_RETURN","","","0","0","0","0","","CALLINGCARD MEDAL GAMEMODE","rui/medals/ctf","",0
+"FlyerKill","#SCORE_EVENT_KILLED_FLYER","#MEDAL_KILLED_FLYER","POINTVALUE_KILL_FLYER","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"GetToChopper","#SCORE_EVENT_GET_TO_THE_CHOPPER","#SCORE_EVENT_GET_TO_THE_CHOPPER","POINTVALUE_GET_TO_CHOPPER","","","0","0","0","0","","MEDAL","rui/medals/extract","",0
+"GiveRide","#SCORE_EVENT_GIVE_A_FRIEND_A_LIFT","#SCORE_EVENT_GIVE_A_FRIEND_A_LIFT","POINTVALUE_FRIEND_RIDE","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"HappyHourBonus","#SCORE_EVENT_HAPPY_HOUR_BONUS","#SCORE_EVENT_HAPPY_HOUR_BONUS","","","","5","0","0","0","HAPPY_HOUR","MEDAL CHALLENGE GAMEMODE","rui/medals/victory","",0
+"HardpointAssault","#SCORE_EVENT_HARDPOINT_ASSAULT","#SCORE_EVENT_HARDPOINT_ASSAULT","POINTVALUE_HARDPOINT_ASSAULT","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"HardpointDefense","#SCORE_EVENT_HARDPOINT_DEFENSE","#SCORE_EVENT_HARDPOINT_DEFENSE","POINTVALUE_HARDPOINT_DEFENSE","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"HardpointPerimeterDefense","#SCORE_EVENT_HARDPOINT_PERIMETER_DEFENSE","#SCORE_EVENT_HARDPOINT_PERIMETER_DEFENSE","POINTVALUE_HARDPOINT_PERIMETER_DEFENSE","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"HardpointSiege","#SCORE_EVENT_HARDPOINT_SIEGE","#SCORE_EVENT_HARDPOINT_SIEGE","POINTVALUE_HARDPOINT_SIEGE","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"HardpointSnipe","#SCORE_EVENT_HARDPOINT_RANGED_SUPPORT","#SCORE_EVENT_HARDPOINT_RANGED_SUPPORT","POINTVALUE_HARDPOINT_SNIPE","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"Headshot","#MEDAL_HEADSHOT","#MEDAL_HEADSHOT","POINTVALUE_HEADSHOT","","","0","0","0","0","","MEDAL","rui/medals/headshot","",0
+"HitchRide","#SCORE_EVENT_HITCH_A_RIDE","#SCORE_EVENT_HITCH_A_RIDE","POINTVALUE_RODEOD_FRIEND","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"HotZoneExtract","#SCORE_EVENT_HOT_ZONE_EXTRACT","#SCORE_EVENT_HOT_ZONE_EXTRACT","POINTVALUE_HOTZONE_EXTRACT","","","1","0","0","1","EVAC","MEDAL","rui/medals/extract","",0
+"KillAutoTitan","#MEDAL_KILLED_TITAN","#MEDAL_KILLED_TITAN","POINTVALUE_KILL_TITAN","BURN_METER_SMALL_POINT_VALUE","","0","0","1","0","","CALLINGCARD MEDAL","rui/medals/kill_robot","",0
+"KillDrone","#SCORE_EVENT_KILLED_DRONE","#MEDAL_KILLED_DRONE","POINTVALUE_KILL_DRONE","","","0","0","0","0","","MEDAL","rui/medals/kill_robot","",0
+"KillDropship","#SCORE_EVENT_EVAC_DENIED","#SCORE_EVENT_EVAC_DENIED","POINTVALUE_EVAC_DENIED","","","0","0","0","0","","MEDAL","rui/medals/extract","",0
+"KilledEscapee","#SCORE_EVENT_KILLED_EVACUATING_ENEMY","#MEDAL_KILLED_EVACUATING_ENEMY","POINTVALUE_KILLED_ESCAPEE","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","",0
+"KilledMVP","#SCORE_EVENT_KILLED_MVP","#MEDAL_KILLED_MVP","POINTVALUE_KILLED_MVP","","","0","0","0","0","","MEDAL","rui/medals/kill","",0
+"KillGrunt","#SCORE_EVENT_KILLED_MILITIA","#SCORE_EVENT_KILLED_MILITIA","POINTVALUE_KILL_FIRETEAM_AI","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/kill","",0
+"KillHeavyTurret","#SCORE_EVENT_KILLED_HEAVY_TURRET","#SCORE_EVENT_KILLED_HEAVY_TURRET","POINTVALUE_KILL_HEAVY_TURRET","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"KillingSpree","#SCORE_EVENT_KILLING_SPREE","#SCORE_EVENT_KILLING_SPREE","POINTVALUE_KILLINGSPREE","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","kc_killingspree",0
+"KillLightTurret","#SCORE_EVENT_KILLED_LIGHT_TURRET","#SCORE_EVENT_KILLED_LIGHT_TURRET","POINTVALUE_KILL_LIGHT_TURRET","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"KillPilot","#SCORE_EVENT_KILLED_PILOT","#MEDAL_KILLED_PILOT","1","BURN_METER_SMALL_POINT_VALUE","","0","0","1","0","KILL","BIG MEDAL CENTER","rui/medals/kill","",0
+"KillProwler","#SCORE_EVENT_KILLED_PROWLER","#MEDAL_KILLED_PROWLER","POINTVALUE_KILL_PROWLER","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"KillRescueShip","#SCORE_EVENT_EVAC_DENIED","#SCORE_EVENT_EVAC_DENIED","POINTVALUE_EVAC_DENIED","","","0","0","0","0","","MEDAL","rui/medals/extract","",0
+"KillSpectre","#SCORE_EVENT_KILLED_SPECTRE","#MEDAL_KILLED_SPECTRE","POINTVALUE_KILL_SPECTRE","","","0","0","0","0","","MEDAL","rui/medals/kill_robot","",0
+"KillStalker","#SCORE_EVENT_KILLED_STALKER","#MEDAL_KILLED_STALKER","POINTVALUE_KILL_STALKER","","","0","0","0","0","","MEDAL","rui/medals/kill_robot","",0
+"KillHackedSpectre","#SCORE_EVENT_KILLED_HACKED_SPECTRE","#MEDAL_KILLED_SPECTRE","POINTVALUE_KILL_SPECTRE","","","0","0","0","0","","MEDAL","rui/medals/kill_robot","",0
+"KillSuperSpectre","#SCORE_EVENT_KILLED_SUPER_SPECTRE","#MEDAL_KILLED_SUPER_SPECTRE","POINTVALUE_KILL_SUPER_SPECTRE","","","0","0","0","0","","MEDAL","rui/medals/kill_robot","",0
+"KillTitan","#MEDAL_KILLED_TITAN","#MEDAL_KILLED_TITAN","POINTVALUE_KILL_TITAN","BURN_METER_SMALL_POINT_VALUE","","0","0","1","0","KILL","CALLINGCARD MEDAL CENTER","rui/medals/kill_robot","",0
+"TitanKillTitan","#MEDAL_KILLED_TITAN","#MEDAL_KILLED_TITAN","POINTVALUE_KILL_TITAN","BURN_METER_SMALL_POINT_VALUE","","0","1","1","0","KILL","CALLINGCARD MEDAL CENTER","rui/medals/kill_robot","",0
+"LeechDrone","#SCORE_EVENT_LEECHED_DRONE","#SCORE_EVENT_LEECHED_DRONE","POINTVALUE_LEECH_DRONE","","","0","0","0","0","","MEDAL","rui/medals/kill_robot","",0
+"LeechSpectre","#SCORE_EVENT_LEECHED_SPECTRE","#SCORE_EVENT_LEECHED_SPECTRE","POINTVALUE_LEECH_SPECTRE","","","0","0","0","0","","MEDAL","rui/medals/kill_robot","",0
+"LeechSuperSpectre","#SCORE_EVENT_LEECHED_SUPER_SPECTRE","#SCORE_EVENT_LEECHED_SUPER_SPECTRE","POINTVALUE_LEECH_SUPER_SPECTRE","","","0","0","0","0","","MEDAL","rui/medals/kill_robot","",0
+"MarkedEscort","#SCORE_EVENT_PROTECTED_MARKED_TARGET","#SCORE_EVENT_PROTECTED_MARKED_TARGET","POINTVALUE_MARKED_ESCORT","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/kill","",0
+"MarkedKilledMarked","#SCORE_EVENT_MARKED_KILLED_MARKED","#SCORE_EVENT_MARKED_KILLED_MARKED","POINTVALUE_MARKED_KILLED_MARKED","","","0","0","0","0","","MEDAL GAMEMODE","rui/hud/gametype_icons/mfd/mfd_enemy","",0
+"MarkedOutlastedEnemyMarked","#SCORE_EVENT_MARKED_OUTLASTED_ENEMY_MARK","#SCORE_EVENT_MARKED_OUTLASTED_ENEMY_MARK","POINTVALUE_MARKED_OUTLASTED_ENEMY_MARKED","","","0","0","0","0","","MEDAL GAMEMODE","rui/hud/gametype_icons/mfd/mfd_friendly","",0
+"MarkedSurvival","#SCORE_EVENT_MARKED_SURVIVAL","#SCORE_EVENT_MARKED_SURVIVAL","POINTVALUE_MARKED_SURVIVAL","","","0","0","0","0","","MEDAL GAMEMODE","rui/hud/gametype_icons/mfd/mfd_friendly","",0
+"MarkedTargetKilled","#SCORE_EVENT_KILLED_MARKED_TARGET","#SCORE_EVENT_KILLED_MARKED_TARGET","POINTVALUE_MARKED_TARGET_KILLED","","","0","0","0","0","","MEDAL GAMEMODE","rui/hud/gametype_icons/mfd/mfd_enemy","",0
+"MatchComplete","#SCORE_EVENT_COMPLETION","#SCORE_EVENT_COMPLETION","POINTVALUE_MATCH_COMPLETION","","","1","0","0","0","MATCH_COMPLETED","MEDAL","rui/medals/victory","",0
+"MatchVictory","#SCORE_EVENT_VICTORY","#SCORE_EVENT_VICTORY","POINTVALUE_MATCH_VICTORY","","","1","0","0","1","MATCH_VICTORY","MEDAL GAMEMODE","rui/medals/victory","",0
+"Mayhem","#SCORE_EVENT_MAYHEM","#SCORE_EVENT_MAYHEM","POINTVALUE_MAYHEM","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","",0
+"MegaKill","#SCORE_EVENT_MEGA_KILL","#SCORE_EVENT_MEGA_KILL","POINTVALUE_MEGAKILL","","","0","0","0","0","","CALLINGCARD MEDAL","rui/medals/kill_multiple","kc_megakill",0
+"Nemesis","#SCORE_EVENT_NEMESIS","#SCORE_EVENT_NEMESIS","POINTVALUE_NEMESIS","","","0","0","0","0","","MEDAL","rui/medals/kill","kc_retribution",0
+"NewPlayerBonus","#SCORE_EVENT_NEW_PLAYER_BONUS","#SCORE_EVENT_NEW_PLAYER_BONUS","POINTVALUE_MATCH_VICTORY","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/victory","",0
+"NPCHeadshot","#SCORE_EVENT_HEADSHOT","#SCORE_EVENT_HEADSHOT","POINTVALUE_NPC_HEADSHOT","","","0","0","0","0","","MEDAL","rui/medals/headshot","",0
+"Onslaught","#SCORE_EVENT_ONSLAUGHT","#SCORE_EVENT_ONSLAUGHT","POINTVALUE_ONSLAUGHT","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","",0
+"PilotAmmoPickup","Picked Up Ammo","Picked Up Ammo","50","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",1
+"PilotAssist","#MEDAL_ASSIST_PILOT","#MEDAL_ASSIST_PILOT","POINTVALUE_ASSIST","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/kill","",0
+"PilotBatteryApplied","#SCORE_EVENT_BATTERY_APPLY","#SCORE_EVENT_BATTERY_APPLY","50","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",1
+"PilotBatteryPickup","#SCORE_EVENT_BATTERY_PICKUP","#SCORE_EVENT_BATTERY_PICKUP","50","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",1
+"PilotBatteryStolen","#SCORE_EVENT_BATTERY_STEAL","#SCORE_EVENT_BATTERY_STEAL","50","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",1
+"QuickRevenge","#SCORE_EVENT_QUICK_REVENGE","#SCORE_EVENT_QUICK_REVENGE","POINTVALUE_REVENGE_QUICK","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","",0
+"Rampage","#SCORE_EVENT_RAMPAGE","#SCORE_EVENT_RAMPAGE","POINTVALUE_RAMPAGE","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","kc_rampage",0
+"Revenge","#SCORE_EVENT_REVENGE","#SCORE_EVENT_REVENGE","POINTVALUE_REVENGE","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","",0
+"RodeoEnemyTitan","#SCORE_EVENT_RODEOED_ENEMY_TITAN","#SCORE_EVENT_RODEOED_ENEMY_TITAN","POINTVALUE_RODEOD","","","0","0","0","0","","MEDAL","rui/medals/medal_prototype","",0
+"RoundComplete","#SCORE_EVENT_ROUND_COMPLETION","#SCORE_EVENT_ROUND_COMPLETION","POINTVALUE_ROUND_COMPLETION","","","0","0","0","0","","MEDAL","rui/medals/victory","",0
+"RoundVictory","#SCORE_EVENT_ROUND_WIN","#SCORE_EVENT_ROUND_WIN","POINTVALUE_ROUND_WIN","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/victory","",0
+"SelfBonusKilledAll","#SCORE_EVENT_SOLO_KILLED_ALL_COMBATANTS","#SCORE_EVENT_SOLO_KILLED_ALL_COMBATANTS","POINTVALUE_FULL_TEAM_KILL_SOLO","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","",0
+"Showstopper","#SCORE_EVENT_SHOWSTOPPER","#SCORE_EVENT_SHOWSTOPPER","POINTVALUE_SHOWSTOPPER","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","kc_iced",0
+"SoleSurvivor","#SCORE_EVENT_SOLE_SURVIVOR","#SCORE_EVENT_SOLE_SURVIVOR","POINTVALUE_SOLE_SURVIVOR","","","0","0","0","0","","MEDAL","rui/medals/extract","",0
+"SpotAssist","#SCORE_EVENT_SPOT_ASSIST","#SCORE_EVENT_SPOT_ASSIST","POINTVALUE_ASSIST","","","0","0","0","0","","MEDAL","rui/medals/headshot","",0
+"TeamBonusFullEvac","#SCORE_EVENT_TEAM_BONUS_FULL_TEAM_EVAC","#SCORE_EVENT_TEAM_BONUS_FULL_TEAM_EVAC","POINTVALUE_FULL_TEAM_EVAC","","","0","0","0","0","","MEDAL","rui/medals/extract","",0
+"TeamBonusKilledAll","#SCORE_EVENT_TEAM_BONUS_KILLED_ALL_COMBATANTS","#SCORE_EVENT_TEAM_BONUS_KILLED_ALL_COMBATANTS","POINTVALUE_FULL_TEAM_KILL","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","",0
+"TitanAssist","#MEDAL_ASSIST_TITAN","#MEDAL_ASSIST_TITAN","POINTVALUE_ASSIST_TITAN","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"TitanCoreEarned","#SCORE_EVENT_CORE_EARNED","#SCORE_EVENT_CORE_EARNED","POINTVALUE_CALLED_IN_TITAN","","","0","1","0","0","TITAN_CORE_EARNED","CALLINGCARD MEDAL","rui/medals/titanfall","",0
+"Titanfall","#SCORE_EVENT_TITANFALL","#SCORE_EVENT_TITANFALL","POINTVALUE_CALLED_IN_TITAN","","","0","1","0","0","TITAN_FALL","CALLINGCARD MEDAL","rui/medals/titanfall","",0
+"TripleKill","#SCORE_EVENT_TRIPLE_KILL","#SCORE_EVENT_TRIPLE_KILL","POINTVALUE_TRIPLEKILL","","","0","0","0","0","","MEDAL","rui/medals/kill_multiple","kc_triplekill",0
+"VictoryKill","#SCORE_EVENT_VICTORY_KILL","#SCORE_EVENT_VICTORY_KILL","POINTVALUE_VICTORYKILL","BURN_METER_SMALL_POINT_VALUE","","0","0","0","0","","CALLINGCARD MEDAL GAMEMODE","rui/medals/kill","",0
+"FactionLevelUp","","","","","","1","0","0","0","FACTION_LEVELED","","","",0
+"TitanLevelUp","","","","","","1","0","0","0","TITAN_LEVELED","","","",0
+"WeaponLevelUp","","","","","","1","0","0","0","WEAPON_LEVELED","","","",0
+"FortWarAssault","#SCORE_EVENT_FW_ASSAULT","#SCORE_EVENT_FW_ASSAULT","POINTVALUE_FW_ASSAULT","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarDefense","#SCORE_EVENT_FW_DEFENSE","#SCORE_EVENT_FW_DEFENSE","POINTVALUE_FW_DEFENSE","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarPerimeterDefense","#SCORE_EVENT_FW_PERIMETER_DEFENSE","#SCORE_EVENT_FW_PERIMETER_DEFENSE","POINTVALUE_FW_PERIMETER_DEFENSE","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarSiege","#SCORE_EVENT_FW_SIEGE","#SCORE_EVENT_FW_SIEGE","POINTVALUE_FW_SIEGE","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarSnipe","#SCORE_EVENT_FW_RANGED_SUPPORT","#SCORE_EVENT_FW_RANGED_SUPPORT","POINTVALUE_FW_SNIPE","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarBaseConstruction","#SCORE_EVENT_FW_BASE_CONSTRUCTION","#SCORE_EVENT_FW_BASE_CONSTRUCTION","POINTVALUE_FW_BASE_CONSTRUCTION","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarForwardConstruction","#SCORE_EVENT_FW_FORWARD_CONSTRUCTION","#SCORE_EVENT_FW_FORWARD_CONSTRUCTION","POINTVALUE_FW_FORWARD_CONSTRUCTION","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarInvasiveConstruction","#SCORE_EVENT_FW_INVASIVE_CONSTRUCTION","#SCORE_EVENT_FW_INVASIVE_CONSTRUCTION","POINTVALUE_FW_INVASIVE_CONSTRUCTION","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarShieldConstruction","#SCORE_EVENT_FW_SHIELD_CONSTRUCTION","#SCORE_EVENT_FW_SHIELD_CONSTRUCTION","POINTVALUE_FW_SHIELD_CONSTRUCTION","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarResourceDenial","#SCORE_EVENT_FW_RESOURCE_DENIAL","#SCORE_EVENT_FW_RESOURCE_DENIAL","POINTVALUE_FW_RESOURCE_DENIAL","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarTowerDamage","#SCORE_EVENT_FW_TOWER_DAMAGE","#SCORE_EVENT_FW_TOWER_DAMAGE","POINTVALUE_FW_TOWER_DAMAGE","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarTowerDefense","#SCORE_EVENT_FW_TOWER_DEFENSE","#SCORE_EVENT_FW_TOWER_DEFENSE","POINTVALUE_FW_TOWER_DEFENSE","","DEFENSE","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarTeamTurretControlBonus_One","#SCORE_EVENT_FW_TURRET_CONTROL_ONE","#SCORE_EVENT_FW_TURRET_CONTROL_ONE","POINTVALUE_FW_TEAM_TURRET_CONTROL","","DEFENSE","0","0","0","0","","MEDAL","rui/medals/hardpoint","",0
+"FortWarTeamTurretControlBonus_Two","#SCORE_EVENT_FW_TURRET_CONTROL_TWO","#SCORE_EVENT_FW_TURRET_CONTROL_TWO","POINTVALUE_FW_TEAM_TURRET_CONTROL","","DEFENSE","0","0","0","0","","MEDAL","rui/medals/hardpoint","",0
+"FortWarTeamTurretControlBonus_Three","#SCORE_EVENT_FW_TURRET_CONTROL_THREE","#SCORE_EVENT_FW_TURRET_CONTROL_THREE","POINTVALUE_FW_TEAM_TURRET_CONTROL","","DEFENSE","0","0","0","0","","MEDAL","rui/medals/hardpoint","",0
+"FortWarTeamTurretControlBonus_Four","#SCORE_EVENT_FW_TURRET_CONTROL_FOUR","#SCORE_EVENT_FW_TURRET_CONTROL_FOUR","POINTVALUE_FW_TEAM_TURRET_CONTROL","","DEFENSE","0","0","0","0","","MEDAL","rui/medals/hardpoint","",0
+"FortWarTeamTurretControlBonus_Five","#SCORE_EVENT_FW_TURRET_CONTROL_FIVE","#SCORE_EVENT_FW_TURRET_CONTROL_FIVE","POINTVALUE_FW_TEAM_TURRET_CONTROL","","DEFENSE","0","0","0","0","","MEDAL","rui/medals/hardpoint","",0
+"FortWarTeamTurretControlBonus_Six","#SCORE_EVENT_FW_TURRET_CONTROL_SIX","#SCORE_EVENT_FW_TURRET_CONTROL_SIX","POINTVALUE_FW_TEAM_TURRET_CONTROL","","DEFENSE","0","0","0","0","","MEDAL","rui/medals/hardpoint","",0
+"FortWarSecuringGatheredResources","#SCORE_EVENT_FW_SECURING_RESOURCES","#SCORE_EVENT_FW_SECURING_RESOURCES","POINTVALUE_FW_SECURING_RESOURCES","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"FortWarShieldDestroyed","#SCORE_EVENT_FW_DESTROYED_TURRET_SHIELD","#SCORE_EVENT_FW_DESTROYED_TURRET_SHIELD","POINTVALUE_FW_DESTROY_TURRET_SHIELD","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/hardpoint","",0
+"HuntedEliminatePilot","#SCORE_EVENT_HUNTED_HUNTER_KILLED","#SCORE_EVENT_HUNTED_HUNTER_KILLED","POINTVALUE_HUNTED_ELIMINATE_HUNTER","","DEFENSE","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"HuntedEliminateGrunt","#SCORE_EVENT_HUNTED_GRUNT_KILLED","#SCORE_EVENT_HUNTED_GRUNT_KILLED","POINTVALUE_HUNTED_ELIMINATE_GRUNT","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"HuntedEliminateSquad","#SCORE_EVENT_HUNTED_SQUAD_KILLED","#SCORE_EVENT_HUNTED_SQUAD_KILLED","POINTVALUE_HUNTED_ELIMINATE_SQUAD","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_multiple","",0
+"HuntedAquireAsset","#SCORE_EVENT_HUNTED_AQUIRE_ASSET","#SCORE_EVENT_HUNTED_AQUIRE_ASSET","POINTVALUE_HUNTED_AQUIRE_ASSET","","DEFENSE","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"HuntedSecureAsset","#SCORE_EVENT_HUNTED_AQUIRE_SECURED","#SCORE_EVENT_HUNTED_AQUIRE_SECURED","POINTVALUE_HUNTED_SECURE_ASSET","","DEFENSE","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"HuntedExtractAsset","#SCORE_EVENT_HUNTED_AQUIRE_EXTRACTED","#SCORE_EVENT_HUNTED_AQUIRE_EXTRACTED","POINTVALUE_HUNTED_EXTRACT_ASSET","","DEFENSE","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"HuntedSurvival","#SCORE_EVENT_HUNTED_OBJECTIVE_SURVIVAL","#SCORE_EVENT_HUNTED_OBJECTIVE_SURVIVAL","POINTVALUE_HUNTED_OBJECTIVE_SURVIVAL","","DEFENSE","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"HuntedMissionSurvival","#SCORE_EVENT_HUNTED_MISSION_SURVIVAL","#SCORE_EVENT_HUNTED_MISSION_SURVIVAL","POINTVALUE_HUNTED_MISSION_SURVIVAL","","DEFENSE","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"MFDMarked","#SCORE_EVENT_MARKED_FOR_DEATH_TARGET","#SCORE_EVENT_MARKED_FOR_DEATH_TARGET","POINTVALUE_MARKED_TARGET","","","0","1","0","0","","CALLINGCARD MEDAL","rui/medals/titanfall","",0
+"BombPlantRaid","#SCORE_EVENT_BOMB_PLANT_POINTS","#SCORE_EVENT_BOMB_PLANT_POINTS","GAMEMODE_RAID_SCORE_PLANT","","ASSAULT","0","0","0","0","","MEDAL GAMEMODE","rui/medals/bomb","",0
+"ATCOOPAirDroneKilled","#SCORE_EVENT_AT_AIR_DRONE_KILLED","#SCORE_EVENT_AT_AIR_DRONE_KILLED","ATCOOP_SCORE_AIR_DRONE","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"ATCOOPBossKilled","#SCORE_EVENT_AT_BOSS_KILLED","#SCORE_EVENT_AT_BOSS_KILLED","ATCOOP_SCORE_BOSS","","ASSAULT","0","0","0","0","","CALLINGCARD MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"ATCOOPBountySurvived","#SCORE_EVENT_AT_BOUNTY_SURVIVED","#SCORE_EVENT_AT_BOUNTY_SURVIVED","ATCOOP_SCORE_BOUNTY_SURVIVAL","","","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/victory","",0
+"ATCOOPGruntKilled","#SCORE_EVENT_AT_GRUNT_KILLED","#SCORE_EVENT_AT_GRUNT_KILLED","ATCOOP_SCORE_GRUNT","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"ATCOOPMegaTurretKilled","#SCORE_EVENT_AT_MEGA_TURRET_KILLED","#SCORE_EVENT_AT_MEGA_TURRET_KILLED","ATCOOP_SCORE_MEGATURRET","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"ATCOOPSentryTurretKilled","#SCORE_EVENT_AT_SENTRY_TURRET_KILLED","#SCORE_EVENT_AT_SENTRY_TURRET_KILLED","ATCOOP_SCORE_SENTRYTURRET","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"ATCOOPPilotKilled","#SCORE_EVENT_AT_PILOT_KILLED","#SCORE_EVENT_AT_PILOT_KILLED","ATCOOP_SCORE_PILOT","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"ATCOOPProwlerKilled","#SCORE_EVENT_AT_PROWLER_KILLED","#SCORE_EVENT_AT_PROWLER_KILLED","ATCOOP_SCORE_PROWLER","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"ATCOOPSpectreKilled","#SCORE_EVENT_AT_SPECTRE_KILLED","#SCORE_EVENT_AT_SPECTRE_KILLED","ATCOOP_SCORE_SPECTRE","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"ATCOOPStalkerKilled","#SCORE_EVENT_AT_STALKER_KILLED","#SCORE_EVENT_AT_STALKER_KILLED","ATCOOP_SCORE_STALKER","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"ATCOOPSuperSpectreKilled","#SCORE_EVENT_AT_SUPER_SPECTRE_KILLED","#SCORE_EVENT_AT_SUPER_SPECTRE_KILLED","ATCOOP_SCORE_SUPER_SPECTRE","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"ATCOOPTitanDoomed","#SCORE_EVENT_AT_TITAN_DOOMED","#SCORE_EVENT_AT_TITAN_DOOMED","ATCOOP_SCORE_TITAN","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"ATCOOPTitanKilled","#SCORE_EVENT_AT_TITAN_DOOMED","#SCORE_EVENT_AT_TITAN_DOOMED","ATCOOP_SCORE_TITAN","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"ATCOOPCashedBonus","#SCORE_EVENT_AT_CASHED_BONUS","#SCORE_EVENT_AT_CASHED_BONUS","ATCOOP_SCORE_BONUS","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"ATCOOPBonusStolen","#SCORE_EVENT_AT_BONUS_STOLEN","#SCORE_EVENT_AT_BONUS_STOLEN","ATCOOP_SCORE_BONUS_STOLEN","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXAirDroneKilled","#SCORE_EVENT_AT_AIR_DRONE_KILLED","#SCORE_EVENT_AT_AIR_DRONE_KILLED","PVE_SANDBOX_SCORE_AIR_DRONE","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXBossKilled","#SCORE_EVENT_AT_BOSS_KILLED","#SCORE_EVENT_AT_BOSS_KILLED","PVE_SANDBOX_SCORE_BOSS","","ASSAULT","0","0","0","0","","CALLINGCARD MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXBountySurvived","#SCORE_EVENT_AT_BOUNTY_SURVIVED","#SCORE_EVENT_AT_BOUNTY_SURVIVED","PVE_SANDBOX_SCORE_BOUNTY_SURVIVAL","","","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/victory","",0
+"PVESANDBOXGruntKilled","#SCORE_EVENT_AT_GRUNT_KILLED","#SCORE_EVENT_AT_GRUNT_KILLED","PVE_SANDBOX_SCORE_GRUNT","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"PVESANDBOXMegaTurretKilled","#SCORE_EVENT_AT_MEGA_TURRET_KILLED","#SCORE_EVENT_AT_MEGA_TURRET_KILLED","PVE_SANDBOX_SCORE_MEGATURRET","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXSentryTurretKilled","#SCORE_EVENT_AT_SENTRY_TURRET_KILLED","#SCORE_EVENT_AT_SENTRY_TURRET_KILLED","PVE_SANDBOX_SCORE_SENTRYTURRET","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXPilotKilled","#SCORE_EVENT_AT_PILOT_KILLED","#SCORE_EVENT_AT_PILOT_KILLED","PVE_SANDBOX_SCORE_PILOT","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"PVESANDBOXProwlerKilled","#SCORE_EVENT_AT_PROWLER_KILLED","#SCORE_EVENT_AT_PROWLER_KILLED","PVE_SANDBOX_SCORE_PROWLER","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"PVESANDBOXSpectreKilled","#SCORE_EVENT_AT_SPECTRE_KILLED","#SCORE_EVENT_AT_SPECTRE_KILLED","PVE_SANDBOX_SCORE_SPECTRE","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXStalkerKilled","#SCORE_EVENT_AT_STALKER_KILLED","#SCORE_EVENT_AT_STALKER_KILLED","PVE_SANDBOX_SCORE_STALKER","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXSuperSpectreKilled","#SCORE_EVENT_AT_SUPER_SPECTRE_KILLED","#SCORE_EVENT_AT_SUPER_SPECTRE_KILLED","PVE_SANDBOX_SCORE_SUPER_SPECTRE","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXTitanDoomed","#SCORE_EVENT_AT_TITAN_DOOMED","#SCORE_EVENT_AT_TITAN_DOOMED","PVE_SANDBOX_SCORE_TITAN","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXTitanKilled","#SCORE_EVENT_AT_TITAN_DOOMED","#SCORE_EVENT_AT_TITAN_DOOMED","PVE_SANDBOX_SCORE_TITAN","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"PVESANDBOXCashedBonus","#SCORE_EVENT_AT_CASHED_BONUS","#SCORE_EVENT_AT_CASHED_BONUS","PVE_SANDBOX_SCORE_BONUS","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"PVESANDBOXBonusStolen","#SCORE_EVENT_AT_BONUS_STOLEN","#SCORE_EVENT_AT_BONUS_STOLEN","PVE_SANDBOX_SCORE_BONUS_STOLEN","","ASSAULT","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"FDAirDroneKilled","#SCORE_EVENT_AT_AIR_DRONE_KILLED","#SCORE_EVENT_AT_AIR_DRONE_KILLED","FD_SCORE_AIR_DRONE","","","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"FDGruntKilled","#SCORE_EVENT_AT_GRUNT_KILLED","#SCORE_EVENT_AT_GRUNT_KILLED","FD_SCORE_GRUNT","","","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill","",0
+"FDSpectreKilled","#SCORE_EVENT_AT_SPECTRE_KILLED","#SCORE_EVENT_AT_SPECTRE_KILLED","FD_SCORE_SPECTRE","","","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"FDStalkerKilled","#SCORE_EVENT_AT_STALKER_KILLED","#SCORE_EVENT_AT_STALKER_KILLED","FD_SCORE_STALKER","","","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"FDSuperSpectreKilled","#SCORE_EVENT_AT_SUPER_SPECTRE_KILLED","#SCORE_EVENT_AT_SUPER_SPECTRE_KILLED","FD_SCORE_SUPER_SPECTRE","","","0","0","0","0","","ATTRITION MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"FDTitanKilled","#SCORE_EVENT_AT_TITAN_DOOMED","#SCORE_EVENT_AT_TITAN_DOOMED","FD_SCORE_TITAN","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"FDEnemiesKilled","#SCORE_EVENT_FD_ENEMIES_KILLED","#SCORE_EVENT_FD_ENEMIES_KILLED","FD_SCORE_ENEMIES","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/kill","",0
+"FDWaveMVP","#SCORE_EVENT_FD_WAVE_MVP","#SCORE_EVENT_FD_WAVE_MVP","FD_SCORE_MVP","","","0","0","0","0","","MEDAL_FORCED GAMEMODE CALLINGCARD","rui/medals/mvp","",0
+"FDTeamFinalWave","#SCORE_EVENT_FD_TEAM_FINAL_WAVE","#SCORE_EVENT_FD_TEAM_FINAL_WAVE","FD_SCORE_TEAM_FINAL_WAVE","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"FDTeamWave","#SCORE_EVENT_FD_TEAM_WAVE","#SCORE_EVENT_FD_TEAM_WAVE","FD_SCORE_TEAM_WAVE","","","0","0","0","0","","MEDAL_FORCED GAMEMODE","rui/medals/harvester_protect","",0
+"FDTeamFlawlessWave","#SCORE_EVENT_FD_TEAM_FLAWLESS_WAVE","#SCORE_EVENT_FD_TEAM_FLAWLESS_WAVE","FD_SCORE_TEAM_FLAWLESS_WAVE","","","0","0","0","0","","MEDAL_FORCED GAMEMODE","rui/medals/shielded_harvester","",0
+"PasTitanHunter","#GEAR_AT_HUNTER_KIT","#GEAR_AT_HUNTER_KIT","","","","0","0","0","0","KILL","MEDAL","rui/medals/kill_robot","",0
+"Execution","#SCORE_EVENT_EXECUTION","#SCORE_EVENT_EXECUTION","0","BURN_METER_LARGE_POINT_VALUE","","0","0","0","0","","MEDAL","rui/medals/kill","",0
+"FDRepairTurret","#SCORE_EVENT_REPAIR_TURRET","#SCORE_EVENT_REPAIR_TURRET","FD_SCORE_REPAIR_TURRET","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"FDDidntDie","#SCORE_EVENT_DIDNT_DIE","#SCORE_EVENT_DIDNT_DIE","FD_SCORE_DIDNT_DIE","","","0","0","0","0","","MEDAL_FORCED GAMEMODE","rui/medals/heal","",0
+"FDShieldHarvester","#SCORE_EVENT_FD_SHIELD_HARVESTER","#SCORE_EVENT_FD_SHIELD_HARVESTER","FD_SCORE_SHIELD_HARVESTER","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/shielded_harvester","",0
+"FDSonarPulse","#SCORE_EVENT_FD_SONAR_PULSE","#SCORE_EVENT_FD_SONAR_PULSE","FD_SCORE_SONAR_PULSE","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"FDArcTrapTriggered","#SCORE_EVENT_FD_ARC_TRAP_TRIGGERED","#SCORE_EVENT_FD_ARC_TRAP_TRIGGERED","FD_SCORE_ARC_TRAP_TRIGGERED","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"FDTetherTriggered","#SCORE_EVENT_FD_TETHER_TRAP_TRIGGERED","#SCORE_EVENT_FD_TETHER_TRAP_TRIGGERED","FD_SCORE_TETHER_TRAP_TRIGGERED","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/kill_robot","",0
+"FDArcWave","#SCORE_EVENT_FD_ARC_WAVE","#SCORE_EVENT_FD_ARC_WAVE","FD_SCORE_ARC_WAVE","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/medal_prototype","",0
+"FDTeamHeal","#SCORE_EVENT_FD_TEAM_HEAL","#SCORE_EVENT_FD_TEAM_HEAL","FD_SCORE_TEAM_HEAL","","","0","0","0","0","","MEDAL GAMEMODE","rui/medals/heal","",0
+"FDDamageBonus","#SCORE_EVENT_FD_DAMAGE_BONUS","#SCORE_EVENT_FD_DAMAGE_BONUS","FD_SCORE_DAMAGE_BONUS","","","0","0","0","0","","MEDAL_FORCED GAMEMODE SHOW_SCORE","rui/medals/medal_prototype","",0
+"FDHealingBonus","#SCORE_EVENT_FD_HEALING_BONUS","#SCORE_EVENT_FD_HEALING_BONUS","FD_SCORE_HEALING_BONUS","","","0","0","0","0","","MEDAL_FORCED GAMEMODE SHOW_SCORE","rui/medals/heal","",0
+"FDSupportBonus","#SCORE_EVENT_FD_SUPPORT_BONUS","#SCORE_EVENT_FD_SUPPORT_BONUS","FD_SCORE_SUPPORT_BONUS","","","0","0","0","0","","MEDAL_FORCED GAMEMODE SHOW_SCORE","rui/medals/kill_robot","",0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/sp_levels.csv b/Northstar.CustomServers/mod/scripts/datatable/sp_levels.csv
new file mode 100644
index 00000000..f373d4ed
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/sp_levels.csv
@@ -0,0 +1,17 @@
+levelNum,level,startPoint,levelId,mainEntry,showLions,title,desc,missionSelectImage
+0,"sp_training","Pod Intro","sp_training",1,1,"#SP_TRAINING_CAMPAIGN_NAME","#SP_TRAINING_CAMPAIGN_DESC_MILITIA","rui/menu/level_select/level_image1"
+0,"sp_training","Gauntlet Mode","training_gauntlet_mode",0,1,"","",""
+1,"sp_crashsite","LevelStart","sp_crashsite",1,1,"#SP_CRASHSITE_CAMPAIGN_NAME","#SP_CRASHSITE_CAMPAIGN_DESC_MILITIA","rui/menu/level_select/level_image2"
+2,"sp_sewers1","Channel Mortar Run","sp_sewers1",1,1,"#SP_SEWERS1_CAMPAIGN_NAME","#SP_SEWERS1_CAMPAIGN_DESC_MILITIA","rui/menu/level_select/level_image3"
+3,"sp_boomtown_start","Intro","sp_boomtown_start",1,1,"#SP_BOOMTOWN_START_CAMPAIGN_NAME","#SP_BOOMTOWN_START_CAMPAIGN_DESC_MILITIA","rui/menu/level_select/level_image4"
+3,"sp_boomtown","Start","sp_boomtown",0,1,"","",""
+3,"sp_boomtown_end","Intro Caves","sp_boomtown_end",0,1,"","",""
+4,"sp_hub_timeshift","LECTURE HALLS","sp_hub_timeshift",1,0,"#SP_HUB_TIMESHIFT_CAMPAIGN_NAME","#SP_HUB_TIMESHIFT_CAMPAIGN_DESC_MILITIA","rui/menu/level_select/level_image5"
+4,"sp_timeshift_spoke02","Timeshift Device","sp_timeshift_spoke02",0,1,"","",""
+4,"sp_hub_timeshift","PRISTINE CAMPUS","timeshift_pt3",0,1,"","",""
+5,"sp_beacon","Level Start","sp_beacon",1,0,"#SP_BEACON_CAMPAIGN_NAME","#SP_BEACON_CAMPAIGN_DESC_MILITIA","rui/menu/level_select/level_image6"
+5,"sp_beacon_spoke0","Level Start","sp_beacon_spoke0",0,1,"","",""
+5,"sp_beacon","Spoke_0_Complete","beacon_pt3",0,1,"","",""
+6,"sp_tday","Intro","sp_tday",1,1,"#SP_TDAY_CAMPAIGN_NAME","#SP_TDAY_CAMPAIGN_DESC_MILITIA","rui/menu/level_select/level_image7"
+7,"sp_s2s","Level Start","sp_s2s",1,1,"#SP_S2S_CAMPAIGN_NAME","#SP_S2S_CAMPAIGN_DESC_MILITIA","rui/menu/level_select/level_image8"
+8,"sp_skyway_v1","Level Start","sp_skyway_v1",1,1,"#SP_SKYWAY_V1_CAMPAIGN_NAME","#SP_SKYWAY_V1_CAMPAIGN_DESC_MILITIA","rui/menu/level_select/level_image9" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/spectre_chatter_mp.csv b/Northstar.CustomServers/mod/scripts/datatable/spectre_chatter_mp.csv
new file mode 100644
index 00000000..4194a938
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/spectre_chatter_mp.csv
@@ -0,0 +1,8 @@
+conversationname,priority,debounce
+"diag_imc_spectre_gs_spotclosetitancall_01",200,20.000000
+"diag_imc_spectre_gs_engagepilotenemy_01_1",200,20.000000
+"diag_imc_spectre_gs_grenadeout_01_1",200,20.000000
+"diag_imc_spectre_gs_killenemypilot_01_1",200,20.000000
+"diag_imc_spectre_gs_gruntkillstitan_02_1",200,20.000000
+"diag_imc_spectre_gs_squaddeplete_01_1",200,20.000000
+"diag_imc_spectre_gs_allygrundown_05_1",200,20.000000 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/spotlight_images.csv b/Northstar.CustomServers/mod/scripts/datatable/spotlight_images.csv
new file mode 100644
index 00000000..1e6946c1
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/spotlight_images.csv
@@ -0,0 +1,30 @@
+image
+"rui/menu/main_menu/spotlight_01"
+"rui/menu/main_menu/spotlight_02"
+"rui/menu/main_menu/spotlight_03"
+"rui/menu/main_menu/spotlight_04"
+"rui/menu/main_menu/spotlight_05"
+"rui/menu/main_menu/spotlight_06"
+"rui/menu/main_menu/spotlight_07"
+"rui/menu/main_menu/spotlight_08"
+"rui/menu/main_menu/spotlight_09"
+"rui/menu/main_menu/spotlight_10"
+"rui/menu/main_menu/spotlight_11"
+"rui/menu/main_menu/spotlight_12"
+"rui/menu/main_menu/spotlight_13"
+"rui/menu/main_menu/spotlight_14"
+"rui/menu/main_menu/spotlight_15"
+"rui/menu/main_menu/spotlight_16"
+"rui/menu/main_menu/spotlight_17"
+"rui/menu/main_menu/spotlight_18"
+"rui/menu/main_menu/spotlight_19"
+"rui/menu/main_menu/spotlight_20"
+"rui/menu/main_menu/spotlight_21"
+"rui/menu/main_menu/spotlight_22"
+"rui/menu/main_menu/spotlight_23"
+"rui/menu/main_menu/spotlight_24"
+"rui/menu/main_menu/spotlight_25"
+"rui/menu/main_menu/spotlight_26"
+"rui/menu/main_menu/spotlight_27"
+"rui/menu/main_menu/spotlight_28"
+"rui/menu/main_menu/spotlight_29" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/startpoints.csv b/Northstar.CustomServers/mod/scripts/datatable/startpoints.csv
new file mode 100644
index 00000000..a171cd8a
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/startpoints.csv
@@ -0,0 +1,313 @@
+level,startpoint,loadScreenIndex,hasDetente,spLog,spLogTitle,isLeft
+"sp_grunt_arena","Pilot vs 3 Grunts (Trial)",0,0,"","",0
+"sp_grunt_arena","Pilot and Leeched Spectre vs Grunts (Trial)",0,0,"","",0
+"sp_grunt_arena","Pilot vs Spectre (Trial)",0,0,"","",0
+"sp_grunt_arena","Arc Grenades vs Spectres and Grunts (Trial)",0,0,"","",0
+"sp_grunt_arena","Learn to use Shield Drone (Trial)",0,0,"","",0
+"sp_grunt_arena","Pilot vs Grunts (Small Arena)",0,0,"","",0
+"sp_grunt_arena","Pilot vs Grunts (Big Arena)",0,0,"","",0
+"sp_grunt_arena","Shield Drone vs Turrets (Trial)",0,0,"","",0
+"sp_grunt_arena","Pilot vs Captains (Trial)",0,0,"","",0
+"sp_grunt_arena","Use Turrets vs AI (Trial)",0,0,"","",0
+"sp_grunt_arena","Turret Maze Part 1 (Trial)",0,0,"","",0
+"sp_grunt_arena","Turret Maze Part 2 (Trial)",0,0,"","",0
+"sp_grunt_arena","Learn to use Suicide Spectres (Trial)",0,0,"","",0
+"sp_grunt_arena","Pilot vs Mixed AI (Big Arena)",0,0,"","",0
+"sp_grunt_arena","Pilot and Titan vs Many AI (Big Arena)",0,0,"","",0
+"sp_grunt_arena","Titan Wingman Arena (Big Arena)",0,0,"","",0
+"sp_grunt_arena","Titan vs Many AI (Big Arena)",0,0,"","",0
+"sp_grunt_arena","Pilot vs Super Spectre (Big Arena)",0,0,"","",0
+"sp_grunt_arena","Pilot vs Super Spectres (Small Arena)",0,0,"","",0
+"sp_grunt_arena","Titan vs Super Spectres (Big Arena)",0,0,"","",0
+"sp_grunt_arena","Pilot vs Spectres (Big Arena)",0,0,"","",0
+"sp_grunt_arena","Pilot vs Stalker progressive (Big Arena)",0,0,"","",0
+"sp_grunt_arena","The End!",0,0,"","",0
+"sp_ab_gym","gym_windmills",0,0,"","",0
+"sp_ab_gym","gym_bongo_board",0,0,"","",0
+"sp_ab_testfire2","room1",0,0,"","",0
+"sp_ab_testfire2","room2",0,0,"","",0
+"sp_ola_hub","topside",0,0,"","",0
+"sp_ola_hub","hub_intro",0,0,"","",0
+"sp_ola_hub","engine1_connect",0,0,"","",0
+"sp_ola_spoke1","sp_ola_spoke1",0,0,"","",0
+"sp_ola_spoke1","ola_spoke1_cleanroom",0,0,"","",0
+"sp_ola_spoke1","ola_spoke1_powerrod_factory",0,0,"","",0
+"sp_ola_spoke1","ola_spoke1_canyon",0,0,"","",0
+"sp_ola_sewers","ola_sewers_drain",0,0,"","",0
+"sp_ola_sewers","ola_sewers_sludge_test1",0,0,"","",0
+"sp_ola_sewers","ola_sewers_sludge_test2",0,0,"","",0
+"sp_ola_sewers","ola_sewers_sludge_test3",0,0,"","",0
+"sp_ola_sewers","ola_sewers_sludge_test4",0,0,"","",0
+"sp_ola_sewers","ola_sewers_sludge_bt_test1",0,0,"","",0
+"sp_ola_canyon_test","ola_canyontest1",0,0,"","",0
+"sp_ola_canyon_test","ola_canyontest2",0,0,"","",0
+"sp_ola_canyon_test","ola_canyontest3",0,0,"","",0
+"sp_ola_canyon_test","ola_canyontest4",0,0,"","",0
+"sp_ola_canyon_test","dish_array",0,0,"","",0
+"sp_ola_canyon_test","ledge_run",0,0,"","",0
+"sp_ab_canyon_prowler","prowler_canyon_1",0,0,"","",0
+"sp_ab_canyon_prowler","prowler_canyon_2",0,0,"","",0
+"sp_ab_canyon_prowler","prowler_canyon_3",0,0,"","",0
+"sp_hub_timeshift","LECTURE HALLS",0,1,"SP_MISSION_LOG_TIMESHIFT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_hub_timeshift","OVERGROWN CAMPUS",1,0,"SP_MISSION_LOG_TIMESHIFT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_hub_timeshift","Corpse Search",1,0,"SP_MISSION_LOG_TIMESHIFT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_hub_timeshift","BT EXPLAINS IT ALL",1,0,"SP_MISSION_LOG_TIMESHIFT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_hub_timeshift","Zipline",1,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",1
+"sp_hub_timeshift","Security",1,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",1
+"sp_hub_timeshift","Skybridge",1,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",1
+"sp_hub_timeshift","PRISTINE CAMPUS",2,1,"SP_MISSION_LOG_TIMESHIFT_SPLOG1","MENU_SP_LOG_TITLE_TIMESHIFT2",1
+"sp_hub_timeshift","Hub Fight",2,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG1","MENU_SP_LOG_TITLE_TIMESHIFT2",1
+"sp_hub_timeshift","Extended Bridge",2,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG1","MENU_SP_LOG_TITLE_TIMESHIFT2",1
+"sp_hub_timeshift","REACTOR MELTDOWN",2,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG1","MENU_SP_LOG_TITLE_TIMESHIFT2",1
+"sp_hub_timeshift","Core Scan",2,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG1","MENU_SP_LOG_TITLE_TIMESHIFT2",1
+"sp_hub_timeshift","Level End",2,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG1","MENU_SP_LOG_TITLE_TIMESHIFT2",1
+"sp_timeshift_spoke02","Timeshift Device",1,1,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",1
+"sp_timeshift_spoke02","WILDLIFE RESEARCH",1,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",1
+"sp_timeshift_spoke02","First Timeshift Fight",1,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",1
+"sp_timeshift_spoke02","Elevator Fight",1,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",1
+"sp_timeshift_spoke02","HUMAN RESEARCH",2,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",1
+"sp_timeshift_spoke02","Sphere Room",2,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",1
+"sp_timeshift_spoke02","Human Room",3,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",0
+"sp_timeshift_spoke02","CAMPUS RETURN",3,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",0
+"sp_timeshift_spoke02","Fan Drop",3,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",0
+"sp_timeshift_spoke02","Fan Drop End",3,0,"SP_MISSION_LOG_TIMESHIFT_SPLOG0","MENU_SP_LOG_TITLE_TIMESHIFT1",0
+"sp_beacon","Level Start",1,1,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",0
+"sp_beacon","Control Room",1,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Spoke_0_Complete",2,1,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Power Relays Online",1,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Fastball to Spoke 1",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Spoke 1 Start",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Spoke 1 First Combat",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Spoke 1 Second Combat",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Spoke 1 Pillar Room",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Double Crane Puzzle",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Second Beacon",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Spoke 1 Arena",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Wallrun Panels",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Back at HUB",3,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Climb Dish",1,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon","Final Battle",4,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",0
+"sp_beacon","Send Signal",4,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",0
+"sp_beacon","Beacon Ending",4,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",0
+"sp_beacon_spoke0","Level Start",0,1,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon_spoke0","First Fight",1,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon_spoke0","Get Arc Tool",1,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon_spoke0","Got Arc Tool",2,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",0
+"sp_beacon_spoke0","Horizontal Fan",2,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",0
+"sp_beacon_spoke0","Horizontal Fan Complete",2,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",0
+"sp_beacon_spoke0","Fan Wallrun",2,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",0
+"sp_beacon_spoke2","Level Start",0,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon_spoke2","Fastball",0,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_beacon_spoke2","Loading Dock",0,0,"#SP_MISSION_LOG_BEACON","MENU_SP_OBJECTIVES_TITLE",1
+"sp_boomtown_ride","1 - Level Start",0,0,"","",0
+"sp_boomtown_ride","2 - Assembly Line",0,0,"","",0
+"sp_boomtown_ride","3 - First Fight",0,0,"","",0
+"sp_boomtown_ride","4 - First Ride",0,0,"","",0
+"sp_boomtown_ride","5 - Second Ride",0,0,"","",0
+"sp_ab_moving_geo_combat_1","1 - Moving Cover",0,0,"","",0
+"sp_ab_moving_geo_combat_1","2 - Moving Cover with High Ceiling",0,0,"","",0
+"sp_ab_moving_geo_combat_1","3 - Rotating Cover (CQC)",0,0,"","",0
+"sp_ab_moving_geo_combat_1","4 - Rotating Cover - Only Grunts",0,0,"","",0
+"sp_ab_moving_geo_combat_1","5 - Rotating Cover - Stationary Reaper",0,0,"","",0
+"sp_ab_moving_geo_combat_1","6 - Rotating Cover - Mobile Reaper",0,0,"","",0
+"sp_ab_moving_geo_combat_1","7 - Building Assembly Battle",0,0,"","",0
+"sp_training","Pod Intro",1,1,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Basic Movement",1,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Zen Garden",1,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Firing Range",1,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Gauntlet",1,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Gauntlet Challenge",1,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Titanfall",1,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Pod Outro",1,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Meet OG",1,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Gauntlet Mode",1,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_FIRSTRUN",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_WIP",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_01",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_02",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_03",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_04",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_05",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_06",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_07",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_08",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","DEV_GHOSTREC_GAUNTLET_CHAL_09",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Pod Intro DEV",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_training","Pod Outro DEV",0,0,"#SP_MISSION_LOG_TRAINING","MENU_SP_OBJECTIVES_TITLE",0
+"sp_wild","1 - Spoke 1: Escape Pod",0,0,"","",0
+"sp_wild","2 - Spoke 1: Basic Movement",0,0,"","",0
+"sp_wild","placeholder 3 - Spoke 1: Spectre Forest",0,0,"","",0
+"sp_wild","placeholder 4 - Spoke 1: Friend or Foe?",0,0,"","",0
+"sp_wild","placeholder 5 - Spoke 1: Grand Reveal",0,0,"","",0
+"sp_wild","placeholder 6 - Spoke 1: Checkpoint Zulu",0,0,"","",0
+"sp_wild","placeholder 7 - Spoke 1: Pistol Puzzle",0,0,"","",0
+"sp_wild","placeholder 8 - Spoke 1: Intruder",0,0,"","",0
+"sp_wild","placeholder 9 - HUB p-1: Gravity",0,0,"","",0
+"sp_wild","placeholder 10 - HUB p-1: Flirtation",0,0,"","",0
+"sp_wild","placeholder 11 - HUB p-1: Chase",0,0,"","",0
+"sp_wild","placeholder 12 - Spoke 2: Gorilla in the Mist",0,0,"","",0
+"sp_wild","13 - Spoke 2: Upgrades",0,0,"","",0
+"sp_wild","placeholder 14 - Spoke 2: A whole new world",0,0,"","",0
+"sp_wild","placeholder 15 - HUB p-2: Making Friends",0,0,"","",0
+"sp_wild","placeholder 16 - HUB p-2: Influencing People",0,0,"","",0
+"sp_wild","placeholder 17 - Spoke 3: Survivors",0,0,"","",0
+"sp_wild","placeholder 18 - Spoke 3: Prisoners",0,0,"","",0
+"sp_wild","placeholder 19 - HUB p-3: Snatch and Grab",0,0,"","",0
+"sp_wild","placeholder 20 - HUB p-3: Hero to Zero",0,0,"","",0
+"sp_wild","placeholder 21 - Spoke 4: Outpost Cherokee",0,0,"","",0
+"sp_wild","placeholder 22 - Spoke 4: Grenades",0,0,"","",0
+"sp_wild","placeholder 23 - Spoke 4: QRF",0,0,"","",0
+"sp_wild","placeholder 24 - Spoke 4: Those Eyes",0,0,"","",0
+"sp_wild","placeholder 25 - Spoke 5: Tree of Life",0,0,"","",0
+"sp_wild","26 - Spoke 5: Gift From the Gods",0,0,"","",0
+"sp_wild","placeholder 27 - HUB p-4: Energizer",0,0,"","",0
+"sp_wild","placeholder 28 - HUB p-4: One Man Army",0,0,"","",0
+"sp_wild","placeholder 29 - Spoke 5: p-2: Sleeping Beauty",0,0,"","",0
+"sp_wild","placeholder 30 - Spoke 5: p-2: Prowler Den",0,0,"","",0
+"sp_wild","placeholder 31 - Spoke 5: p-2: Common Ground",0,0,"","",0
+"sp_wild","placeholder 32 - Spoke 5: p-2: Dharma Hatch",0,0,"","",0
+"sp_crashsite","LevelStart",1,1,"SP_MISSION_LOG_WILDS_SPLOG0","MENU_SP_LOG_TITLE_WILDS",1
+"sp_crashsite","BT_Intro",1,0,"SP_MISSION_LOG_WILDS_SPLOG0","MENU_SP_LOG_TITLE_WILDS",1
+"sp_crashsite","Blisk_Intro",1,0,"SP_MISSION_LOG_WILDS_SPLOG0","MENU_SP_LOG_TITLE_WILDS",1
+"sp_crashsite","FamilyPhoto",1,0,"SP_MISSION_LOG_WILDS_SPLOG0","MENU_SP_LOG_TITLE_WILDS",1
+"sp_crashsite","Waking_Up",2,0,"SP_MISSION_LOG_WILDS_SPLOG0","MENU_SP_LOG_TITLE_WILDS",1
+"sp_crashsite","Field_Promotion",2,0,"SP_MISSION_LOG_WILDS_SPLOG0","MENU_SP_LOG_TITLE_WILDS",1
+"sp_crashsite","Grave",2,0,"SP_MISSION_LOG_WILDS_ALT","MENU_SP_OBJECTIVES_TITLE",0
+"sp_crashsite","Battery2_Path",2,0,"SP_MISSION_LOG_WILDS_ALT","MENU_SP_OBJECTIVES_TITLE",0
+"sp_crashsite","Battery2_Combat",2,0,"SP_MISSION_LOG_WILDS_ALT","MENU_SP_OBJECTIVES_TITLE",0
+"sp_crashsite","Battery2_Ship",2,0,"SP_MISSION_LOG_WILDS_ALT","MENU_SP_OBJECTIVES_TITLE",0
+"sp_crashsite","Battery3_Path",2,0,"SP_MISSION_LOG_WILDS_ALT","MENU_SP_OBJECTIVES_TITLE",0
+"sp_crashsite","Battery3_Combat",2,0,"SP_MISSION_LOG_WILDS_ALT","MENU_SP_OBJECTIVES_TITLE",0
+"sp_crashsite","Battery3_Ship",2,0,"SP_MISSION_LOG_WILDS_ALT","MENU_SP_OBJECTIVES_TITLE",0
+"sp_crashsite","PilotLink",2,0,"SP_MISSION_LOG_WILDS_ALT","MENU_SP_OBJECTIVES_TITLE",0
+"sp_ship_01","S2S_GOBLIN",0,0,"","",0
+"sp_ship_01","Goblin Deploy",0,0,"","",0
+"sp_ship_01","Goblin Deploy Zip",0,0,"","",0
+"sp_ship_01","S2S_FASTBALL",0,0,"","",0
+"sp_ship_01","S2S_REDEYE",0,0,"","",0
+"sp_ship_01","S2S_SENTINEL",0,0,"","",0
+"sp_ship_01","Model Test [ solid ]",0,0,"","",0
+"sp_ship_01","Model Test [ notsolid ]",0,0,"","",0
+"sp_ship_01","Bug: Skycam Parent",0,0,"","",0
+"sp_ship_01","Bug: Skycam Move/Rotate",0,0,"","",0
+"sp_ship_01","Bug: Ents outside Bounds",0,0,"","",0
+"sp_ship_01","S2S_COMBAT1-p1",0,0,"","",0
+"sp_ship_01","S2S_COMBAT1-p2",0,0,"","",0
+"sp_ship_01","S2S_COMBAT1-p3",0,0,"","",0
+"sp_ship_01","S2S_COMBAT1-p4",0,0,"","",0
+"sp_ship_03","hull combat p1",0,0,"","",0
+"sp_ship_04","hull combat p1",0,0,"","",0
+"sp_ship_05","hull combat p1",0,0,"","",0
+"sp_s2s","Level Start",1,1,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Intro",1,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Gibraltar",1,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Boss Intro",1,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Widow Fall",1,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Barker Ship",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","FastBall 1",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Malta Intro",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Malta Drone Room",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Malta Guns",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Malta Widow Jump",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Malta Hangar",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Malta Breach",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Malta Bridge",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Reunite with BT",2,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Malta Deck",3,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","BT Tackle",3,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Boss Fight",3,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Viper Dead",3,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",0
+"sp_s2s","Life Boats",4,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",1
+"sp_s2s","Core Room",4,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",1
+"sp_s2s","OLA Crash",4,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",1
+"sp_s2s","--------------",4,0,"SP_MISSION_LOG_S2S","MENU_SP_OBJECTIVES_TITLE",1
+"sp_s2s","TestBed",1,0,"","",0
+"sp_s2s","Dropship Combat Test",1,0,"","",0
+"sp_s2s","LightEdit Connect",1,0,"","",0
+"sp_s2s","TRAILER bridge",1,0,"","",0
+"sp_scrapyard_test","Arena 1",0,0,"","",0
+"sp_sewers1","Channel Mortar Run",1,1,"SP_MISSION_LOG_SEWERS","MENU_SP_OBJECTIVES_TITLE",1
+"sp_sewers1","Sewer Split",2,0,"SP_MISSION_LOG_SEWERS","MENU_SP_OBJECTIVES_TITLE",0
+"sp_sewers1","Sludge Falls",3,0,"SP_MISSION_LOG_SEWERS","MENU_SP_OBJECTIVES_TITLE",1
+"sp_sewers1","Corkscrew Room",4,0,"SP_MISSION_LOG_SEWERS","MENU_SP_OBJECTIVES_TITLE",0
+"sp_sewers1","Pipe Room Climb",5,0,"SP_MISSION_LOG_SEWERS","MENU_SP_OBJECTIVES_TITLE",1
+"sp_sewers1","Sewer Arena",6,0,"SP_MISSION_LOG_SEWERS","MENU_SP_OBJECTIVES_TITLE",1
+"sp_sewers1","Sewer Arena Defend",6,0,"SP_MISSION_LOG_SEWERS","MENU_SP_OBJECTIVES_TITLE",1
+"sp_sewers1","Kane Arena",7,0,"SP_MISSION_LOG_SEWERS","MENU_SP_OBJECTIVES_TITLE",1
+"sp_davis_sewer_battle","Sneaky Section",0,0,"","",0
+"sp_davis_sewer_battle","Combat Arena",0,0,"","",0
+"sp_davis_sewer_battle","Cylinder Tick Tunnels",0,0,"","",0
+"sp_ab_titan_arena","TvT Linear w 1.5 Boss Titans",0,0,"","",0
+"sp_ab_titan_arena","TvT Arena",0,0,"","",0
+"sp_ab_titan_arena","1v1 Arena A",0,0,"","",0
+"sp_ab_titan_arena","1v1 Arena B",0,0,"","",0
+"sp_ab_titan_arena","1v1 Arena C",0,0,"","",0
+"sp_ab_titan_arena","1v1 Arena D",0,0,"","",0
+"sp_boomtown_townboot","Town Boot",0,0,"","",0
+"sp_boomtown_townboot","Town Delivery",0,0,"","",0
+"sp_boomtown_townboot","Spotlight",0,0,"","",0
+"sp_ab_wilds_combat_test","Battery2",0,0,"","",0
+"sp_ab_wilds_combat_test","Battery3",0,0,"","",0
+"sp_skyway_v1","Level Start",0,1,"","",0
+"sp_skyway_v1","Torture Room B",1,0,"","",0
+"sp_skyway_v1","Smart Pistol Run",1,0,"","",0
+"sp_skyway_v1","Bridge Fight",2,0,"","",0
+"sp_skyway_v1","BT Reunion",3,0,"","",0
+"sp_skyway_v1","Titan Hill",3,0,"","",0
+"sp_skyway_v1","Titan Smash Hallway",3,0,"","",0
+"sp_skyway_v1","Sculptor Climb",3,0,"","",0
+"sp_skyway_v1","Targeting Room",4,0,"","",0
+"sp_skyway_v1","Injector Room",4,0,"","",0
+"sp_skyway_v1","Blisk's Farewell",4,0,"","",0
+"sp_skyway_v1","BT Sacrifice",4,0,"","",0
+"sp_skyway_v1","Rising World Run",5,0,"","",0
+"sp_skyway_v1","Rising World Jump",5,0,"","",0
+"sp_skyway_v1","Exploding Planet",5,0,"","",0
+"sp_skyway_v1","Harmony",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","--- CREDITS ---",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","sarah intro",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","walk",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","hug",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","cheer",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","actor jack cooper",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","actor BT / OG",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","actor blisk",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","actor sarah",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","actor bosses",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","actor marder",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","actors 6-4",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","actor barker",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","new BT",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","board ship",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_skyway_v1","final image",5,0,"SP_MISSION_LOG_SKYWAY_ALT","MENU_SP_OBJECTIVES_TITLE",1
+"sp_template","Level Start",0,0,"","",0
+"sp_template","Big Fight",0,0,"","",0
+"sp_template","Finale",0,0,"","",0
+"sp_tday","Intro",1,1,"SP_MISSION_LOG_TDAY","MENU_SP_OBJECTIVES_TITLE",1
+"sp_tday","Wall Bombardment",1,0,"SP_MISSION_LOG_TDAY","MENU_SP_OBJECTIVES_TITLE",1
+"sp_tday","Militia Big Charge",1,0,"SP_MISSION_LOG_TDAY","MENU_SP_OBJECTIVES_TITLE",1
+"sp_tday","Underground Fuel Storage",2,0,"SP_MISSION_LOG_TDAY","MENU_SP_OBJECTIVES_TITLE",1
+"sp_tday","Elevator",2,0,"SP_MISSION_LOG_TDAY","MENU_SP_OBJECTIVES_TITLE",1
+"sp_tday","VTOL",3,0,"SP_MISSION_LOG_TDAY","MENU_SP_OBJECTIVES_TITLE",0
+"sp_tday","Fire on the Runway",4,0,"SP_MISSION_LOG_TDAY","MENU_SP_OBJECTIVES_TITLE",0
+"sp_tday","OLA Launch",4,0,"SP_MISSION_LOG_TDAY","MENU_SP_OBJECTIVES_TITLE",0
+"sp_boomtown_start","Intro",0,1,"SP_MISSION_LOG_BOOMTOWN_1","MENU_SP_OBJECTIVES_TITLE",0
+"sp_boomtown_start","Prop House",1,0,"SP_MISSION_LOG_BOOMTOWN_1","MENU_SP_OBJECTIVES_TITLE",1
+"sp_boomtown_start","Narrow Hallway",1,0,"SP_MISSION_LOG_BOOMTOWN_1","MENU_SP_OBJECTIVES_TITLE",1
+"sp_boomtown_start","Titan Arena",1,0,"SP_MISSION_LOG_BOOMTOWN_1","MENU_SP_OBJECTIVES_TITLE",1
+"sp_boomtown_start","Loading Dock",1,0,"SP_MISSION_LOG_BOOMTOWN_1","MENU_SP_OBJECTIVES_TITLE",1
+"sp_boomtown","Start",0,1,"SP_MISSION_LOG_BOOMTOWN_SPLOG0","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown","Assembly_Start",1,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG0","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown","Assembly_Dirt",1,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG0","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown","Assembly_Wallrun",1,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG0","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown","Assembly_Furniture",1,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG0","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown","Assembly_Walls",1,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG0","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown","Assembly_Highway",1,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG0","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown","Town_Climb_Entry",1,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG0","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown","ReaperTown",2,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG0","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown_end","Intro Caves",0,1,"SP_MISSION_LOG_BOOMTOWN_SPLOG1","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown_end","Prowler Towers",0,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG1","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown_end","Above The Dome",1,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG1","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown_end","Pre Ash Fight",2,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG1","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"sp_boomtown_end","Ash Fight",3,0,"SP_MISSION_LOG_BOOMTOWN_SPLOG1","MENU_SP_LOG_TITLE_BOOMTOWN",1
+"eof","eof",0,0,"","",0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_abilities.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_abilities.csv
new file mode 100644
index 00000000..46164792
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_abilities.csv
@@ -0,0 +1,36 @@
+itemRef,type,damageSource,hidden
+"mp_titancore_flame_wave","TITAN_CORE_ABILITY",1,0
+"mp_titancore_flight_core","TITAN_CORE_ABILITY",0,0
+"mp_titancore_laser_cannon","TITAN_CORE_ABILITY",1,0
+"mp_titancore_salvo_core","TITAN_CORE_ABILITY",1,0
+"mp_titancore_shift_core","TITAN_CORE_ABILITY",0,0
+"mp_titancore_siege_mode","TITAN_CORE_ABILITY",0,0
+"mp_titancore_amp_core","TITAN_CORE_ABILITY",0,0
+"mp_titancore_dash_core","TITAN_CORE_ABILITY",0,0
+"mp_titanweapon_arc_wave","TITAN_ORDNANCE",1,0
+"mp_titanweapon_dumbfire_rockets","TITAN_ORDNANCE",1,0
+"mp_titanweapon_flame_wall","TITAN_ORDNANCE",1,0
+"mp_titanweapon_homing_rockets","TITAN_ORDNANCE",1,0
+"mp_titanweapon_laser_lite","TITAN_ORDNANCE",1,0
+"mp_titanweapon_salvo_rockets","TITAN_ORDNANCE",1,0
+"mp_titanweapon_shoulder_rockets","TITAN_ORDNANCE",1,0
+"mp_titanweapon_tracker_rockets","TITAN_ORDNANCE",1,0
+"mp_titanability_power_shot","TITAN_ORDNANCE",1,0
+"mp_titanability_tether_trap","TITAN_ORDNANCE",0,0
+"mp_titanability_basic_block","TITAN_SPECIAL",0,0
+"mp_titanability_particle_wall","TITAN_SPECIAL",0,0
+"mp_titanweapon_vortex_shield","TITAN_SPECIAL",1,0
+"mp_titanweapon_vortex_shield_ion","TITAN_SPECIAL",1,0
+"mp_titanweapon_heat_shield","TITAN_SPECIAL",1,0
+"mp_titanability_gun_shield","TITAN_SPECIAL",1,0
+"mp_titanability_smoke","TITAN_SPECIAL",0,0
+"mp_titanability_hover","TITAN_ANTIRODEO",0,0
+"mp_titanability_phase_dash","TITAN_ANTIRODEO",0,0
+"mp_titanability_laser_trip","TITAN_ANTIRODEO",0,0
+"mp_titanability_slow_trap","TITAN_ANTIRODEO",0,0
+"mp_titanability_ammo_swap","TITAN_ANTIRODEO",1,0
+"mp_titanability_sonar_pulse","TITAN_ANTIRODEO",1,0
+"mp_titanability_rocketeer_ammo_swap","TITAN_ANTIRODEO",0,0
+"mp_titanweapon_stun_laser","TITAN_SPECIAL",1,0
+"mp_titanability_rearm","TITAN_ANTIRODEO",1,0
+"mp_titancore_upgrade","TITAN_CORE_ABILITY",0,0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_executions.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_executions.csv
new file mode 100644
index 00000000..09c4a4d1
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_executions.csv
@@ -0,0 +1,26 @@
+ref,setfile,attackerAnim,attackerAnimVsAutoTitan,victimAnim_lt,victimAnim_md,victimAnim_hv,victimAnim_pt_lt,victimAnim_pt_md,victimAnim_pt_hv,attackerAnim_pt_lt,attackerAnim_pt_mt,attackerAnim_pt_ht,sound_1p,sound_3p,camAttach,type,name,description,image,hidden,cost,reqPrime,linkedExecutions
+"execution_legion","titan_ogre_minigun","htPRED_MP_Sync_Execution_Attacker","htPRED_MP_Sync_Execution_Attacker","t_MeleeExecuted_By_htPred","t_MeleeExecuted_By_htPred","t_MeleeExecuted_By_htPred","","","","","","","Ogre_1p_Sync_Melee","Ogre_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_LEGION_EXECUTION","#TITAN_EXECUTION_LEGION","#TITAN_EXECUTION_LEGION_DESC","rui/titan_loadout/execution/titan_execution_legion",0,0,0,""
+"execution_legion_prime","titan_ogre_legion_prime","htLegionPrime_MP_Sync_Execution_attacker","htLegionPrime_MP_Sync_Execution_attacker","t_MeleeExecuted_By_htLegionPrime","t_MeleeExecuted_By_htLegionPrime","t_MeleeExecuted_By_htLegionPrime","","","","","","","Ogre_1p_Sync_Melee","Ogre_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_LEGION_EXECUTION","#TITAN_EXECUTION_LEGION_PRIME","#TITAN_EXECUTION_LEGION_PRIME_DESC","rui/titan_loadout/execution/titan_execution_legion_prime",0,0,1,""
+"execution_scorch","titan_ogre_meteor","htThermite_MP_Sync_Execution_Attacker","htThermite_MP_Sync_Execution_Attacker","t_MeleeExecuted_By_htThermite","t_MeleeExecuted_By_htThermite","t_MeleeExecuted_By_htThermite","","","","","","","Ogre_1p_Sync_Melee","Ogre_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_SCORCH_EXECUTION","#TITAN_EXECUTION_SCORCH","#TITAN_EXECUTION_SCORCH_DESC","rui/titan_loadout/execution/titan_execution_scorch",0,0,0,""
+"execution_scorch_prime","titan_ogre_scorch_prime","htScorchPrime_MP_Sync_Execution_attacker","htScorchPrime_MP_Sync_Execution_attacker","t_MeleeExecuted_By_htScorchPrime","t_MeleeExecuted_By_htScorchPrime","t_MeleeExecuted_By_htScorchPrime","","","","","","","Ogre_1p_Sync_Melee","Ogre_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_SCORCH_EXECUTION","#TITAN_EXECUTION_SCORCH_PRIME","#TITAN_EXECUTION_SCORCH_PRIME_DESC","rui/titan_loadout/execution/titan_execution_scorch_prime",0,0,1,""
+"execution_ronin","titan_stryder_leadwall","lt_execution_attacker_sword_01","lt_execution_attacker_sword_01","lt_execution_victim_sword_01","mt_execution_victim_sword_01","ht_execution_victim_sword_01","pt_lt_execution_victim_sword_01","pt_mt_execution_victim_sword_01","pt_ht_execution_victim_sword_01","","","","Stryder_1p_Sync_Melee","Stryder_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_RONIN_EXECUTION","#TITAN_EXECUTION_RONIN","#TITAN_EXECUTION_RONIN_DESC","rui/titan_loadout/execution/titan_execution_ronin",0,0,0,""
+"execution_ronin_prime","titan_stryder_ronin_prime","lt_execution_attacker_sword_02_prime","lt_execution_attacker_sword_02_prime","lt_execution_victim_sword_02","mt_execution_victim_sword_02","ht_execution_victim_sword_02","pt_lt_execution_victim_sword_02","pt_mt_execution_victim_sword_02","pt_ht_execution_victim_sword_02","","","","Stryder_1p_Sync_Melee","Stryder_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_RONIN_EXECUTION","#TITAN_EXECUTION_RONIN_PRIME","#TITAN_EXECUTION_RONIN_PRIME_DESC","rui/titan_loadout/execution/titan_execution_ronin_prime",0,0,1,""
+"execution_ion","titan_atlas_stickybomb","mt_execution_attacker_laser","mt_execution_attacker_laser","lt_execution_victim_laser","mt_execution_victim_laser","ht_execution_victim_laser","pt_execution_victim_laser","pt_execution_victim_laser","pt_execution_victim_laser","","","","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_ION_EXECUTION","#TITAN_EXECUTION_ION","#TITAN_EXECUTION_ION_DESC","rui/titan_loadout/execution/titan_execution_ion",0,0,0,""
+"execution_ion_prime","titan_atlas_ion_prime","mt_execution_attacker_ion_prime","mt_execution_attacker_ion_prime","lt_execution_victim_ion_prime","mt_execution_victim_ion_prime","ht_execution_victim_ion_prime","pt_execution_victim_ion_prime","pt_execution_victim_ion_prime","pt_execution_victim_ion_prime","","","","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_ION_EXECUTION","#TITAN_EXECUTION_ION_PRIME","#TITAN_EXECUTION_ION_PRIME_DESC","rui/titan_loadout/execution/titan_execution_ion_prime",0,0,1,""
+"execution_bt","titan_buddy","bt_synced_titan_execute_flip_takedown_A","bt_synced_titan_execute_flip_takedown_A","titan_synced_bt_execute_flip_takedown_V","titan_synced_bt_execute_flip_takedown_V","titan_synced_bt_execute_flip_takedown_V","","","","","","","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","","","","",1,0,0,""
+"execution_tone","titan_atlas_tracker","mt_execution_attacker_tone","mt_execution_attacker_tone_auto","lt_execution_victim_tone","mt_execution_victim_tone","ht_execution_victim_tone","pt_execution_victim_tone","pt_execution_victim_tone","pt_execution_victim_tone","","","","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_TONE_EXECUTION","#TITAN_EXECUTION_TONE","#TITAN_EXECUTION_TONE_DESC","rui/titan_loadout/execution/titan_execution_tone",0,0,0,""
+"execution_tone_prime","titan_atlas_tone_prime","mt_execution_attacker_tone_prime","mt_execution_attacker_tone_prime","lt_execution_victim_tone_prime","mt_execution_victim_tone_prime","ht_execution_victim_tone_prime","","","","","","","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_TONE_EXECUTION","#TITAN_EXECUTION_TONE_PRIME","#TITAN_EXECUTION_TONE_PRIME_DESC","rui/titan_loadout/execution/titan_execution_tone_prime",0,0,1,""
+"execution_northstar","titan_stryder_sniper","lt_execution_attacker_sweep_01","lt_execution_attacker_sweep_01","lt_execution_victim_sweep_01","mt_execution_victim_sweep_01","ht_execution_victim_sweep_01","","","","","","","Stryder_1p_Sync_Melee","Stryder_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_NORTHSTAR_EXECUTION","#TITAN_EXECUTION_NORTHSTAR","#TITAN_EXECUTION_NORTHSTAR_DESC","rui/titan_loadout/execution/titan_execution_northstar",0,0,0,""
+"execution_northstar_prime","titan_stryder_northstar_prime","lt_execution_attacker_armsrip","lt_execution_attacker_armsrip","lt_execution_victim_armsrip","mt_execution_victim_armsrip","ht_execution_victim_armsrip","","","","","","","Stryder_1p_Sync_Melee","Stryder_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_NORTHSTAR_EXECUTION","#TITAN_EXECUTION_NORTHSTAR_PRIME","#TITAN_EXECUTION_NORTHSTAR_PRIME_DESC","rui/titan_loadout/execution/titan_execution_northstar_prime",0,0,1,""
+"execution_vanguard","titan_atlas_vanguard","mt_execution_attacker_vanguard","mt_execution_autoTitan_attacker_vanguard","lt_execution_victim_vanguard","mt_execution_victim_vanguard","ht_execution_victim_vanguard","pt_lt_execution_victim_vanguard","pt_mt_execution_victim_vanguard","pt_ht_execution_victim_vanguard","","","","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","TITAN_VANGUARD_EXECUTION","#TITAN_EXECUTION_VANGUARD","#TITAN_EXECUTION_VANGUARD_DESC","rui/titan_loadout/execution/titan_execution_monarch",0,0,0,""
+"execution_vanguard_kit","titan_atlas_vanguard_kit","mt_execution_battery_attacker_vanguard","mt_execution_battery_attacker_vanguard","lt_execution_battery_victim_vanguard","mt_execution_battery_victim_vanguard","ht_execution_battery_victim_vanguard","pt_lt_execution_battery_victim_vanguard","pt_mt_execution_battery_victim_vanguard","pt_ht_execution_battery_victim_vanguard","pt_lt_execution_battery_attacker_vanguard","pt_mt_execution_battery_attacker_vanguard","pt_ht_execution_battery_attacker_vanguard","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","","","","",1,0,0,""
+"execution_random_0","","","","","","","","","","","","","","","","TITAN_ION_EXECUTION","#TITAN_EXECUTION_RANDOM","#TITAN_EXECUTION_RANDOM_DESC","rui/pilot_loadout/execution/execution_random",0,0,0,"execution_ion,execution_ion_prime"
+"execution_random_1","","","","","","","","","","","","","","","","TITAN_SCORCH_EXECUTION","#TITAN_EXECUTION_RANDOM","#TITAN_EXECUTION_RANDOM_DESC","rui/pilot_loadout/execution/execution_random",0,0,0,"execution_scorch,execution_scorch_prime"
+"execution_random_2","","","","","","","","","","","","","","","","TITAN_NORTHSTAR_EXECUTION","#TITAN_EXECUTION_RANDOM","#TITAN_EXECUTION_RANDOM_DESC","rui/pilot_loadout/execution/execution_random",0,0,0,"execution_northstar,execution_northstar_prime"
+"execution_random_3","","","","","","","","","","","","","","","","TITAN_RONIN_EXECUTION","#TITAN_EXECUTION_RANDOM","#TITAN_EXECUTION_RANDOM_DESC","rui/pilot_loadout/execution/execution_random",0,0,0,"execution_ronin,execution_ronin_prime"
+"execution_random_4","","","","","","","","","","","","","","","","TITAN_TONE_EXECUTION","#TITAN_EXECUTION_RANDOM","#TITAN_EXECUTION_RANDOM_DESC","rui/pilot_loadout/execution/execution_random",0,0,0,"execution_tone,execution_tone_prime"
+"execution_random_5","","","","","","","","","","","","","","","","TITAN_LEGION_EXECUTION","#TITAN_EXECUTION_RANDOM","#TITAN_EXECUTION_RANDOM_DESC","rui/pilot_loadout/execution/execution_random",0,0,0,"execution_legion,execution_legion_prime"
+"execution_random_6","","","","","","","","","","","","","","","","TITAN_VANGUARD_EXECUTION","#TITAN_EXECUTION_RANDOM","#TITAN_EXECUTION_RANDOM_DESC","rui/pilot_loadout/execution/execution_random",0,0,0,"execution_vanguard"
+"execution_bt_flip","titan_buddy","bt_synced_titan_execute_flip_takedown_A","bt_synced_titan_execute_flip_takedown_A","titan_synced_bt_execute_flip_takedown_V","titan_synced_bt_execute_flip_takedown_V","titan_synced_bt_execute_flip_takedown_V","","","","","","","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","","","","",1,0,0,""
+"execution_bt_kickshoot","titan_buddy","bt_synced_titan_execute_kickshoot_A","bt_synced_titan_execute_kickshoot_A","titan_synced_bt_execute_kickshoot_V","titan_synced_bt_execute_kickshoot_V","titan_synced_bt_execute_kickshoot_V","pt_lt_synced_bt_execute_kickshoot_V","pt_mt_synced_bt_execute_kickshoot_V","pt_ht_synced_bt_execute_kickshoot_V","","","","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","","","","",1,0,0,""
+"execution_bt_pilotrip","titan_buddy","bt_synced_titan_execute_pilot_rip_A","bt_synced_titan_execute_pilot_rip_A","titan_synced_bt_execute_pilot_rip_V","titan_synced_bt_execute_pilot_rip_V","titan_synced_bt_execute_pilot_rip_V","pt_lt_synced_bt_execute_pilot_rip_V","pt_mt_synced_bt_execute_pilot_rip_V","pt_ht_synced_bt_execute_pilot_rip_V","","","","Atlas_1p_Sync_Melee","Atlas_3p_Sync_Melee","vehicle_driver_eyes camera_2 camera_3","","","","",1,0,0,"" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_fd_upgrades.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_fd_upgrades.csv
new file mode 100644
index 00000000..aab61afb
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_fd_upgrades.csv
@@ -0,0 +1,50 @@
+ref,parentref,upgradeType,name,description,image,lockedImage,cost,slot,unlockLevel
+"fd_upgrade_tone_utility_tier_1","tone","utility","#FD_UPGRADE_TONE_UTILITY_TIER_1","#FD_UPGRADE_TONE_UTILITY_TIER_1_DESC","rui/menu/fd_menu/upgrade_tone_signal_strength","rui/menu/fd_menu/disabled/upgrade_tone_signal_strength",1,2,12
+"fd_upgrade_tone_utility_tier_2","tone","utility","#FD_UPGRADE_TONE_UTILITY_TIER_2","#FD_UPGRADE_TONE_UTILITY_TIER_2_DESC","rui/menu/fd_menu/upgrade_tone_weak_points","rui/menu/fd_menu/disabled/upgrade_tone_weak_points",2,3,6
+"fd_upgrade_tone_defense_tier_1","tone","defensive","#FD_UPGRADE_TONE_DEFENSE_TIER_1","#FD_UPGRADE_TONE_DEFENSE_TIER_1_DESC","rui/menu/fd_menu/upgrade_tone_chassis","rui/menu/fd_menu/disabled/upgrade_tone_chassis",1,0,4
+"fd_upgrade_tone_defense_tier_2","tone","defensive","#FD_UPGRADE_TONE_DEFENSE_TIER_2","#FD_UPGRADE_TONE_DEFENSE_TIER_2_DESC","rui/menu/fd_menu/upgrade_tone_shield","rui/menu/fd_menu/disabled/upgrade_tone_shield",2,1,10
+"fd_upgrade_tone_weapon_tier_1","tone","weapon","#FD_UPGRADE_TONE_WEAPON_TIER_1","#FD_UPGRADE_TONE_WEAPON_TIER_1_DESC","rui/menu/fd_menu/upgrade_tone_splasher_rounds","rui/menu/fd_menu/disabled/upgrade_tone_splasher_rounds",1,4,8
+"fd_upgrade_tone_weapon_tier_2","tone","weapon","#FD_UPGRADE_TONE_WEAPON_TIER_2","#FD_UPGRADE_TONE_WEAPON_TIER_2_DESC","rui/menu/fd_menu/upgrade_tone_extended_ammo","rui/menu/fd_menu/disabled/upgrade_tone_extended_ammo",2,5,2
+"fd_upgrade_tone_ultimate","tone","ultimate","#FD_UPGRADE_TONE_ULTIMATE","#FD_UPGRADE_TONE_ULTIMATE_DESC","rui/menu/fd_menu/upgrade_tone_salvo_core","rui/menu/fd_menu/disabled/upgrade_tone_salvo_core",3,6,14
+"fd_upgrade_ion_utility_tier_1","ion","utility","#FD_UPGRADE_ION_UTILITY_TIER_1","#FD_UPGRADE_ION_UTILITY_TIER_1_DESC","rui/menu/fd_menu/upgrade_ion_energy_master","rui/menu/fd_menu/disabled/upgrade_ion_energy_master",1,2,12
+"fd_upgrade_ion_utility_tier_2","ion","utility","#FD_UPGRADE_ION_UTILITY_TIER_2","#FD_UPGRADE_ION_UTILITY_TIER_2_DESC","rui/menu/fd_menu/upgrade_ion_energy_storage","rui/menu/fd_menu/disabled/upgrade_ion_energy_storage",2,3,6
+"fd_upgrade_ion_defense_tier_1","ion","defensive","#FD_UPGRADE_ION_DEFENSE_TIER_1","#FD_UPGRADE_ION_DEFENSE_TIER_1_DESC","rui/menu/fd_menu/upgrade_ion_chassis","rui/menu/fd_menu/disabled/upgrade_ion_chassis",1,0,4
+"fd_upgrade_ion_defense_tier_2","ion","defensive","#FD_UPGRADE_ION_DEFENSE_TIER_2","#FD_UPGRADE_ION_DEFENSE_TIER_2_DESC","rui/menu/fd_menu/upgrade_ion_shield","rui/menu/fd_menu/disabled/upgrade_ion_shield",2,1,10
+"fd_upgrade_ion_weapon_tier_1","ion","weapon","#FD_UPGRADE_ION_WEAPON_TIER_1","#FD_UPGRADE_ION_WEAPON_TIER_1_DESC","rui/menu/fd_menu/upgrade_ion_split_shot","rui/menu/fd_menu/disabled/upgrade_ion_split_shot",1,4,2
+"fd_upgrade_ion_weapon_tier_2","ion","weapon","#FD_UPGRADE_ION_WEAPON_TIER_2","#FD_UPGRADE_ION_WEAPON_TIER_2_DESC","rui/menu/fd_menu/upgrade_ion_splitter_damage","rui/menu/fd_menu/disabled/upgrade_ion_splitter_damage",2,5,8
+"fd_upgrade_ion_ultimate","ion","ultimate","#FD_UPGRADE_ION_ULTIMATE","#FD_UPGRADE_ION_ULTIMATE_DESC","rui/menu/fd_menu/upgrade_ion_laser_reset","rui/menu/fd_menu/disabled/upgrade_ion_laser_reset",3,6,14
+"fd_upgrade_vanguard_utility_tier_1","vanguard","utility","#FD_UPGRADE_VANGUARD_UTILITY_TIER_1","#FD_UPGRADE_VANGUARD_UTILITY_TIER_1_DESC","rui/menu/fd_menu/upgrade_monarch_energize_smoke","rui/menu/fd_menu/disabled/upgrade_monarch_energize_smoke",1,2,2
+"fd_upgrade_vanguard_utility_tier_2","vanguard","utility","#FD_UPGRADE_VANGUARD_UTILITY_TIER_2","#FD_UPGRADE_VANGUARD_UTILITY_TIER_2_DESC","rui/menu/fd_menu/upgrade_monarch_energize_smoke_2","rui/menu/fd_menu/disabled/upgrade_monarch_energize_smoke_2",2,3,8
+"fd_upgrade_vanguard_defense_tier_1","vanguard","defensive","#FD_UPGRADE_VANGUARD_DEFENSE_TIER_1","#FD_UPGRADE_VANGUARD_DEFENSE_TIER_1_DESC","rui/menu/fd_menu/upgrade_monarch_chassis","rui/menu/fd_menu/disabled/upgrade_monarch_chassis",1,0,4
+"fd_upgrade_vanguard_defense_tier_2","vanguard","defensive","#FD_UPGRADE_VANGUARD_DEFENSE_TIER_2","#FD_UPGRADE_VANGUARD_DEFENSE_TIER_2_DESC","rui/menu/fd_menu/upgrade_monarch_shield","rui/menu/fd_menu/disabled/upgrade_monarch_shield",2,1,10
+"fd_upgrade_vanguard_weapon_tier_1","vanguard","weapon","#FD_UPGRADE_VANGUARD_WEAPON_TIER_1","#FD_UPGRADE_VANGUARD_WEAPON_TIER_1_DESC","rui/menu/fd_menu/upgrade_monarch_threat_optics","rui/menu/fd_menu/disabled/upgrade_monarch_threat_optics",1,4,6
+"fd_upgrade_vanguard_weapon_tier_2","vanguard","weapon","#FD_UPGRADE_VANGUARD_WEAPON_TIER_2","#FD_UPGRADE_VANGUARD_WEAPON_TIER_2_DESC","rui/menu/fd_menu/upgrade_monarch_xo16_sniper","rui/menu/fd_menu/disabled/upgrade_monarch_xo16_sniper",2,5,12
+"fd_upgrade_vanguard_ultimate","vanguard","ultimate","#FD_UPGRADE_VANGUARD_ULTIMATE","#FD_UPGRADE_VANGUARD_ULTIMATE_DESC","rui/menu/fd_menu/upgrade_monarch_apex_titan","rui/menu/fd_menu/disabled/upgrade_monarch_apex_titan",3,6,14
+"fd_upgrade_ronin_utility_tier_1","ronin","utility","#FD_UPGRADE_RONIN_UTILITY_TIER_1","#FD_UPGRADE_RONIN_UTILITY_TIER_1_DESC","rui/menu/fd_menu/upgrade_ronin_ghost_machine","rui/menu/fd_menu/disabled/upgrade_ronin_ghost_machine",1,2,6
+"fd_upgrade_ronin_utility_tier_2","ronin","utility","#FD_UPGRADE_RONIN_UTILITY_TIER_2","#FD_UPGRADE_RONIN_UTILITY_TIER_2_DESC","rui/menu/fd_menu/upgrade_ronin_wraith","rui/menu/fd_menu/disabled/upgrade_ronin_wraith",2,3,12
+"fd_upgrade_ronin_defense_tier_1","ronin","defensive","#FD_UPGRADE_RONIN_DEFENSE_TIER_1","#FD_UPGRADE_RONIN_DEFENSE_TIER_1_DESC","rui/menu/fd_menu/upgrade_ronin_chassis","rui/menu/fd_menu/disabled/upgrade_ronin_chassis",1,0,4
+"fd_upgrade_ronin_defense_tier_2","ronin","defensive","#FD_UPGRADE_RONIN_DEFENSE_TIER_2","#FD_UPGRADE_RONIN_DEFENSE_TIER_2_DESC","rui/menu/fd_menu/upgrade_ronin_shield","rui/menu/fd_menu/disabled/upgrade_ronin_shield",2,1,10
+"fd_upgrade_ronin_weapon_tier_1","ronin","weapon","#FD_UPGRADE_RONIN_WEAPON_TIER_1","#FD_UPGRADE_RONIN_WEAPON_TIER_1_DESC","rui/menu/fd_menu/upgrade_ronin_sword_mastery","rui/menu/fd_menu/disabled/upgrade_ronin_sword_mastery",1,4,2
+"fd_upgrade_ronin_weapon_tier_2","ronin","weapon","#FD_UPGRADE_RONIN_WEAPON_TIER_2","#FD_UPGRADE_RONIN_WEAPON_TIER_2_DESC","rui/menu/fd_menu/upgrade_ronin_kinetic_transfer","rui/menu/fd_menu/disabled/upgrade_ronin_kinetic_transfer",2,5,8
+"fd_upgrade_ronin_ultimate","ronin","ultimate","#FD_UPGRADE_RONIN_ULTIMATE","#FD_UPGRADE_RONIN_ULTIMATE_DESC","rui/menu/fd_menu/upgrade_ronin_blade_master","rui/menu/fd_menu/disabled/upgrade_ronin_blade_master",3,6,14
+"fd_upgrade_northstar_utility_tier_1","northstar","utility","#FD_UPGRADE_NORTHSTAR_UTILITY_TIER_1","#FD_UPGRADE_NORTHSTAR_UTILITY_TIER_1_DESC","rui/menu/fd_menu/upgrade_northstar_explosive_trap","rui/menu/fd_menu/disabled/upgrade_northstar_explosive_trap",1,2,2
+"fd_upgrade_northstar_utility_tier_2","northstar","utility","#FD_UPGRADE_NORTHSTAR_UTILITY_TIER_2","#FD_UPGRADE_NORTHSTAR_UTILITY_TIER_2_DESC","rui/menu/fd_menu/upgrade_northstar_trap_master","rui/menu/fd_menu/disabled/upgrade_northstar_trap_master",2,3,8
+"fd_upgrade_northstar_defense_tier_1","northstar","defensive","#FD_UPGRADE_NORTHSTAR_DEFENSE_TIER_1","#FD_UPGRADE_NORTHSTAR_DEFENSE_TIER_1_DESC","rui/menu/fd_menu/upgrade_northstar_chassis","rui/menu/fd_menu/disabled/upgrade_northstar_chassis",1,0,4
+"fd_upgrade_northstar_defense_tier_2","northstar","defensive","#FD_UPGRADE_NORTHSTAR_DEFENSE_TIER_2","#FD_UPGRADE_NORTHSTAR_DEFENSE_TIER_2_DESC","rui/menu/fd_menu/upgrade_northstar_shield","rui/menu/fd_menu/disabled/upgrade_northstar_shield",2,1,10
+"fd_upgrade_northstar_weapon_tier_1","northstar","weapon","#FD_UPGRADE_NORTHSTAR_WEAPON_TIER_1","#FD_UPGRADE_NORTHSTAR_WEAPON_TIER_1_DESC","rui/menu/fd_menu/upgrade_northstar_quick_charge","rui/menu/fd_menu/disabled/upgrade_northstar_quick_charge",1,4,6
+"fd_upgrade_northstar_weapon_tier_2","northstar","weapon","#FD_UPGRADE_NORTHSTAR_WEAPON_TIER_2","#FD_UPGRADE_NORTHSTAR_WEAPON_TIER_2_DESC","rui/menu/fd_menu/upgrade_northstar_one_shot_kill","rui/menu/fd_menu/disabled/upgrade_northstar_one_shot_kill",2,5,12
+"fd_upgrade_northstar_ultimate","northstar","ultimate","#FD_UPGRADE_NORTHSTAR_ULTIMATE","#FD_UPGRADE_NORTHSTAR_ULTIMATE_DESC","rui/menu/fd_menu/upgrade_northstar_twin_cluster","rui/menu/fd_menu/disabled/upgrade_northstar_twin_cluster",3,6,14
+"fd_upgrade_scorch_utility_tier_1","scorch","utility","#FD_UPGRADE_SCORCH_UTILITY_TIER_1","#FD_UPGRADE_SCORCH_UTILITY_TIER_1_DESC","rui/menu/fd_menu/upgrade_scorch_hot_streak","rui/menu/fd_menu/disabled/upgrade_scorch_hot_streak",1,2,6
+"fd_upgrade_scorch_utility_tier_2","scorch","utility","#FD_UPGRADE_SCORCH_UTILITY_TIER_2","#FD_UPGRADE_SCORCH_UTILITY_TIER_2_DESC","rui/menu/fd_menu/upgrade_scorch_roaring_flame","rui/menu/fd_menu/disabled/upgrade_scorch_roaring_flame",2,3,8
+"fd_upgrade_scorch_defense_tier_1","scorch","defensive","#FD_UPGRADE_SCORCH_DEFENSE_TIER_1","#FD_UPGRADE_SCORCH_DEFENSE_TIER_1_DESC","rui/menu/fd_menu/upgrade_scorch_chassis","rui/menu/fd_menu/disabled/upgrade_scorch_chassis",1,0,4
+"fd_upgrade_scorch_defense_tier_2","scorch","defensive","#FD_UPGRADE_SCORCH_DEFENSE_TIER_2","#FD_UPGRADE_SCORCH_DEFENSE_TIER_2_DESC","rui/menu/fd_menu/upgrade_scorch_shield","rui/menu/fd_menu/disabled/upgrade_scorch_shield",2,1,10
+"fd_upgrade_scorch_weapon_tier_1","scorch","weapon","#FD_UPGRADE_SCORCH_WEAPON_TIER_1","#FD_UPGRADE_SCORCH_WEAPON_TIER_1_DESC","rui/menu/fd_menu/upgrade_scorch_double_threat","rui/menu/fd_menu/disabled/upgrade_scorch_double_threat",1,4,2
+"fd_upgrade_scorch_weapon_tier_2","scorch","weapon","#FD_UPGRADE_SCORCH_WEAPON_TIER_2","#FD_UPGRADE_SCORCH_WEAPON_TIER_2_DESC","rui/menu/fd_menu/upgrade_scorch_triple_threat","rui/menu/fd_menu/disabled/upgrade_scorch_triple_threat",2,5,12
+"fd_upgrade_scorch_ultimate","scorch","ultimate","#FD_UPGRADE_SCORCH_ULTIMATE","#FD_UPGRADE_SCORCH_ULTIMATE_DESC","rui/menu/fd_menu/upgrade_scorch_explosive_barrells","rui/menu/fd_menu/disabled/upgrade_scorch_explosive_barrells",3,6,14
+"fd_upgrade_legion_utility_tier_1","legion","utility","#FD_UPGRADE_LEGION_UTILITY_TIER_1","#FD_UPGRADE_LEGION_UTILITY_TIER_1_DESC","rui/menu/fd_menu/upgrade_legion_executioner","rui/menu/fd_menu/disabled/upgrade_legion_executioner",1,2,6
+"fd_upgrade_legion_utility_tier_2","legion","utility","#FD_UPGRADE_LEGION_UTILITY_TIER_2","#FD_UPGRADE_LEGION_UTILITY_TIER_2_DESC","rui/menu/fd_menu/upgrade_legion_drill_shot","rui/menu/fd_menu/disabled/upgrade_legion_drill_shot",2,3,12
+"fd_upgrade_legion_defense_tier_1","legion","defensive","#FD_UPGRADE_LEGION_DEFENSE_TIER_1","#FD_UPGRADE_LEGION_DEFENSE_TIER_1_DESC","rui/menu/fd_menu/upgrade_legion_chassis","rui/menu/fd_menu/disabled/upgrade_legion_chassis",1,0,4
+"fd_upgrade_legion_defense_tier_2","legion","defensive","#FD_UPGRADE_LEGION_DEFENSE_TIER_2","#FD_UPGRADE_LEGION_DEFENSE_TIER_2_DESC","rui/menu/fd_menu/upgrade_legion_shield","rui/menu/fd_menu/disabled/upgrade_legion_shield",2,1,10
+"fd_upgrade_legion_weapon_tier_1","legion","weapon","#FD_UPGRADE_LEGION_WEAPON_TIER_1","#FD_UPGRADE_LEGION_WEAPON_TIER_1_DESC","rui/menu/fd_menu/upgrade_legion_piercing_shot","rui/menu/fd_menu/disabled/upgrade_legion_piercing_shot",1,4,2
+"fd_upgrade_legion_weapon_tier_2","legion","weapon","#FD_UPGRADE_LEGION_WEAPON_TIER_2","#FD_UPGRADE_LEGION_WEAPON_TIER_2_DESC","rui/menu/fd_menu/upgrade_legion_redirect","rui/menu/fd_menu/disabled/upgrade_legion_redirect",2,5,8
+"fd_upgrade_legion_ultimate","legion","ultimate","#FD_UPGRADE_LEGION_ULTIMATE","#FD_UPGRADE_LEGION_ULTIMATE_DESC","rui/menu/fd_menu/upgrade_legion_double_down","rui/menu/fd_menu/disabled/upgrade_legion_double_down",3,6,14 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_nose_art.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_nose_art.csv
new file mode 100644
index 00000000..56bcf556
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_nose_art.csv
@@ -0,0 +1,191 @@
+ref,titanRef,image,name,cost
+"ion_nose_art_none","ion","rui/menu/common/no_art","#FACTORY_ISSUE_NAME",0
+"ion_nose_art_01","ion","rui/titan_loadout/nose_art/ion_nose_art_01","#ION_NOSE_ART_01",0
+"ion_nose_art_02","ion","rui/titan_loadout/nose_art/ion_nose_art_02","#ION_NOSE_ART_02",0
+"ion_nose_art_03","ion","rui/titan_loadout/nose_art/ion_nose_art_03","#ION_NOSE_ART_03",0
+"ion_nose_art_04","ion","rui/titan_loadout/nose_art/ion_nose_art_04","#ION_NOSE_ART_04",0
+"ion_nose_art_05","ion","rui/titan_loadout/nose_art/ion_nose_art_05","#ION_NOSE_ART_05",300
+"ion_nose_art_06","ion","rui/titan_loadout/nose_art/ion_nose_art_06","#ION_NOSE_ART_06",0
+"ion_nose_art_07","ion","rui/titan_loadout/nose_art/ion_nose_art_07","#ION_NOSE_ART_07",0
+"ion_nose_art_08","ion","rui/titan_loadout/nose_art/ion_nose_art_08","#ION_NOSE_ART_08",0
+"ion_nose_art_09","ion","rui/titan_loadout/nose_art/ion_nose_art_09","#ION_NOSE_ART_09",0
+"ion_nose_art_10","ion","rui/titan_loadout/nose_art/ion_nose_art_10","#ION_NOSE_ART_10",0
+"ion_nose_art_11","ion","rui/titan_loadout/nose_art/ion_nose_art_11","#ION_NOSE_ART_11",0
+"ion_nose_art_12","ion","rui/titan_loadout/nose_art/ion_nose_art_12","#ION_NOSE_ART_12",0
+"ion_nose_art_13","ion","rui/titan_loadout/nose_art/ion_nose_art_13","#ION_NOSE_ART_13",0
+"ion_nose_art_14","ion","rui/titan_loadout/nose_art/ion_nose_art_14","#ION_NOSE_ART_14",0
+"scorch_nose_art_none","scorch","rui/menu/common/no_art","#FACTORY_ISSUE_NAME",0
+"scorch_nose_art_01","scorch","rui/titan_loadout/nose_art/scorch_nose_art_01","#SCORCH_NOSE_ART_01",0
+"scorch_nose_art_02","scorch","rui/titan_loadout/nose_art/scorch_nose_art_02","#SCORCH_NOSE_ART_02",0
+"scorch_nose_art_03","scorch","rui/titan_loadout/nose_art/scorch_nose_art_03","#SCORCH_NOSE_ART_03",0
+"scorch_nose_art_04","scorch","rui/titan_loadout/nose_art/scorch_nose_art_04","#SCORCH_NOSE_ART_04",0
+"scorch_nose_art_05","scorch","rui/titan_loadout/nose_art/scorch_nose_art_05","#SCORCH_NOSE_ART_05",0
+"scorch_nose_art_06","scorch","rui/titan_loadout/nose_art/scorch_nose_art_06","#SCORCH_NOSE_ART_06",0
+"scorch_nose_art_07","scorch","rui/titan_loadout/nose_art/scorch_nose_art_07","#SCORCH_NOSE_ART_07",0
+"scorch_nose_art_08","scorch","rui/titan_loadout/nose_art/scorch_nose_art_08","#SCORCH_NOSE_ART_08",0
+"scorch_nose_art_09","scorch","rui/titan_loadout/nose_art/scorch_nose_art_09","#SCORCH_NOSE_ART_09",0
+"scorch_nose_art_10","scorch","rui/titan_loadout/nose_art/scorch_nose_art_10","#SCORCH_NOSE_ART_10",0
+"scorch_nose_art_11","scorch","rui/titan_loadout/nose_art/scorch_nose_art_11","#SCORCH_NOSE_ART_11",0
+"scorch_nose_art_12","scorch","rui/titan_loadout/nose_art/scorch_nose_art_12","#SCORCH_NOSE_ART_12",300
+"scorch_nose_art_13","scorch","rui/titan_loadout/nose_art/scorch_nose_art_13","#SCORCH_NOSE_ART_13",0
+"scorch_nose_art_14","scorch","rui/titan_loadout/nose_art/scorch_nose_art_14","#SCORCH_NOSE_ART_14",0
+"ronin_nose_art_none","ronin","rui/menu/common/no_art","#FACTORY_ISSUE_NAME",0
+"ronin_nose_art_01","ronin","rui/titan_loadout/nose_art/ronin_nose_art_01","#RONIN_NOSE_ART_01",0
+"ronin_nose_art_02","ronin","rui/titan_loadout/nose_art/ronin_nose_art_02","#RONIN_NOSE_ART_02",0
+"ronin_nose_art_03","ronin","rui/titan_loadout/nose_art/ronin_nose_art_03","#RONIN_NOSE_ART_03",0
+"ronin_nose_art_04","ronin","rui/titan_loadout/nose_art/ronin_nose_art_04","#RONIN_NOSE_ART_04",0
+"ronin_nose_art_05","ronin","rui/titan_loadout/nose_art/ronin_nose_art_05","#RONIN_NOSE_ART_05",0
+"ronin_nose_art_06","ronin","rui/titan_loadout/nose_art/ronin_nose_art_06","#RONIN_NOSE_ART_06",0
+"ronin_nose_art_07","ronin","rui/titan_loadout/nose_art/ronin_nose_art_07","#RONIN_NOSE_ART_07",0
+"ronin_nose_art_08","ronin","rui/titan_loadout/nose_art/ronin_nose_art_08","#RONIN_NOSE_ART_08",300
+"ronin_nose_art_09","ronin","rui/titan_loadout/nose_art/ronin_nose_art_09","#RONIN_NOSE_ART_09",0
+"ronin_nose_art_10","ronin","rui/titan_loadout/nose_art/ronin_nose_art_10","#RONIN_NOSE_ART_10",0
+"ronin_nose_art_11","ronin","rui/titan_loadout/nose_art/ronin_nose_art_11","#RONIN_NOSE_ART_11",0
+"ronin_nose_art_12","ronin","rui/titan_loadout/nose_art/ronin_nose_art_12","#RONIN_NOSE_ART_12",300
+"ronin_nose_art_13","ronin","rui/titan_loadout/nose_art/ronin_nose_art_13","#RONIN_NOSE_ART_13",0
+"ronin_nose_art_14","ronin","rui/titan_loadout/nose_art/ronin_nose_art_14","#RONIN_NOSE_ART_14",0
+"tone_nose_art_none","tone","rui/menu/common/no_art","#FACTORY_ISSUE_NAME",0
+"tone_nose_art_01","tone","rui/titan_loadout/nose_art/tone_nose_art_01","#TONE_NOSE_ART_01",0
+"tone_nose_art_02","tone","rui/titan_loadout/nose_art/tone_nose_art_02","#TONE_NOSE_ART_02",0
+"tone_nose_art_03","tone","rui/titan_loadout/nose_art/tone_nose_art_03","#TONE_NOSE_ART_03",0
+"tone_nose_art_04","tone","rui/titan_loadout/nose_art/tone_nose_art_04","#TONE_NOSE_ART_04",0
+"tone_nose_art_05","tone","rui/titan_loadout/nose_art/tone_nose_art_05","#TONE_NOSE_ART_05",0
+"tone_nose_art_06","tone","rui/titan_loadout/nose_art/tone_nose_art_06","#TONE_NOSE_ART_06",0
+"tone_nose_art_07","tone","rui/titan_loadout/nose_art/tone_nose_art_07","#TONE_NOSE_ART_07",0
+"tone_nose_art_08","tone","rui/titan_loadout/nose_art/tone_nose_art_08","#TONE_NOSE_ART_08",0
+"tone_nose_art_09","tone","rui/titan_loadout/nose_art/tone_nose_art_09","#TONE_NOSE_ART_09",0
+"tone_nose_art_10","tone","rui/titan_loadout/nose_art/tone_nose_art_10","#TONE_NOSE_ART_10",0
+"tone_nose_art_11","tone","rui/titan_loadout/nose_art/tone_nose_art_11","#TONE_NOSE_ART_11",300
+"tone_nose_art_12","tone","rui/titan_loadout/nose_art/tone_nose_art_12","#TONE_NOSE_ART_12",300
+"tone_nose_art_13","tone","rui/titan_loadout/nose_art/tone_nose_art_13","#TONE_NOSE_ART_13",0
+"tone_nose_art_14","tone","rui/titan_loadout/nose_art/tone_nose_art_14","#TONE_NOSE_ART_14",0
+"northstar_nose_art_none","northstar","rui/menu/common/no_art","#FACTORY_ISSUE_NAME",0
+"northstar_nose_art_01","northstar","rui/titan_loadout/nose_art/northstar_nose_art_01","#NORTHSTAR_NOSE_ART_01",0
+"northstar_nose_art_02","northstar","rui/titan_loadout/nose_art/northstar_nose_art_02","#NORTHSTAR_NOSE_ART_02",0
+"northstar_nose_art_03","northstar","rui/titan_loadout/nose_art/northstar_nose_art_03","#NORTHSTAR_NOSE_ART_03",0
+"northstar_nose_art_04","northstar","rui/titan_loadout/nose_art/northstar_nose_art_04","#NORTHSTAR_NOSE_ART_04",0
+"northstar_nose_art_05","northstar","rui/titan_loadout/nose_art/northstar_nose_art_05","#NORTHSTAR_NOSE_ART_05",0
+"northstar_nose_art_06","northstar","rui/titan_loadout/nose_art/northstar_nose_art_06","#NORTHSTAR_NOSE_ART_06",0
+"northstar_nose_art_07","northstar","rui/titan_loadout/nose_art/northstar_nose_art_07","#NORTHSTAR_NOSE_ART_07",0
+"northstar_nose_art_08","northstar","rui/titan_loadout/nose_art/northstar_nose_art_08","#NORTHSTAR_NOSE_ART_08",0
+"northstar_nose_art_09","northstar","rui/titan_loadout/nose_art/northstar_nose_art_09","#NORTHSTAR_NOSE_ART_09",0
+"northstar_nose_art_10","northstar","rui/titan_loadout/nose_art/northstar_nose_art_10","#NORTHSTAR_NOSE_ART_10",0
+"northstar_nose_art_11","northstar","rui/titan_loadout/nose_art/northstar_nose_art_11","#NORTHSTAR_NOSE_ART_11",0
+"northstar_nose_art_12","northstar","rui/titan_loadout/nose_art/northstar_nose_art_12","#NORTHSTAR_NOSE_ART_12",300
+"northstar_nose_art_13","northstar","rui/titan_loadout/nose_art/northstar_nose_art_13","#NORTHSTAR_NOSE_ART_13",300
+"northstar_nose_art_14","northstar","rui/titan_loadout/nose_art/northstar_nose_art_14","#NORTHSTAR_NOSE_ART_14",0
+"legion_nose_art_none","legion","rui/menu/common/no_art","#FACTORY_ISSUE_NAME",0
+"legion_nose_art_01","legion","rui/titan_loadout/nose_art/legion_nose_art_01","#LEGION_NOSE_ART_01",0
+"legion_nose_art_02","legion","rui/titan_loadout/nose_art/legion_nose_art_02","#LEGION_NOSE_ART_02",0
+"legion_nose_art_03","legion","rui/titan_loadout/nose_art/legion_nose_art_03","#LEGION_NOSE_ART_03",0
+"legion_nose_art_04","legion","rui/titan_loadout/nose_art/legion_nose_art_04","#LEGION_NOSE_ART_04",0
+"legion_nose_art_05","legion","rui/titan_loadout/nose_art/legion_nose_art_05","#LEGION_NOSE_ART_05",0
+"legion_nose_art_06","legion","rui/titan_loadout/nose_art/legion_nose_art_06","#LEGION_NOSE_ART_06",0
+"legion_nose_art_07","legion","rui/titan_loadout/nose_art/legion_nose_art_07","#LEGION_NOSE_ART_07",0
+"legion_nose_art_08","legion","rui/titan_loadout/nose_art/legion_nose_art_08","#LEGION_NOSE_ART_08",0
+"legion_nose_art_09","legion","rui/titan_loadout/nose_art/legion_nose_art_09","#LEGION_NOSE_ART_09",0
+"legion_nose_art_10","legion","rui/titan_loadout/nose_art/legion_nose_art_10","#LEGION_NOSE_ART_10",0
+"legion_nose_art_11","legion","rui/titan_loadout/nose_art/legion_nose_art_11","#LEGION_NOSE_ART_11",0
+"legion_nose_art_12","legion","rui/titan_loadout/nose_art/legion_nose_art_12","#LEGION_NOSE_ART_12",300
+"legion_nose_art_13","legion","rui/titan_loadout/nose_art/legion_nose_art_13","#LEGION_NOSE_ART_13",0
+"legion_nose_art_14","legion","rui/titan_loadout/nose_art/legion_nose_art_14","#LEGION_NOSE_ART_14",300
+"ion_nose_art_17","ion","rui/titan_loadout/nose_art/ion_nose_art_17","#ION_NOSE_ART_17",0
+"ion_nose_art_18","ion","rui/titan_loadout/nose_art/ion_nose_art_18","#ION_NOSE_ART_18",0
+"ion_nose_art_19","ion","rui/titan_loadout/nose_art/ion_nose_art_19","#ION_NOSE_ART_19",0
+"ion_nose_art_20","ion","rui/titan_loadout/nose_art/ion_nose_art_20","#ION_NOSE_ART_20",0
+"ion_nose_art_21","ion","rui/titan_loadout/nose_art/ion_nose_art_21","#ION_NOSE_ART_21",0
+"scorch_nose_art_15","scorch","rui/titan_loadout/nose_art/scorch_nose_art_15","#SCORCH_NOSE_ART_15",0
+"scorch_nose_art_16","scorch","rui/titan_loadout/nose_art/scorch_nose_art_16","#SCORCH_NOSE_ART_16",0
+"scorch_nose_art_17","scorch","rui/titan_loadout/nose_art/scorch_nose_art_17","#SCORCH_NOSE_ART_17",0
+"scorch_nose_art_18","scorch","rui/titan_loadout/nose_art/scorch_nose_art_18","#SCORCH_NOSE_ART_18",0
+"scorch_nose_art_19","scorch","rui/titan_loadout/nose_art/scorch_nose_art_19","#SCORCH_NOSE_ART_19",0
+"ronin_nose_art_16","ronin","rui/titan_loadout/nose_art/ronin_nose_art_16","#RONIN_NOSE_ART_16",0
+"ronin_nose_art_17","ronin","rui/titan_loadout/nose_art/ronin_nose_art_17","#RONIN_NOSE_ART_17",0
+"ronin_nose_art_18","ronin","rui/titan_loadout/nose_art/ronin_nose_art_18","#RONIN_NOSE_ART_18",0
+"ronin_nose_art_19","ronin","rui/titan_loadout/nose_art/ronin_nose_art_19","#RONIN_NOSE_ART_19",0
+"ronin_nose_art_20","ronin","rui/titan_loadout/nose_art/ronin_nose_art_20","#RONIN_NOSE_ART_20",0
+"tone_nose_art_17","tone","rui/titan_loadout/nose_art/tone_nose_art_17","#TONE_NOSE_ART_17",0
+"tone_nose_art_18","tone","rui/titan_loadout/nose_art/tone_nose_art_18","#TONE_NOSE_ART_18",0
+"tone_nose_art_19","tone","rui/titan_loadout/nose_art/tone_nose_art_19","#TONE_NOSE_ART_19",0
+"tone_nose_art_20","tone","rui/titan_loadout/nose_art/tone_nose_art_20","#TONE_NOSE_ART_20",0
+"tone_nose_art_21","tone","rui/titan_loadout/nose_art/tone_nose_art_21","#TONE_NOSE_ART_21",0
+"northstar_nose_art_18","northstar","rui/titan_loadout/nose_art/northstar_nose_art_18","#NORTHSTAR_NOSE_ART_18",0
+"northstar_nose_art_19","northstar","rui/titan_loadout/nose_art/northstar_nose_art_19","#NORTHSTAR_NOSE_ART_19",0
+"northstar_nose_art_20","northstar","rui/titan_loadout/nose_art/northstar_nose_art_20","#NORTHSTAR_NOSE_ART_20",0
+"northstar_nose_art_21","northstar","rui/titan_loadout/nose_art/northstar_nose_art_21","#NORTHSTAR_NOSE_ART_21",0
+"northstar_nose_art_22","northstar","rui/titan_loadout/nose_art/northstar_nose_art_22","#NORTHSTAR_NOSE_ART_22",0
+"legion_nose_art_17","legion","rui/titan_loadout/nose_art/legion_nose_art_17","#LEGION_NOSE_ART_17",0
+"legion_nose_art_18","legion","rui/titan_loadout/nose_art/legion_nose_art_18","#LEGION_NOSE_ART_18",0
+"legion_nose_art_19","legion","rui/titan_loadout/nose_art/legion_nose_art_19","#LEGION_NOSE_ART_19",0
+"legion_nose_art_20","legion","rui/titan_loadout/nose_art/legion_nose_art_20","#LEGION_NOSE_ART_20",0
+"legion_nose_art_21","legion","rui/titan_loadout/nose_art/legion_nose_art_21","#LEGION_NOSE_ART_21",0
+"ion_nose_art_22","ion","rui/titan_loadout/nose_art/ion_nose_art_22","#ION_NOSE_ART_22",0
+"ion_nose_art_23","ion","rui/titan_loadout/nose_art/ion_nose_art_23","#ION_NOSE_ART_23",0
+"ion_nose_art_24","ion","rui/titan_loadout/nose_art/ion_nose_art_24","#ION_NOSE_ART_24",0
+"ion_nose_art_25","ion","rui/titan_loadout/nose_art/ion_nose_art_25","#ION_NOSE_ART_25",0
+"ion_nose_art_26","ion","rui/titan_loadout/nose_art/ion_nose_art_26","#ION_NOSE_ART_26",0
+"scorch_nose_art_20","scorch","rui/titan_loadout/nose_art/scorch_nose_art_20","#SCORCH_NOSE_ART_20",0
+"scorch_nose_art_21","scorch","rui/titan_loadout/nose_art/scorch_nose_art_21","#SCORCH_NOSE_ART_21",0
+"scorch_nose_art_22","scorch","rui/titan_loadout/nose_art/scorch_nose_art_22","#SCORCH_NOSE_ART_22",0
+"scorch_nose_art_23","scorch","rui/titan_loadout/nose_art/scorch_nose_art_23","#SCORCH_NOSE_ART_23",0
+"scorch_nose_art_24","scorch","rui/titan_loadout/nose_art/scorch_nose_art_24","#SCORCH_NOSE_ART_24",0
+"ronin_nose_art_21","ronin","rui/titan_loadout/nose_art/ronin_nose_art_21","#RONIN_NOSE_ART_21",0
+"ronin_nose_art_22","ronin","rui/titan_loadout/nose_art/ronin_nose_art_22","#RONIN_NOSE_ART_22",0
+"ronin_nose_art_23","ronin","rui/titan_loadout/nose_art/ronin_nose_art_23","#RONIN_NOSE_ART_23",0
+"ronin_nose_art_24","ronin","rui/titan_loadout/nose_art/ronin_nose_art_24","#RONIN_NOSE_ART_24",0
+"ronin_nose_art_25","ronin","rui/titan_loadout/nose_art/ronin_nose_art_25","#RONIN_NOSE_ART_25",0
+"tone_nose_art_22","tone","rui/titan_loadout/nose_art/tone_nose_art_22","#TONE_NOSE_ART_22",0
+"tone_nose_art_23","tone","rui/titan_loadout/nose_art/tone_nose_art_23","#TONE_NOSE_ART_23",0
+"tone_nose_art_24","tone","rui/titan_loadout/nose_art/tone_nose_art_24","#TONE_NOSE_ART_24",0
+"tone_nose_art_25","tone","rui/titan_loadout/nose_art/tone_nose_art_25","#TONE_NOSE_ART_25",0
+"tone_nose_art_26","tone","rui/titan_loadout/nose_art/tone_nose_art_26","#TONE_NOSE_ART_26",0
+"northstar_nose_art_23","northstar","rui/titan_loadout/nose_art/northstar_nose_art_23","#NORTHSTAR_NOSE_ART_23",0
+"northstar_nose_art_24","northstar","rui/titan_loadout/nose_art/northstar_nose_art_24","#NORTHSTAR_NOSE_ART_24",0
+"northstar_nose_art_25","northstar","rui/titan_loadout/nose_art/northstar_nose_art_25","#NORTHSTAR_NOSE_ART_25",0
+"northstar_nose_art_26","northstar","rui/titan_loadout/nose_art/northstar_nose_art_26","#NORTHSTAR_NOSE_ART_26",0
+"northstar_nose_art_27","northstar","rui/titan_loadout/nose_art/northstar_nose_art_27","#NORTHSTAR_NOSE_ART_27",0
+"legion_nose_art_22","legion","rui/titan_loadout/nose_art/legion_nose_art_22","#LEGION_NOSE_ART_22",0
+"legion_nose_art_23","legion","rui/titan_loadout/nose_art/legion_nose_art_23","#LEGION_NOSE_ART_23",0
+"legion_nose_art_24","legion","rui/titan_loadout/nose_art/legion_nose_art_24","#LEGION_NOSE_ART_24",0
+"legion_nose_art_25","legion","rui/titan_loadout/nose_art/legion_nose_art_25","#LEGION_NOSE_ART_25",0
+"legion_nose_art_26","legion","rui/titan_loadout/nose_art/legion_nose_art_26","#LEGION_NOSE_ART_26",0
+"vanguard_nose_art_none","vanguard","rui/menu/common/no_art","#FACTORY_ISSUE_NAME",0
+"vanguard_nose_art_01","vanguard","rui/titan_loadout/nose_art/monarch_nose_art_02","#VANGUARD_NOSE_ART_01",0
+"vanguard_nose_art_02","vanguard","rui/titan_loadout/nose_art/monarch_nose_art_03","#VANGUARD_NOSE_ART_02",0
+"vanguard_nose_art_03","vanguard","rui/titan_loadout/nose_art/monarch_nose_art_04","#VANGUARD_NOSE_ART_03",0
+"vanguard_nose_art_04","vanguard","rui/titan_loadout/nose_art/monarch_nose_art_05","#VANGUARD_NOSE_ART_04",0
+"vanguard_nose_art_05","vanguard","rui/titan_loadout/nose_art/monarch_nose_art_06","#VANGUARD_NOSE_ART_05",0
+"vanguard_nose_art_06","vanguard","rui/titan_loadout/nose_art/monarch_nose_art_07","#VANGUARD_NOSE_ART_06",0
+"vanguard_nose_art_07","vanguard","rui/titan_loadout/nose_art/monarch_nose_art_08","#VANGUARD_NOSE_ART_07",0
+"vanguard_nose_art_08","vanguard","rui/titan_loadout/nose_art/monarch_nose_art_09","#VANGUARD_NOSE_ART_08",0
+"vanguard_nose_art_09","vanguard","rui/titan_loadout/nose_art/monarch_nose_art_10","#VANGUARD_NOSE_ART_09",0
+"ion_nose_art_27","ion","rui/titan_loadout/nose_art/ion_nose_art_27","#ION_NOSE_ART_27",0
+"ion_nose_art_28","ion","rui/titan_loadout/nose_art/ion_nose_art_28","#ION_NOSE_ART_28",0
+"ion_nose_art_29","ion","rui/titan_loadout/nose_art/ion_nose_art_29","#ION_NOSE_ART_29",0
+"ion_nose_art_30","ion","rui/titan_loadout/nose_art/ion_nose_art_30","#ION_NOSE_ART_30",0
+"ion_nose_art_31","ion","rui/titan_loadout/nose_art/ion_nose_art_31","#ION_NOSE_ART_31",0
+"scorch_nose_art_25","scorch","rui/titan_loadout/nose_art/scorch_nose_art_25","#SCORCH_NOSE_ART_25",0
+"scorch_nose_art_26","scorch","rui/titan_loadout/nose_art/scorch_nose_art_26","#SCORCH_NOSE_ART_26",0
+"scorch_nose_art_27","scorch","rui/titan_loadout/nose_art/scorch_nose_art_27","#SCORCH_NOSE_ART_27",0
+"scorch_nose_art_28","scorch","rui/titan_loadout/nose_art/scorch_nose_art_28","#SCORCH_NOSE_ART_28",0
+"scorch_nose_art_29","scorch","rui/titan_loadout/nose_art/scorch_nose_art_29","#SCORCH_NOSE_ART_29",0
+"ronin_nose_art_26","ronin","rui/titan_loadout/nose_art/ronin_nose_art_26","#RONIN_NOSE_ART_26",0
+"ronin_nose_art_27","ronin","rui/titan_loadout/nose_art/ronin_nose_art_27","#RONIN_NOSE_ART_27",0
+"ronin_nose_art_28","ronin","rui/titan_loadout/nose_art/ronin_nose_art_28","#RONIN_NOSE_ART_28",0
+"ronin_nose_art_29","ronin","rui/titan_loadout/nose_art/ronin_nose_art_29","#RONIN_NOSE_ART_29",0
+"ronin_nose_art_30","ronin","rui/titan_loadout/nose_art/ronin_nose_art_30","#RONIN_NOSE_ART_30",0
+"tone_nose_art_27","tone","rui/titan_loadout/nose_art/tone_nose_art_27","#TONE_NOSE_ART_27",0
+"tone_nose_art_28","tone","rui/titan_loadout/nose_art/tone_nose_art_28","#TONE_NOSE_ART_28",0
+"tone_nose_art_29","tone","rui/titan_loadout/nose_art/tone_nose_art_29","#TONE_NOSE_ART_29",0
+"tone_nose_art_30","tone","rui/titan_loadout/nose_art/tone_nose_art_30","#TONE_NOSE_ART_30",0
+"tone_nose_art_31","tone","rui/titan_loadout/nose_art/tone_nose_art_31","#TONE_NOSE_ART_31",0
+"northstar_nose_art_28","northstar","rui/titan_loadout/nose_art/northstar_nose_art_28","#NORTHSTAR_NOSE_ART_28",0
+"northstar_nose_art_29","northstar","rui/titan_loadout/nose_art/northstar_nose_art_29","#NORTHSTAR_NOSE_ART_29",0
+"northstar_nose_art_30","northstar","rui/titan_loadout/nose_art/northstar_nose_art_30","#NORTHSTAR_NOSE_ART_30",0
+"northstar_nose_art_31","northstar","rui/titan_loadout/nose_art/northstar_nose_art_31","#NORTHSTAR_NOSE_ART_31",0
+"northstar_nose_art_32","northstar","rui/titan_loadout/nose_art/northstar_nose_art_32","#NORTHSTAR_NOSE_ART_32",0
+"legion_nose_art_27","legion","rui/titan_loadout/nose_art/legion_nose_art_27","#LEGION_NOSE_ART_27",0
+"legion_nose_art_28","legion","rui/titan_loadout/nose_art/legion_nose_art_28","#LEGION_NOSE_ART_28",0
+"legion_nose_art_29","legion","rui/titan_loadout/nose_art/legion_nose_art_29","#LEGION_NOSE_ART_29",0
+"legion_nose_art_30","legion","rui/titan_loadout/nose_art/legion_nose_art_30","#LEGION_NOSE_ART_30",0
+"legion_nose_art_31","legion","rui/titan_loadout/nose_art/legion_nose_art_31","#LEGION_NOSE_ART_31",0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_os_conversations.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_os_conversations.csv
new file mode 100644
index 00000000..6d7a8623
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_os_conversations.csv
@@ -0,0 +1,67 @@
+conversationname,priority,debounce
+"bettyAlarm",400,0.100000
+"warning",400,3.000000
+"caution",400,3.000000
+"multiTitanEngage",400,15.000000
+"outnumbered2to1",400,15.000000
+"outnumbered3to1",400,15.000000
+"outnumbered4to1",400,15.000000
+"embark",400,3.000000
+"disembark",400,3.000000
+"guard",2000,3.000000
+"follow",2000,3.000000
+"briefCriticalDamage",400,10.000000
+"manualEjectNotice",400,0.100000
+"autoEjectNotice",400,0.100000
+"doomState",400,0.100000
+"halfDoomState",400,0.100000
+"rodeoWarning",2000,3.000000
+"allyRodeoAttach",400,3.000000
+"allyRodeoDetach",400,3.000000
+"killEnemyRodeo",400,10.000000
+"warningEnemyPilot",400,10.000000
+"warningEnemyPilotMulti",400,10.000000
+"elimTarget",400,3.000000
+"elimEnemyPilot",400,3.000000
+"ejectedEnemy",400,3.000000
+"ejectedFriendly",400,3.000000
+"assistedByFriendlyTitan",400,10.000000
+"elimEnemyTitan",400,3.000000
+"elimFriendlyTitan",400,3.000000
+"assistedByFriendlyPilot",400,3.000000
+"hostileTitanInbound",2000,3.000000
+"friendlyRodeoOnEnemyTitan",400,3.000000
+"autoEngageGrunt",400,10.000000
+"autoEngagePilot",400,10.000000
+"autoEngageTitan",400,10.000000
+"autoEngageTitans",400,10.000000
+"killEnemyRodeoGnrc",400,10.000000
+"batteryGot",400,3.000000
+"predRangeLong",400,3.000000
+"predRangeShort",400,3.000000
+"smartCoreOnline",400,3.000000
+"smartCoreActivated",400,3.000000
+"smartCoreOffline",400,3.000000
+"flamewavecoreOnline",400,3.000000
+"flamewavecoreActivated",400,3.000000
+"hack_bt_workaround",400,3.000000
+"lasercoreOnline",400,3.000000
+"lasercoreActivated",400,3.000000
+"sonarPulse",400,3.000000
+"flightCoreOnline",400,3.000000
+"flightCoreActivated",400,3.000000
+"flightCoreOffline",400,3.000000
+"SalvocoreOnline",400,3.000000
+"SalvocoreActivated",400,3.000000
+"swordCoreOnline",400,3.000000
+"swordCoreActivated",400,3.000000
+"swordCoreOffline",400,3.000000
+"burstCoreOnline",400,3.000000
+"burstCoreActivated",400,3.000000
+"UpgradecoreOnline",400,3.000000
+"UpgradecoreActivated",400,3.000000
+"upgradeTo1",400,3.000000
+"upgradeTo2",400,3.000000
+"upgradeTo3",400,3.000000
+"upgradeToFin",400,3.000000
+"upgradeShieldReplenish",400,3.000000 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_passives.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_passives.csv
new file mode 100644
index 00000000..79f093c2
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_passives.csv
@@ -0,0 +1,52 @@
+passive,type,name,description,longdescription,image,hidden,cost
+"pas_build_up_nuclear_core","TITAN_GENERAL_PASSIVE","#GEAR_NUCLEAR_CORE","#GEAR_NUCLEAR_CORE_DESC","#GEAR_NUCLEAR_CORE_LONGDESC","rui/titan_loadout/passive/nuke_eject",0,5
+"pas_enhanced_titan_ai","TITAN_GENERAL_PASSIVE","#GEAR_ASSAULT_CHIP","#GEAR_ASSAULT_CHIP_DESC","#GEAR_ASSAULT_CHIP_LONGDESC","rui/titan_loadout/passive/assault_chip",0,25
+"pas_auto_eject","TITAN_GENERAL_PASSIVE","#GEAR_AUTO_EJECT","#GEAR_AUTO_EJECT_DESC","#GEAR_AUTO_EJECT_LONGDESC","rui/titan_loadout/passive/auto_eject",0,25
+"pas_hyper_core","TITAN_GENERAL_PASSIVE","#GEAR_CORE_HEADSTART","#GEAR_CORE_HEADSTART_DESC","#GEAR_CORE_HEADSTART_LONGDESC","rui/titan_loadout/passive/overcore",0,5
+"pas_anti_rodeo","TITAN_GENERAL_PASSIVE","#GEAR_ANTI_RODEO","#GEAR_ANTI_RODEO_DESC","#GEAR_ANTI_RODEO_LONGDESC","rui/titan_loadout/passive/improved_anti_rodeo",0,5
+"pas_mobility_dash_capacity","TITAN_GENERAL_PASSIVE","#GEAR_DASH_CAPACITY","#GEAR_DASH_CAPACITY_DESC","#GEAR_DASH_CAPACITY_LONGDESC","rui/titan_loadout/passive/dash_plus",0,5
+"pas_warpfall","TITAN_TITANFALL_PASSIVE","#GEAR_WARPFALL","#GEAR_WARPFALL_DESC","#GEAR_WARPFALL_LONGDESC","rui/titan_loadout/passive/titanfall_kit_warpfall",0,25
+"pas_bubbleshield","TITAN_TITANFALL_PASSIVE","#GEAR_BUBBLESHIELD","#GEAR_BUBBLESHIELD_DESC","#GEAR_BUBBLESHIELD_LONGDESC","rui/titan_loadout/passive/titanfall_kit_bubbleshield",0,14
+"pas_ronin_weapon","TITAN_RONIN_PASSIVE","#GEAR_RONIN_WEAPON","#GEAR_RONIN_WEAPON_DESC","#GEAR_RONIN_WEAPON_LONGDESC","rui/titan_loadout/passive/ronin_ricochet_round",0,25
+"pas_northstar_weapon","TITAN_NORTHSTAR_PASSIVE","#GEAR_NORTHSTAR_WEAPON","#GEAR_NORTHSTAR_WEAPON_DESC","#GEAR_NORTHSTAR_WEAPON_LONGDESC","rui/titan_loadout/passive/northstar_piercing_shot",0,25
+"pas_ion_weapon","TITAN_ION_PASSIVE","#GEAR_ION_WEAPON","#GEAR_ION_WEAPON_DESC","#GEAR_ION_WEAPON_LONGDESC","rui/titan_loadout/passive/ion_entangled_energy",0,25
+"pas_tone_weapon","TITAN_TONE_PASSIVE","#GEAR_TONE_WEAPON","#GEAR_TONE_WEAPON_DESC","#GEAR_TONE_WEAPON_LONGDESC","rui/titan_loadout/passive/tone_enhanced_tracker",0,25
+"pas_scorch_weapon","TITAN_SCORCH_PASSIVE","#GEAR_SCORCH_WEAPON","#GEAR_SCORCH_WEAPON_DESC","#GEAR_SCORCH_WEAPON_LONGDESC","rui/titan_loadout/passive/scorch_wildfire_launcher",0,25
+"pas_legion_weapon","TITAN_LEGION_PASSIVE","#GEAR_LEGION_WEAPON","#GEAR_LEGION_WEAPON_DESC","#GEAR_LEGION_WEAPON_LONGDESC","rui/titan_loadout/passive/legion_enhanced_ammo",0,25
+"pas_ion_tripwire","TITAN_ION_PASSIVE","#GEAR_ION_TRIPWIRE","#GEAR_ION_TRIPWIRE_DESC","#GEAR_ION_TRIPWIRE_LONGDESC","rui/titan_loadout/passive/ion_zero_point_tripwire",0,24
+"pas_ion_vortex","TITAN_ION_PASSIVE","#GEAR_ION_VORTEX","#GEAR_ION_VORTEX_DESC","#GEAR_ION_VORTEX_LONGDESC","rui/titan_loadout/passive/ion_vortex_amp",0,24
+"pas_ion_lasercannon","TITAN_ION_PASSIVE","#GEAR_ION_LASERCANNON","#GEAR_ION_LASERCANNON_DESC","#GEAR_ION_LASERCANNON_LONGDESC","rui/titan_loadout/passive/ion_grand_canon",0,24
+"pas_tone_rockets","TITAN_TONE_PASSIVE","#GEAR_TONE_ROCKETS","#GEAR_TONE_ROCKETS_DESC","#GEAR_TONE_ROCKETS_LONGDESC","rui/titan_loadout/passive/tone_rocket_barrage",0,24
+"pas_tone_sonar","TITAN_TONE_PASSIVE","#GEAR_TONE_SONAR","#GEAR_TONE_SONAR_DESC","#GEAR_TONE_SONAR_LONGDESC","rui/titan_loadout/passive/tone_pulse_echo",0,24
+"pas_tone_wall","TITAN_TONE_PASSIVE","#GEAR_TONE_WALL","#GEAR_TONE_WALL_DESC","#GEAR_TONE_WALL_LONGDESC","rui/titan_loadout/passive/tone_reinforced_partical_wall",0,24
+"pas_ronin_arcwave","TITAN_RONIN_PASSIVE","#GEAR_RONIN_ARCWAVE","#GEAR_RONIN_ARCWAVE_DESC","#GEAR_RONIN_ARCWAVE_LONGDESC","rui/titan_loadout/passive/ronin_thunderstorm",0,24
+"pas_ronin_phase","TITAN_RONIN_PASSIVE","#GEAR_RONIN_PHASE","#GEAR_RONIN_PHASE_DESC","#GEAR_RONIN_PHASE_LONGDESC","rui/titan_loadout/passive/ronin_temporal_anomaly",0,24
+"pas_ronin_swordcore","TITAN_RONIN_PASSIVE","#GEAR_RONIN_SWORDCORE","#GEAR_RONIN_SWORDCORE_DESC","#GEAR_RONIN_SWORDCORE_LONGDESC","rui/titan_loadout/passive/ronin_highlander",0,24
+"pas_northstar_cluster","TITAN_NORTHSTAR_PASSIVE","#GEAR_NORTHSTAR_CLUSTER","#GEAR_NORTHSTAR_CLUSTER_DESC","#GEAR_NORTHSTAR_CLUSTER_LONGDESC","rui/titan_loadout/passive/northstar_enhanced_payload",0,24
+"pas_northstar_trap","TITAN_NORTHSTAR_PASSIVE","#GEAR_NORTHSTAR_TRAP","#GEAR_NORTHSTAR_TRAP_DESC","#GEAR_NORTHSTAR_TRAP_LONGDESC","rui/titan_loadout/passive/northstar_twin_trap",0,24
+"pas_northstar_flightcore","TITAN_NORTHSTAR_PASSIVE","#GEAR_NORTHSTAR_FLIGHTCORE","#GEAR_NORTHSTAR_FLIGHTCORE_DESC","#GEAR_NORTHSTAR_FLIGHTCORE_LONGDESC","rui/titan_loadout/passive/northstar_viper_thrusters",0,24
+"pas_scorch_firewall","TITAN_SCORCH_PASSIVE","#GEAR_SCORCH_FIREWALL","#GEAR_SCORCH_FIREWALL_DESC","#GEAR_SCORCH_FIREWALL_LONGDESC","rui/titan_loadout/passive/scorch_fuel",0,24
+"pas_scorch_shield","TITAN_SCORCH_PASSIVE","#GEAR_SCORCH_SHIELD","#GEAR_SCORCH_SHIELD_DESC","#GEAR_SCORCH_SHIELD_LONGDESC","rui/titan_loadout/passive/scorch_inferno_shield",0,24
+"pas_scorch_selfdmg","TITAN_SCORCH_PASSIVE","#GEAR_SCORCH_SELFDMG","#GEAR_SCORCH_SELFDMG_DESC","#GEAR_SCORCH_SELFDMG_LONGDESC","rui/titan_loadout/passive/scorch_tempered_plating",0,24
+"pas_legion_spinup","TITAN_LEGION_PASSIVE","#GEAR_LEGION_SPINUP","#GEAR_LEGION_SPINUP_DESC","#GEAR_LEGION_SPINUP_LONGDESC","rui/titan_loadout/passive/legion_lightweight_alloys",0,24
+"pas_legion_gunshield","TITAN_LEGION_PASSIVE","#GEAR_LEGION_GUNSHIELD","#GEAR_LEGION_GUNSHIELD_DESC","#GEAR_LEGION_GUNSHIELD_LONGDESC","rui/titan_loadout/passive/legion_bulwark",0,24
+"pas_legion_smartcore","TITAN_LEGION_PASSIVE","#GEAR_LEGION_SMARTCORE","#GEAR_LEGION_SMARTCORE_DESC","#GEAR_LEGION_SMARTCORE_LONGDESC","rui/titan_loadout/passive/legion_sensor_array",0,24
+"pas_ion_weapon_ads","TITAN_ION_PASSIVE","#GEAR_ION_SPLIT","#GEAR_ION_SPLIT_DESC","#GEAR_ION_SPLIT_LONGDESC","rui/titan_loadout/passive/ion_diffraction_lens",0,24
+"pas_tone_burst","TITAN_TONE_PASSIVE","#GEAR_TONE_BURST","#GEAR_TONE_BURST_DESC","#GEAR_TONE_BURST_LONGDESC","rui/titan_loadout/passive/tone_40mm_burst",0,24
+"pas_legion_chargeshot","TITAN_LEGION_PASSIVE","#GEAR_LEGION_CHARGESHOT","#GEAR_LEGION_CHARGESHOT_DESC","#GEAR_LEGION_CHARGESHOT_LONGDESC","rui/titan_loadout/passive/legion_siege_mode",0,24
+"pas_ronin_autoshift","TITAN_RONIN_PASSIVE","#GEAR_RONIN_AUTOSHIFT","#GEAR_RONIN_AUTOSHIFT_DESC","#GEAR_RONIN_AUTOSHIFT_LONGDESC","rui/titan_loadout/passive/ronin_auto_shift",0,24
+"pas_northstar_optics","TITAN_NORTHSTAR_PASSIVE","#GEAR_NORTHSTAR_OPTICS","#GEAR_NORTHSTAR_OPTICS_DESC","#GEAR_NORTHSTAR_OPTICS_LONGDESC","rui/titan_loadout/passive/northstar_threat_optics",0,24
+"pas_scorch_flamecore","TITAN_SCORCH_PASSIVE","#GEAR_SCORCH_FLAMECORE","#GEAR_SCORCH_FLAMECORE_DESC","#GEAR_SCORCH_FLAMECORE_LONGDESC","rui/titan_loadout/passive/scorch_scorched_earth",0,24
+"pas_vanguard_coremeter","TITAN_VANGUARD_PASSIVE","#GEAR_VANGUARD_COREMETER","#GEAR_VANGUARD_COREMETER_DESC","#GEAR_VANGUARD_COREMETER_LONGDESC","rui/titan_loadout/passive/vanguard_siphon",0,24
+"pas_vanguard_shield","TITAN_VANGUARD_PASSIVE","#GEAR_VANGUARD_SHIELD","#GEAR_VANGUARD_SHIELD_DESC","#GEAR_VANGUARD_SHIELD_LONGDESC","rui/titan_loadout/passive/vanguard_survivor",0,24
+"pas_vanguard_rearm","TITAN_VANGUARD_PASSIVE","#GEAR_VANGUARD_REARM","#GEAR_VANGUARD_REARM_DESC","#GEAR_VANGUARD_REARM_LONGDESC","rui/titan_loadout/passive/vanguard_rearm",0,24
+"pas_vanguard_doom","TITAN_VANGUARD_PASSIVE","#GEAR_VANGUARD_DOOM","#GEAR_VANGUARD_DOOM_DESC","#GEAR_VANGUARD_DOOM_LONGDESC","rui/titan_loadout/passive/vanguard_fittest",0,24
+"pas_vanguard_core1","TITAN_UPGRADE1_PASSIVE","#GEAR_VANGUARD_CORE1","#GEAR_VANGUARD_CORE1_DESC","#GEAR_VANGUARD_CORE1_LONGDESC","rui/titan_loadout/passive/monarch_core_arc_rounds",0,24
+"pas_vanguard_core2","TITAN_UPGRADE1_PASSIVE","#GEAR_VANGUARD_CORE2","#GEAR_VANGUARD_CORE2_DESC","#GEAR_VANGUARD_CORE2_LONGDESC","rui/titan_loadout/passive/monarch_core_missile_racks",0,24
+"pas_vanguard_core3","TITAN_UPGRADE1_PASSIVE","#GEAR_VANGUARD_CORE3","#GEAR_VANGUARD_CORE3_DESC","#GEAR_VANGUARD_CORE3_LONGDESC","rui/titan_loadout/passive/monarch_core_energy_field",0,24
+"pas_vanguard_core4","TITAN_UPGRADE2_PASSIVE","#GEAR_VANGUARD_CORE4","#GEAR_VANGUARD_CORE4_DESC","#GEAR_VANGUARD_CORE4_LONGDESC","rui/titan_loadout/passive/monarch_core_swift_rearm",0,24
+"pas_vanguard_core5","TITAN_UPGRADE2_PASSIVE","#GEAR_VANGUARD_CORE5","#GEAR_VANGUARD_CORE5_DESC","#GEAR_VANGUARD_CORE5_LONGDESC","rui/titan_loadout/passive/monarch_core_maelstrom",0,24
+"pas_vanguard_core6","TITAN_UPGRADE2_PASSIVE","#GEAR_VANGUARD_CORE6","#GEAR_VANGUARD_CORE6_DESC","#GEAR_VANGUARD_CORE6_LONGDESC","rui/titan_loadout/passive/monarch_core_energy_transfer",0,24
+"pas_vanguard_core7","TITAN_UPGRADE3_PASSIVE","#GEAR_VANGUARD_CORE7","#GEAR_VANGUARD_CORE7_DESC","#GEAR_VANGUARD_CORE7_LONGDESC","rui/titan_loadout/passive/monarch_core_multi_target",0,24
+"pas_vanguard_core8","TITAN_UPGRADE3_PASSIVE","#GEAR_VANGUARD_CORE8","#GEAR_VANGUARD_CORE8_DESC","#GEAR_VANGUARD_CORE8_LONGDESC","rui/titan_loadout/passive/monarch_core_superior_chassis",0,24
+"pas_vanguard_core9","TITAN_UPGRADE3_PASSIVE","#GEAR_VANGUARD_CORE9","#GEAR_VANGUARD_CORE9_DESC","#GEAR_VANGUARD_CORE9_LONGDESC","rui/titan_loadout/passive/monarch_core_xo16",0,24 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_primary_mods.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_primary_mods.csv
new file mode 100644
index 00000000..d25cc30d
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_primary_mods.csv
@@ -0,0 +1 @@
+mod,weapon,statDamage,statAccuracy,statRange,statFireRate,statClipSize,hidden \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_primary_mods_common.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_primary_mods_common.csv
new file mode 100644
index 00000000..e4659a7e
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_primary_mods_common.csv
@@ -0,0 +1 @@
+mod,name,description,image \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_primary_weapons.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_primary_weapons.csv
new file mode 100644
index 00000000..c617d653
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_primary_weapons.csv
@@ -0,0 +1,10 @@
+itemRef,hidden,defaultMod2,hidden
+"mp_titanweapon_leadwall",0,"",0
+"mp_titanweapon_meteor",0,"",0
+"mp_titanweapon_particle_accelerator",0,"",0
+"mp_titanweapon_predator_cannon",0,"",0
+"mp_titanweapon_sniper",0,"",0
+"mp_titanweapon_sticky_40mm",0,"",0
+"mp_titanweapon_xo16_shorty",1,"",0
+"mp_titanweapon_rocketeer_rocketstream",1,"",0
+"mp_titanweapon_xo16_vanguard",0,"",0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_properties.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_properties.csv
new file mode 100644
index 00000000..4b9369ca
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_properties.csv
@@ -0,0 +1,10 @@
+setFile,primeSetFile,titanRef,primeTitanRef,titanLoadoutInSP,mp_npcUseAllowed,difficulty,coreBuildingIcon,coreReadyIcon,hintIcon,loadoutIcon,loadoutIconFD,fdRole,desc,speedDisplay,damageDisplay,healthDisplay,dashDisplay,primary,melee,ordnance,special,antirodeo,coreAbility,passive1,passive2,passive3,passive4,passive5,passive6,titanExecution,bossCharacter,hidden,menuItem,menuTitle,menuSubTitle,menuLongDesc,menuFlavorText,dialogHint,weaponImage
+"titan_buddy","","bt","",1,0,1,"rui\titan_loadout\core\titan_core_burst_core","rui\titan_loadout\core\titan_core_burst_core","rui\menu\common\bulb_hint_icon","rui/menu/postgame/vanguard_icon","rui/menu/fd_menu/fd_icon_monarch","#FD_ROLE_MONARCH","#MP_TITAN_LOADOUT_DESC_XO16",2,1,2,2,"mp_titanweapon_xo16_shorty","melee_titan_punch","mp_titanweapon_shoulder_rockets","mp_titanweapon_vortex_shield","mp_titanability_smoke","mp_titancore_amp_core","TITAN_GENERAL_PASSIVE","TITAN_GENERAL_PASSIVE","TITAN_TITANFALL_PASSIVE","TITAN_UPGRADE1_PASSIVE","TITAN_UPGRADE2_PASSIVE","TITAN_UPGRADE3_PASSIVE","","",1,"#SP_TITAN_LOADOUT_MENUITEM_XO16","#SP_TITAN_LOADOUT_TITLE_XO16","#SP_TITAN_LOADOUT_SUBTITLE_XO16","#SP_TITAN_LOADOUT_DESC_XO16","#SP_TITAN_LOADOUT_FLAVOR_XO16","diag_sp_extra_GB101_47_01_mcor_bt","rui\titan_loadout\loadout_select\ls_wep_ttn_x016_shorty"
+"titan_atlas_tracker","titan_atlas_tone_prime","tone","tone_prime",1,1,2,"rui\titan_loadout\core\titan_core_salvo","rui\titan_loadout\core\titan_core_salvo","rui\menu\common\bulb_hint_icon","rui/menu/postgame/tone_icon","rui/menu/fd_menu/fd_icon_tone","#FD_ROLE_TONE","#MP_TITAN_LOADOUT_DESC_TONE",2,2,2,1,"mp_titanweapon_sticky_40mm","melee_titan_punch_tone","mp_titanweapon_tracker_rockets","mp_titanability_particle_wall","mp_titanability_sonar_pulse","mp_titancore_salvo_core","TITAN_GENERAL_PASSIVE","TITAN_TONE_PASSIVE","TITAN_TITANFALL_PASSIVE","TITAN_UPGRADE1_PASSIVE","TITAN_UPGRADE2_PASSIVE","TITAN_UPGRADE3_PASSIVE","TITAN_TONE_EXECUTION","Richter",0,"#SP_TITAN_LOADOUT_MENUITEM_TONE","#SP_TITAN_LOADOUT_TITLE_TONE","#SP_TITAN_LOADOUT_SUBTITLE_TONE","#SP_TITAN_LOADOUT_DESC_TONE","#SP_TITAN_LOADOUT_FLAVOR_TONE","diag_sp_extra_GB101_37_01_mcor_bt","rui\titan_loadout\loadout_select\ls_wep_ttn_40mm"
+"titan_ogre_meteor","titan_ogre_scorch_prime","scorch","scorch_prime",1,1,3,"rui\titan_loadout\core\titan_core_flame_wave","rui\titan_loadout\core\titan_core_flame_wave","rui\menu\common\bulb_hint_icon","rui/menu/postgame/scorch_icon","rui/menu/fd_menu/fd_icon_scorch","#FD_ROLE_SCORCH","#MP_TITAN_LOADOUT_DESC_SCORCH",1,3,3,0,"mp_titanweapon_meteor","melee_titan_punch_scorch","mp_titanweapon_flame_wall","mp_titanweapon_heat_shield","mp_titanability_slow_trap","mp_titancore_flame_wave","TITAN_GENERAL_PASSIVE","TITAN_SCORCH_PASSIVE","TITAN_TITANFALL_PASSIVE","TITAN_UPGRADE1_PASSIVE","TITAN_UPGRADE2_PASSIVE","TITAN_UPGRADE3_PASSIVE","TITAN_SCORCH_EXECUTION","Kane",0,"#SP_TITAN_LOADOUT_MENUITEM_SCORCH","#SP_TITAN_LOADOUT_TITLE_SCORCH","#SP_TITAN_LOADOUT_SUBTITLE_SCORCH","#SP_TITAN_LOADOUT_DESC_SCORCH","#SP_TITAN_LOADOUT_FLAVOR_SCORCH","diag_sp_extra_GB101_33_01_mcor_bt","rui\titan_loadout\loadout_select\ls_wep_ttn_meteor"
+"titan_stryder_rocketeer","","brute4","",1,0,1,"rui\titan_loadout\core\titan_core_flight","rui\titan_loadout\core\titan_core_flight","rui\menu\common\bulb_hint_icon","rui/menu/postgame/northstar_icon","rui/menu/fd_menu/fd_icon_northstar","#FD_ROLE_NORTHSTAR","#MP_TITAN_LOADOUT_DESC_BRUTE",3,2,1,2,"mp_titanweapon_rocketeer_rocketstream","melee_titan_punch","mp_titanweapon_shoulder_rockets","mp_titanweapon_vortex_shield","mp_titanability_hover","mp_titancore_flight_core","TITAN_GENERAL_PASSIVE","TITAN_GENERAL_PASSIVE","TITAN_TITANFALL_PASSIVE","TITAN_UPGRADE1_PASSIVE","TITAN_UPGRADE2_PASSIVE","TITAN_UPGRADE3_PASSIVE","","",0,"#SP_TITAN_LOADOUT_MENUITEM_BRUTE","#SP_TITAN_LOADOUT_TITLE_BRUTE","#SP_TITAN_LOADOUT_SUBTITLE_BRUTE","#SP_TITAN_LOADOUT_DESC_BRUTE","#SP_TITAN_LOADOUT_FLAVOR_BRUTE","diag_sp_extra_GB101_47_01_mcor_bt","rui\titan_loadout\loadout_select\ls_wep_ttn_rocketeer"
+"titan_atlas_stickybomb","titan_atlas_ion_prime","ion","ion_prime",1,1,1,"rui\titan_loadout\core\titan_core_laser","rui\titan_loadout\core\titan_core_laser","rui\menu\common\bulb_hint_icon","rui/menu/postgame/ion_icon","rui/menu/fd_menu/fd_icon_ion","#FD_ROLE_ION","#MP_TITAN_LOADOUT_DESC_ION",2,1,2,1,"mp_titanweapon_particle_accelerator","melee_titan_punch_ion","mp_titanweapon_laser_lite","mp_titanweapon_vortex_shield_ion","mp_titanability_laser_trip","mp_titancore_laser_cannon","TITAN_GENERAL_PASSIVE","TITAN_ION_PASSIVE","TITAN_TITANFALL_PASSIVE","TITAN_UPGRADE1_PASSIVE","TITAN_UPGRADE2_PASSIVE","TITAN_UPGRADE3_PASSIVE","TITAN_ION_EXECUTION","Slone",0,"#SP_TITAN_LOADOUT_MENUITEM_ION","#SP_TITAN_LOADOUT_TITLE_ION","#SP_TITAN_LOADOUT_SUBTITLE_ION","#SP_TITAN_LOADOUT_DESC_ION","#SP_TITAN_LOADOUT_FLAVOR_ION","diag_sp_extra_GB101_34_01_mcor_bt","rui\titan_loadout\loadout_select\ls_wep_ttn_particle_accelerator"
+"titan_stryder_leadwall","titan_stryder_ronin_prime","ronin","ronin_prime",1,1,3,"rui\titan_loadout\core\titan_core_sword","rui\titan_loadout\core\titan_core_sword","rui\menu\common\bulb_hint_icon","rui/menu/postgame/ronin_icon","rui/menu/fd_menu/fd_icon_ronin","#FD_ROLE_RONIN","#MP_TITAN_LOADOUT_DESC_RONIN",3,2,1,2,"mp_titanweapon_leadwall","melee_titan_sword","mp_titanweapon_arc_wave","mp_titanability_basic_block","mp_titanability_phase_dash","mp_titancore_shift_core","TITAN_GENERAL_PASSIVE","TITAN_RONIN_PASSIVE","TITAN_TITANFALL_PASSIVE","TITAN_UPGRADE1_PASSIVE","TITAN_UPGRADE2_PASSIVE","TITAN_UPGRADE3_PASSIVE","TITAN_RONIN_EXECUTION","Ash",0,"#SP_TITAN_LOADOUT_MENUITEM_RONIN","#SP_TITAN_LOADOUT_TITLE_RONIN","#SP_TITAN_LOADOUT_SUBTITLE_RONIN","#SP_TITAN_LOADOUT_DESC_RONIN","#SP_TITAN_LOADOUT_FLAVOR_RONIN","diag_sp_extra_GB101_35_01_mcor_bt","rui\titan_loadout\loadout_select\ls_wep_ttn_leadwall"
+"titan_stryder_sniper","titan_stryder_northstar_prime","northstar","northstar_prime",1,1,2,"rui\titan_loadout\core\titan_core_flight","rui\titan_loadout\core\titan_core_flight","rui\menu\common\bulb_hint_icon","rui/menu/postgame/northstar_icon","rui/menu/fd_menu/fd_icon_northstar","#FD_ROLE_NORTHSTAR","#MP_TITAN_LOADOUT_DESC_NORTHSTAR",3,3,1,2,"mp_titanweapon_sniper","melee_titan_punch_northstar","mp_titanweapon_dumbfire_rockets","mp_titanability_tether_trap","mp_titanability_hover","mp_titancore_flight_core","TITAN_GENERAL_PASSIVE","TITAN_NORTHSTAR_PASSIVE","TITAN_TITANFALL_PASSIVE","TITAN_UPGRADE1_PASSIVE","TITAN_UPGRADE2_PASSIVE","TITAN_UPGRADE3_PASSIVE","TITAN_NORTHSTAR_EXECUTION","Viper",0,"#SP_TITAN_LOADOUT_MENUITEM_NORTHSTAR","#SP_TITAN_LOADOUT_TITLE_NORTHSTAR","#SP_TITAN_LOADOUT_SUBTITLE_NORTHSTAR","#SP_TITAN_LOADOUT_DESC_NORTHSTAR","#SP_TITAN_LOADOUT_FLAVOR_NORTHSTAR","diag_sp_extra_GB101_36_01_mcor_bt","rui\titan_loadout\loadout_select\ls_wep_ttn_sniper"
+"titan_ogre_minigun","titan_ogre_legion_prime","legion","legion_prime",1,1,2,"rui\titan_loadout\core\titan_core_smart","rui\titan_loadout\core\titan_core_smart","rui\menu\common\bulb_hint_icon","rui/menu/postgame/legion_icon","rui/menu/fd_menu/fd_icon_legion","#FD_ROLE_LEGION","#MP_TITAN_LOADOUT_DESC_LEGION",1,3,3,0,"mp_titanweapon_predator_cannon","melee_titan_punch_legion","mp_titanability_power_shot","mp_titanability_gun_shield","mp_titanability_ammo_swap","mp_titancore_siege_mode","TITAN_GENERAL_PASSIVE","TITAN_LEGION_PASSIVE","TITAN_TITANFALL_PASSIVE","TITAN_UPGRADE1_PASSIVE","TITAN_UPGRADE2_PASSIVE","TITAN_UPGRADE3_PASSIVE","TITAN_LEGION_EXECUTION","Blisk",0,"#SP_TITAN_LOADOUT_MENUITEM_LEGION","#SP_TITAN_LOADOUT_TITLE_LEGION","#SP_TITAN_LOADOUT_SUBTITLE_LEGION","#SP_TITAN_LOADOUT_DESC_LEGION","#SP_TITAN_LOADOUT_FLAVOR_LEGION","diag_sp_extra_GB101_32_01_mcor_bt","rui\titan_loadout\loadout_select\ls_wep_ttn_predator"
+"titan_atlas_vanguard","","vanguard","vanguard_prime",0,1,3,"rui\titan_loadout\core\titan_core_smart","rui\titan_loadout\core\titan_core_smart","rui\menu\common\bulb_hint_icon","rui/menu/postgame/vanguard_icon","rui/menu/fd_menu/fd_icon_monarch","#FD_ROLE_MONARCH","#MP_TITAN_LOADOUT_DESC_VANGUARD",2,2,2,1,"mp_titanweapon_xo16_vanguard","melee_titan_punch_vanguard","mp_titanweapon_salvo_rockets","mp_titanweapon_stun_laser","mp_titanability_rearm","mp_titancore_upgrade","TITAN_GENERAL_PASSIVE","TITAN_VANGUARD_PASSIVE","TITAN_TITANFALL_PASSIVE","TITAN_UPGRADE1_PASSIVE","TITAN_UPGRADE2_PASSIVE","TITAN_UPGRADE3_PASSIVE","TITAN_VANGUARD_EXECUTION","Richter",0,"#SP_TITAN_LOADOUT_MENUITEM_TONE","#SP_TITAN_LOADOUT_TITLE_TONE","#SP_TITAN_LOADOUT_SUBTITLE_TONE","#SP_TITAN_LOADOUT_DESC_TONE","#SP_TITAN_LOADOUT_FLAVOR_TONE","diag_sp_extra_GB101_37_01_mcor_bt","rui\titan_loadout\loadout_select\ls_wep_ttn_40mm" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_skins.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_skins.csv
new file mode 100644
index 00000000..da8b3172
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_skins.csv
@@ -0,0 +1,56 @@
+ref,titanRef,image,name,cost,skinIndex
+"ion_skin_01","ion","rui/titan_loadout/skins/ion_skin_01","#ION_SKIN_01",0,1
+"ion_skin_02","ion","rui/titan_loadout/skins/ion_skin_02","#ION_SKIN_02",0,3
+"ion_skin_03","ion","rui/titan_loadout/skins/ion_skin_03","#ION_SKIN_03",500,4
+"ion_skin_04","ion","rui/titan_loadout/skins/ion_skin_04","#ION_SKIN_04",0,5
+"ion_skin_06","ion","rui/titan_loadout/skins/ion_skin_06","#ION_SKIN_06",0,6
+"scorch_skin_01","scorch","rui/titan_loadout/skins/scorch_skin_01","#SCORCH_SKIN_01",0,1
+"scorch_skin_02","scorch","rui/titan_loadout/skins/scorch_skin_02","#SCORCH_SKIN_02",0,3
+"scorch_skin_03","scorch","rui/titan_loadout/skins/scorch_skin_03","#SCORCH_SKIN_03",0,4
+"scorch_skin_04","scorch","rui/titan_loadout/skins/scorch_skin_04","#SCORCH_SKIN_04",0,5
+"ronin_skin_01","ronin","rui/titan_loadout/skins/ronin_skin_01","#RONIN_SKIN_01",0,1
+"ronin_skin_02","ronin","rui/titan_loadout/skins/ronin_skin_02","#RONIN_SKIN_02",0,3
+"ronin_skin_03","ronin","rui/titan_loadout/skins/ronin_skin_03","#RONIN_SKIN_03",0,4
+"ronin_skin_04","ronin","rui/titan_loadout/skins/ronin_skin_04","#RONIN_SKIN_04",0,5
+"tone_skin_01","tone","rui/titan_loadout/skins/tone_skin_01","#TONE_SKIN_01",0,1
+"tone_skin_02","tone","rui/titan_loadout/skins/tone_skin_02","#TONE_SKIN_02",0,3
+"tone_skin_03","tone","rui/titan_loadout/skins/tone_skin_03","#TONE_SKIN_03",0,4
+"tone_skin_04","tone","rui/titan_loadout/skins/tone_skin_04","#TONE_SKIN_04",0,5
+"northstar_skin_01","northstar","rui/titan_loadout/skins/northstar_skin_01","#NORTHSTAR_SKIN_01",0,1
+"northstar_skin_02","northstar","rui/titan_loadout/skins/northstar_skin_02","#NORTHSTAR_SKIN_02",0,3
+"northstar_skin_03","northstar","rui/titan_loadout/skins/northstar_skin_03","#NORTHSTAR_SKIN_03",0,4
+"northstar_skin_04","northstar","rui/titan_loadout/skins/northstar_skin_04","#NORTHSTAR_SKIN_04",0,5
+"legion_skin_01","legion","rui/titan_loadout/skins/legion_skin_01","#LEGION_SKIN_01",0,1
+"legion_skin_02","legion","rui/titan_loadout/skins/legion_skin_02","#LEGION_SKIN_02",0,3
+"legion_skin_03","legion","rui/titan_loadout/skins/legion_skin_03","#LEGION_SKIN_03",0,4
+"legion_skin_04","legion","rui/titan_loadout/skins/legion_skin_04","#LEGION_SKIN_04",0,5
+"ion_skin_10","ion","rui/titan_loadout/skins/ion_skin_10","#ION_SKIN_10",0,7
+"tone_skin_06","tone","rui/titan_loadout/skins/tone_skin_06","#TONE_SKIN_06",0,6
+"scorch_skin_07","scorch","rui/titan_loadout/skins/scorch_skin_07","#SCORCH_SKIN_07",0,6
+"ronin_skin_10","ronin","rui/titan_loadout/skins/ronin_skin_10","#RONIN_SKIN_10",0,6
+"northstar_skin_10","northstar","rui/titan_loadout/skins/northstar_skin_10","#NORTHSTAR_SKIN_10",0,6
+"legion_skin_07","legion","rui/titan_loadout/skins/legion_skin_07","#LEGION_SKIN_07",0,6
+"ion_skin_11","ion","rui/titan_loadout/skins/ion_skin_11","#ION_SKIN_11",0,8
+"tone_skin_07","tone","rui/titan_loadout/skins/tone_skin_07","#TONE_SKIN_07",0,7
+"scorch_skin_08","scorch","rui/titan_loadout/skins/scorch_skin_08","#SCORCH_SKIN_08",0,7
+"ronin_skin_11","ronin","rui/titan_loadout/skins/ronin_skin_11","#RONIN_SKIN_11",0,7
+"northstar_skin_11","northstar","rui/titan_loadout/skins/northstar_skin_11","#NORTHSTAR_SKIN_11",0,7
+"legion_skin_08","legion","rui/titan_loadout/skins/legion_skin_08","#LEGION_SKIN_08",0,7
+"vanguard_skin_01","vanguard","rui/titan_loadout/skins/legion_skin_01","#LEGION_SKIN_01",0,1
+"vanguard_skin_02","vanguard","rui/titan_loadout/skins/legion_skin_02","#LEGION_SKIN_02",0,3
+"vanguard_skin_03","vanguard","rui/titan_loadout/skins/legion_skin_03","#LEGION_SKIN_03",0,4
+"vanguard_skin_07","vanguard","rui/titan_loadout/skins/legion_skin_07","#LEGION_SKIN_07",0,6
+"vanguard_skin_08","vanguard","rui/titan_loadout/skins/legion_skin_08","#LEGION_SKIN_08",0,7
+"northstar_skin_06","northstar","rui/titan_loadout/skins/northstar_skin_06","#NORTHSTAR_SKIN_06",0,8
+"ronin_skin_07","ronin","rui/titan_loadout/skins/ronin_skin_07","#RONIN_SKIN_07",0,8
+"scorch_skin_06","scorch","rui/titan_loadout/skins/scorch_skin_06","#SCORCH_SKIN_06",0,8
+"ion_skin_07","ion","rui/titan_loadout/skins/ion_skin_07","#ION_SKIN_07",0,9
+"legion_skin_09","legion","rui/titan_loadout/skins/legion_skin_09","#LEGION_SKIN_09",0,8
+"tone_skin_08","tone","rui/titan_loadout/skins/tone_skin_08","#TONE_SKIN_08",0,8
+"northstar_skin_fd","northstar","rui/titan_loadout/skins/northstar_skin_fd","#NORTHSTAR_SKIN_FD",0,9
+"ronin_skin_fd","ronin","rui/titan_loadout/skins/ronin_skin_fd","#RONIN_SKIN_FD",0,9
+"scorch_skin_fd","scorch","rui/titan_loadout/skins/scorch_skin_fd","#SCORCH_SKIN_FD",0,9
+"ion_skin_fd","ion","rui/titan_loadout/skins/ion_skin_fd","#ION_SKIN_FD",0,10
+"legion_skin_fd","legion","rui/titan_loadout/skins/legion_skin_fd","#LEGION_SKIN_FD",0,9
+"tone_skin_fd","tone","rui/titan_loadout/skins/tone_skin_fd","#TONE_SKIN_FD",0,9
+"monarch_skin_fd","vanguard","rui/titan_loadout/skins/monarch_skin_fd","#MONARCH_SKIN_FD",0,3 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titan_voices.csv b/Northstar.CustomServers/mod/scripts/datatable/titan_voices.csv
new file mode 100644
index 00000000..8a762f9b
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titan_voices.csv
@@ -0,0 +1,9 @@
+weapon,name,description,image,hidden
+"titanos_bt","#TITAN_OS_BT_NAME","#TITAN_OS_BT_LONGDESC","material/ui/menu/voice_personality_icons/betty_voice_icon_gen.rpak",0
+"titanos_legion","#TITAN_OS_LEGION_NAME","#TITAN_OS_LEGION_LONGDESC","material/ui/menu/voice_personality_icons/betty_voice_icon_gen.rpak",0
+"titanos_scorch","#TITAN_OS_SCORCH_NAME","#TITAN_OS_SCORCH_LONGDESC","material/ui/menu/voice_personality_icons/betty_voice_icon_gen.rpak",0
+"titanos_ronin","#TITAN_OS_RONIN_NAME","#TITAN_OS_RONIN_LONGDESC","material/ui/menu/voice_personality_icons/betty_voice_icon_gen.rpak",0
+"titanos_northstar","#TITAN_OS_NORTHSTAR_NAME","#TITAN_NORTHSTAR_BETTY_LONGDESC","material/ui/menu/voice_personality_icons/betty_voice_icon_gen.rpak",0
+"titanos_ion","#TITAN_OS_ION_NAME","#TITAN_OS_ION_LONGDESC","material/ui/menu/voice_personality_icons/betty_voice_icon_gen.rpak",0
+"titanos_tone","#TITAN_OS_TONE_NAME","#TITAN_OS_TONE_LONGDESC","material/ui/menu/voice_personality_icons/betty_voice_icon_gen.rpak",0
+"titanos_vanguard","#TITAN_OS_VANGUARD_NAME","#TITAN_OS_VANGUARD_LONGDESC","material/ui/menu/voice_personality_icons/betty_voice_icon_gen.rpak",0 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/titans_mp.csv b/Northstar.CustomServers/mod/scripts/datatable/titans_mp.csv
new file mode 100644
index 00000000..a0873690
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/titans_mp.csv
@@ -0,0 +1,8 @@
+titanRef,cost,coreIcon,image
+"ion",5,"rui/titan_loadout/core/titan_core_laser","rui/menu/postgame/ion_icon"
+"scorch",5,"rui/titan_loadout/core/titan_core_flame_wave","rui/menu/postgame/scorch_icon"
+"northstar",5,"rui/titan_loadout/core/titan_core_flight","rui/menu/postgame/northstar_icon"
+"ronin",10,"rui/titan_loadout/core/titan_core_sword","rui/menu/postgame/ronin_icon"
+"tone",15,"rui/titan_loadout/core/titan_core_salvo","rui/menu/postgame/tone_icon"
+"legion",20,"rui/titan_loadout/core/titan_core_smart","rui/menu/postgame/legion_icon"
+"vanguard",200,"rui/titan_loadout/core/titan_core_vanguard","rui/menu/postgame/vanguard_icon" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/unlocks_faction_level.csv b/Northstar.CustomServers/mod/scripts/datatable/unlocks_faction_level.csv
new file mode 100644
index 00000000..6f9fa4c1
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/unlocks_faction_level.csv
@@ -0,0 +1,82 @@
+factionLevel,faction_apex,faction_64,faction_vinson,faction_marauder,faction_aces,faction_ares,faction_marvin
+0,"faction_apex","faction_64","faction_vinson","faction_marauder","faction_aces","faction_ares","faction_marvin"
+1,"","","","","","",""
+2,"gc_icon_dollarsign,random","gc_icon_fox,random ","gc_icon_gear,random ","gc_icon_prowler,random ","gc_icon_ace,random","gc_icon_radar,random","gc_icon_mrvn,random"
+3,"random,callsign_06_col","random,callsign_96_col","random,callsign_05_col","random,callsign_46_col","random,callsign_01_col","random,callsign_41_col","random,callsign_163_col"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random,callsign_06_col_fire","random,callsign_96_col_fire","random,callsign_05_col_fire","random,callsign_46_col_fire","random,callsign_01_col_fire","random,callsign_41_col_fire","random,callsign_163_col_fire"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random,callsign_06_col_gold","random,callsign_96_col_gold","random,callsign_05_col_gold","random,callsign_46_col_gold","random,callsign_01_col_gold","random,callsign_41_col_gold","random,callsign_163_col_gold"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random,callsign_163_col_prism"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random,callsign_164_col"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random,callsign_164_col_fire"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random,callsign_164_col_gold"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random,callsign_164_col_prism"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random"
+1,"random","random","random","random","random","random","random"
+2,"random","random","random","random","random","random","random"
+3,"random","random","random","random","random","random","random"
+4,"random","random","random","random","random","random","random"
+5,"random","random","random","random","random","random","random" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/unlocks_fd_titan_level.csv b/Northstar.CustomServers/mod/scripts/datatable/unlocks_fd_titan_level.csv
new file mode 100644
index 00000000..957af6d6
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/unlocks_fd_titan_level.csv
@@ -0,0 +1,26 @@
+titanLevel,ion,scorch,northstar,ronin,tone,legion,vanguard,end
+"","ion","scorch","northstar","ronin","tone","legion","vanguard","END"
+"1","","","","","","","",""
+"2","fd_upgrade_ion_weapon_tier_2","fd_upgrade_scorch_weapon_tier_1","fd_upgrade_northstar_utility_tier_1","fd_upgrade_ronin_weapon_tier_1","fd_upgrade_tone_weapon_tier_1","fd_upgrade_legion_weapon_tier_1","fd_upgrade_vanguard_utility_tier_1",""
+"3","random","random","random","random","random","random","random",""
+"4","random","random","random","random","random","random","random",""
+"5","fd_upgrade_ion_defense_tier_1","fd_upgrade_scorch_defense_tier_1","fd_upgrade_northstar_defense_tier_1","fd_upgrade_ronin_defense_tier_1","fd_upgrade_tone_defense_tier_1","fd_upgrade_legion_defense_tier_1","fd_upgrade_vanguard_defense_tier_1",""
+"6","random","random","random","random","random","random","random",""
+"7","random","random","random","random","random","random","random",""
+"8","fd_upgrade_ion_utility_tier_2","fd_upgrade_scorch_utility_tier_1","fd_upgrade_northstar_weapon_tier_1","fd_upgrade_ronin_utility_tier_1","fd_upgrade_tone_utility_tier_2","fd_upgrade_legion_utility_tier_1","fd_upgrade_vanguard_weapon_tier_1",""
+"9","random","random","random","random","random","random","random",""
+"10","random","random","random","random","random","random","random",""
+"11","fd_upgrade_ion_weapon_tier_1","fd_upgrade_scorch_utility_tier_2","fd_upgrade_northstar_utility_tier_2","fd_upgrade_ronin_weapon_tier_2","fd_upgrade_tone_weapon_tier_2","fd_upgrade_legion_weapon_tier_2","fd_upgrade_vanguard_utility_tier_2",""
+"12","random","random","random","random","random","random","random",""
+"13","random","random","random","random","random","random","random",""
+"14","fd_upgrade_ion_defense_tier_2","fd_upgrade_scorch_defense_tier_2","fd_upgrade_northstar_defense_tier_2","fd_upgrade_ronin_defense_tier_2","fd_upgrade_tone_defense_tier_2","fd_upgrade_legion_defense_tier_2","fd_upgrade_vanguard_defense_tier_2",""
+"15","random","random","random","random","random","random","random",""
+"16","random","random","random","random","random","random","random",""
+"17","fd_upgrade_ion_utility_tier_1","fd_upgrade_scorch_weapon_tier_2","fd_upgrade_northstar_weapon_tier_2","fd_upgrade_ronin_utility_tier_2","fd_upgrade_tone_utility_tier_1","fd_upgrade_legion_utility_tier_2","fd_upgrade_vanguard_weapon_tier_2",""
+"18","random","random","random","random","random","random","random",""
+"19","random","random","random","random","random","random","random",""
+"20","fd_upgrade_ion_ultimate","fd_upgrade_scorch_ultimate","fd_upgrade_northstar_ultimate","fd_upgrade_ronin_ultimate","fd_upgrade_tone_ultimate","fd_upgrade_legion_ultimate","fd_upgrade_vanguard_ultimate",""
+"21","random","random","random","random","random","random","random",""
+"22","random","random","random","random","random","random","random",""
+"23","random","random","random","random","random","random","random",""
+"24","callsign_24_col_prism","callsign_47_col_prism","callsign_36_col_prism","callsign_45_col_prism","callsign_68_col_prism","callsign_26_col_prism","callsign_165_col_prism","" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/unlocks_player_level.csv b/Northstar.CustomServers/mod/scripts/datatable/unlocks_player_level.csv
new file mode 100644
index 00000000..693bcc15
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/unlocks_player_level.csv
@@ -0,0 +1,996 @@
+playerLevel,pilotWeapons,pilotOffhands,pilotKits,pilotTactical,titanChassis,burnCard,feature,callSign,callsignIcon,pilotExecutions,pilotCamo,factions,random
+1,"mp_weapon_rspn101_og, mp_weapon_rspn101,mp_weapon_car,mp_weapon_lmg,mp_weapon_shotgun,mp_weapon_sniper,mp_weapon_smr,mp_weapon_semipistol,mp_weapon_autopistol,mp_weapon_defender,mp_weapon_mgl,mp_weapon_wingman_n","mp_weapon_frag_grenade,mp_weapon_grenade_emp,mp_weapon_thermite_grenade","pas_enemy_death_icons,pas_wallhang,pas_fast_health_regen,pas_power_cell","mp_ability_grapple,mp_ability_cloak,mp_weapon_grenade_sonar,mp_ability_shifter_super,grapple,geist,medium","ion,scorch,northstar","burnmeter_amped_weapons","cp,ctf,lts,ffa,fw,ps,tdm,at,aitdm,lf,communities,happy_hour, pilot_loadout_1, pilot_loadout_2, pilot_loadout_3, pilot_loadout_4, pilot_loadout_5, pilot_loadout_6,hunted,mfd,fd_easy,fd_normal","callsign_16_col","gc_icon_titanfall","execution_neck_snap","pilot_camo_skin00","faction_marauder",""
+2,"mp_weapon_lstar","","","","","","","","","","","",""
+3,"","","","mp_ability_heal,nomad","","","","","","","","",""
+4,"","","","","","","coliseum","","","","","",""
+5,"mp_weapon_epg","","","","","","","callsign_53_col","","","","",""
+6,"","","","","","burnmeter_ticks","","","","","","",""
+7,"","","","","ronin","","","","","","","",""
+8,"mp_weapon_alternator_smg","","","","","","","","","","","",""
+9,"","mp_weapon_grenade_gravity","","","","","","","","","","",""
+10,"mp_weapon_shotgun_pistol","","","","","","","","","","pilot_camo_skin01","",""
+11,"","","","","tone","","","","","","","",""
+12,"","","","","","","","","","","","faction_apex",""
+13,"mp_weapon_hemlok","","","","","","","","","","","",""
+14,"","","","mp_weapon_deployable_cover,heavy","","","","","","","","",""
+15,"","","","","legion","","","callsign_67_col","","","","",""
+16,"mp_weapon_doubletake","","","","","","","","","","","",""
+17,"","","","","","burnmeter_ap_turret_weapon","","","","","","",""
+18,"","","pas_ordnance_pack","","","","","","","","","",""
+19,"mp_weapon_mastiff","","","","","","","","","","","",""
+20,"","","","","vanguard","","pilot_loadout_7","","","","pilot_camo_skin03","faction_vinson",""
+21,"mp_weapon_arc_launcher","","","","","","","","","","","",""
+22,"","mp_weapon_grenade_electric_smoke","","","","","","","","","","",""
+23,"","","","","","burnmeter_maphack","","","","","","",""
+24,"mp_weapon_esaw","","","","","","","","","","","",""
+25,"","","pas_ads_hover","","","","","callsign_100_col","","","","",""
+26,"","","","mp_ability_shifter,light","","","","","","","","",""
+27,"mp_weapon_hemlok_smg","","","","","","","","","","","",""
+28,"","","","","","burnmeter_emergency_battery","","","","","","",""
+29,"","","pas_fast_embark","","","","","","","","","",""
+30,"mp_weapon_softball","","","","","","pilot_loadout_8","","","","pilot_camo_skin02","",""
+31,"","","","","","burnmeter_radar_jammer","","","","","","",""
+32,"mp_weapon_wingman","","","","","","","","","","","",""
+33,"","","","","","","","","","","","faction_aces",""
+34,"","","","","","burnmeter_at_turret_weapon","","","","","","",""
+35,"mp_weapon_g2","","","","","","","callsign_09_col","","","","",""
+36,"","","","mp_ability_holopilot,stalker","","","","","","","","",""
+37,"","mp_weapon_satchel","","","","","","","","","","",""
+38,"mp_weapon_dmr","","","","","","","","","","","",""
+39,"","","","","","","","","","","","faction_64",""
+40,"","","pas_stealth_movement","","","","pilot_loadout_9","","","","pilot_camo_skin97","",""
+41,"mp_weapon_r97","","","","","","","","","","","",""
+42,"","","","","","burnmeter_smart_pistol","","","","","","",""
+43,"mp_weapon_rocket_launcher","","","","","","","","","","","",""
+44,"","","","","","burnmeter_phase_rewind","","","","","","",""
+45,"mp_weapon_pulse_lmg","","","","","","","callsign_70_col","","","","",""
+46,"","","","","","burnmeter_hard_cover","","","","","","",""
+47,"mp_weapon_vinson","","","","","","","","","","","",""
+48,"","","","","","burnmeter_holopilot_nova","","","","","","",""
+49,"","","pas_at_hunter","","","","","","","","","faction_ares",""
+50,"","","","","","burnmeter_random_foil","pilot_loadout_10","","gc_icon_gen0","","pilot_camo_skin24","faction_marvin",""
+1,"","","","","","","","callsign_35_col","gc_icon_gen1","","pilot_camo_skin16","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","callsign_53_col_fire","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","callsign_67_col_fire","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","callsign_100_col_fire","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","pilot_camo_skin25","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","callsign_09_col_fire","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","callsign_70_col_fire","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","callsign_07_col","gc_icon_gen2","","pilot_camo_skin14","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","callsign_53_col_gold","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","callsign_67_col_gold","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","callsign_100_col_gold","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","pilot_camo_skin26","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","callsign_09_col_gold","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","callsign_70_col_gold","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","callsign_34_col","gc_icon_gen3","","pilot_camo_skin83","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","pilot_camo_skin27","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","callsign_39_col","gc_icon_gen4","","pilot_camo_skin31","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","pilot_camo_skin19","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","callsign_16_col_fire","gc_icon_gen5","","pilot_camo_skin82","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","callsign_35_col_fire","gc_icon_sgt_major,gc_icon_gen6","","pilot_camo_skin15","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","callsign_07_col_fire","gc_icon_gen7","","pilot_camo_skin17","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","callsign_34_col_fire","gc_icon_gen8","","pilot_camo_skin81","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","callsign_39_col_fire","gc_icon_gen9","","pilot_camo_skin18","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","callsign_16_col_gold,callsign_35_col_gold,callsign_07_col_gold,callsign_34_col_gold,callsign_39_col_gold","","","pilot_camo_skin30","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random"
+46,"","","","","","","","","","","","",""
+47,"","","","","","","","","","","","",""
+48,"","","","","","","","","","","","",""
+49,"","","","","","","","","","","","",""
+50,"","","","","","","","","","","","",""
+1,"","","","","","","","","","","","",""
+2,"","","","","","","","","","","","",""
+3,"","","","","","","","","","","","",""
+4,"","","","","","","","","","","","",""
+5,"","","","","","","","","","","","","random"
+6,"","","","","","","","","","","","",""
+7,"","","","","","","","","","","","",""
+8,"","","","","","","","","","","","",""
+9,"","","","","","","","","","","","",""
+10,"","","","","","","","","","","","",""
+11,"","","","","","","","","","","","",""
+12,"","","","","","","","","","","","",""
+13,"","","","","","","","","","","","",""
+14,"","","","","","","","","","","","",""
+15,"","","","","","","","","","","","","random"
+16,"","","","","","","","","","","","",""
+17,"","","","","","","","","","","","",""
+18,"","","","","","","","","","","","",""
+19,"","","","","","","","","","","","",""
+20,"","","","","","","","","","","","",""
+21,"","","","","","","","","","","","",""
+22,"","","","","","","","","","","","",""
+23,"","","","","","","","","","","","",""
+24,"","","","","","","","","","","","",""
+25,"","","","","","","","","","","","","random"
+26,"","","","","","","","","","","","",""
+27,"","","","","","","","","","","","",""
+28,"","","","","","","","","","","","",""
+29,"","","","","","","","","","","","",""
+30,"","","","","","","","","","","","",""
+31,"","","","","","","","","","","","",""
+32,"","","","","","","","","","","","",""
+33,"","","","","","","","","","","","",""
+34,"","","","","","","","","","","","",""
+35,"","","","","","","","","","","","","random"
+36,"","","","","","","","","","","","",""
+37,"","","","","","","","","","","","",""
+38,"","","","","","","","","","","","",""
+39,"","","","","","","","","","","","",""
+40,"","","","","","","","","","","","",""
+41,"","","","","","","","","","","","",""
+42,"","","","","","","","","","","","",""
+43,"","","","","","","","","","","","",""
+44,"","","","","","","","","","","","",""
+45,"","","","","","","","","","","","","random" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/unlocks_random.csv b/Northstar.CustomServers/mod/scripts/datatable/unlocks_random.csv
new file mode 100644
index 00000000..b35f17af
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/unlocks_random.csv
@@ -0,0 +1,359 @@
+ref,weight
+"callsign_02_col",15.000000
+"callsign_08_col",15.000000
+"callsign_10_col",15.000000
+"callsign_12_col",15.000000
+"callsign_17_col",15.000000
+"callsign_20_col",15.000000
+"callsign_25_col",15.000000
+"callsign_28_col",15.000000
+"callsign_32_col",15.000000
+"callsign_43_col",15.000000
+"callsign_44_col",15.000000
+"callsign_49_col",15.000000
+"callsign_50_col",15.000000
+"callsign_52_col",15.000000
+"callsign_54_col",15.000000
+"callsign_56_col",15.000000
+"callsign_58_col",15.000000
+"callsign_60_col",15.000000
+"callsign_61_col",15.000000
+"callsign_62_col",15.000000
+"callsign_63_col",15.000000
+"callsign_64_col",15.000000
+"callsign_71_col",15.000000
+"callsign_72_col",15.000000
+"callsign_73_col",15.000000
+"callsign_74_col",15.000000
+"callsign_77_col",15.000000
+"callsign_80_col",15.000000
+"callsign_81_col",15.000000
+"callsign_82_col",15.000000
+"callsign_83_col",15.000000
+"callsign_84_col",15.000000
+"callsign_85_col",15.000000
+"callsign_86_col",15.000000
+"callsign_87_col",15.000000
+"callsign_88_col",15.000000
+"callsign_89_col",15.000000
+"callsign_90_col",15.000000
+"callsign_91_col",15.000000
+"callsign_93_col",15.000000
+"callsign_95_col",15.000000
+"callsign_98_col",15.000000
+"callsign_101_col",15.000000
+"callsign_102_col",15.000000
+"callsign_02_col_prism",3.000000
+"callsign_08_col_prism",3.000000
+"callsign_10_col_prism",3.000000
+"callsign_12_col_prism",3.000000
+"callsign_17_col_prism",3.000000
+"callsign_20_col_prism",3.000000
+"callsign_25_col_prism",3.000000
+"callsign_28_col_prism",3.000000
+"callsign_32_col_prism",3.000000
+"callsign_43_col_prism",3.000000
+"callsign_44_col_prism",3.000000
+"callsign_49_col_prism",3.000000
+"callsign_50_col_prism",3.000000
+"callsign_52_col_prism",3.000000
+"callsign_54_col_prism",3.000000
+"callsign_56_col_prism",3.000000
+"callsign_58_col_prism",3.000000
+"callsign_60_col_prism",3.000000
+"callsign_61_col_prism",3.000000
+"callsign_62_col_prism",3.000000
+"callsign_63_col_prism",3.000000
+"callsign_64_col_prism",3.000000
+"callsign_71_col_prism",3.000000
+"callsign_72_col_prism",3.000000
+"callsign_73_col_prism",3.000000
+"callsign_74_col_prism",3.000000
+"callsign_77_col_prism",3.000000
+"callsign_80_col_prism",3.000000
+"callsign_81_col_prism",3.000000
+"callsign_82_col_prism",3.000000
+"callsign_83_col_prism",3.000000
+"callsign_84_col_prism",3.000000
+"callsign_85_col_prism",3.000000
+"callsign_86_col_prism",3.000000
+"callsign_87_col_prism",3.000000
+"callsign_88_col_prism",3.000000
+"callsign_89_col_prism",3.000000
+"callsign_90_col_prism",3.000000
+"callsign_91_col_prism",3.000000
+"callsign_93_col_prism",3.000000
+"callsign_95_col_prism",3.000000
+"callsign_98_col_prism",3.000000
+"callsign_101_col_prism",3.000000
+"callsign_102_col_prism",3.000000
+"execution_backshot",3.000000
+"execution_combo",3.000000
+"execution_knockout",3.000000
+"credit_award",50.000000
+"coliseum_ticket",35.000000
+"northstar.northstar_nose_art_12",5.000000
+"northstar.northstar_nose_art_13",5.000000
+"ronin.ronin_nose_art_08",5.000000
+"ronin.ronin_nose_art_12",5.000000
+"tone.tone_nose_art_11",5.000000
+"tone.tone_nose_art_12",5.000000
+"scorch.scorch_nose_art_12",5.000000
+"legion.legion_nose_art_12",5.000000
+"legion.legion_nose_art_14",5.000000
+"camo_skin04",5.000000
+"camo_skin05",5.000000
+"camo_skin06",5.000000
+"camo_skin11",5.000000
+"camo_skin12",5.000000
+"camo_skin20",5.000000
+"camo_skin21",5.000000
+"camo_skin22",5.000000
+"camo_skin23",5.000000
+"camo_skin28",5.000000
+"camo_skin29",5.000000
+"camo_skin32",5.000000
+"camo_skin33",5.000000
+"camo_skin34",5.000000
+"camo_skin35",5.000000
+"camo_skin36",5.000000
+"camo_skin37",5.000000
+"camo_skin38",5.000000
+"camo_skin39",5.000000
+"camo_skin40",5.000000
+"camo_skin41",5.000000
+"camo_skin42",5.000000
+"camo_skin43",5.000000
+"camo_skin44",5.000000
+"camo_skin45",5.000000
+"camo_skin46",5.000000
+"camo_skin47",5.000000
+"camo_skin48",5.000000
+"camo_skin49",5.000000
+"camo_skin50",5.000000
+"camo_skin51",5.000000
+"camo_skin52",5.000000
+"camo_skin53",5.000000
+"camo_skin54",5.000000
+"camo_skin68",5.000000
+"camo_skin69",5.000000
+"camo_skin70",5.000000
+"camo_skin71",5.000000
+"camo_skin72",5.000000
+"camo_skin73",5.000000
+"camo_skin74",5.000000
+"camo_skin75",5.000000
+"camo_skin76",5.000000
+"camo_skin77",5.000000
+"camo_skin78",5.000000
+"camo_skin79",5.000000
+"camo_skin80",5.000000
+"camo_skin84",5.000000
+"camo_skin86",5.000000
+"camo_skin88",5.000000
+"camo_skin89",5.000000
+"camo_skin90",5.000000
+"camo_skin95",5.000000
+"camo_skin96",5.000000
+"camo_skin98",5.000000
+"camo_skin99",5.000000
+"pilot_camo_skin04",10.000000
+"pilot_camo_skin05",10.000000
+"pilot_camo_skin06",10.000000
+"pilot_camo_skin11",10.000000
+"pilot_camo_skin12",10.000000
+"pilot_camo_skin20",10.000000
+"pilot_camo_skin21",10.000000
+"pilot_camo_skin22",10.000000
+"pilot_camo_skin23",10.000000
+"pilot_camo_skin28",10.000000
+"pilot_camo_skin29",10.000000
+"pilot_camo_skin32",10.000000
+"pilot_camo_skin33",10.000000
+"pilot_camo_skin34",10.000000
+"pilot_camo_skin35",10.000000
+"pilot_camo_skin36",10.000000
+"pilot_camo_skin37",10.000000
+"pilot_camo_skin38",10.000000
+"pilot_camo_skin39",10.000000
+"pilot_camo_skin40",10.000000
+"pilot_camo_skin41",10.000000
+"pilot_camo_skin42",10.000000
+"pilot_camo_skin43",10.000000
+"pilot_camo_skin44",10.000000
+"pilot_camo_skin45",10.000000
+"pilot_camo_skin46",10.000000
+"pilot_camo_skin47",10.000000
+"pilot_camo_skin48",10.000000
+"pilot_camo_skin49",10.000000
+"pilot_camo_skin50",10.000000
+"pilot_camo_skin51",10.000000
+"pilot_camo_skin52",10.000000
+"pilot_camo_skin53",10.000000
+"pilot_camo_skin54",10.000000
+"pilot_camo_skin68",10.000000
+"pilot_camo_skin69",10.000000
+"pilot_camo_skin70",10.000000
+"pilot_camo_skin71",10.000000
+"pilot_camo_skin72",10.000000
+"pilot_camo_skin73",10.000000
+"pilot_camo_skin74",10.000000
+"pilot_camo_skin75",10.000000
+"pilot_camo_skin76",10.000000
+"pilot_camo_skin77",10.000000
+"pilot_camo_skin78",10.000000
+"pilot_camo_skin79",10.000000
+"pilot_camo_skin80",10.000000
+"pilot_camo_skin84",10.000000
+"pilot_camo_skin86",10.000000
+"pilot_camo_skin88",10.000000
+"pilot_camo_skin89",10.000000
+"pilot_camo_skin90",10.000000
+"pilot_camo_skin95",10.000000
+"pilot_camo_skin96",10.000000
+"pilot_camo_skin98",10.000000
+"pilot_camo_skin99",10.000000
+"titan_camo_skin04",10.000000
+"titan_camo_skin05",10.000000
+"titan_camo_skin06",10.000000
+"titan_camo_skin11",10.000000
+"titan_camo_skin12",10.000000
+"titan_camo_skin20",10.000000
+"titan_camo_skin21",10.000000
+"titan_camo_skin22",10.000000
+"titan_camo_skin23",10.000000
+"titan_camo_skin28",10.000000
+"titan_camo_skin29",10.000000
+"titan_camo_skin32",10.000000
+"titan_camo_skin33",10.000000
+"titan_camo_skin34",10.000000
+"titan_camo_skin35",10.000000
+"titan_camo_skin36",10.000000
+"titan_camo_skin37",10.000000
+"titan_camo_skin38",10.000000
+"titan_camo_skin39",10.000000
+"titan_camo_skin40",10.000000
+"titan_camo_skin41",10.000000
+"titan_camo_skin42",10.000000
+"titan_camo_skin43",10.000000
+"titan_camo_skin44",10.000000
+"titan_camo_skin45",10.000000
+"titan_camo_skin46",10.000000
+"titan_camo_skin47",10.000000
+"titan_camo_skin48",10.000000
+"titan_camo_skin49",10.000000
+"titan_camo_skin50",10.000000
+"titan_camo_skin51",10.000000
+"titan_camo_skin52",10.000000
+"titan_camo_skin53",10.000000
+"titan_camo_skin54",10.000000
+"titan_camo_skin68",10.000000
+"titan_camo_skin69",10.000000
+"titan_camo_skin70",10.000000
+"titan_camo_skin71",10.000000
+"titan_camo_skin72",10.000000
+"titan_camo_skin73",10.000000
+"titan_camo_skin74",10.000000
+"titan_camo_skin75",10.000000
+"titan_camo_skin76",10.000000
+"titan_camo_skin77",10.000000
+"titan_camo_skin78",10.000000
+"titan_camo_skin79",10.000000
+"titan_camo_skin80",10.000000
+"titan_camo_skin84",10.000000
+"titan_camo_skin86",10.000000
+"titan_camo_skin88",10.000000
+"titan_camo_skin89",10.000000
+"titan_camo_skin90",10.000000
+"titan_camo_skin95",10.000000
+"titan_camo_skin96",10.000000
+"titan_camo_skin98",10.000000
+"titan_camo_skin99",10.000000
+"gc_icon_5star",15.000000
+"gc_icon_angryface",15.000000
+"gc_icon_bear",15.000000
+"gc_icon_bee",15.000000
+"gc_icon_bigcat",15.000000
+"gc_icon_bird",15.000000
+"gc_icon_bunnyskull",15.000000
+"gc_icon_chicken",15.000000
+"gc_icon_club",15.000000
+"gc_icon_corporal",15.000000
+"gc_icon_cow",15.000000
+"gc_icon_cupcake",15.000000
+"gc_icon_dataknife",15.000000
+"gc_icon_doublerainbow",15.000000
+"gc_icon_dragon",15.000000
+"gc_icon_earthworm",15.000000
+"gc_icon_fair_warning",15.000000
+"gc_icon_falcon",15.000000
+"gc_icon_fingerprint",15.000000
+"gc_icon_flying_skull",15.000000
+"gc_icon_ghostface",15.000000
+"gc_icon_hamburger",15.000000
+"gc_icon_hammer",15.000000
+"gc_icon_handprint",15.000000
+"gc_icon_hawkmoth",15.000000
+"gc_icon_heartless",15.000000
+"gc_icon_hvt",15.000000
+"gc_icon_jollyrgr",15.000000
+"gc_icon_knife",15.000000
+"gc_icon_lol",15.000000
+"gc_icon_mad_hat",15.000000
+"gc_icon_marksman",15.000000
+"gc_icon_omg",15.000000
+"gc_icon_ordnance",15.000000
+"gc_icon_pizza",15.000000
+"gc_icon_pvt",15.000000
+"gc_icon_question",15.000000
+"gc_icon_rainbow",15.000000
+"gc_icon_ram",15.000000
+"gc_icon_respawn",15.000000
+"gc_icon_saturn",15.000000
+"gc_icon_scorpion",15.000000
+"gc_icon_senior_sgt_e6",15.000000
+"gc_icon_senior_sgt",15.000000
+"gc_icon_sgt",15.000000
+"gc_icon_skull",15.000000
+"gc_icon_snake",15.000000
+"gc_icon_stab",15.000000
+"gc_icon_teabage",15.000000
+"gc_icon_widow",15.000000
+"gc_icon_witch_hat",15.000000
+"gc_icon_wizard_hat",15.000000
+"gc_icon_wraith",15.000000
+"gc_icon_wtf",15.000000
+"gc_icon_down",35.000000
+"gc_icon_joy",35.000000
+"execution_face_stab",1.000000
+"ion.ion_skin_03",1.000000
+"camo_skin08",1.000000
+"pilot_camo_skin08",1.000000
+"titan_camo_skin08",1.000000
+"ion.ion_nose_art_05",1.000000
+"callsign_tt_gameover",10.000000
+"callsign_tt_gameover_prism",1.000000
+"callsign_tt_guardtheflag",10.000000
+"callsign_tt_guardtheflag_prism",1.000000
+"callsign_tt_megamarvin",10.000000
+"callsign_tt_megamarvin_prism",1.000000
+"callsign_tt_nessievault",10.000000
+"callsign_tt_nessievault_prism",1.000000
+"callsign_tt_nsbt",10.000000
+"callsign_tt_nsbt_prism",1.000000
+"callsign_tt_protocol2",10.000000
+"callsign_tt_protocol2_prism",1.000000
+"callsign_tt_rekt",10.000000
+"callsign_tt_rekt_prism",1.000000
+"callsign_tt_titantoons",10.000000
+"callsign_tt_titantoons_prism",1.000000
+"callsign_eat_ion",1.000000
+"callsign_eat_legion",1.000000
+"callsign_eat_northstar",1.000000
+"callsign_eat_ronin",1.000000
+"callsign_eat_scorch",1.000000
+"callsign_eat_tone",1.000000
+"callsign_126_col_prism",1.000000
+"callsign_127_col_prism",1.000000
+"callsign_130_col_prism",1.000000
+"callsign_131_col_prism",1.000000 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/unlocks_titan_level.csv b/Northstar.CustomServers/mod/scripts/datatable/unlocks_titan_level.csv
new file mode 100644
index 00000000..24f54fea
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/unlocks_titan_level.csv
@@ -0,0 +1,402 @@
+titanLevel,ion,scorch,northstar,ronin,tone,legion,vanguard,end
+"","ion","scorch","northstar","ronin","tone","legion","vanguard","END"
+"1","pas_ion_weapon,pas_mobility_dash_capacity,pas_enhanced_titan_ai,pas_auto_eject,pas_bubbleshield,titan_camo_skin00,camo_skin00,ion_nose_art_none,pas_vanguard_core1, pas_vanguard_core4,pas_vanguard_core7,execution_random_0,execution_ion,execution_ion_prime","pas_scorch_weapon,pas_mobility_dash_capacity,pas_enhanced_titan_ai,pas_auto_eject,pas_bubbleshield,titan_camo_skin00,camo_skin00,scorch_nose_art_none,pas_vanguard_core1, pas_vanguard_core4,pas_vanguard_core7,execution_random_1,execution_scorch,execution_scorch_prime","pas_northstar_weapon,pas_mobility_dash_capacity,pas_enhanced_titan_ai,pas_auto_eject,pas_bubbleshield,titan_camo_skin00,camo_skin00,northstar_nose_art_none,pas_vanguard_core1, pas_vanguard_core4,pas_vanguard_core7,execution_random_2,execution_northstar,execution_northstar_prime","pas_ronin_weapon,pas_mobility_dash_capacity,pas_enhanced_titan_ai,pas_auto_eject,pas_bubbleshield,titan_camo_skin00,camo_skin00,ronin_nose_art_none,pas_vanguard_core1, pas_vanguard_core4,pas_vanguard_core7,execution_random_3,execution_ronin,execution_ronin_prime","pas_tone_weapon,pas_mobility_dash_capacity,pas_enhanced_titan_ai,pas_auto_eject,pas_bubbleshield,titan_camo_skin00,camo_skin00,tone_nose_art_none,pas_vanguard_core1, pas_vanguard_core4,pas_vanguard_core7,execution_random_4,execution_tone,execution_tone_prime","pas_legion_weapon,pas_mobility_dash_capacity,pas_enhanced_titan_ai,pas_auto_eject,pas_bubbleshield,titan_camo_skin00,camo_skin00,legion_nose_art_none,pas_vanguard_core1, pas_vanguard_core4,pas_vanguard_core7,execution_random_5,execution_legion,execution_legion_prime","pas_vanguard_shield,pas_mobility_dash_capacity,pas_enhanced_titan_ai,pas_auto_eject,pas_bubbleshield,pas_vanguard_core1,pas_vanguard_core4,pas_vanguard_core7,titan_camo_skin00,camo_skin00,vanguard_nose_art_none,execution_random_6,execution_vanguard",""
+"2","pas_hyper_core","pas_hyper_core","pas_hyper_core","pas_hyper_core","pas_hyper_core","pas_hyper_core","pas_hyper_core",""
+"3","pas_build_up_nuclear_core","pas_build_up_nuclear_core","pas_build_up_nuclear_core","pas_build_up_nuclear_core","pas_build_up_nuclear_core","pas_build_up_nuclear_core","pas_vanguard_core2,pas_build_up_nuclear_core",""
+"4","pas_anti_rodeo,ion_nose_art_14","pas_anti_rodeo,scorch_nose_art_11","pas_anti_rodeo,northstar_nose_art_11","pas_anti_rodeo,ronin_nose_art_14","pas_anti_rodeo,tone_nose_art_13","pas_anti_rodeo,legion_nose_art_13","pas_anti_rodeo,vanguard_nose_art_01",""
+"5","pas_warpfall,random","pas_warpfall,random","pas_warpfall,random","pas_warpfall,random","pas_warpfall,random","pas_warpfall,random","pas_warpfall,pas_vanguard_core5,random",""
+"6","pas_ion_tripwire,titan_camo_skin01","pas_scorch_selfdmg,titan_camo_skin01","pas_northstar_cluster,titan_camo_skin01","pas_ronin_arcwave,titan_camo_skin01","pas_tone_wall,titan_camo_skin01","pas_legion_smartcore,titan_camo_skin01","pas_vanguard_coremeter,titan_camo_skin01",""
+"7","pas_ion_vortex","pas_scorch_shield","pas_northstar_trap","pas_ronin_phase","pas_tone_sonar","pas_legion_gunshield","pas_vanguard_core8,pas_vanguard_rearm",""
+"8","pas_ion_lasercannon,camo_skin01","pas_scorch_firewall,camo_skin01","pas_northstar_flightcore,camo_skin01","pas_ronin_swordcore,camo_skin01","pas_tone_rockets,camo_skin01","pas_legion_spinup,camo_skin01","pas_vanguard_core3,pas_vanguard_doom,camo_skin01",""
+"9","titan_camo_skin03,pas_ion_weapon_ads","titan_camo_skin03,pas_scorch_flamecore","titan_camo_skin03,pas_northstar_optics","titan_camo_skin03,pas_ronin_autoshift","titan_camo_skin03,pas_tone_burst","titan_camo_skin03,pas_legion_chargeshot","titan_camo_skin03,pas_vanguard_core6",""
+"10","gc_icon_cateye,ion_nose_art_01","gc_icon_fireball,scorch_nose_art_02","gc_icon_stinger,northstar_nose_art_08","gc_icon_sword,ronin_nose_art_02","gc_icon_crosshair,tone_nose_art_14","gc_icon_bullet,legion_nose_art_09","gc_icon_monarch,vanguard_nose_art_02,pas_vanguard_core9",""
+"11","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03",""
+"12","titan_camo_skin02","titan_camo_skin02","titan_camo_skin02","titan_camo_skin02","titan_camo_skin02","titan_camo_skin02","titan_camo_skin02",""
+"13","","","","","","","",""
+"14","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02",""
+"15","titan_camo_skin97","titan_camo_skin97","titan_camo_skin97","titan_camo_skin97","titan_camo_skin97","titan_camo_skin97","titan_camo_skin97",""
+"16","","","","","","","",""
+"17","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97",""
+"18","titan_camo_skin24","titan_camo_skin24","titan_camo_skin24","titan_camo_skin24","titan_camo_skin24","titan_camo_skin24","titan_camo_skin24",""
+"19","","","","","","","",""
+"20","camo_skin24,ion_nose_art_08","camo_skin24,scorch_nose_art_03","camo_skin24,northstar_nose_art_06","camo_skin24,ronin_nose_art_11","camo_skin24,tone_nose_art_04","camo_skin24,legion_nose_art_06","camo_skin24,vanguard_nose_art_03",""
+"1","titan_camo_skin16,callsign_24_col","titan_camo_skin16,callsign_47_col","titan_camo_skin16,callsign_36_col","titan_camo_skin16,callsign_45_col","titan_camo_skin16,callsign_68_col","titan_camo_skin16,callsign_26_col","titan_camo_skin16,callsign_165_col",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","camo_skin16","camo_skin16","camo_skin16","camo_skin16","camo_skin16","camo_skin16","camo_skin16",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","titan_camo_skin25","titan_camo_skin25","titan_camo_skin25","titan_camo_skin25","titan_camo_skin25","titan_camo_skin25","titan_camo_skin25",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","ion_nose_art_04","scorch_nose_art_05","northstar_nose_art_04","ronin_nose_art_05","tone_nose_art_03","legion_nose_art_03","vanguard_nose_art_04",""
+"1","titan_camo_skin14,callsign_24_col_fire","titan_camo_skin14,callsign_47_col_fire","titan_camo_skin14,callsign_36_col_fire","titan_camo_skin14,callsign_45_col_fire","titan_camo_skin14,callsign_68_col_fire","titan_camo_skin14,callsign_26_col_fire","titan_camo_skin14,callsign_165_col_fire",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","titan_camo_skin26","titan_camo_skin26","titan_camo_skin26","titan_camo_skin26","titan_camo_skin26","titan_camo_skin26","titan_camo_skin26",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","ion_nose_art_03","scorch_nose_art_01","northstar_nose_art_01","ronin_nose_art_06","tone_nose_art_07","legion_nose_art_10","vanguard_nose_art_05",""
+"1","titan_camo_skin83,callsign_24_col_gold","titan_camo_skin83,callsign_47_col_gold","titan_camo_skin83,callsign_36_col_gold","titan_camo_skin83,callsign_45_col_gold","titan_camo_skin83,callsign_68_col_gold","titan_camo_skin83,callsign_26_col_gold","titan_camo_skin83,callsign_165_col_gold",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","titan_camo_skin27","titan_camo_skin27","titan_camo_skin27","titan_camo_skin27","titan_camo_skin27","titan_camo_skin27","titan_camo_skin27",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","ion_nose_art_12","scorch_nose_art_13","northstar_nose_art_09","ronin_nose_art_04","tone_nose_art_02","legion_nose_art_02","vanguard_nose_art_06",""
+"1","titan_camo_skin31","titan_camo_skin31","titan_camo_skin31","titan_camo_skin31","titan_camo_skin31","titan_camo_skin31","titan_camo_skin31",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","titan_camo_skin19","titan_camo_skin19","titan_camo_skin19","titan_camo_skin19","titan_camo_skin19","titan_camo_skin19","titan_camo_skin19",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","ion_nose_art_13","scorch_nose_art_10","northstar_nose_art_03","ronin_nose_art_09","tone_nose_art_09","legion_nose_art_01","vanguard_nose_art_07",""
+"1","titan_camo_skin82","titan_camo_skin82","titan_camo_skin82","titan_camo_skin82","titan_camo_skin82","titan_camo_skin82","titan_camo_skin82",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","titan_camo_skin15","titan_camo_skin15","titan_camo_skin15","titan_camo_skin15","titan_camo_skin15","titan_camo_skin15","titan_camo_skin15",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","ion_nose_art_02","scorch_nose_art_04","northstar_nose_art_02","ronin_nose_art_01","tone_nose_art_01","legion_nose_art_08","",""
+"1","titan_camo_skin17","titan_camo_skin17","titan_camo_skin17","titan_camo_skin17","titan_camo_skin17","titan_camo_skin17","titan_camo_skin17",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","ion_nose_art_06","scorch_nose_art_14","northstar_nose_art_10","ronin_nose_art_13","tone_nose_art_05","legion_nose_art_11","vanguard_nose_art_08",""
+"1","titan_camo_skin81","titan_camo_skin81","titan_camo_skin81","titan_camo_skin81","titan_camo_skin81","titan_camo_skin81","titan_camo_skin81",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","ion_nose_art_11","scorch_nose_art_09","northstar_nose_art_05","ronin_nose_art_10","tone_nose_art_08","legion_nose_art_04","",""
+"1","titan_camo_skin18","titan_camo_skin18","titan_camo_skin18","titan_camo_skin18","titan_camo_skin18","titan_camo_skin18","titan_camo_skin18",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","ion_nose_art_10","scorch_nose_art_08","northstar_nose_art_14","ronin_nose_art_03","tone_nose_art_06","legion_nose_art_05","vanguard_nose_art_09",""
+"1","titan_camo_skin30","titan_camo_skin30","titan_camo_skin30","titan_camo_skin30","titan_camo_skin30","titan_camo_skin30","titan_camo_skin30",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","ion_nose_art_09","","","","","","",""
+"1","","","","","","","",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","",""
+"1","random","random","random","random","random","random","random",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","",""
+"1","random","random","random","random","random","random","random",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","",""
+"1","random","random","random","random","random","random","random",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","",""
+"1","random","random","random","random","random","random","random",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","",""
+"1","random","random","random","random","random","random","random",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","",""
+"1","random","random","random","random","random","random","random",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","",""
+"1","random","random","random","random","random","random","random",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","",""
+"1","random","random","random","random","random","random","random",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","",""
+"1","random","random","random","random","random","random","random",""
+"2","","","","","","","",""
+"3","","","","","","","",""
+"4","","","","","","","",""
+"5","","","","","","","",""
+"6","","","","","","","",""
+"7","","","","","","","",""
+"8","","","","","","","",""
+"9","","","","","","","",""
+"10","","","","","","","",""
+"11","","","","","","","",""
+"12","","","","","","","",""
+"13","","","","","","","",""
+"14","","","","","","","",""
+"15","","","","","","","",""
+"16","","","","","","","",""
+"17","","","","","","","",""
+"18","","","","","","","",""
+"19","","","","","","","",""
+"20","","","","","","","","" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/unlocks_weapon_level_pilot.csv b/Northstar.CustomServers/mod/scripts/datatable/unlocks_weapon_level_pilot.csv
new file mode 100644
index 00000000..5c881ac5
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/unlocks_weapon_level_pilot.csv
@@ -0,0 +1,383 @@
+weaponLevel,weap1,weap2,weap3,weap4,weap5,weap6,weap7,weap8,weap9,weap10,weap11,weap12,weap13,weap14,weap15,weap16,weap17,weap18,weap19,weap20,weap21,weap22,weap23,weap24,weap25,weap26,weap27,weap28,weap29,weap30,end
+"","mp_weapon_alternator_smg","mp_weapon_arc_launcher","mp_weapon_autopistol","mp_weapon_car","mp_weapon_defender","mp_weapon_dmr","mp_weapon_doubletake","mp_weapon_epg","mp_weapon_esaw","mp_weapon_g2","mp_weapon_hemlok","mp_weapon_hemlok_smg","mp_weapon_lmg","mp_weapon_lstar","mp_weapon_mastiff","mp_weapon_mgl","mp_weapon_pulse_lmg","mp_weapon_r97","mp_weapon_rocket_launcher","mp_weapon_rspn101","mp_weapon_semipistol","mp_weapon_shotgun","mp_weapon_shotgun_pistol","mp_weapon_smr","mp_weapon_sniper","mp_weapon_softball","mp_weapon_vinson","mp_weapon_wingman","mp_weapon_wingman_n","mp_weapon_rspn101_og","END"
+"1","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen","camo_skin00, pro_screen",""
+"2","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo","extended_ammo",""
+"3","hcog","pas_fast_reload","silencer","holosight","quick_charge","scope_4x","scope_4x","pas_run_and_gun","aog","hcog","hcog","holosight","aog","aog","holosight","pas_fast_reload","pas_run_and_gun","holosight","pas_fast_reload","hcog","silencer","holosight","silencer","pas_run_and_gun","scope_4x","pas_run_and_gun","hcog","silencer","ricochet","hcog",""
+"4","pas_run_and_gun","pas_fast_ads","pas_run_and_gun","pas_run_and_gun","pas_fast_ads","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_run_and_gun","pas_run_and_gun","pas_run_and_gun","pas_run_and_gun","pas_run_and_gun","pas_run_and_gun","pas_run_and_gun","pas_fast_ads","pas_fast_reload","pas_run_and_gun","pas_fast_ads","pas_run_and_gun","pas_run_and_gun","pas_run_and_gun","pas_run_and_gun","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_run_and_gun","pas_run_and_gun","pas_run_and_gun","pas_run_and_gun",""
+"5","pas_fast_reload","pas_fast_swap","pas_fast_reload","pas_fast_reload","pas_fast_swap","pas_fast_ads","pas_fast_ads","pas_fast_ads","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_fast_swap","pas_fast_ads","pas_fast_reload","pas_fast_swap","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_fast_ads","pas_fast_ads","pas_fast_ads","pas_fast_reload","pas_fast_reload","pas_fast_reload","pas_fast_reload",""
+"6","redline_sight","secondarymod2","pas_fast_ads","redline_sight","secondarymod2","pas_fast_swap","pas_fast_swap","pas_fast_swap","redline_sight","redline_sight","redline_sight","redline_sight","redline_sight","redline_sight","redline_sight","secondarymod2","pas_fast_swap","redline_sight","secondarymod2","redline_sight","pas_fast_ads","redline_sight","pas_fast_ads","pas_fast_swap","pas_fast_swap","pas_fast_swap","redline_sight","pas_fast_ads","pas_fast_ads","redline_sight",""
+"7","pas_fast_ads","","tactical_cdr_on_kill","pas_fast_ads","","tactical_cdr_on_kill","tactical_cdr_on_kill","tactical_cdr_on_kill","pas_fast_ads","pas_fast_ads","pas_fast_ads","pas_fast_ads","pas_fast_ads","pas_fast_ads","pas_fast_ads","","tactical_cdr_on_kill","pas_fast_ads","","pas_fast_ads","tactical_cdr_on_kill","pas_fast_ads","tactical_cdr_on_kill","tactical_cdr_on_kill","tactical_cdr_on_kill","tactical_cdr_on_kill","pas_fast_ads","tactical_cdr_on_kill","tactical_cdr_on_kill","pas_fast_ads",""
+"8","pas_fast_swap,random","random","secondarymod2,random","pas_fast_swap,random","random","threat_scope,random","threat_scope,random","random,primarymod2","pas_fast_swap,random","pas_fast_swap,random","pas_fast_swap,random","pas_fast_swap,random","pas_fast_swap,random","pas_fast_swap,random","pas_fast_swap,random","random","primarymod2,random","pas_fast_swap,random","random","pas_fast_swap,random","secondarymod2,random","pas_fast_swap,random","primarymod2,random","primarymod2,random","threat_scope,random","primarymod2,random","pas_fast_swap,random","secondarymod2,random","primarymod2,random","pas_fast_swap,random",""
+"9","tactical_cdr_on_kill","","","tactical_cdr_on_kill","","primarymod2","ricochet","","tactical_cdr_on_kill","tactical_cdr_on_kill","tactical_cdr_on_kill","tactical_cdr_on_kill","tactical_cdr_on_kill","tactical_cdr_on_kill","tactical_cdr_on_kill","","","tactical_cdr_on_kill","","tactical_cdr_on_kill","","tactical_cdr_on_kill","","","ricochet","","tactical_cdr_on_kill","","","tactical_cdr_on_kill",""
+"10","threat_scope,camo_skin01","camo_skin01","camo_skin01","threat_scope,camo_skin01","camo_skin01","camo_skin01","primarymod2,camo_skin01","camo_skin01","threat_scope,camo_skin01","threat_scope,camo_skin01","threat_scope,camo_skin01","threat_scope,camo_skin01","threat_scope,camo_skin01","threat_scope,camo_skin01","threat_scope,camo_skin01","camo_skin01","camo_skin01","threat_scope,camo_skin01","camo_skin01","threat_scope,camo_skin01","camo_skin01","threat_scope,camo_skin01","camo_skin01","camo_skin01","primarymod2,camo_skin01","camo_skin01","threat_scope,camo_skin01","camo_skin01","camo_skin01","threat_scope,camo_skin01",""
+"11","primarymod2","","","primarymod2","","","","","primarymod2","primarymod2","primarymod2","primarymod2","primarymod2","primarymod2","primarymod2","","","primarymod2","","primarymod2","","primarymod2","","","","","primarymod2","","","primarymod2",""
+"12","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03","camo_skin03",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02","camo_skin02",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97","camo_skin97",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24","camo_skin24",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","gc_icon_yuckface","gc_icon_raincloud","gc_icon_diamond","gc_icon_wasp","gc_icon_bullseye","gc_icon_fin","gc_icon_dice","gc_icon_comet","gc_icon_heart","gc_icon_ramskull","gc_icon_waves","gc_icon_lightning","gc_icon_hawk","gc_icon_star","gc_icon_paw","gc_icon_frag","gc_icon_moon","gc_icon_assault","gc_icon_rocket","gc_icon_atom","gc_icon_spade","gc_icon_8ball","gc_icon_clawmark","gc_icon_bomb_02","gc_icon_happyface","gc_icon_bomb_01","gc_icon_medic","gc_icon_dragonfly","gc_icon_b3_wing","gc_icon_mushroom",""
+"1","primarymod3,camo_skin16","secondarymod3,camo_skin16","secondarymod3,camo_skin16","primarymod3,camo_skin16","secondarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","secondarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","secondarymod3,camo_skin16","primarymod3,camo_skin16","secondarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16","secondarymod3,camo_skin16","primarymod3,camo_skin16","primarymod3,camo_skin16",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","callsign_13_col","callsign_76_col","callsign_29_col","callsign_55_col","callsign_66_col","callsign_30_col","callsign_21_col","callsign_94_col","callsign_15_col","callsign_31_col","callsign_40_col","callsign_18_col","callsign_27_col","callsign_22_col","callsign_48_col","callsign_42_col","callsign_78_col","callsign_75_col","callsign_92_col","callsign_03_col","callsign_37_col","callsign_79_col","callsign_51_col","callsign_11_col","callsign_57_col","callsign_59_col","callsign_69_col","callsign_19_col","callsign_139_col","callsign_128_col",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25","camo_skin25",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14","camo_skin14",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","callsign_13_col_fire","callsign_76_col_fire","callsign_29_col_fire","callsign_55_col_fire","callsign_66_col_fire","callsign_30_col_fire","callsign_21_col_fire","callsign_94_col_fire","callsign_15_col_fire","callsign_31_col_fire","callsign_40_col_fire","callsign_18_col_fire","callsign_27_col_fire","callsign_22_col_fire","callsign_48_col_fire","callsign_42_col_fire","callsign_78_col_fire","callsign_75_col_fire","callsign_92_col_fire","callsign_03_col_fire","callsign_37_col_fire","callsign_79_col_fire","callsign_51_col_fire","callsign_11_col_fire","callsign_57_col_fire","callsign_59_col_fire","callsign_69_col_fire","callsign_19_col_fire","callsign_139_col_fire","callsign_128_col_fire",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26","camo_skin26",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83","camo_skin83",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","callsign_13_col_gold","callsign_76_col_gold","callsign_29_col_gold","callsign_55_col_gold","callsign_66_col_gold","callsign_30_col_gold","callsign_21_col_gold","callsign_94_col_gold","callsign_15_col_gold","callsign_31_col_gold","callsign_40_col_gold","callsign_18_col_gold","callsign_27_col_gold","callsign_22_col_gold","callsign_48_col_gold","callsign_42_col_gold","callsign_78_col_gold","callsign_75_col_gold","callsign_92_col_gold","callsign_03_col_gold","callsign_37_col_gold","callsign_79_col_gold","callsign_51_col_gold","callsign_11_col_gold","callsign_57_col_gold","callsign_59_col_gold","callsign_69_col_gold","callsign_19_col_gold","callsign_139_col_gold","callsign_128_col_gold",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27","camo_skin27",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31","camo_skin31",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19","camo_skin19",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82","camo_skin82",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15","camo_skin15",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17","camo_skin17",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81","camo_skin81",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18","camo_skin18",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30","camo_skin30",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random",""
+"2","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"3","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"4","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"5","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"6","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"7","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"8","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"9","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"10","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"11","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"12","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"13","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"14","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"15","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"16","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"17","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"18","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"19","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"20","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""
+"1","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","random","" \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/weapon_skins.csv b/Northstar.CustomServers/mod/scripts/datatable/weapon_skins.csv
new file mode 100644
index 00000000..0f074882
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/weapon_skins.csv
@@ -0,0 +1,35 @@
+ref,weaponRef,image,name,cost,skinIndex,skinType
+"skin_rspn101_wasteland","mp_weapon_rspn101","rui/weapon_skin_swatches/swatch_wasteland","#SKIN_WASTELAND",0,8,1
+"skin_g2_masterwork","mp_weapon_g2","rui/weapon_skin_swatches/swatch_masterwork","#SKIN_MASTERWORK",0,6,1
+"skin_vinson_blue_fade","mp_weapon_vinson","rui/weapon_skin_swatches/swatch_blue_fade","#SKIN_BLUE_FADE",0,7,1
+"skin_car_crimson_fury","mp_weapon_car","rui/weapon_skin_swatches/swatch_crimson_fury","#SKIN_CRIMSON_FURY",0,6,1
+"skin_alternator_patriot","mp_weapon_alternator_smg","rui/weapon_skin_swatches/swatch_patriot","#SKIN_PATRIOT",0,2,1
+"skin_shotgun_badlands","mp_weapon_shotgun","rui/weapon_skin_swatches/swatch_badlands","#SKIN_BADLANDS",0,4,1
+"skin_wingman_aqua_fade","mp_weapon_wingman","rui/weapon_skin_swatches/swatch_aqua_fade","#SKIN_AQUA_FADE",0,7,1
+"skin_rocket_launcher_psych_spectre","mp_weapon_rocket_launcher","rui/weapon_skin_swatches/swatch_psych_spectre","#SKIN_PHANTOM",0,2,1
+"skin_rspn101_patriot","mp_weapon_rspn101","rui/weapon_skin_swatches/swatch_patriot","#SKIN_PATRIOT",0,9,1
+"skin_hemlok_mochi","mp_weapon_hemlok","rui/weapon_skin_swatches/swatch_mochi","#SKIN_MOCHI",0,5,1
+"skin_r97_purple_fade","mp_weapon_r97","rui/weapon_skin_swatches/swatch_purple_fade","#SKIN_PURPLE_FADE",0,7,1
+"skin_kraber_masterwork","mp_weapon_sniper","rui/weapon_skin_swatches/swatch_masterwork","#SKIN_MASTERWORK",0,2,1
+"skin_spitfire_lead_farmer","mp_weapon_lmg","rui/weapon_skin_swatches/swatch_lead_farmer","#SKIN_LEAD_FARMER",0,8,1
+"skin_devotion_rspn_customs","mp_weapon_esaw","rui/weapon_skin_swatches/swatch_rspn_customs","#SKIN_RSPN_CUSTOMS",0,8,1
+"skin_mozambique_crimson_fury","mp_weapon_shotgun_pistol","rui/weapon_skin_swatches/swatch_crimson_fury","#SKIN_CRIMSON_FURY",0,7,1
+"skin_thunderbolt_8bit","mp_weapon_arc_launcher","rui/weapon_skin_swatches/swatch_8_bit","#SKIN_8_BIT",0,2,1
+"skin_lstar_heatsink","mp_weapon_lstar","rui/weapon_skin_swatches/swatch_heatsink","#SKIN_HEAT_SINK",0,6,1
+"skin_mastiff_crimson_fury","mp_weapon_mastiff","rui/weapon_skin_swatches/swatch_crimson_fury","#SKIN_CRIMSON_FURY",0,7,1
+"skin_sidewinder_masterwork","mp_weapon_smr","rui/weapon_skin_swatches/swatch_masterwork","#SKIN_MASTERWORK",0,2,1
+"skin_rspn101_halloween","mp_weapon_rspn101","rui/weapon_skin_swatches/swatch_rspn101_halloween","#SKIN_HALLOWEEN",0,10,1
+"skin_car_halloween","mp_weapon_car","rui/weapon_skin_swatches/swatch_car_halloween","#SKIN_HALLOWEEN",0,8,1
+"skin_spitfire_halloween","mp_weapon_lmg","rui/weapon_skin_swatches/swatch_spitfire_halloween","#SKIN_HALLOWEEN",0,9,1
+"skin_rspn101_og_blue_fade","mp_weapon_rspn101_og","rui/weapon_skin_swatches/swatch_blue_fade","#SKIN_BLUE_FADE",0,7,1
+"skin_vinson_badlands","mp_weapon_vinson","rui/weapon_skin_swatches/swatch_badlands_flatline","#SKIN_BADLANDS",0,8,1
+"skin_volt_heatsink","mp_weapon_hemlok_smg","rui/weapon_skin_swatches/swatch_heatsink","#SKIN_HEAT_SINK",0,6,1
+"skin_alternator_headhunter","mp_weapon_alternator_smg","rui/weapon_skin_swatches/swatch_headhunter","#SKIN_HEADHUNTER",0,5,1
+"skin_softball_masterwork","mp_weapon_softball","rui/weapon_skin_swatches/swatch_masterwork","#SKIN_MASTERWORK",0,2,1
+"skin_epg_mrvn","mp_weapon_epg","rui/weapon_skin_swatches/swatch_mrvn","#SKIN_MRVN",0,5,1
+"skin_dmr_phantom","mp_weapon_dmr","rui/weapon_skin_swatches/swatch_psych_spectre","#SKIN_PHANTOM",0,3,1
+"skin_doubletake_masterwork","mp_weapon_doubletake","rui/weapon_skin_swatches/swatch_masterwork","#SKIN_MASTERWORK",0,2,1
+"skin_g2_purple_fade","mp_weapon_g2","rui/weapon_skin_swatches/swatch_purple_fade","#SKIN_PURPLE_FADE",0,7,1
+"skin_coldwar_heatsink","mp_weapon_pulse_lmg","rui/weapon_skin_swatches/swatch_heatsink","#SKIN_HEAT_SINK",0,6,1
+"skin_r97_sky","mp_weapon_r97","rui/weapon_skin_swatches/swatch_sky","#SKIN_SKY",0,5,1
+"skin_rspn101_crimson_fury","mp_weapon_rspn101","rui/weapon_skin_swatches/swatch_crimson_fury","#SKIN_CRIMSON_FURY",0,7,1 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/xp_per_faction_level.csv b/Northstar.CustomServers/mod/scripts/datatable/xp_per_faction_level.csv
new file mode 100644
index 00000000..e28cf8c0
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/xp_per_faction_level.csv
@@ -0,0 +1,6 @@
+level,xpPerLevel
+1,2
+2,3
+3,4
+4,5
+5,5 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/xp_per_fd_titan_level.csv b/Northstar.CustomServers/mod/scripts/datatable/xp_per_fd_titan_level.csv
new file mode 100644
index 00000000..28c33f5e
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/xp_per_fd_titan_level.csv
@@ -0,0 +1,25 @@
+level,xpPerLevel
+1,12
+2,12
+3,14
+4,14
+5,16
+6,16
+7,18
+8,18
+9,20
+10,20
+11,20
+12,20
+13,20
+14,20
+15,20
+16,20
+17,20
+18,20
+19,20
+20,20
+21,20
+22,20
+23,20
+24,20 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/xp_per_player_level.csv b/Northstar.CustomServers/mod/scripts/datatable/xp_per_player_level.csv
new file mode 100644
index 00000000..36e52bc8
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/xp_per_player_level.csv
@@ -0,0 +1,51 @@
+level,xpPerLevel
+1,3
+2,4
+3,5
+4,6
+5,7
+6,8
+7,9
+8,10
+9,10
+10,10
+11,10
+12,10
+13,10
+14,10
+15,10
+16,10
+17,10
+18,10
+19,10
+20,10
+21,10
+22,10
+23,10
+24,10
+25,10
+26,10
+27,10
+28,10
+29,10
+30,10
+31,10
+32,10
+33,10
+34,10
+35,10
+36,10
+37,10
+38,10
+39,10
+40,10
+41,10
+42,10
+43,10
+44,10
+45,10
+46,10
+47,10
+48,10
+49,10
+50,10 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/xp_per_titan_level.csv b/Northstar.CustomServers/mod/scripts/datatable/xp_per_titan_level.csv
new file mode 100644
index 00000000..d607f589
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/xp_per_titan_level.csv
@@ -0,0 +1,21 @@
+level,xpPerLevel
+1,3
+2,5
+3,7
+4,10
+5,10
+6,10
+7,10
+8,10
+9,10
+10,10
+11,10
+12,10
+13,10
+14,10
+15,10
+16,10
+17,10
+18,10
+19,10
+20,10 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/datatable/xp_per_weapon_level.csv b/Northstar.CustomServers/mod/scripts/datatable/xp_per_weapon_level.csv
new file mode 100644
index 00000000..a6859594
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/datatable/xp_per_weapon_level.csv
@@ -0,0 +1,21 @@
+level,default,sniper,pistol,antititan
+1,3,2,2,2
+2,5,3,3,3
+3,7,4,4,4
+4,10,5,5,4
+5,10,5,5,4
+6,10,5,5,4
+7,10,5,5,4
+8,10,5,5,4
+9,10,5,5,4
+10,10,5,5,4
+11,10,5,5,4
+12,10,5,5,4
+13,10,5,5,4
+14,10,5,5,4
+15,10,5,5,4
+16,10,5,5,4
+17,10,5,5,4
+18,10,5,5,4
+19,10,5,5,4
+20,10,5,5,4 \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_bubble_shield.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_bubble_shield.gnut
index 30758bec..f09ef957 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_bubble_shield.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_bubble_shield.gnut
@@ -223,6 +223,7 @@ entity function CreateBubbleShieldWithSettings( int team, vector origin, vector
entity neutralColoredFX = StartParticleEffectInWorld_ReturnEntity( BUBBLE_SHIELD_FX_PARTICLE_SYSTEM_INDEX, coloredFXOrigin, <0, 0, 0> )
SetTeam( neutralColoredFX, team )
bubbleShieldDotS.neutralColoredFX <- neutralColoredFX
+ neutralColoredFX.DisableHibernation()
bubbleShieldFXs.append( neutralColoredFX )
}
else
@@ -232,11 +233,13 @@ entity function CreateBubbleShieldWithSettings( int team, vector origin, vector
SetTeam( friendlyColoredFX, team )
friendlyColoredFX.kv.VisibilityFlags = ENTITY_VISIBLE_TO_FRIENDLY
EffectSetControlPointVector( friendlyColoredFX, 1, FRIENDLY_COLOR_FX )
+ friendlyColoredFX.DisableHibernation()
entity enemyColoredFX = StartParticleEffectInWorld_ReturnEntity( BUBBLE_SHIELD_FX_PARTICLE_SYSTEM_INDEX, coloredFXOrigin, <0, 0, 0> )
SetTeam( enemyColoredFX, team )
enemyColoredFX.kv.VisibilityFlags = ENTITY_VISIBLE_TO_ENEMY
EffectSetControlPointVector( enemyColoredFX, 1, ENEMY_COLOR_FX )
+ enemyColoredFX.DisableHibernation()
bubbleShieldDotS.friendlyColoredFX <- friendlyColoredFX
bubbleShieldDotS.enemyColoredFX <- enemyColoredFX
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_chat.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_chat.gnut
index 97ed959c..44836bc9 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_chat.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_chat.gnut
@@ -25,26 +25,26 @@ void function Chat_PrivateMessage(entity fromPlayer, entity toPlayer, string tex
}
// Broadcasts a message from the server to all players.
-void function Chat_ServerBroadcast(string text)
+void function Chat_ServerBroadcast(string text, bool withServerTag = true)
{
NSBroadcastMessage(
-1,
-1,
text,
- false,
+ !withServerTag,
false,
eChatMessageType.CHAT
)
}
// Sends a message from the server to one player. Will be shown as a whisper if whisper is set.
-void function Chat_ServerPrivateMessage(entity toPlayer, string text, bool whisper)
+void function Chat_ServerPrivateMessage(entity toPlayer, string text, bool whisper, bool withServerTag = true)
{
NSBroadcastMessage(
-1,
toPlayer.GetPlayerIndex(),
text,
- false,
+ !withServerTag,
false,
whisper ? eChatMessageType.WHISPER : eChatMessageType.CHAT
)
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_codecallbacks_common.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_codecallbacks_common.gnut
index d2621db3..3704b5cc 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_codecallbacks_common.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_codecallbacks_common.gnut
@@ -46,6 +46,8 @@ global function CodeCallback_OnEntityChangedTeam
global function AddEntityCallback_OnDamaged
global function RemoveEntityCallback_OnDamaged
+global function AddEntityCallback_OnFinalDamaged
+global function RemoveEntityCallback_OnFinalDamaged
global function AddEntityCallback_OnPostDamaged
global function RemoveEntityCallback_OnPostDamaged
global function AddEntityCallback_OnKilled
@@ -121,6 +123,13 @@ void function CodeCallback_DamageEntity( entity ent, var damageInfo )
printt( " after class damage final callbacks:", DamageInfo_GetDamage( damageInfo ) )
#endif
+ foreach ( callbackFunc in ent.e.entFinalDamageCallbacks )
+ callbackFunc( ent, damageInfo )
+
+ #if VERBOSE_DAMAGE_PRINTOUTS
+ printt( " after AddEntityCallback_OnFinalDamaged callbacks:", DamageInfo_GetDamage( damageInfo ) )
+ #endif
+
// make destructible vehicles take more damage from DF_EXPLOSION damage type
if ( "isDestructibleVehicle" in ent.s && DamageInfo_GetCustomDamageType( damageInfo ) & DF_EXPLOSION )
{
@@ -566,7 +575,23 @@ void function RemoveEntityCallback_OnDamaged( entity ent, void functionref( enti
Assert( index != -1, "Requested DamageCallback " + string( callbackFunc ) + " to be removed not found! " )
ent.e.entDamageCallbacks.fastremove( index )
- if ( ent.e.entDamageCallbacks.len() == 0 && ent.e.entPostDamageCallbacks.len() == 0 )
+ if ( ent.e.entDamageCallbacks.len() == 0 && ent.e.entFinalDamageCallbacks.len() == 0 && ent.e.entPostDamageCallbacks.len() == 0 )
+ ent.SetDamageNotifications( false )
+}
+
+void function AddEntityCallback_OnFinalDamaged( entity ent, void functionref( entity ent, var damageInfo ) callbackFunc )
+{
+ Assert( !ent.e.entFinalDamageCallbacks.contains( callbackFunc ), "Already added " + string( callbackFunc ) + " to entity" )
+
+ ent.SetDamageNotifications( true )
+ ent.e.entFinalDamageCallbacks.append( callbackFunc )
+}
+
+void function RemoveEntityCallback_OnFinalDamaged( entity ent, void functionref( entity ent, var damageInfo ) callbackFunc )
+{
+ ent.e.entFinalDamageCallbacks.fastremovebyvalue( callbackFunc )
+
+ if ( ent.e.entFinalDamageCallbacks.len() == 0 && ent.e.entPostDamageCallbacks.len() == 0 && ent.e.entDamageCallbacks.len() == 0 )
ent.SetDamageNotifications( false )
}
@@ -585,7 +610,7 @@ void function RemoveEntityCallback_OnPostDamaged( entity ent, void functionref(
Assert( index != -1, "Requested PostDamageCallback " + string( callbackFunc ) + " to be removed not found! " )
ent.e.entPostDamageCallbacks.fastremove( index )
- if ( ent.e.entPostDamageCallbacks.len() == 0 && ent.e.entDamageCallbacks.len() == 0 )
+ if ( ent.e.entPostDamageCallbacks.len() == 0 && ent.e.entDamageCallbacks.len() == 0 && ent.e.entFinalDamageCallbacks.len() == 0 )
ent.SetDamageNotifications( false )
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_custom_codecallbacks.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_custom_codecallbacks.gnut
index 4a7f8189..ee21116d 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_custom_codecallbacks.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_custom_codecallbacks.gnut
@@ -1,7 +1,10 @@
untyped
global function AddCallback_OnReceivedSayTextMessage
-global function NSSetupChathooksServer
+
+// this is global due to squirrel bridge v3 making native not be able to find non-global funcs properly
+// temp fix (surely it will get replaced), do not use this function please
+global function CServerGameDLL_ProcessMessageStartThread
global struct ClServer_MessageStruct {
string message
@@ -53,7 +56,3 @@ void function AddCallback_OnReceivedSayTextMessage( ClServer_MessageStruct funct
{
NsCustomCallbacks.OnReceivedSayTextMessageCallbacks.append(callbackFunc)
}
-
-void function NSSetupChathooksServer() {
- getroottable().rawset("CServerGameDLL_ProcessMessageStartThread", CServerGameDLL_ProcessMessageStartThread)
-}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_entitystructs.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_entitystructs.gnut
index 378ceae3..9dadea15 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_entitystructs.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_entitystructs.gnut
@@ -232,6 +232,7 @@ global struct ServerEntityStruct
SpawnPointData spawnPointData
array<void functionref( entity ent, var damageInfo )> entDamageCallbacks
+ array<void functionref( entity ent, var damageInfo )> entFinalDamageCallbacks
array<void functionref( entity ent, var damageInfo )> entPostDamageCallbacks
array<void functionref( entity titan, entity attacker )> entSegmentLostCallbacks
array<void functionref( entity ent, var damageInfo, float actualShieldDamage )> entPostShieldDamageCallbacks
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_harvester.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_harvester.gnut
index 37b89169..3bdb1d8a 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_harvester.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_harvester.gnut
@@ -1 +1,72 @@
-//fuck \ No newline at end of file
+global function SpawnHarvester
+global function generateBeamFX
+global function generateShieldFX
+
+global struct HarvesterStruct {
+ entity harvester
+ entity particleBeam
+ entity particleShield
+ entity rings
+ float lastDamage
+ bool shieldBoost
+ bool harvesterShieldDown
+ float harvesterDamageTaken
+ bool havesterWasDamaged
+
+}
+
+HarvesterStruct function SpawnHarvester( vector origin, vector angles, int health, int shieldHealth, int team )
+{
+ entity harvester = CreateEntity( "prop_script" )
+ harvester.SetValueForModelKey( $"models/props/generator_coop/generator_coop.mdl" )
+ harvester.SetOrigin( origin )
+ harvester.SetAngles( angles )
+ harvester.kv.solid = SOLID_VPHYSICS
+
+ harvester.SetMaxHealth( health )
+ harvester.SetHealth( health )
+ harvester.SetShieldHealthMax( shieldHealth )
+ harvester.SetShieldHealth( shieldHealth )
+ harvester.EnableAttackableByAI( 30, 0, AI_AP_FLAG_NONE )
+ SetCustomSmartAmmoTarget( harvester, true )
+ SetObjectCanBeMeleed( harvester, true )
+ SetVisibleEntitiesInConeQueriableEnabled( harvester, true )
+ SetTeam(harvester,team)
+
+ DispatchSpawn( harvester )
+
+
+ entity blackbox = CreatePropDynamic( MODEL_HARVESTER_TOWER_COLLISION, origin, angles, 0 )
+ blackbox.Hide()
+ blackbox.Solid()
+ // blackbox.kv.CollisionGroup = TRACE_COLLISION_GROUP_PLAYER
+
+ entity rings = CreatePropDynamic( MODEL_HARVESTER_TOWER_RINGS, origin, angles, 6 )
+ thread PlayAnim( rings, "generator_cycle_fast" )
+
+
+
+ HarvesterStruct ret
+ ret.harvester = harvester
+ ret.lastDamage = Time()
+ ret.rings = rings
+
+ return ret
+}
+
+HarvesterStruct function generateBeamFX( HarvesterStruct harvester )
+{
+ entity Harvester_Beam = StartParticleEffectOnEntity_ReturnEntity( harvester.harvester, GetParticleSystemIndex( FX_HARVESTER_BEAM ), FX_PATTACH_ABSORIGIN_FOLLOW ,0 )
+ EffectSetControlPointVector( Harvester_Beam, 1, GetShieldTriLerpColor( 0.0 ) )
+ harvester.particleBeam = Harvester_Beam
+ Harvester_Beam.DisableHibernation()
+ return harvester
+}
+
+HarvesterStruct function generateShieldFX( HarvesterStruct harvester )
+{
+ entity Harvester_Shield = StartParticleEffectOnEntity_ReturnEntity( harvester.harvester, GetParticleSystemIndex( FX_HARVESTER_OVERSHIELD ), FX_PATTACH_ABSORIGIN_FOLLOW, 0 )
+ EffectSetControlPointVector( Harvester_Shield, 1, GetShieldTriLerpColor( 0.0 ) )
+ harvester.particleShield = Harvester_Shield
+ return harvester
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_items.nut b/Northstar.CustomServers/mod/scripts/vscripts/_items.nut
index 539b72bc..96623086 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_items.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_items.nut
@@ -5698,7 +5698,13 @@ bool function IsUnlockValid( string ref, string parentRef = "" )
bool function IsSubItemLocked( entity player, string ref, string parentRef )
{
- if ( DevEverythingUnlocked() )
+ if ( DevEverythingUnlocked( player ) )
+ return false
+
+ if ( IsItemPurchasableEntitlement( ref, parentRef ) )
+ return false
+
+ if ( GetItemType( ref ) == eItemTypes.PRIME_TITAN || GetSubitemType( parentRef, ref ) == eItemTypes.PRIME_TITAN )
return false
if ( IsItemInEntitlementUnlock( ref, parentRef ) )
@@ -5817,7 +5823,13 @@ bool function IsSubItemLocked( entity player, string ref, string parentRef )
bool function IsItemLocked( entity player, string ref )
{
- if ( DevEverythingUnlocked() )
+ if ( DevEverythingUnlocked( player ) )
+ return false
+
+ if ( IsItemPurchasableEntitlement( ref ) )
+ return false
+
+ if ( GetItemType( ref ) == eItemTypes.PRIME_TITAN )
return false
if ( IsItemInEntitlementUnlock( ref ) )
@@ -5906,7 +5918,7 @@ bool function IsItemLockedForEntitlement( entity player, string ref, string pare
bool function IsSubItemOwned( entity player, string ref, string parentRef )
{
- if ( DevEverythingUnlocked() )
+ if ( DevEverythingUnlocked( player ) )
return false
Assert( IsValid( player ) )
@@ -5990,7 +6002,7 @@ bool function IsSubItemOwned( entity player, string ref, string parentRef )
bool function IsItemOwned( entity player, string ref )
{
- if ( DevEverythingUnlocked() )
+ if ( DevEverythingUnlocked( player ) )
return false
Assert( IsValid( player ) )
@@ -10082,7 +10094,7 @@ void function InitUnlockAsEntitlement( string itemRef, string parentRef, int ent
unlock = file.entitlementUnlocks[fullRef]
}
- unlock.entitlementIds.append( entitlementId )
+ unlock.entitlementIds.append( 1 ) // Using `1` here instead of the huge DLC check I did previously. Having the `1` seems to keep all paid cosmetics unlocked with progression enabled.
}
array<int> function GetEntitlementIds( string itemRef, string parentRef = "" )
@@ -10219,6 +10231,10 @@ void function StatUnlock_Unlocked( entity player, string itemRef, string parentR
if ( IsItemNew( player, itemRef, parentRef ) )
return
+ // early out if the player has progression disabled
+ if ( !ProgressionEnabledForPlayer( player ) )
+ return
+
int refGuid = file.itemRefToGuid[itemRef]
int parentRefGuid = parentRef == "" ? 0 : file.itemRefToGuid[parentRef]
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut
index 3e8ac9ea..141cfe15 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_loadouts_mp.gnut
@@ -51,8 +51,6 @@ void function SvLoadoutsMP_Init()
AddClientCommandCallback( "InGameMPMenuClosed", ClientCommandCallback_InGameMPMenuClosed )
AddClientCommandCallback( "LoadoutMenuClosed", ClientCommandCallback_LoadoutMenuClosed )
}
-
- AddCallback_OnPlayerKilled( DestroyDroppedWeapon )
}
void function SetLoadoutGracePeriodEnabled( bool enabled )
@@ -62,20 +60,10 @@ void function SetLoadoutGracePeriodEnabled( bool enabled )
void function SetWeaponDropsEnabled( bool enabled )
{
- file.weaponDropsEnabled = enabled
-}
-
-void function DestroyDroppedWeapon( entity victim, entity attacker, var damageInfo )
-{
- if ( !file.weaponDropsEnabled && IsValid( victim.GetActiveWeapon() ) )
- thread DelayDestroyDroppedWeapon( victim.GetActiveWeapon() )
-}
-
-void function DelayDestroyDroppedWeapon( entity weapon )
-{
- WaitEndFrame()
- if ( IsValid( weapon ) )
- weapon.Destroy()
+ if( enabled )
+ FlagSet( "WeaponDropsAllowed" )
+ else
+ FlagClear( "WeaponDropsAllowed" )
}
void function AddCallback_OnTryGetTitanLoadout( TryGetTitanLoadoutCallbackType callback )
@@ -179,6 +167,8 @@ bool function ClientCommandCallback_SetPersistentLoadoutValue( entity player, ar
if ( args[0] == "pilot" )
SetPlayerLoadoutDirty( player )
+ UnlockAchievement( player, achievements.CUSTOMIZE_LOADOUT )
+
return true
}
@@ -191,6 +181,10 @@ bool function ClientCommandCallback_SwapSecondaryAndWeapon3PersistentLoadoutData
// get loadout
int index = args[0].tointeger()
+
+ if ( !IsValidPilotLoadoutIndex(index) )
+ return false
+
PilotLoadoutDef loadout = GetPilotLoadoutFromPersistentData( player, index )
// swap loadouts
@@ -222,7 +216,6 @@ bool function ClientCommandCallback_SetBurnCardPersistenceSlot( entity player, a
print( player + " SetBurnCardPersistenceSlot " + args[0] )
- // insecure, could be used to set invalid burnmeterslot potentially
if ( IsRefValidAndOfType( args[0], eItemTypes.BURN_METER_REWARD ) )
player.SetPersistentVar( "burnmeterSlot", BurnReward_GetByRef( args[0] ).id )
else
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut
index 02be47a4..6499faa2 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut
@@ -9,7 +9,7 @@ void function MenuCallbacks_Init()
bool function ClientCommandCallback_LeaveMatch( entity player, array<string> args )
{
// note: this is imperfect if we have multiple people of the same uid on a server, but that's only a thing in testing
- if ( NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
+ if ( NSIsPlayerLocalPlayer( player ) )
{
if ( GetConVarBool( "ns_should_return_to_lobby" ) && GetMapName() != "mp_lobby" )
{
@@ -41,9 +41,11 @@ void function WritePersistenceAndLeaveForLocalPlayerOnly( entity player )
void function WritePersistenceAndLeave( entity player )
{
+ player.EndSignal( "OnDestroy" )
+
// write player persistence before we leave, since leaving player might load local lobby before server writes persistence, so they won't get newest
// not super essential, but a nice qol thing
- NSEarlyWritePlayerIndexPersistenceForLeave( player.GetPlayerIndex() )
+ NSEarlyWritePlayerPersistenceForLeave( player )
while ( NSIsWritingPlayerPersistence() )
WaitFrame()
@@ -63,6 +65,8 @@ bool function ClientCommandCallback_GenUp( entity player, array<string> args )
player.GenChanged()
player.XPChanged()
}
+
+ RegenPersistentLoadouts(player)
return true
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_northstar_cheatcommands.nut b/Northstar.CustomServers/mod/scripts/vscripts/_northstar_cheatcommands.nut
index c79265ac..af3dfea5 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_northstar_cheatcommands.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_northstar_cheatcommands.nut
@@ -14,6 +14,11 @@ bool function ClientCommandCallbackToggleNoclip( entity player, array<string> ar
{
if ( !GetConVarBool( "sv_cheats" ) )
return true
+ if( player.GetParent() ) // change movetype while setparented will crash the server
+ {
+ print( player + " failed noclipping because the entity is parented" )
+ return true
+ }
print( player + " TOGGLED NOCLIP" )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_utility.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_utility.gnut
index c0e69ba5..4e5c5aa9 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_utility.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_utility.gnut
@@ -2127,8 +2127,13 @@ void function EntityDemigod_TryAdjustDamageInfo( entity ent, var damageInfo )
return
int bottomLimit = 5
+ // Set it up so that you at least take 1 damage, for hit indicators etc to trigger
if ( ent.GetHealth() <= bottomLimit )
- ent.SetHealth( bottomLimit + 1 ) //Set it up so that you at least take 1 damage, for hit indicators etc to trigger
+ {
+ // Prevent going over max health to avoid a server crash.
+ int newHealth = bottomLimit + 1
+ ent.SetHealth( ent.GetMaxHealth() < newHealth ? ent.GetMaxHealth() : newHealth )
+ }
int health = ent.GetHealth()
@@ -4012,7 +4017,7 @@ int function GameTime_TimeLeftMinutes()
if ( GetGameState() == eGameState.Prematch )
return int( ( expect float( GetServerVar( "gameStartTime" ) ) - Time()) / 60.0 )
- return floor( GameTime_TimeLimitMinutes() - GameTime_PlayingTime() / 60 ).tointeger()
+ return floor( GameTime_PlayingTime() / 60 ).tointeger()
}
int function GameTime_TimeLeftSeconds()
@@ -4020,30 +4025,25 @@ int function GameTime_TimeLeftSeconds()
if ( GetGameState() == eGameState.Prematch )
return int( expect float( GetServerVar( "gameStartTime" ) ) - Time() )
- return floor( GameTime_TimeLimitSeconds() - GameTime_PlayingTime() ).tointeger()
+ return GameTime_PlayingTime().tointeger()
}
+// WARN: this function includes WaitingForPlayers and Prematch duration!
int function GameTime_Seconds()
{
return floor( Time() ).tointeger()
}
+// WARN: this function includes WaitingForPlayers Prematch duration!
int function GameTime_Minutes()
{
return int( floor( GameTime_Seconds() / 60 ) )
}
+// this function only counts the time limit during eGameState.Playing
float function GameTime_PlayingTime()
{
- return GameTime_PlayingTimeSince( Time() )
-}
-
-float function GameTime_PlayingTimeSince( float sinceTime )
-{
int gameState = GetGameState()
-
- // temp fix because i have no fucking clue why this crashes
-
if ( gameState < eGameState.Playing )
return 0
@@ -4052,17 +4052,15 @@ float function GameTime_PlayingTimeSince( float sinceTime )
if ( gameState > eGameState.SuddenDeath )
return (expect float( GetServerVar( "roundEndTime" ) ) - expect float( GetServerVar( "roundStartTime" ) ) )
else
- return sinceTime - expect float( GetServerVar( "roundStartTime" ) )
-
+ return floor( expect float( GetServerVar( "roundEndTime" ) ) - Time() )
}
else
{
if ( gameState > eGameState.SuddenDeath )
return (expect float( GetServerVar( "gameEndTime" ) ) - expect float( GetServerVar( "gameStartTime" ) ) )
else
- return sinceTime - expect float( GetServerVar( "gameStartTime" ) )
+ return floor( expect float( GetServerVar( "gameEndTime" ) ) - Time() )
}
-
unreachable
}
@@ -4406,4 +4404,4 @@ bool function PlayerHasTitan( entity player )
return true
return false
-} \ No newline at end of file
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_utility_shared.nut b/Northstar.CustomServers/mod/scripts/vscripts/_utility_shared.nut
index c5887f2b..47dd9294 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_utility_shared.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_utility_shared.nut
@@ -1601,6 +1601,9 @@ float function GetPulseFrac( rate = 1, startTime = 0 )
bool function IsPetTitan( titan )
{
Assert( titan.IsTitan() )
+
+ if ( !titan.GetTitanSoul() )
+ return false
return titan.GetTitanSoul().GetBossPlayer() != null
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut
index 6f044b7a..97d993e6 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut
@@ -1,6 +1,7 @@
global function SvXP_Init
global function PlayerProgressionAllowed
global function HandleXPGainForScoreEvent
+global function AddXP
void function SvXP_Init()
{
@@ -15,7 +16,10 @@ void function SetupPlayerPreviousXPValues( entity player )
player.SetPersistentVar( "previousFactionXP[" + xpFaction + "]", FactionGetXP( player, xpFaction ) )
foreach ( string xpTitan in shTitanXP.titanClasses )
+ {
player.SetPersistentVar( "previousTitanXP[" + xpTitan + "]", TitanGetXP( player, xpTitan ) )
+ player.SetPersistentVar( "fdPreviousTitanXP[" + xpTitan + "]", FD_TitanGetXP( player, xpTitan ) )
+ }
foreach ( string xpWeapon in shWeaponXP.weaponClassNames )
player.SetPersistentVar( GetItemPersistenceStruct( xpWeapon ) + ".previousWeaponXP", WeaponGetXP( player, xpWeapon ) )
@@ -29,46 +33,51 @@ bool function PlayerProgressionAllowed( entity player )
void function HandleXPGainForScoreEvent( entity player, ScoreEvent event )
{
// note: obviously all xp stuff can be cheated in if people want to on customs, this is mainly just here for fun for those who want it and feature completeness
- // most score events don't have this, so we'll set this to the xp value of other categories later if needed
+
int xpValue = ScoreEvent_GetXPValue( event )
int weaponXp = ScoreEvent_GetXPValueWeapon( event )
int titanXp = ScoreEvent_GetXPValueTitan( event )
-
- if ( xpValue < weaponXp )
- xpValue = weaponXp
- else if ( xpValue < titanXp )
- xpValue = titanXp
+ int factionXp = ScoreEvent_GetXPValueFaction( event )
+
+ if ( player.GetPlayerNetInt( "xpMultiplier" ) > 0 || GetCurrentPlaylistVarInt( "double_xp_enabled", 0 ) == 1 )
+ {
+ xpValue *= 2
+ weaponXp *= 2
+ titanXp *= 2
+ factionXp *= 2
+ }
entity weapon = player.GetActiveWeapon()
- if ( IsValid( weapon ) && ShouldTrackXPForWeapon( weapon.GetWeaponClassName() ) )
- AddWeaponXP( player, xpValue )
+ if ( IsValid( weapon ) && ShouldTrackXPForWeapon( weapon.GetWeaponClassName() ) && weaponXp != 0 )
+ AddWeaponXP( player, weaponXp )
// if we specifically gain titan xp, then give titan xp no matter what, otherwise only give it when we're in a titan
- if ( titanXp != 0 || player.IsTitan() )
- AddTitanXP( player, xpValue )
-
- // most events don't have faction xp but almost everything should give it
- int factionXp = ScoreEvent_GetXPValueFaction( event )
- if ( xpValue > factionXp )
- factionXp = xpValue
- else if ( xpValue < factionXp )
- xpValue = factionXp
+ if ( titanXp != 0 )
+ AddTitanXP( player, titanXp )
if ( factionXp != 0 )
AddFactionXP( player, factionXp )
- if ( xpValue == 0 )
- return
-
// global xp
+ if ( xpValue != 0 )
+ AddXP( player, xpValue )
+}
+
+void function AddXP( entity player, int amount )
+{
int oldXp = player.GetPersistentVarAsInt( "xp" )
- if(oldXp<0) oldXp = 0
+ if( oldXp < 0 ) oldXp = 0
int oldLevel = GetLevelForXP( oldXp )
- player.SetPersistentVar( "xp", min( oldXp + xpValue, PlayerGetMaxXPPerGen() ) )
+ player.SetPersistentVar( "xp", min( oldXp + amount, PlayerGetMaxXPPerGen() ) )
player.XPChanged() // network xp change to client, gen can't change here
int newXp = player.GetPersistentVarAsInt( "xp" )
int newLevel = GetLevelForXP( newXp )
if ( newLevel != oldLevel )
+ {
Remote_CallFunction_NonReplay( player, "ServerCallback_PlayerLeveledUp", player.GetPersistentVarAsInt( "gen" ), newLevel )
-} \ No newline at end of file
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForPlayerLevels( player, player.GetPersistentVarAsInt( "gen" ), newLevel )
+ }
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_soldiers.gnut b/Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_soldiers.gnut
index 9717c76d..89fb7a82 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_soldiers.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_soldiers.gnut
@@ -60,6 +60,19 @@ function AiSoldiers_Init()
level.COOP_AT_WEAPON_RATES[ "mp_weapon_smr" ] <- 0.4
level.COOP_AT_WEAPON_RATES[ "mp_weapon_mgl" ] <- 0.1
+ // add stub death callback, because in _codecallbacks_common.gnut there is
+ // CodeCallback_OnEntityKilled which is only called when an entity is being tracked. An
+ // entity is set to be tracked if it has a death callback for it's class, unfortunately this
+ // is then relayed to clients and used for client side death callbacks. The end result of
+ // not having this function called is that clients become completely unaware of any grunt
+ // deaths. A noticeable difference here is that grunts do not play the kill confirmed audio
+ // except on War Games, which does register a callback for grunt deaths to make them dissolve.
+ //
+ // Whilst this may seem like a bit of a hacky solution, it is generally better than simply
+ // tracking all entities. If a different callback is created in the future for grunt deaths
+ // that is not specific to a gamemode, map, etc. then this could be removed
+ AddDeathCallback( "npc_soldier", void function( entity guy, var damageInfo ){} )
+
PrecacheSprite( $"sprites/glow_05.vmt" )
FlagInit( "disable_npcs" )
FlagInit( "Disable_IMC" )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut
index afa8ff3d..107a3f7f 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut
@@ -15,8 +15,10 @@ global function InitBurnMeterPersistentData
const float PHASE_REWIND_LENGTH = 2.0
// taken from wraith portal in apex, assuming it's the same as tf2's
-const float PHASE_REWIND_PATH_SNAPSHOT_INTERVAL = 0.1
-const int PHASE_REWIND_MAX_SNAPSHOTS = int( PHASE_REWIND_LENGTH / PHASE_REWIND_PATH_SNAPSHOT_INTERVAL )
+// 0.1 can be too fast for ttf2 scripts
+const float PHASE_REWIND_PATH_SNAPSHOT_INTERVAL = 0.2 // mainly controlled by this const, the higher the rewind path will be longer, assuming this one is like vanilla's
+const int PHASE_REWIND_MAX_SNAPSHOTS = int( PHASE_REWIND_LENGTH / PHASE_REWIND_PATH_SNAPSHOT_INTERVAL ) - 1
+const int PHASE_REWIND_DATA_MAX_POSITIONS = int( PHASE_REWIND_LENGTH * 10 ) + 1 // hardcoded but maybe good
const float AMPED_WEAPONS_LENGTH = 30.0
@@ -216,18 +218,18 @@ void function PhaseRewindLifetime( entity player )
{
PhaseRewindData rewindData
rewindData.origin = player.GetOrigin()
- rewindData.angles = player.GetAngles()
+ rewindData.angles = < 0, player.EyeAngles().y, 0 >
rewindData.velocity = player.GetVelocity()
rewindData.wasInContextAction = player.ContextAction_IsActive()
rewindData.wasCrouched = player.IsCrouched()
- if ( player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions.len() >= PHASE_REWIND_MAX_SNAPSHOTS )
+ if ( player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions.len() >= PHASE_REWIND_DATA_MAX_POSITIONS )
{
// shift all snapshots left
- for ( int i = 0; i < PHASE_REWIND_MAX_SNAPSHOTS - 1; i++ )
+ for ( int i = 0; i < PHASE_REWIND_DATA_MAX_POSITIONS - 1; i++ )
player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions[ i ] = player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions[ i + 1 ]
- player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions[ PHASE_REWIND_MAX_SNAPSHOTS - 1 ] = rewindData
+ player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions[ PHASE_REWIND_DATA_MAX_POSITIONS - 1 ] = rewindData
}
else
player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions.append( rewindData )
@@ -257,7 +259,9 @@ void function UseBurnCardWeapon( entity weapon, entity player )
if ( PlayerEarnMeter_IsRewardAvailable( player ) )
PlayerEarnMeter_SetRewardUsed( player )
- PlayerInventory_PopInventoryItem( player )
+ thread PlayerInventory_PopInventoryItem( player )
+
+ UpdatePlayerStat( player, "misc_stats", "boostsActivated" )
}
void function UseBurnCardWeaponInCriticalSection( entity weapon, entity ownerPlayer )
@@ -287,10 +291,14 @@ void function InitBurnMeterPersistentData( entity player )
void function PlayerUsesAmpedWeaponsBurncard( entity player )
{
thread PlayerUsesAmpedWeaponsBurncardThreaded( player )
+
}
void function PlayerUsesAmpedWeaponsBurncardThreaded( entity player )
{
+ player.Signal( "StopAmpedWeapons" )
+ player.EndSignal("StopAmpedWeapons")
+
array<entity> weapons = player.GetMainWeapons()
//weapons.extend( player.GetOffhandWeapons() ) // idk? unsure of vanilla behaviour here
foreach ( entity weapon in weapons )
@@ -403,6 +411,10 @@ void function PlayerUsesMaphackBurncardThreaded( entity player )
entities.extend( GetPlayerDecoyArray() )
IncrementSonarPerTeam( playerTeam )
+
+ if ( IsAlive( player ) ) // only owner can see the pulse
+ Remote_CallFunction_Replay( player, "ServerCallback_SonarPulseFromPosition", player.GetOrigin().x, player.GetOrigin().y, player.GetOrigin().z, SONAR_GRENADE_RADIUS )
+
foreach ( entity ent in entities )
{
if ( !IsValid( ent ) ) // Not sure why we can get invalid entities at this point
@@ -410,9 +422,6 @@ void function PlayerUsesMaphackBurncardThreaded( entity player )
if ( ent.IsPlayer() )
{
- if ( IsAlive( player ) )
- Remote_CallFunction_Replay( ent, "ServerCallback_SonarPulseFromPosition", player.GetOrigin().x, player.GetOrigin().y, player.GetOrigin().z, SONAR_GRENADE_RADIUS )
-
// Map Hack also gives radar on enemies for longer than the sonar duration.
if ( ent.GetTeam() == playerTeam )
thread ScanMinimap( ent, false, MAPHACK_PULSE_DELAY - 0.2 )
@@ -466,18 +475,65 @@ void function PlayerUsesPhaseRewindBurncardThreaded( entity player )
array<PhaseRewindData> positions = clone player.p.burnCardPhaseRewindStruct.phaseRetreatSavedPositions
+ array<PhaseRewindData> tempArray
+ int segment = positions.len() / PHASE_REWIND_MAX_SNAPSHOTS
+ for( int i = 0; i < PHASE_REWIND_MAX_SNAPSHOTS; i++ )
+ {
+ if( positions.len() <= segment * i )
+ break
+ tempArray.append( positions[ segment * i ] )
+ }
+ positions = tempArray
+
+ vector startOrigin = player.GetOrigin()
ViewConeZero( player )
player.HolsterWeapon()
player.SetPredictionEnabled( false )
PhaseShift( player, 0.0, positions.len() * PHASE_REWIND_PATH_SNAPSHOT_INTERVAL * 1.5 )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "pilot_phaserewind_1p" )
+ EmitSoundOnEntityExceptToPlayer( player, player, "pilot_phaserewind_3p" )
for ( int i = positions.len() - 1; i > -1; i-- )
{
- mover.NonPhysicsMoveTo( positions[ i ].origin, PHASE_REWIND_PATH_SNAPSHOT_INTERVAL, 0, 0 )
- mover.NonPhysicsRotateTo( positions[ i ].angles, PHASE_REWIND_PATH_SNAPSHOT_INTERVAL, 0, 0 )
+ // should looks better?
+ float moveTime = PHASE_REWIND_PATH_SNAPSHOT_INTERVAL + 0.1
+ player.SetVelocity( -positions[ i ].velocity )
+ mover.NonPhysicsMoveTo( positions[ i ].origin, moveTime, 0, 0 )
+ mover.NonPhysicsRotateTo( positions[ i ].angles, moveTime, 0, 0 )
wait PHASE_REWIND_PATH_SNAPSHOT_INTERVAL
}
player.SetVelocity( <0, 0, 0> )
+ if( positions[0].wasCrouched )
+ player.SetOrigin( player.GetOrigin() + < 0, 0, 20 > )
+
+ // fix position after rewind
+ PutPhaseRewindedPlayerInSafeSpot( player, 1 )
+}
+
+void function PutPhaseRewindedPlayerInSafeSpot( entity player, int severity )
+{
+ vector baseOrigin = player.GetOrigin()
+
+ if ( PutEntityInSafeSpot( player, player, null, < baseOrigin.x, baseOrigin.y + severity, baseOrigin.z >, baseOrigin ) )
+ return
+
+ if ( PutEntityInSafeSpot( player, player, null, < baseOrigin.x, baseOrigin.y - severity, baseOrigin.z >, baseOrigin ) )
+ return
+
+ if ( PutEntityInSafeSpot( player, player, null, < baseOrigin.x + severity, baseOrigin.y, baseOrigin.z >, baseOrigin ) )
+ return
+
+ if ( PutEntityInSafeSpot( player, player, null, < baseOrigin.x - severity, baseOrigin.y, baseOrigin.z >, baseOrigin ) )
+ return
+
+ if ( PutEntityInSafeSpot( player, player, null, < baseOrigin.x, baseOrigin.y, baseOrigin.z + severity >, baseOrigin ) )
+ return
+
+ if ( PutEntityInSafeSpot( player, player, null, < baseOrigin.x, baseOrigin.y, baseOrigin.z - severity >, baseOrigin ) )
+ return
+
+ return PutPhaseRewindedPlayerInSafeSpot( player, severity + 5 )
+
}
void function PlayerUsesNukeTitanBurncard( entity player )
@@ -559,4 +615,4 @@ void function PlayerUsesReaperfallBurncard( entity player )
DispatchSpawn( reaper )
thread SuperSpectre_WarpFall( reaper )
-} \ No newline at end of file
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/class/cplayer.nut b/Northstar.CustomServers/mod/scripts/vscripts/class/cplayer.nut
index b9f8f7eb..19c6b6df 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/class/cplayer.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/class/cplayer.nut
@@ -328,6 +328,26 @@ function CodeCallback_RegisterClass_CPlayer()
}
}
}
+
+ function CPlayer::GetUserInfoString( key, defaultValue = "" )
+ {
+ return GetUserInfoKVString_Internal( this, key, defaultValue )
+ }
+
+ function CPlayer::GetUserInfoInt( key, defaultValue = 0 )
+ {
+ return GetUserInfoKVInt_Internal( this, key, defaultValue )
+ }
+
+ function CPlayer::GetUserInfoFloat( key, defaultValue = 0 )
+ {
+ return GetUserInfoKVFloat_Internal( this, key, defaultValue )
+ }
+
+ function CPlayer::GetUserInfoBool( key, defaultValue = false )
+ {
+ return GetUserInfoKVBool_Internal( this, key, defaultValue )
+ }
}
void function PlayerDropsScriptedItems( entity player )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/conversation/_grunt_chatter_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/conversation/_grunt_chatter_mp.gnut
index 1a70c289..4eb423fd 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/conversation/_grunt_chatter_mp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/conversation/_grunt_chatter_mp.gnut
@@ -1,11 +1,33 @@
global function GruntChatter_MP_Init
global function PlayGruntChatterMPLine
+const float CHATTER_FRIENDLY_GRUNT_DOWN_DIST_MAX = 1100.0
+const float CHATTER_SQUAD_DEPLETED_FRIENDLY_NEARBY_DIST = 650.0 // if any other friendly grunt is within this dist, squad deplete chatter won't play
+const float CHATTER_ENEMY_TITAN_DOWN_DIST_MAX = 1500.0
+const float CHATTER_NEARBY_GRUNT_TRACEFRAC_MIN = 0.95 // for when we need "LOS" trace
+
void function GruntChatter_MP_Init()
{
- //ShGruntChatter_MP_Init()
+ Assert( IsMultiplayer(), "MP Grunt chatter is restricted to Multiplayer only." )
+
+ AddCallback_OnPlayerKilled( GruntChatter_OnPlayerOrNPCKilled )
+ AddCallback_OnNPCKilled( GruntChatter_OnPlayerOrNPCKilled )
}
+
+
+
+/*=====================================================================================================================================================
+ _____ _ _____ _ _ _ __ __ _ _ _ _
+ / ____| | | / ____|| | | | | | | \/ | | || | (_) | |
+ | | __ _ __ _ _ _ __ | |_ | | | |__ __ _ | |_ | |_ ___ _ __ | \ / | _ _ | || |_ _ _ __ | | __ _ _ _ ___ _ __
+ | | |_ || '__|| | | || '_ \ | __| | | | '_ \ / _` || __|| __|/ _ \| '__| | |\/| || | | || || __|| || '_ \ | | / _` || | | | / _ \| '__|
+ | |__| || | | |_| || | | || |_ | |____ | | | || (_| || |_ | |_| __/| | | | | || |_| || || |_ | || |_) || || (_| || |_| || __/| |
+ \_____||_| \__,_||_| |_| \__| \_____||_| |_| \__,_| \__| \__|\___||_| |_| |_| \__,_||_| \__||_|| .__/ |_| \__,_| \__, | \___||_|
+ | | __/ |
+ |_| |___/
+/*===================================================================================================================================================*/
+
void function PlayGruntChatterMPLine( entity grunt, string conversationType )
{
#if !GRUNT_CHATTER_MP_ENABLED
@@ -15,4 +37,175 @@ void function PlayGruntChatterMPLine( entity grunt, string conversationType )
foreach ( entity player in GetPlayerArray() )
if ( ShouldPlayGruntChatterMPLine( conversationType, player, grunt ) )
Remote_CallFunction_Replay( player, "ServerCallback_PlayGruntChatterMP", GetConversationIndex( conversationType ), grunt.GetEncodedEHandle() )
+}
+
+void function GruntChatter_OnPlayerOrNPCKilled( entity deadGuy, entity attacker, var damageInfo )
+{
+ if ( !IsValid( deadGuy ) || !IsValid( attacker ) )
+ return
+
+ if( IsGrunt( attacker ) && IsPilot( deadGuy ) )
+ PlayGruntChatterMPLine( attacker, "bc_killenemypilot" )
+ else
+ GruntChatter_TryEnemyTitanDown( deadGuy )
+
+ if ( IsGrunt( deadGuy ) )
+ {
+ GruntChatter_TryFriendlyDown( deadGuy )
+ GruntChatter_TrySquadDepleted( deadGuy )
+ }
+}
+
+void function GruntChatter_TryFriendlyDown( entity deadGuy )
+{
+ entity closestGrunt = GruntChatter_FindClosestFriendlyHumanGrunt_LOS( deadGuy.GetOrigin(), deadGuy.GetTeam(), CHATTER_FRIENDLY_GRUNT_DOWN_DIST_MAX )
+ if ( !closestGrunt )
+ return
+
+ if ( !GruntChatter_CanGruntChatterNow( closestGrunt ) )
+ return
+
+ PlayGruntChatterMPLine( closestGrunt, "bc_allygruntdown" )
+}
+
+void function GruntChatter_TrySquadDepleted( entity deadGuy )
+{
+ string deadGuySquadName = expect string( deadGuy.kv.squadname )
+ if ( deadGuySquadName == "" )
+ return
+
+ array<entity> squad = GetNPCArrayBySquad( deadGuySquadName )
+ entity lastSquadMember
+ if ( squad.len() == 1 )
+ lastSquadMember = squad[0]
+
+ if ( !GruntChatter_CanGruntChatterNow( lastSquadMember ) )
+ return
+
+ if ( lastSquadMember.GetNPCState() == "idle" )
+ return
+
+ // if another grunt from another squad is nearby, don't chatter about being alone
+ array<entity> nearbyGrunts = GetNearbyFriendlyGrunts( lastSquadMember.GetOrigin(), lastSquadMember.GetTeam(), CHATTER_SQUAD_DEPLETED_FRIENDLY_NEARBY_DIST )
+ nearbyGrunts.fastremovebyvalue( lastSquadMember )
+ if ( nearbyGrunts.len() )
+ return
+
+ PlayGruntChatterMPLine( lastSquadMember, "bc_squaddeplete" )
+}
+
+void function GruntChatter_TryEnemyTitanDown( entity deadGuy )
+{
+ if ( deadGuy.IsTitan() )
+ {
+ entity closestGrunt = GruntChatter_FindClosestEnemyHumanGrunt_LOS( deadGuy.GetOrigin(), deadGuy.GetTeam(), CHATTER_ENEMY_TITAN_DOWN_DIST_MAX )
+ if ( !closestGrunt )
+ return
+
+ PlayGruntChatterMPLine( closestGrunt, "bc_enemytitandown" )
+ }
+}
+
+entity function GruntChatter_FindClosestEnemyHumanGrunt_LOS( vector searchOrigin, int enemyTeam, float searchDist )
+{
+ array<entity> humanGrunts = GetNearbyEnemyHumanGrunts( searchOrigin, enemyTeam, searchDist )
+ return GruntChatter_GetClosestGrunt_LOS( humanGrunts, searchOrigin )
+}
+
+entity function GruntChatter_FindClosestFriendlyHumanGrunt_LOS( vector searchOrigin, int friendlyTeam, float searchDist )
+{
+ array<entity> humanGrunts = GetNearbyFriendlyHumanGrunts( searchOrigin, friendlyTeam, searchDist )
+ return GruntChatter_GetClosestGrunt_LOS( humanGrunts, searchOrigin )
+}
+
+entity function GruntChatter_GetClosestGrunt_LOS( array<entity> nearbyGrunts, vector searchOrigin )
+{
+ entity closestGrunt = null
+ float closestDist = 10000
+
+ foreach ( grunt in nearbyGrunts )
+ {
+ vector gruntOrigin = grunt.GetOrigin()
+
+ // CanSee doesn't return true if the target is dead
+ if ( !GruntChatter_CanGruntTraceToLocation( grunt, searchOrigin ) )
+ continue
+
+ if ( !closestGrunt )
+ {
+ closestGrunt = grunt
+ continue
+ }
+
+ float distFromSearchOrigin = Distance( grunt.GetOrigin(), searchOrigin )
+
+ if ( closestDist > distFromSearchOrigin )
+ continue
+
+ closestGrunt = grunt
+ closestDist = distFromSearchOrigin
+ }
+
+ return closestGrunt
+}
+
+bool function GruntChatter_CanGruntTraceToLocation( entity grunt, vector traceEnd )
+{
+ float traceFrac = TraceLineSimple( grunt.GetOrigin(), traceEnd, grunt )
+ return traceFrac > CHATTER_NEARBY_GRUNT_TRACEFRAC_MIN
+}
+
+array<entity> function GetNearbyFriendlyHumanGrunts( vector searchOrigin, int friendlyTeam, float ornull searchRange = null )
+{
+ array<entity> nearbyGrunts = GetNearbyFriendlyGrunts( searchOrigin, friendlyTeam, searchRange )
+ array<entity> humanGrunts = []
+ foreach ( grunt in nearbyGrunts )
+ {
+ if ( grunt.IsMechanical() )
+ continue
+
+ humanGrunts.append( grunt )
+ }
+
+ return humanGrunts
+}
+
+array<entity> function GetNearbyEnemyHumanGrunts( vector searchOrigin, int enemyTeam, float ornull searchRange = null )
+{
+ array<entity> nearbyGrunts = GetNearbyEnemyGrunts( searchOrigin, enemyTeam, searchRange )
+ array<entity> humanGrunts = []
+ foreach ( grunt in nearbyGrunts )
+ {
+ if ( grunt.IsMechanical() )
+ continue
+
+ humanGrunts.append( grunt )
+ }
+
+ return humanGrunts
+}
+
+bool function GruntChatter_CanGruntChatterNow( entity grunt )
+{
+ if ( !IsAlive( grunt ) )
+ return false
+
+ if ( !GruntChatter_IsGruntTypeEligibleForChatter( grunt ) )
+ return false
+
+ if ( grunt.ContextAction_IsMeleeExecution() )
+ return false
+
+ string squadname = expect string( grunt.kv.squadname )
+ // we only care about this because the grunt conversation system wants it
+ return squadname != ""
+}
+
+bool function GruntChatter_IsGruntTypeEligibleForChatter( entity grunt )
+{
+ if ( !IsGrunt( grunt ) )
+ return false
+
+ // mechanical grunts don't chatter
+ return !grunt.IsMechanical()
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/earn_meter/sv_earn_meter.gnut b/Northstar.CustomServers/mod/scripts/vscripts/earn_meter/sv_earn_meter.gnut
index dda84976..b4e77375 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/earn_meter/sv_earn_meter.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/earn_meter/sv_earn_meter.gnut
@@ -315,12 +315,12 @@ void function PlayerEarnMeter_Empty( entity player )
PlayerEarnMeter_SetRewardFrac( player, 0.0 )
}
-
void function EarnMeterDecayThink( entity player )
{
player.EndSignal( "OnDeath" )
player.Signal( "EarnMeterDecayThink" )
player.EndSignal( "EarnMeterDecayThink" )
+ thread OverDriveClearOnDeath( player )
if ( EarnMeter_DecayHold() < 0 )
return
@@ -348,6 +348,12 @@ void function EarnMeterDecayThink( entity player )
}
}
+void function OverDriveClearOnDeath( entity player )
+{
+ player.EndSignal( "OnDestroy" )
+ player.WaitSignal( "OnDeath" )
+ PlayerEarnMeter_SetEarnedFrac( player, PlayerEarnMeter_GetOwnedFrac( player ) )
+}
bool function PlayerEarnMeter_TryMakeGoalAvailable( entity player )
{
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut b/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut
index bce8b4c7..760daef0 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut
@@ -69,11 +69,21 @@ struct {
entity evacIcon
} file
+struct EvacShipSetting
+{
+ asset shipModel
+ string flyinSound
+ string hoverSound
+ string flyoutSound
+}
+
void function Evac_Init()
{
EvacShared_Init()
RegisterSignal( "EvacShipLeaves" )
RegisterSignal( "EvacOver" )
+
+ PrecacheParticleSystem( FX_EVAC_MARKER )
}
void function AddEvacNode( entity evacNode )
@@ -100,7 +110,7 @@ void function EvacEpilogueSetup()
void function EvacEpilogue()
{
- int winner = GetWinningTeam()
+ int winner = GetWinningTeam()
// make sure we don't run evac if it won't be supported for current map/gamestate
bool canRunEvac = GetCurrentPlaylistVarInt( "max_teams", 2 ) == 2 &&
@@ -110,6 +120,10 @@ void function EvacEpilogue()
if ( canRunEvac )
{
thread SetRespawnAndWait( false )
+
+ // no players can evac? end match
+ thread CheckIfAnyPlayerLeft( GetOtherTeam( winner ) )
+
thread Evac( GetOtherTeam( winner ), EVAC_INITIAL_WAIT, EVAC_ARRIVAL_TIME, EVAC_WAIT_TIME, EvacEpiloguePlayerCanBoard, EvacEpilogueShouldLeaveEarly, EvacEpilogueCompleted )
}
else
@@ -139,6 +153,10 @@ void function SetRespawnAndWait( bool mode )
{
wait GAME_EPILOGUE_PLAYER_RESPAWN_LEEWAY
SetRespawnsEnabled( mode )
+
+ // clear any respawn availablity, or players are able to save there respawn for whenever they want
+ foreach( entity player in GetPlayerArray() )
+ ClearRespawnAvailable( player )
}
bool function EvacEpiloguePlayerCanBoard( entity dropship, entity player )
@@ -174,12 +192,16 @@ void function EvacEpilogueCompleted( entity dropship )
ScreenFadeToBlackForever( player, 2.0 )
wait 2.0
- SetGameState( eGameState.Postmatch )
+ if( GetGameState() != eGameState.Postmatch )
+ SetGameState( eGameState.Postmatch )
}
// global evac func, anything can call this, it's not necessarily an epilogue thing
void function Evac( int evacTeam, float initialWait, float arrivalTime, float waitTime, bool functionref( entity, entity ) canBoardCallback, bool functionref( entity ) shouldLeaveEarlyCallback, void functionref( entity ) completionCallback, entity customEvacNode = null )
{
+ // get evac ship sound and model for specific team
+ EvacShipSetting evacShip = GetEvacShipSettingByTeam( evacTeam )
+
wait initialWait
// setup evac nodes if not manually registered
@@ -212,23 +234,39 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
DispatchSpawn( file.evacIcon )
file.evacIcon.DisableHibernation()
+ // start evac beam
+ int index = GetParticleSystemIndex( FX_EVAC_MARKER )
+
+ entity effectFriendly = StartParticleEffectInWorld_ReturnEntity( index, evacNode.GetOrigin(), < 0,0,0 > )
+ SetTeam( effectFriendly, evacTeam )
+ effectFriendly.kv.VisibilityFlags = ENTITY_VISIBLE_TO_FRIENDLY
+
wait 0.5 // need to wait here, or the target won't appear on clients for some reason
// eta until arrive
SetTeamActiveObjective( evacTeam, "EG_DropshipExtract", Time() + arrivalTime, file.evacIcon )
SetTeamActiveObjective( GetOtherTeam( evacTeam ), "EG_StopExtract", Time() + arrivalTime, file.evacIcon )
+ foreach( entity player in GetPlayerArrayOfTeam( evacTeam ) ) //Show the Evac Match Goal for players of the team that lost the match
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 0 )
+
// would've liked to use cd_dropship_rescue_side_start length here, but can't since this is done before dropship spawn, can't
wait arrivalTime - 4.33333
entity dropship = CreateDropship( evacTeam, evacNode.GetOrigin(), evacNode.GetAngles() )
- dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
- dropship.SetValueForModelKey( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
+
+ thread DropShipTempHide( dropship ) // prevent showing model and health bar on spawn
+ dropship.SetModel( evacShip.shipModel )
+ dropship.SetValueForModelKey( evacShip.shipModel )
+
dropship.SetMaxHealth( EVAC_SHIP_HEALTH )
dropship.SetHealth( EVAC_SHIP_HEALTH )
dropship.SetShieldHealth( EVAC_SHIP_SHIELDS )
SetTargetName( dropship, "#NPC_EVAC_DROPSHIP" )
DispatchSpawn( dropship )
- dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
+
+ // reduce nuclear core's damage
+ AddEntityCallback_OnDamaged( dropship, EvacDropshipDamaged )
+
AddEntityCallback_OnKilled( dropship, EvacDropshipKilled )
dropship.s.evacSlots <- [ null, null, null, null, null, null, null, null ]
@@ -241,9 +279,15 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
dropship.s.evacTrigger.Destroy()
// this should be for both teams
- SetTeamActiveObjective( evacTeam, "EG_DropshipExtractDropshipDestroyed" )
- SetTeamActiveObjective( GetOtherTeam( evacTeam ), "EG_DropshipExtractDropshipDestroyed" )
-
+ if( !IsValid( dropship ) )
+ {
+ SetTeamActiveObjective( evacTeam, "EG_DropshipExtractDropshipDestroyed" )
+ SetTeamActiveObjective( GetOtherTeam( evacTeam ), "EG_DropshipExtractDropshipDestroyed" )
+
+ foreach( entity player in GetPlayerArrayOfTeam( evacTeam ) )
+ SetPlayerChallengeEvacState( player, 0 )
+ }
+
foreach ( entity player in dropship.s.evacSlots )
{
if ( !IsValid( player ) )
@@ -255,10 +299,14 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
// this is called whether dropship is destroyed or evac finishes, callback can handle this itself
thread completionCallback( dropship )
})
-
+
// flyin
Spectator_SetCustomSpectatorFunc( EvacSpectatorFunc )
thread PlayAnim( dropship, "cd_dropship_rescue_side_start", evacNode )
+
+ // fly in sound and effect
+ EmitSoundOnEntity( dropship, evacShip.flyinSound )
+ thread WarpInEffectEvacShip( dropship )
// calculate time until idle start
float sequenceDuration = dropship.GetSequenceDuration( "cd_dropship_rescue_side_start" )
@@ -266,11 +314,22 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
wait sequenceDuration * cycleFrac
thread PlayAnim( dropship, "cd_dropship_rescue_side_idle", evacNode )
+
+ // hover sound
+ EmitSoundOnEntity( dropship, evacShip.hoverSound )
// eta until leave
- SetTeamActiveObjective( evacTeam, "EG_DropshipExtract2", Time() + EVAC_WAIT_TIME, file.evacIcon )
- SetTeamActiveObjective( GetOtherTeam( evacTeam ), "EG_StopExtract2", Time() + EVAC_WAIT_TIME, file.evacIcon )
+ SetTeamActiveObjective( evacTeam, "EG_DropshipExtract2", Time() + waitTime, file.evacIcon )
+ SetTeamActiveObjective( GetOtherTeam( evacTeam ), "EG_StopExtract2", Time() + waitTime, file.evacIcon )
+ // dialogue
+ PlayFactionDialogueToTeam( "mp_evacGo", evacTeam )
+ PlayFactionDialogueToTeam( "mp_evacStop", GetOtherTeam( evacTeam ) )
+
+ // stop evac beam
+ if( IsValid( effectFriendly ) )
+ EffectStop( effectFriendly )
+
// setup evac trigger
entity trigger = CreateEntity( "trigger_cylinder" )
trigger.SetRadius( 150 )
@@ -298,6 +357,10 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
WaitFrame()
}
+
+ // fly out sound
+ StopSoundOnEntity( dropship, evacShip.hoverSound )
+ EmitSoundOnEntity( dropship, evacShip.flyoutSound )
// holster all weapons
foreach ( entity player in dropship.s.evacSlots )
@@ -320,6 +383,12 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
Remote_CallFunction_NonReplay( player, "ServerCallback_PlayScreenFXWarpJump" )
wait WARPINFXTIME
+
+ dropship.kv.VisibilityFlags = 0 // prevent jetpack trails being like "dive" into ground
+ WaitFrame() // better wait because we are server
+ if( !IsValid( dropship ) )
+ return
+ thread __WarpOutEffectShared( dropship )
// go to space
@@ -339,24 +408,64 @@ void function Evac( int evacTeam, float initialWait, float arrivalTime, float wa
if ( !PlayerInDropship( player, dropship ) )
{
if ( player.GetTeam() == dropship.GetTeam() )
+ {
SetPlayerActiveObjective( player, "EG_DropshipExtractFailedEscape" )
+ SetPlayerChallengeEvacState( player, 2 )
+ }
continue
}
SetPlayerActiveObjective( player, "EG_DropshipExtractSuccessfulEscape" )
+
+ // let evacing team able to see the ship again
+ dropship.kv.VisibilityFlags = ENTITY_VISIBLE_TO_FRIENDLY
// skybox
player.SetSkyCamera( GetEnt( SKYBOXSPACE ) )
Remote_CallFunction_NonReplay( player, "ServerCallback_DisableHudForEvac" )
Remote_CallFunction_NonReplay( player, "ServerCallback_SetClassicSkyScale", dropship.GetEncodedEHandle(), 0.7 )
Remote_CallFunction_NonReplay( player, "ServerCallback_SetMapSettings", 4.0, false, 0.4, 0.125 )
+ SetPlayerChallengeEvacState( player, 1 )
// display player [Evacuated] in killfeed
foreach ( entity otherPlayer in GetPlayerArray() )
Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_EvacObit", player.GetEncodedEHandle() )
}
-
+
+ // award player score to evacing team
+ int evacCount = 0
+ array<entity> evacingPlayers = GetPlayerArrayOfTeam( dropship.GetTeam() ) // all players that are supposed to evac in the dropship
+
+ // count how many players are in the dropship
+ foreach ( entity player in evacingPlayers )
+ {
+ if ( !PlayerInDropship( player, dropship ) )
+ continue
+
+ evacCount++
+ }
+
+ bool allEvac = evacCount == evacingPlayers.len()
+
+ foreach(entity player in evacingPlayers)
+ {
+ if ( !PlayerInDropship( player, dropship ) )
+ continue
+
+ AddPlayerScore( player, "HotZoneExtract" )
+ UpdatePlayerStat( player, "misc_stats", "evacsSurvived" )
+
+ if ( allEvac )
+ AddPlayerScore( player, "TeamBonusFullEvac" )
+ }
+
+ // sole survivor (but not the only one on the team)
+ if ( evacCount == 1 && !allEvac )
+ {
+ // we can assume there is one player in the array because otherwise evacCount wouldn't be 1
+ AddPlayerScore( evacingPlayers[0], "SoleSurvivor" )
+ }
}
void function AddPlayerToEvacDropship( entity dropship, entity player )
@@ -376,7 +485,12 @@ void function AddPlayerToEvacDropship( entity dropship, entity player )
// no slots available
if ( !PlayerInDropship( player, dropship ) )
return
-
+
+ UpdatePlayerStat( player, "misc_stats", "evacsAttempted" )
+
+ // need to cancel if the dropship dies
+ dropship.EndSignal( "OnDeath", "OnDestroy" )
+
player.SetInvulnerable()
player.UnforceCrouch()
player.ForceStand()
@@ -391,7 +505,7 @@ void function AddPlayerToEvacDropship( entity dropship, entity player )
EmitSoundOnEntityOnlyToPlayer( player, player, SHIFTER_START_SOUND_3P )
// should play SHIFTER_START_SOUND_1P when they actually arrive in the ship i think, unsure how this is supposed to be done
PlayPhaseShiftDisappearFX( player )
- waitthread FirstPersonSequence( fp, player, dropship )
+ FirstPersonSequence( fp, player, dropship )
FirstPersonSequenceStruct idleFp
idleFp.firstPersonAnimIdle = EVAC_IDLE_ANIMS_1P[ slot ]
@@ -426,3 +540,97 @@ void function EvacDropshipKilled( entity dropship, var damageInfo )
}
}
}
+
+// if there's no player left in evacing team, we end this match
+void function CheckIfAnyPlayerLeft( int evacTeam )
+{
+ wait GAME_EPILOGUE_PLAYER_RESPAWN_LEEWAY
+ float startTime = Time()
+
+ OnThreadEnd(
+ function() : ( evacTeam )
+ {
+ SetTeamActiveObjective( evacTeam, "EG_DropshipExtractEvacPlayersKilled" )
+ SetTeamActiveObjective( GetOtherTeam( evacTeam ), "EG_StopExtractEvacPlayersKilled" )
+ thread EvacEpilogueCompleted( null )
+
+ // score for killing the entire evacing team
+ foreach ( entity player in GetPlayerArray() )
+ {
+ if ( player.GetTeam() == evacTeam )
+ continue
+
+ AddPlayerScore( player, "TeamBonusKilledAll")
+ }
+ }
+ )
+ while( true )
+ {
+ if( GetPlayerArrayOfTeam_Alive( evacTeam ).len() == 0 )
+ break
+ if( GetGameState() == eGameState.Postmatch )
+ return
+ WaitFrame()
+ }
+}
+
+void function DropShipTempHide( entity dropship )
+{
+ dropship.kv.VisibilityFlags = 0 // or it will still shows the jetpack fxs
+ HideName( dropship )
+ wait 0.46
+ if( IsValid( dropship ) )
+ {
+ dropship.kv.VisibilityFlags = ENTITY_VISIBLE_TO_EVERYONE
+ ShowName( dropship )
+ }
+}
+
+EvacShipSetting function GetEvacShipSettingByTeam( int team )
+{
+ EvacShipSetting tempSetting
+ tempSetting.shipModel = $"models/vehicle/goblin_dropship/goblin_dropship_hero.mdl"
+ tempSetting.flyinSound = "Goblin_IMC_Evac_Flyin"
+ tempSetting.hoverSound = "Goblin_IMC_Evac_Hover"
+ tempSetting.flyoutSound = "Goblin_IMC_Evac_FlyOut"
+
+ if( team == TEAM_MILITIA )
+ {
+ tempSetting.shipModel = $"models/vehicle/crow_dropship/crow_dropship_hero.mdl"
+ tempSetting.flyinSound = "Crow_MCOR_Evac_Flyin"
+ tempSetting.hoverSound = "Crow_MCOR_Evac_Hover"
+ tempSetting.flyoutSound = "Crow_MCOR_Evac_Flyout"
+ }
+ return tempSetting
+}
+
+void function EvacDropshipDamaged( entity evacShip, var damageInfo )
+{
+ int damageSourceID = DamageInfo_GetDamageSourceIdentifier( damageInfo )
+ if( damageSourceID == damagedef_nuclear_core )
+ DamageInfo_SetDamage( damageInfo, DamageInfo_GetDamage( damageInfo ) * EVAC_SHIP_DAMAGE_MULTIPLIER_AGAINST_NUCLEAR_CORE )
+}
+
+void function WarpInEffectEvacShip( entity dropship )
+{
+ dropship.EndSignal( "OnDestroy" )
+ float sfxWait = 0.1
+ float totalTime = WARPINFXTIME
+ float preWaitTime = 0.16 // give it some time so it's actually playing anim, and we can get it's "origin" attatch for playing warp in effect
+ string sfx = "dropship_warpin"
+
+ wait preWaitTime
+
+ int attach = dropship.LookupAttachment( "origin" )
+ vector origin = dropship.GetAttachmentOrigin( attach )
+ vector angles = dropship.GetAttachmentAngles( attach )
+
+ entity fx = PlayFX( FX_GUNSHIP_CRASH_EXPLOSION_ENTRANCE, origin, angles )
+ fx.FXEnableRenderAlways()
+ fx.DisableHibernation()
+
+ wait sfxWait
+ EmitSoundAtPosition( TEAM_UNASSIGNED, origin, sfx )
+
+ wait totalTime - sfxWait
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut
index 5fd7d101..5aee1104 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/faction_xp.gnut
@@ -3,8 +3,20 @@ global function AddFactionXP
void function AddFactionXP( entity player, int amount )
{
string faction = GetFactionChoice( player )
+ int oldLevel = FactionGetLevel( player, faction )
+ int FactionXPMatch = player.GetPersistentVarAsInt( "xp_match[" + XP_TYPE.FACTION_LEVELED + "]" )
+
// increment xp
player.SetPersistentVar( "factionXP[" + faction + "]", min( FactionGetXP( player, faction ) + amount, FactionGetMaxXP( faction ) ) )
// note: no notif for faction level up
+ if ( FactionGetLevel( player, faction ) != oldLevel )
+ {
+ AddPlayerScore( player, "FactionLevelUp" )
+ IncrementPlayerChallengeFactionLeveledUp( player )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.FACTION_LEVELED + "]", FactionXPMatch + 1 )
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForFactionLevels( player, faction, oldLevel, FactionGetLevel( player, faction ) )
+ }
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut
index d6d578bb..4ed7ee4a 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut
@@ -1,7 +1,6 @@
global function AiGameModes_Init
-global function AiGameModes_SetGruntWeapons
-global function AiGameModes_SetSpectreWeapons
+global function AiGameModes_SetNPCWeapons
global function AiGameModes_SpawnDropShip
global function AiGameModes_SpawnDropPod
@@ -15,25 +14,20 @@ const INTRO_DROPSHIP_CUTOFF = 2000
struct
{
- array< string > gruntWeapons = [ "mp_weapon_rspn101" ]
- array< string > spectreWeapons = [ "mp_weapon_hemlok_smg" ]
+ table< string, array<string> > npcWeaponsTable // npcs have their default weapon in aisettings file
} file
void function AiGameModes_Init()
{
-
}
//------------------------------------------------------
-void function AiGameModes_SetGruntWeapons( array< string > weapons )
-{
- file.gruntWeapons = weapons
-}
-
-void function AiGameModes_SetSpectreWeapons( array< string > weapons )
+void function AiGameModes_SetNPCWeapons( string npcClass, array<string> weapons )
{
- file.spectreWeapons = weapons
+ if ( !( npcClass in file.npcWeaponsTable ) )
+ file.npcWeaponsTable[ npcClass ] <- []
+ file.npcWeaponsTable[ npcClass ] = weapons
}
//------------------------------------------------------
@@ -59,7 +53,7 @@ void function AiGameModes_SpawnDropShip( vector pos, vector rot, int team, int c
foreach ( guy in guys )
{
- ReplaceWeapon( guy, file.gruntWeapons[ RandomInt( file.gruntWeapons.len() ) ], [] )
+ SetUpNPCWeapons( guy )
guy.EnableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE | NPC_ALLOW_HAND_SIGNALS | NPC_ALLOW_FLEE )
}
@@ -68,31 +62,23 @@ void function AiGameModes_SpawnDropShip( vector pos, vector rot, int team, int c
}
-void function AiGameModes_SpawnDropPod( vector pos, vector rot, int team, string content /*( ͡° ͜ʖ ͡°)*/, void functionref( array<entity> guys ) squadHandler = null )
+void function AiGameModes_SpawnDropPod( vector pos, vector rot, int team, string content /*( ͡° ͜ʖ ͡°)*/, void functionref( array<entity> guys ) squadHandler = null, int flags = 0 )
{
- string squadName = MakeSquadName( team, UniqueString( "" ) )
- array<entity> guys
-
entity pod = CreateDropPod( pos, <0,0,0> )
- InitFireteamDropPod( pod )
-
+ InitFireteamDropPod( pod, flags )
+
+ waitthread LaunchAnimDropPod( pod, "pod_testpath", pos, rot )
+
+ string squadName = MakeSquadName( team, UniqueString( "" ) )
+ array<entity> guys
for ( int i = 0; i < 4 ;i++ )
{
entity npc = CreateNPC( content, team, pos,<0,0,0> )
DispatchSpawn( npc )
SetSquad( npc, squadName )
- switch ( content )
- {
- case "npc_soldier":
- ReplaceWeapon( npc, file.gruntWeapons[ RandomInt( file.gruntWeapons.len() ) ], [] )
- break
-
- case "npc_spectre":
- ReplaceWeapon( npc, file.spectreWeapons[ RandomInt( file.spectreWeapons.len() ) ], [] )
- break
- }
+ SetUpNPCWeapons( npc )
npc.SetParent( pod, "ATTACH", true )
@@ -100,37 +86,100 @@ void function AiGameModes_SpawnDropPod( vector pos, vector rot, int team, string
guys.append( npc )
}
- // The order here is different so we can show on minimap while were still falling
+ ActivateFireteamDropPod( pod, guys )
+
+ // start searching for enemies
if ( squadHandler != null )
thread squadHandler( guys )
-
- waitthread LaunchAnimDropPod( pod, "pod_testpath", pos, rot )
-
- ActivateFireteamDropPod( pod, guys )
}
+const float REAPER_WARPFALL_DELAY = 4.7 // same as fd does
void function AiGameModes_SpawnReaper( vector pos, vector rot, int team, string aiSettings = "", void functionref( entity reaper ) reaperHandler = null )
{
+ float reaperLandTime = REAPER_WARPFALL_DELAY + 1.2 // reaper takes ~1.2s to warpfall
+ thread HotDrop_Spawnpoint( pos, team, reaperLandTime, false, damagedef_reaper_fall )
+
+ wait REAPER_WARPFALL_DELAY
entity reaper = CreateSuperSpectre( team, pos, rot )
+ reaper.EndSignal( "OnDestroy" )
+ // reaper highlight
+ Highlight_SetFriendlyHighlight( reaper, "sp_enemy_pilot" )
+ reaper.Highlight_SetParam( 1, 0, < 1,1,1 > )
+ SetDefaultMPEnemyHighlight( reaper )
+ Highlight_SetEnemyHighlight( reaper, "enemy_titan" )
+
SetSpawnOption_Titanfall( reaper )
SetSpawnOption_Warpfall( reaper )
if ( aiSettings != "" )
SetSpawnOption_AISettings( reaper, aiSettings )
+ HideName( reaper ) // prevent flash a name onto it
DispatchSpawn( reaper )
-
+
+ reaper.WaitSignal( "WarpfallComplete" )
+ ShowName( reaper ) // show name again after drop
if ( reaperHandler != null )
thread reaperHandler( reaper )
}
+// copied from cl_replacement_titan_hud.gnut
+void function HotDrop_Spawnpoint( vector origin, int team, float impactTime, bool hasFriendlyWarning = false, int damageDef = -1 )
+{
+ array<entity> targetEffects = []
+ vector surfaceNormal = < 0, 0, 1 >
+
+ int index = GetParticleSystemIndex( $"P_ar_titan_droppoint" )
+
+ if( hasFriendlyWarning )
+ {
+ entity effectFriendly = StartParticleEffectInWorld_ReturnEntity( index, origin, surfaceNormal )
+ SetTeam( effectFriendly, team )
+ EffectSetControlPointVector( effectFriendly, 1, FRIENDLY_COLOR_FX )
+ effectFriendly.kv.VisibilityFlags = ENTITY_VISIBLE_TO_FRIENDLY
+ effectFriendly.DisableHibernation() // prevent it from fading out
+ targetEffects.append( effectFriendly )
+ }
+
+ entity effectEnemy = StartParticleEffectInWorld_ReturnEntity( index, origin, surfaceNormal )
+ SetTeam( effectEnemy, team )
+ EffectSetControlPointVector( effectEnemy, 1, ENEMY_COLOR_FX )
+ effectEnemy.kv.VisibilityFlags = ENTITY_VISIBLE_TO_ENEMY
+ effectEnemy.DisableHibernation() // prevent it from fading out
+ targetEffects.append( effectEnemy )
+
+ // so enemy npcs will mostly avoid them
+ entity damageAreaInfo
+ if ( damageDef > -1 )
+ {
+ damageAreaInfo = CreateEntity( "info_target" )
+ DispatchSpawn( damageAreaInfo )
+ AI_CreateDangerousArea_DamageDef( damageDef, damageAreaInfo, team, true, true )
+ }
+
+ wait impactTime
+
+ // clean up
+ foreach( entity targetEffect in targetEffects )
+ {
+ if ( IsValid( targetEffect ) )
+ EffectStop( targetEffect )
+ }
+ if ( IsValid( damageAreaInfo ) )
+ damageAreaInfo.Destroy()
+}
+
// including aisettings stuff specifically for at bounty titans
+const float TITANFALL_WARNING_DURATION = 5.0
void function AiGameModes_SpawnTitan( vector pos, vector rot, int team, string setFile, string aiSettings = "", void functionref( entity titan ) titanHandler = null )
{
entity titan = CreateNPCTitan( setFile, TEAM_BOTH, pos, rot )
SetSpawnOption_Titanfall( titan )
SetSpawnOption_Warpfall( titan )
+
+ // modified: do a hotdrop spawnpoint warning
+ thread HotDrop_Spawnpoint( pos, team, TITANFALL_WARNING_DURATION, false, damagedef_titan_fall )
if ( aiSettings != "" )
SetSpawnOption_AISettings( titan, aiSettings )
@@ -141,12 +190,27 @@ void function AiGameModes_SpawnTitan( vector pos, vector rot, int team, string s
thread titanHandler( titan )
}
-// entity.ReplaceActiveWeapon gave grunts archers sometimes, this is my replacement for it
-void function ReplaceWeapon( entity guy, string weapon, array<string> mods )
+void function SetUpNPCWeapons( entity guy )
{
- guy.TakeActiveWeapon()
- guy.GiveWeapon( weapon, mods )
- guy.SetActiveWeaponByName( weapon )
+ string className = guy.GetClassName()
+
+ array<string> mainWeapons
+ if ( className in file.npcWeaponsTable )
+ mainWeapons = file.npcWeaponsTable[ className ]
+
+ if ( mainWeapons.len() == 0 ) // no valid weapons
+ return
+
+ // take off existing main weapons, or sometimes they'll have a archer by default
+ foreach ( entity weapon in guy.GetMainWeapons() )
+ guy.TakeWeapon( weapon.GetWeaponClassName() )
+
+ if ( mainWeapons.len() > 0 )
+ {
+ string weaponName = mainWeapons[ RandomInt( mainWeapons.len() ) ]
+ guy.GiveWeapon( weaponName )
+ guy.SetActiveWeaponByName( weaponName )
+ }
}
// Checks if we can spawn a dropship at a node, this should guarantee dropship ziplines
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
index 9a94b848..7d73c926 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
@@ -1,22 +1,36 @@
untyped
global function GamemodeAITdm_Init
-const SQUADS_PER_TEAM = 3
+// these are now default settings
+const int SQUADS_PER_TEAM = 4
-const REAPERS_PER_TEAM = 2
+const int REAPERS_PER_TEAM = 2
-const LEVEL_SPECTRES = 125
-const LEVEL_STALKERS = 380
-const LEVEL_REAPERS = 500
+const int LEVEL_SPECTRES = 125
+const int LEVEL_STALKERS = 380
+const int LEVEL_REAPERS = 500
+
+// add settings
+global function AITdm_SetSquadsPerTeam
+global function AITdm_SetReapersPerTeam
+global function AITdm_SetLevelSpectres
+global function AITdm_SetLevelStalkers
+global function AITdm_SetLevelReapers
struct
{
// Due to team based escalation everything is an array
- array< int > levels = [ LEVEL_SPECTRES, LEVEL_SPECTRES ]
+ array< int > levels = [] // Initilazed in `Spawner_Threaded`
array< array< string > > podEntities = [ [ "npc_soldier" ], [ "npc_soldier" ] ]
array< bool > reapers = [ false, false ]
-} file
+ // default settings
+ int squadsPerTeam = SQUADS_PER_TEAM
+ int reapersPerTeam = REAPERS_PER_TEAM
+ int levelSpectres = LEVEL_SPECTRES
+ int levelStalkers = LEVEL_STALKERS
+ int levelReapers = LEVEL_REAPERS
+} file
void function GamemodeAITdm_Init()
{
@@ -34,18 +48,48 @@ void function GamemodeAITdm_Init()
if ( GetCurrentPlaylistVarInt( "aitdm_archer_grunts", 0 ) == 0 )
{
- AiGameModes_SetGruntWeapons( [ "mp_weapon_rspn101", "mp_weapon_dmr", "mp_weapon_r97", "mp_weapon_lmg" ] )
- AiGameModes_SetSpectreWeapons( [ "mp_weapon_hemlok_smg", "mp_weapon_doubletake", "mp_weapon_mastiff" ] )
+ AiGameModes_SetNPCWeapons( "npc_soldier", [ "mp_weapon_rspn101", "mp_weapon_dmr", "mp_weapon_r97", "mp_weapon_lmg" ] )
+ AiGameModes_SetNPCWeapons( "npc_spectre", [ "mp_weapon_hemlok_smg", "mp_weapon_doubletake", "mp_weapon_mastiff" ] )
+ AiGameModes_SetNPCWeapons( "npc_stalker", [ "mp_weapon_hemlok_smg", "mp_weapon_lstar", "mp_weapon_mastiff" ] )
}
else
{
- AiGameModes_SetGruntWeapons( [ "mp_weapon_rocket_launcher" ] )
- AiGameModes_SetSpectreWeapons( [ "mp_weapon_rocket_launcher" ] )
+ AiGameModes_SetNPCWeapons( "npc_soldier", [ "mp_weapon_rocket_launcher" ] )
+ AiGameModes_SetNPCWeapons( "npc_spectre", [ "mp_weapon_rocket_launcher" ] )
+ AiGameModes_SetNPCWeapons( "npc_stalker", [ "mp_weapon_rocket_launcher" ] )
}
ScoreEvent_SetupEarnMeterValuesForMixedModes()
+ SetupGenericTDMChallenge()
}
+// add settings
+void function AITdm_SetSquadsPerTeam( int squads )
+{
+ file.squadsPerTeam = squads
+}
+
+void function AITdm_SetReapersPerTeam( int reapers )
+{
+ file.reapersPerTeam = reapers
+}
+
+void function AITdm_SetLevelSpectres( int level )
+{
+ file.levelSpectres = level
+}
+
+void function AITdm_SetLevelStalkers( int level )
+{
+ file.levelStalkers = level
+}
+
+void function AITdm_SetLevelReapers( int level )
+{
+ file.levelReapers = level
+}
+//
+
// Starts skyshow, this also requiers AINs but doesn't crash if they're missing
void function OnPrematchStart()
{
@@ -74,10 +118,12 @@ void function HandleScoreEvent( entity victim, entity attacker, var damageInfo )
// Basic checks
if ( victim == attacker || !( attacker.IsPlayer() || attacker.IsTitan() ) || GetGameState() != eGameState.Playing )
return
-
// Hacked spectre filter
if ( victim.GetOwner() == attacker )
return
+ // NPC titans without an owner player will not count towards any team's score
+ if ( attacker.IsNPC() && attacker.IsTitan() && !IsValid( GetPetTitanOwner( attacker ) ) )
+ return
// Split score so we can check if we are over the score max
// without showing the wrong value on client
@@ -189,7 +235,7 @@ void function SpawnIntroBatch_Threaded( int team )
int ships = shipNodes.len()
- for ( int i = 0; i < SQUADS_PER_TEAM; i++ )
+ for ( int i = 0; i < file.squadsPerTeam; i++ )
{
if ( pods != 0 || ships == 0 )
{
@@ -234,6 +280,7 @@ void function Spawner_Threaded( int team )
// used to index into escalation arrays
int index = team == TEAM_MILITIA ? 0 : 1
+ file.levels = [ file.levelSpectres, file.levelSpectres ] // due we added settings, should init levels here!
while( true )
{
@@ -248,7 +295,7 @@ void function Spawner_Threaded( int team )
if ( file.reapers[ index ] )
{
array< entity > points = SpawnPoints_GetDropPod()
- if ( reaperCount < REAPERS_PER_TEAM )
+ if ( reaperCount < file.reapersPerTeam )
{
entity node = points[ GetSpawnPointIndex( points, team ) ]
waitthread AiGameModes_SpawnReaper( node.GetOrigin(), node.GetAngles(), team, "npc_super_spectre_aitdm", ReaperHandler )
@@ -256,7 +303,7 @@ void function Spawner_Threaded( int team )
}
// NORMAL SPAWNS
- if ( count < SQUADS_PER_TEAM * 4 - 2 )
+ if ( count < file.squadsPerTeam * 4 - 2 )
{
string ent = file.podEntities[ index ][ RandomInt( file.podEntities[ index ].len() ) ]
@@ -302,19 +349,19 @@ void function Escalate( int team )
// Based on score escalate a team
switch ( file.levels[ index ] )
{
- case LEVEL_SPECTRES:
- file.levels[ index ] = LEVEL_STALKERS
+ case file.levelSpectres:
+ file.levels[ index ] = file.levelStalkers
file.podEntities[ index ].append( "npc_spectre" )
SetGlobalNetInt( defcon, 2 )
return
- case LEVEL_STALKERS:
- file.levels[ index ] = LEVEL_REAPERS
+ case file.levelStalkers:
+ file.levels[ index ] = file.levelReapers
file.podEntities[ index ].append( "npc_stalker" )
SetGlobalNetInt( defcon, 3 )
return
- case LEVEL_REAPERS:
+ case file.levelReapers:
file.reapers[ index ] = true
SetGlobalNetInt( defcon, 4 )
return
@@ -351,30 +398,47 @@ int function GetSpawnPointIndex( array< entity > points, int team )
// AI can also flee deeper into their zone suggesting someone spent way too much time on this
void function SquadHandler( array<entity> guys )
{
+ int team = guys[0].GetTeam()
+ // show the squad enemy radar
+ array<entity> players = GetPlayerArrayOfEnemies( team )
+ foreach ( entity guy in guys )
+ {
+ if ( IsAlive( guy ) )
+ {
+ foreach ( player in players )
+ guy.Minimap_AlwaysShow( 0, player )
+ }
+ }
+
// Not all maps have assaultpoints / have weird assault points ( looking at you ac )
// So we use enemies with a large radius
- array< entity > points = GetNPCArrayOfEnemies( guys[0].GetTeam() )
-
- if ( points.len() == 0 )
+ while ( GetNPCArrayOfEnemies( team ).len() == 0 ) // if we can't find any enemy npcs, keep waiting
+ WaitFrame()
+
+ // our waiting is end, check if any soldiers left
+ bool squadAlive = false
+ foreach ( entity guy in guys )
+ {
+ if ( IsAlive( guy ) )
+ squadAlive = true
+ else
+ guys.removebyvalue( guy )
+ }
+ if ( !squadAlive )
return
+
+ array<entity> points = GetNPCArrayOfEnemies( team )
vector point
point = points[ RandomInt( points.len() ) ].GetOrigin()
- array<entity> players = GetPlayerArrayOfEnemies( guys[0].GetTeam() )
-
- // Setup AI
+ // Setup AI, first assault point
foreach ( guy in guys )
{
guy.EnableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE | NPC_ALLOW_HAND_SIGNALS | NPC_ALLOW_FLEE )
guy.AssaultPoint( point )
guy.AssaultSetGoalRadius( 1600 ) // 1600 is minimum for npc_stalker, works fine for others
-
- // show on enemy radar
- foreach ( player in players )
- guy.Minimap_AlwaysShow( 0, player )
-
-
+
//thread AITdm_CleanupBoredNPCThread( guy )
}
@@ -392,16 +456,32 @@ void function SquadHandler( array<entity> guys )
// Stop func if our squad has been killed off
if ( guys.len() == 0 )
return
+ }
+
+ // Get point and send our whole squad to it
+ points = GetNPCArrayOfEnemies( team )
+ if ( points.len() == 0 ) // can't find any points here
+ {
+ // Have to wait some amount of time before continuing
+ // because if we don't the server will continue checking this
+ // forever, aren't loops fun?
+ // This definitely didn't waste ~8 hours of my time reverting various
+ // launcher PRs before finding this mods PR that caused servers to
+ // freeze forever before having their process killed by the dedi watchdog
+ // without any logging. If anyone reads this, PLEASE add logging to your scripts
+ // for when weird edge cases happen, it can literally only help debugging. -Spoon
+ WaitFrame()
+ continue
+ }
- // Get point and send guy to it
- points = GetNPCArrayOfEnemies( guy.GetTeam() )
- if ( points.len() == 0 )
- continue
-
- point = points[ RandomInt( points.len() ) ].GetOrigin()
-
- guy.AssaultPoint( point )
+ point = points[ RandomInt( points.len() ) ].GetOrigin()
+
+ foreach ( guy in guys )
+ {
+ if ( IsAlive( guy ) )
+ guy.AssaultPoint( point )
}
+
wait RandomFloatRange(5.0,15.0)
}
}
@@ -507,4 +587,4 @@ void function AITdm_CleanupBoredNPCThread( entity guy )
print( "cleaning up bored npc: " + guy + " from team " + guy.GetTeam() )
guy.Destroy()
-} \ No newline at end of file
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut
index 915e03e0..9cf0021d 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut
@@ -1,12 +1,54 @@
+untyped // AddCallback_OnUseEntity() needs this
global function GamemodeAt_Init
global function RateSpawnpoints_AT
-const int BH_AI_TEAM = TEAM_BOTH
-const int BOUNTY_TITAN_DAMAGE_POOL = 400 // Rewarded for damage
-const int BOUNTY_TITAN_KILL_REWARD = 100 // Rewarded for kill
-const float WAVE_STATE_TRANSITION_TIME = 5.0
+// Old bobr note which still applies after a year :)
+// IMPLEMENTATION NOTES:
+// bounty hunt is a mode that was clearly pretty heavily developed, and had alot of scrapped concepts (i.e. most wanted player bounties, turret bounties, collectable blackbox objectives)
+// in the interest of time, this script isn't gonna support any of that atm
+// alot of the remote functions also take parameters that aren't used, i'm not gonna populate these and just use default values for now instead
+// however, if you do want to mess with this stuff, almost all the remote functions for this stuff are still present in cl_gamemode_at, and should work fine with minimal fuckery in my experience
+
+
+// Bank settings
+const float AT_BANKS_OPEN_DURATION = 45.0 // Bank open time
+const int AT_BANK_DEPOSIT_RATE = 10 // Amount deposited per second
+const int AT_BANK_DEPOSIT_RADIUS = 256 // bank radius for depositing
+const float AT_BANK_FORCE_CLOSE_DELAY = 4.0 // If all bonus money has been deposited close the banks after this constant early
+
+// TODO: The reference function no longer exists, check if this still holds true
+// VoyageDB: HACK score events... respawn made things in AT_SetScoreEventOverride() really messed up, have to do some hack here
+const array<string> AT_ENABLE_SCOREEVENTS =
+[
+ // these are disabled in AT_SetScoreEventOverride(), but related scoreEvents are not implemented into gamemode
+ // needs to re-enable them
+ "DoomTitan",
+ "DoomAutoTitan"
+]
+const array<string> AT_DISABLE_SCOREEVENTS =
+[
+ // these are missed in AT_SetScoreEventOverride(), but game actually used them
+ // needs to disable them
+ "KillStalker"
+]
+
+// Wave settings
+// General
+const int AT_AI_TEAM = TEAM_BOTH // Allow AI to attack and be attacked by both player teams
+const float AT_FIRST_WAVE_START_DELAY = 10.0 // First wave has an extra delay before begining
+const float AT_WAVE_TRANSITION_DELAY = 5.0 // Time between each wave and banks opening/closing
+const float AT_WAVE_END_ANNOUNCEMENT_DELAY = 1.0 // Extra wait before announcing wave cleaned
+
+// Squad settings
+const int AT_DROPPOD_SQUADS_ALLOWED_ON_FIELD = 4 // default is 4 droppod squads on field, won't use if AT_USE_TOTAL_ALLOWED_ON_FIELD_CHECK turns on // TODO: verify this
-const array<string> VALID_BOUNTY_TITAN_SETTINGS = [
+// Titan bounty settings
+const float AT_BOUNTY_TITAN_CHECK_DELAY = 10.0 // wait for bounty titans landing before we start checking their life state
+const float AT_BOUNTY_TITAN_HEALTH_MULTIPLIER = 3 // TODO: Verify this
+
+// Titan boss settings, check sh_gamemode_at.nut for more info
+const array<string> AT_BOUNTY_TITANS_AI_SETTINGS =
+[
"npc_titan_atlas_stickybomb_bounty",
"npc_titan_atlas_tracker_bounty",
"npc_titan_ogre_minigun_bounty",
@@ -16,102 +58,582 @@ const array<string> VALID_BOUNTY_TITAN_SETTINGS = [
"npc_titan_atlas_vanguard_bounty"
]
+// Extra
+// Respawn didn't use the "totalAllowedOnField" for npc spawning, they only allow 1 squad to be on field for each type of npc. enabling this might cause too much npcs spawning and crash the game
+const bool AT_USE_TOTAL_ALLOWED_ON_FIELD_CHECK = false
-// IMPLEMENTATION NOTES:
-// bounty hunt is a mode that was clearly pretty heavily developed, and had alot of scrapped concepts (i.e. most wanted player bounties, turret bounties, collectable blackbox objectives)
-// in the interest of time, this script isn't gonna support any of that atm
-// alot of the remote functions also take parameters that aren't used, i'm not gonna populate these and just use default values for now instead
-// however, if you do want to mess with this stuff, almost all the remote functions for this stuff are still present in cl_gamemode_at, and should work fine with minimal fuckery in my experience
+// Objectives
+const int AT_OBJECTIVE_EMPTY = -1 // Remove objective
+const int AT_OBJECTIVE_KILL_DZ = 104 // #AT_OBJECTIVE_KILL_DZ
+const int AT_OBJECTIVE_KILL_DZ_MULTI = 105 // #AT_OBJECTIVE_KILL_DZ_MULTI
+const int AT_OBJECTIVE_KILL_BOSS = 106 // #AT_OBJECTIVE_KILL_BOSS
+const int AT_OBJECTIVE_KILL_BOSS_MULTI = 107 // #AT_OBJECTIVE_KILL_BOSS_MULTI
+const int AT_OBJECTIVE_BANK_OPEN = 109 // #AT_BANK_OPEN_OBJECTIVE
-struct {
- array<entity> campsToRegisterOnEntitiesDidLoad
+// When a player tries to deposit when they have 0 bonus money
+// we show a help mesage, this is the ratelimit for that message
+// so that we dont spam it too much
+const float AT_PLAYER_HUD_MESSAGE_COOLDOWN = 2.5
+// Due to bad navmeshes NPCs may wonder off to bumfuck nowhere or the game
+// might teleport them into the map while trying to correct their position
+// This obviously breaks bounty hunt where the objective is to kill ALL ai
+// so we try to cleanup the camps after a set amount of time of inactivity
+const int AT_CAMP_BORED_NPCS_LEFT_TO_START_CLEANUP = 3
+const float AT_CAMP_BORED_CLEANUP_WAIT = 60.0
+struct
+{
array<entity> banks
array<AT_WaveOrigin> camps
+
+ // Used to track ScriptmanagedEntArrays of ai squads
+ table< int, array<int> > campScriptEntArrays
- table< int, table< string, int > > trackedCampNPCSpawns
+ table< entity, bool > titanIsBountyBoss
+ table< entity, int > bountyTitanRewards
+ table< entity, int > npcStolenBonus
+ table< entity, bool > playerBankUploading
+ table< entity, table<entity, int> > playerSavedBountyDamage
+ table< entity, float > playerHudMessageAllowedTime
} file
void function GamemodeAt_Init()
{
- AddCallback_GameStateEnter( eGameState.Playing, RunATGame )
-
+ // wave
+ RegisterSignal( "ATWaveEnd" )
+ // camp
+ RegisterSignal( "ATCampClean" )
+ RegisterSignal( "ATAllCampsClean" )
+
+ // Set-up score callbacks
+ ScoreEvent_SetupEarnMeterValuesForMixedModes()
+ AddCallback_OnPlayerKilled( AT_PlayerOrNPCKilledScoreEvent )
+ AddCallback_OnNPCKilled( AT_PlayerOrNPCKilledScoreEvent )
+
+ // Set npc weapons
+ AiGameModes_SetNPCWeapons( "npc_soldier", [ "mp_weapon_rspn101", "mp_weapon_dmr", "mp_weapon_r97", "mp_weapon_lmg" ] )
+ AiGameModes_SetNPCWeapons( "npc_spectre", [ "mp_weapon_hemlok_smg", "mp_weapon_doubletake", "mp_weapon_mastiff" ] )
+ AiGameModes_SetNPCWeapons( "npc_stalker", [ "mp_weapon_hemlok_smg", "mp_weapon_lstar", "mp_weapon_mastiff" ] )
+
+ // Gamestate callbacks
+ AddCallback_GameStateEnter( eGameState.Prematch, OnATGamePrematch )
+ AddCallback_GameStateEnter( eGameState.Playing, OnATGamePlaying )
+
+ // Initilaze player
AddCallback_OnClientConnected( InitialiseATPlayer )
- AddSpawnCallbackEditorClass( "info_target", "info_attrition_bank", CreateATBank )
- AddSpawnCallbackEditorClass( "info_target", "info_attrition_camp", CreateATCamp )
- AddCallback_EntitiesDidLoad( CreateATCamps_Delayed )
+ // Initilaze gamemode entities
+ AddCallback_EntitiesDidLoad( OnEntitiesDidLoad )
}
void function RateSpawnpoints_AT( int checkclass, array<entity> spawnpoints, int team, entity player )
{
- RateSpawnpoints_Generic( checkclass, spawnpoints, team, player ) // temp
+ RateSpawnpoints_Generic( checkclass, spawnpoints, team, player )
}
-// world and player inits
+
+
+////////////////////////////////////////
+///// GAMESTATE CALLBACK FUNCTIONS /////
+////////////////////////////////////////
+
+void function OnATGamePrematch()
+{
+ AT_ScoreEventsValueSetUp()
+}
+
+void function OnATGamePlaying()
+{
+ thread AT_GameLoop_Threaded()
+}
+
+////////////////////////////////////////////
+///// GAMESTATE CALLBACK FUNCTIONS END /////
+////////////////////////////////////////////
+
+
+
+////////////////////////////
+///// PLAYER FUNCTIONS /////
+////////////////////////////
void function InitialiseATPlayer( entity player )
{
Remote_CallFunction_NonReplay( player, "ServerCallback_AT_OnPlayerConnected" )
+ player.SetPlayerNetInt( "AT_bonusPointMult", 1 )
+ file.playerBankUploading[ player ] <- false
+ file.playerSavedBountyDamage[ player ] <- {}
+ file.playerHudMessageAllowedTime[ player ] <- 0.0
+ thread AT_PlayerTitleThink( player )
+ thread AT_PlayerObjectiveThink( player )
}
-void function CreateATBank( entity spawnpoint )
+void function AT_PlayerTitleThink( entity player )
{
- entity bank = CreatePropDynamic( spawnpoint.GetModelName(), spawnpoint.GetOrigin(), spawnpoint.GetAngles(), SOLID_VPHYSICS )
- bank.SetScriptName( "AT_Bank" )
-
- // create tracker ent
- // we don't need to store these at all, client just needs to get them
- DispatchSpawn( GetAvailableBankTracker( bank ) )
+ player.EndSignal( "OnDestroy" )
+
+ while ( true )
+ {
+ if ( GetGameState() == eGameState.Playing )
+ {
+ // Set player money count
+ player.SetTitle( "$" + string( AT_GetPlayerBonusPoints( player ) ) )
+
+ if( AT_GetPlayerBonusPoints( player ) >= 600 && !HasPlayerCompletedMeritScore( player ) ) //Challenge is: "Earn $600."
+ {
+ AddPlayerScore( player, "ChallengeATAssault" )
+ SetPlayerChallengeMeritScore( player )
+ }
+ }
+ else if ( GetGameState() >= eGameState.WinnerDetermined )
+ {
+ if ( player.IsTitan() )
+ player.SetTitle( GetTitanPlayerTitle( player ) )
+ else
+ player.SetTitle( "" )
+
+ return
+ }
+
+ WaitFrame()
+ }
+}
+
+string function GetTitanPlayerTitle( entity player )
+{
+ entity soul = player.GetTitanSoul()
+
+ if ( !IsValid( soul ) )
+ return ""
- thread PlayAnim( bank, "mh_inactive_idle" )
+ string settings = GetSoulPlayerSettings( soul )
+ var title = GetPlayerSettingsFieldForClassName( settings, "printname" )
+
+ if ( title == null )
+ return ""
- file.banks.append( bank )
+ return expect string( title )
+}
+
+void function AT_PlayerObjectiveThink( entity player )
+{
+ player.EndSignal( "OnDestroy" )
+
+ int curObjective = AT_OBJECTIVE_EMPTY
+ while ( true )
+ {
+ // game entered other state
+ if ( GetGameState() >= eGameState.WinnerDetermined )
+ {
+ player.SetPlayerNetInt( "gameInfoStatusText", AT_OBJECTIVE_EMPTY )
+ return
+ }
+
+ int nextObjective = AT_OBJECTIVE_EMPTY
+
+ // Determine objective text for player
+ if ( !IsAlive( player ) ) // Don't show objective to dead players
+ {
+ nextObjective = AT_OBJECTIVE_EMPTY
+ }
+ else // We're still alive
+ {
+ if ( GetGlobalNetBool( "banksOpen" ) )
+ {
+ nextObjective = AT_OBJECTIVE_BANK_OPEN
+ }
+ else if ( GetGlobalNetBool( "preBankPhase" ) )
+ {
+ nextObjective = AT_OBJECTIVE_EMPTY
+ }
+ else
+ {
+ // No checks have passed, try to do a "Kill all x near the marked dropzone" objective
+ int dropZoneActiveCount = 0
+ int bossAliveCount = 0
+ array<entity> campEnts
+ campEnts.append( GetGlobalNetEnt( "camp1Ent" ) )
+ campEnts.append( GetGlobalNetEnt( "camp2Ent" ) )
+
+ foreach ( entity ent in campEnts )
+ {
+ if ( IsValid( ent ) )
+ {
+ if ( ent.IsTitan() )
+ bossAliveCount += 1
+ else
+ dropZoneActiveCount += 1
+ }
+ }
+
+ switch( dropZoneActiveCount )
+ {
+ case 1:
+ nextObjective = AT_OBJECTIVE_KILL_DZ
+ break
+ case 2:
+ nextObjective = AT_OBJECTIVE_KILL_DZ_MULTI
+ break
+ }
+
+ switch( bossAliveCount )
+ {
+ case 1:
+ nextObjective = AT_OBJECTIVE_KILL_BOSS
+ break
+ case 2:
+ nextObjective = AT_OBJECTIVE_KILL_BOSS_MULTI
+ break
+ }
+
+ // We couldn't get an objective, set it to empty
+ if ( dropZoneActiveCount == 0 && bossAliveCount == 0 )
+ nextObjective = AT_OBJECTIVE_EMPTY
+ }
+ }
+
+ // Set the objective when changed
+ if ( curObjective != nextObjective )
+ {
+ player.SetPlayerNetInt( "gameInfoStatusText", nextObjective )
+ curObjective = nextObjective
+ }
+
+ WaitFrame()
+ }
}
-void function CreateATCamp( entity spawnpoint )
+////////////////////////////////
+///// PLAYER FUNCTIONS END /////
+////////////////////////////////
+
+
+
+////////////////////////////////////////
+///// GAMEMODE INITILAZE FUNCTIONS /////
+////////////////////////////////////////
+
+void function OnEntitiesDidLoad()
{
- // delay this so we don't do stuff before all spawns are initialised and that
- file.campsToRegisterOnEntitiesDidLoad.append( spawnpoint )
+ foreach ( entity info_target in GetEntArrayByClass_Expensive( "info_target" ) )
+ {
+ if( info_target.HasKey( "editorclass" ) )
+ {
+ switch( info_target.kv.editorclass )
+ {
+ case "info_attrition_bank":
+ entity bank = CreateEntity( "prop_script" )
+ bank.SetScriptName( "AT_Bank" ) // VoyageDB: don't know how to make client able to track it
+ bank.SetOrigin( info_target.GetOrigin() )
+ bank.SetAngles( info_target.GetAngles() )
+ DispatchSpawn( bank )
+ bank.kv.solid = SOLID_VPHYSICS
+ bank.SetModel( info_target.GetModelName() )
+
+ // Minimap icon init
+ bank.Minimap_SetCustomState( eMinimapObject_prop_script.AT_BANK )
+ bank.Minimap_SetAlignUpright( true )
+ bank.Minimap_SetZOrder( MINIMAP_Z_OBJECT )
+ bank.Minimap_Hide( TEAM_IMC, null )
+ bank.Minimap_Hide( TEAM_MILITIA, null )
+
+ // Create tracker ent
+ // we don't need to store these at all, client just needs to get them
+ DispatchSpawn( GetAvailableBankTracker( bank ) )
+
+ // Make sure the bank is in it's disabled pose
+ thread PlayAnim( bank, "mh_inactive_idle" )
+ // Set the bank usable
+ AddCallback_OnUseEntity( bank, OnPlayerUseBank )
+ bank.SetUsable()
+ bank.SetUsePrompts( "#AT_USE_BANK_CLOSED", "#AT_USE_BANK_CLOSED" )
+
+ file.banks.append( bank )
+ break;
+ case "info_attrition_camp":
+ AT_WaveOrigin campStruct
+ campStruct.ent = info_target
+ campStruct.origin = info_target.GetOrigin()
+ campStruct.radius = expect string( info_target.kv.radius ).tofloat()
+ campStruct.height = expect string( info_target.kv.height ).tofloat()
+
+ // Assumes every info_attrition_camp will have all 9 phases, possibly not a good idea?
+ // TODO: verify this on all vanilla maps before release
+ for ( int i = 0; i < 9; i++ )
+ campStruct.phaseAllowed.append( expect string( info_target.kv[ "phase_" + ( i + 1 ) ] ) == "1" )
+
+ // Get droppod spawns within the camp
+ foreach ( entity spawnpoint in SpawnPoints_GetDropPod() )
+ {
+ vector campPos = info_target.GetOrigin()
+ vector spawnPos = spawnpoint.GetOrigin()
+ if ( Distance( campPos, spawnPos ) < campStruct.radius )
+ campStruct.dropPodSpawnPoints.append( spawnpoint )
+ }
+
+ // Get titan spawns within the camp
+ foreach ( entity spawnpoint in SpawnPoints_GetTitan() )
+ {
+ vector campPos = info_target.GetOrigin()
+ vector spawnPos = spawnpoint.GetOrigin()
+ if ( Distance( campPos, spawnPos ) < campStruct.radius )
+ campStruct.titanSpawnPoints.append( spawnpoint )
+ }
+
+ file.camps.append( campStruct )
+ break;
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////
+///// GAMEMODE INITILAZE FUNCTIONS END /////
+////////////////////////////////////////////
+
+
+
+/////////////////////////////
+///// SCORING FUNCTIONS /////
+/////////////////////////////
+
+// TODO: Don't reward in postmatch
+// TODO: Dropping a titan on a bounty with it's dome-shield still up rewards you the bonus, but
+// it doesn't actually damage the bounty titan
+
+void function AT_ScoreEventsValueSetUp()
+{
+ ScoreEvent_SetEarnMeterValues( "KillTitan", 0.10, 0.15 )
+ ScoreEvent_SetEarnMeterValues( "KillAutoTitan", 0.10, 0.15 )
+ ScoreEvent_SetEarnMeterValues( "AttritionTitanKilled", 0.10, 0.15 )
+ ScoreEvent_SetEarnMeterValues( "KillPilot", 0.10, 0.10 )
+ ScoreEvent_SetEarnMeterValues( "AttritionPilotKilled", 0.10, 0.10 )
+ ScoreEvent_SetEarnMeterValues( "AttritionBossKilled", 0.10, 0.20 )
+ ScoreEvent_SetEarnMeterValues( "AttritionGruntKilled", 0.02, 0.02, 0.5 )
+ ScoreEvent_SetEarnMeterValues( "AttritionSpectreKilled", 0.02, 0.02, 0.5 )
+ ScoreEvent_SetEarnMeterValues( "AttritionStalkerKilled", 0.02, 0.02, 0.5 )
+ ScoreEvent_SetEarnMeterValues( "AttritionSuperSpectreKilled", 0.10, 0.10, 0.5 )
+
+ // HACK
+ foreach ( string eventName in AT_ENABLE_SCOREEVENTS )
+ ScoreEvent_Enable( GetScoreEvent( eventName ) )
+
+ foreach ( string eventName in AT_DISABLE_SCOREEVENTS )
+ ScoreEvent_Disable( GetScoreEvent( eventName ) )
}
-void function CreateATCamps_Delayed()
+void function AT_PlayerOrNPCKilledScoreEvent( entity victim, entity attacker, var damageInfo )
{
- // we delay registering camps until EntitiesDidLoad since they rely on spawnpoints and stuff, which might not all be ready in the creation callback
- // unsure if this would be an issue in practice, but protecting against it in case it would be
- foreach ( entity camp in file.campsToRegisterOnEntitiesDidLoad )
+ if ( !IsValid( attacker ) )
+ return
+
+ // Suicide
+ if ( attacker == victim )
{
- AT_WaveOrigin campStruct
- campStruct.ent = camp
- campStruct.origin = camp.GetOrigin()
- campStruct.radius = expect string( camp.kv.radius ).tofloat()
- campStruct.height = expect string( camp.kv.height ).tofloat()
+ if ( victim.IsPlayer() )
+ AT_PlayerBonusLoss( victim, AT_GetPlayerBonusPoints( victim ) / 2 )
- // assumes every info_attrition_camp will have all 9 phases, possibly not a good idea?
- for ( int i = 0; i < 9; i++ )
- campStruct.phaseAllowed.append( expect string( camp.kv[ "phase_" + ( i + 1 ) ] ) == "1" )
+ return
+ }
+
+ // NPC is the attacker
+ if ( !attacker.IsPlayer() )
+ {
+ if ( attacker.IsTitan() && IsValid( GetPetTitanOwner( attacker ) ) ) // Re-asign attacker
+ attacker = GetPetTitanOwner( attacker )
+ else // NPC steals money from player, killing it will award the stolen bonus + normal reward
+ AT_NPCTryStealBonusPoints( attacker, victim )
- // get droppod spawns
- foreach ( entity spawnpoint in SpawnPoints_GetDropPod() )
- if ( Distance( camp.GetOrigin(), spawnpoint.GetOrigin() ) < 1500.0 )
- campStruct.dropPodSpawnPoints.append( spawnpoint )
+ return
+ }
+
+ // Get event name
+ string eventName = GetAttritionScoreEventName( victim.GetClassName() )
+
+ if ( victim.IsTitan() ) // titan specific
+ eventName = GetAttritionScoreEventNameFromAI( victim )
+
+ if ( eventName == "" ) // no valid scoreEvent
+ return
+
+ int scoreVal = ScoreEvent_GetPointValue( GetScoreEvent( eventName ) )
+
+ // pet titan check
+ if ( victim.IsTitan() && IsValid( GetPetTitanOwner( victim ) ) )
+ {
+ if( GetPetTitanOwner( victim ) == attacker ) // Player ejected
+ return
- foreach ( entity spawnpoint in SpawnPoints_GetTitan() )
- if ( Distance( camp.GetOrigin(), spawnpoint.GetOrigin() ) < 1500.0 )
- campStruct.titanSpawnPoints.append( spawnpoint )
+ if( GetPetTitanOwner( victim ).IsPlayer() ) // Killed player npc titan
+ return
+
+ scoreVal = ATTRITION_SCORE_TITAN_MIN
+ }
+
+ // killed npc
+ if ( victim.IsNPC() )
+ {
+ int bonusFromNPC = 0
+ // If NPC was carrying a bonus award it to the attacker
+ if ( victim in file.npcStolenBonus )
+ {
+ bonusFromNPC = file.npcStolenBonus[ victim ]
+ delete file.npcStolenBonus[ victim ]
+ }
+ AT_AddPlayerBonusPointsForEntityKilled( attacker, scoreVal, damageInfo, bonusFromNPC )
+ AddPlayerScore( attacker, eventName ) // we add scoreEvent here, since basic score events has been overwrited by sh_gamemode_at.nut
+ // update score difference and scoreboard
+ AT_AddToPlayerTeamScore( attacker, scoreVal )
+ }
+
+ // bonus stealing check
+ if ( victim.IsPlayer() )
+ AT_PlayerTryStealBonusPoints( attacker, victim, damageInfo )
+}
+
+bool function AT_NPCTryStealBonusPoints( entity attacker, entity victim )
+{
+ // basic checks
+ if ( !attacker.IsNPC() )
+ return false
- // todo: turret spawns someday maybe
+ if ( !victim.IsPlayer() )
+ return false
+
+ int victimBonus = AT_GetPlayerBonusPoints( victim )
+ int bonusToSteal = victimBonus / 2 // npc always steal half the bonus from player, no extra bonus for killing the player
+ if ( bonusToSteal == 0 ) // player has no bonus!
+ return false
+
+ if ( !( attacker in file.npcStolenBonus ) ) // init
+ file.npcStolenBonus[ attacker ] <- 0
- file.camps.append( campStruct )
+ file.npcStolenBonus[ attacker ] += bonusToSteal
+
+ AT_PlayerBonusLoss( victim, bonusToSteal ) // tell victim of bonus stolen
+
+ if ( !( attacker in file.titanIsBountyBoss ) ) // if attacker npc is not a bounty titan, we make them highlighted
+ NPCBountyStolenHighlight( attacker )
+
+ return true
+}
+
+void function NPCBountyStolenHighlight( entity npc )
+{
+ Highlight_SetEnemyHighlight( npc, "enemy_boss_bounty" )
+}
+
+bool function AT_PlayerTryStealBonusPoints( entity attacker, entity victim, var damageInfo )
+{
+ // basic checks
+ if ( !attacker.IsPlayer() )
+ return false
+
+ if ( !victim.IsPlayer() )
+ return false
+
+ int victimBonus = AT_GetPlayerBonusPoints( victim )
+
+ int minScoreCanSteal = ATTRITION_SCORE_PILOT_MIN
+ if ( victim.IsTitan() )
+ minScoreCanSteal = ATTRITION_SCORE_TITAN_MIN
+
+ int bonusToSteal = victimBonus / 2
+ int attackerScore = bonusToSteal
+ bool realStealBonus = true
+ if ( bonusToSteal <= minScoreCanSteal ) // no enough bonus to steal
+ {
+ attackerScore = minScoreCanSteal // give attacker min bonus
+ realStealBonus = false // we don't do attacker steal events below, just half victim's bonus
}
+
+ // servercallback
+ int victimEHandle = victim.GetEncodedEHandle()
+ vector damageOrigin = DamageInfo_GetDamagePosition( damageInfo )
+
+ // only do attacker events if victim has enough bonus to steal
+ if ( realStealBonus )
+ {
+ Remote_CallFunction_NonReplay(
+ attacker,
+ "ServerCallback_AT_PlayerKillScorePopup",
+ bonusToSteal, // stolenScore
+ victimEHandle, // victimEHandle
+ damageOrigin.x, // x
+ damageOrigin.y, // y
+ damageOrigin.z // z
+ )
+ }
+ else // otherwise we do a normal entity killed scoreEvent
+ {
+ AT_AddPlayerBonusPointsForEntityKilled( attacker, attackerScore, damageInfo )
+ }
+
+ // update score difference and scoreboard
+ AT_AddToPlayerTeamScore( attacker, minScoreCanSteal )
+
+ // steal bonus
+ // only do attacker events if victim has enough bonus to steal
+ if ( realStealBonus )
+ {
+ AT_AddPlayerBonusPoints( attacker, bonusToSteal )
+ AddPlayerScore( attacker, "AttritionBonusStolen" )
+ }
+
+ // tell victim of bonus stolen
+ AT_PlayerBonusLoss( victim, bonusToSteal )
- file.campsToRegisterOnEntitiesDidLoad.clear()
+ return realStealBonus
}
-// scoring funcs
+void function AT_PlayerBonusLoss( entity player, int bonusLoss )
+{
+ AT_AddPlayerBonusPoints( player, -bonusLoss )
+ Remote_CallFunction_NonReplay(
+ player,
+ "ServerCallback_AT_ShowStolenBonus",
+ bonusLoss // stolenScore
+ )
+}
+
+// team score meter
+void function AT_AddToPlayerTeamScore( entity player, int amount )
+{
+ // do not award any score after the match is ended
+ if ( GetGameState() > eGameState.Playing )
+ return
+
+ // add to scoreboard
+ player.AddToPlayerGameStat( PGS_ASSAULT_SCORE, amount )
+
+ // Check score so we dont go over max
+ if ( GameRules_GetTeamScore(player.GetTeam()) + amount > GetScoreLimit_FromPlaylist() )
+ {
+ amount = GetScoreLimit_FromPlaylist() - GameRules_GetTeamScore(player.GetTeam())
+ }
+
+ // update score difference
+ AddTeamScore( player.GetTeam(), amount )
+}
+
+// bonus points, players earn from killing
+void function AT_AddPlayerBonusPoints( entity player, int amount )
+{
+ // do not award any score after the match is ended
+ if ( GetGameState() > eGameState.Playing )
+ return
+
+ // add to scoreboard
+ player.AddToPlayerGameStat( PGS_SCORE, amount )
+ AT_SetPlayerBonusPoints( player, player.GetPlayerNetInt( "AT_bonusPoints" ) + ( player.GetPlayerNetInt( "AT_bonusPoints256" ) * 256 ) + amount )
+}
+
+int function AT_GetPlayerBonusPoints( entity player )
+{
+ return player.GetPlayerNetInt( "AT_bonusPoints" ) + player.GetPlayerNetInt( "AT_bonusPoints256" ) * 256
+}
-// don't use this where possible as it doesn't set score and stuff
-void function AT_SetPlayerCash( entity player, int amount )
+void function AT_SetPlayerBonusPoints( entity player, int amount )
{
// split into stacks of 256 where necessary
int stacks = amount / 256 // automatically rounds down because int division
@@ -120,198 +642,986 @@ void function AT_SetPlayerCash( entity player, int amount )
player.SetPlayerNetInt( "AT_bonusPoints", amount - stacks * 256 )
}
-void function AT_AddPlayerCash( entity player, int amount )
+// total points, the value player actually uploaded to team score
+void function AT_AddPlayerTotalPoints( entity player, int amount )
{
- // update score difference
- AddTeamScore( player.GetTeam(), amount / 2 )
- AT_SetPlayerCash( player, player.GetPlayerNetInt( "AT_bonusPoints" ) + ( player.GetPlayerNetInt( "AT_bonusPoints256" ) * 256 ) + amount )
+ // update score difference and scoreboard, calling this function meaning player has deposited their bonus to team score
+ AT_AddToPlayerTeamScore( player, amount )
+ AT_SetPlayerTotalPoints( player, player.GetPlayerNetInt( "AT_totalPoints" ) + ( player.GetPlayerNetInt( "AT_totalPoints256" ) * 256 ) + amount )
+}
+
+void function AT_SetPlayerTotalPoints( entity player, int amount )
+{
+ // split into stacks of 256 where necessary
+ int stacks = amount / 256 // automatically rounds down because int division
+
+ player.SetPlayerNetInt( "AT_totalPoints256", stacks )
+ player.SetPlayerNetInt( "AT_totalPoints", amount - stacks * 256 )
+}
+
+// earn points, seems not used
+void function AT_AddPlayerEarnedPoints( entity player, int amount )
+{
+ AT_SetPlayerBonusPoints( player, player.GetPlayerNetInt( "AT_earnedPoints" ) + ( player.GetPlayerNetInt( "AT_earnedPoints256" ) * 256 ) + amount )
+}
+
+void function AT_SetPlayerEarnedPoints( entity player, int amount )
+{
+ // split into stacks of 256 where necessary
+ int stacks = amount / 256 // automatically rounds down because int division
+
+ player.SetPlayerNetInt( "AT_earnedPoints256", stacks )
+ player.SetPlayerNetInt( "AT_earnedPoints", amount - stacks * 256 )
}
-// run gamestate
+// damaging bounty
+void function AT_AddPlayerBonusPointsForBossDamaged( entity player, entity victim, int amount, var damageInfo )
+{
+ AT_AddPlayerBonusPoints( player, amount )
+ // update score difference and scoreboard
+ AT_AddToPlayerTeamScore( player, amount )
+
+ // send servercallback for damaging
+ int bossEHandle = victim.GetEncodedEHandle()
+ vector damageOrigin = DamageInfo_GetDamagePosition( damageInfo )
-void function RunATGame()
+ Remote_CallFunction_NonReplay(
+ player,
+ "ServerCallback_AT_BossDamageScorePopup",
+ amount, // damageScore
+ amount, // damageBonus
+ bossEHandle, // bossEHandle
+ damageOrigin.x, // x
+ damageOrigin.y, // y
+ damageOrigin.z // z
+ )
+}
+
+void function AT_AddPlayerBonusPointsForEntityKilled( entity player, int amount, var damageInfo, int extraBonus = 0 )
{
- thread RunATGame_Threaded()
+ AT_AddPlayerBonusPoints( player, amount + extraBonus )
+
+ // send servercallback for damaging
+ int attackerEHandle = player.GetEncodedEHandle()
+ vector damageOrigin = DamageInfo_GetDamagePosition( damageInfo )
+
+ Remote_CallFunction_NonReplay(
+ player,
+ "ServerCallback_AT_ShowATScorePopup",
+ attackerEHandle, // attackerEHandle
+ amount, // damageScore
+ amount + extraBonus, // damageBonus
+ damageOrigin.x, // damagePosX
+ damageOrigin.y, // damagePosX
+ damageOrigin.z, // damagePosX
+ 0 // damageType ( not used )
+ )
}
-void function RunATGame_Threaded()
+/////////////////////////////////
+///// SCORING FUNCTIONS END /////
+/////////////////////////////////
+
+
+
+//////////////////////////////
+///// GAMELOOP FUNCTIONS /////
+//////////////////////////////
+
+void function AT_GameLoop_Threaded()
{
svGlobal.levelEnt.EndSignal( "GameStateChanged" )
- OnThreadEnd( function()
- {
- SetGlobalNetBool( "banksOpen", false )
- })
+ // game end func
+ // TODO: Cant seem to be able to get this crash ???
+ OnThreadEnd
+ (
+ function()
+ {
+ // prevent crash before entity creation on map change
+ if ( GetGameState() >= eGameState.Prematch )
+ {
+ SetGlobalNetBool( "preBankPhase", false )
+ SetGlobalNetBool( "banksOpen", false )
+ }
+ }
+ )
- wait WAVE_STATE_TRANSITION_TIME // initial wait before first wave
-
- for ( int waveCount = 1; ; waveCount++ )
+ // Initial wait before first wave
+ wait AT_FIRST_WAVE_START_DELAY - AT_WAVE_TRANSITION_DELAY
+
+ int lastWaveId = -1
+ for ( int waveCount = 1; ; waveCount++ )
{
- wait WAVE_STATE_TRANSITION_TIME
+ wait AT_WAVE_TRANSITION_DELAY
// cap to number of real waves
- int waveId = ( waveCount / 2 )
- // last wave is clearly unfinished so don't use, just cap to last actually used one
- if ( waveId >= GetWaveDataSize() - 1 )
+ int waveId = ( waveCount - 1 ) / 2
+ int waveCapAmount = 2
+ waveId = int( min( waveId, GetWaveDataSize() - waveCapAmount ) )
+
+ // New wave dialogue
+ bool waveChanged = lastWaveId != waveId
+ if ( waveChanged )
+ {
+ PlayFactionDialogueToTeam( "bh_newWave", TEAM_IMC )
+ PlayFactionDialogueToTeam( "bh_newWave", TEAM_MILITIA )
+ }
+ else // same wave, second half
{
- waveId = GetWaveDataSize() - 2
- waveCount = waveId * 2
+ PlayFactionDialogueToTeam( "bh_incoming", TEAM_IMC )
+ PlayFactionDialogueToTeam( "bh_incoming", TEAM_MILITIA )
}
+
+ lastWaveId = waveId
SetGlobalNetInt( "AT_currentWave", waveId )
- bool isBossWave = waveCount / float( 2 ) > waveId // odd number waveCount means boss wave
+ bool isBossWave = waveCount % 2 == 0 // even number waveCount means boss wave
// announce the wave
foreach ( entity player in GetPlayerArray() )
{
if ( isBossWave )
+ {
Remote_CallFunction_NonReplay( player, "ServerCallback_AT_AnnounceBoss" )
+ }
else
- Remote_CallFunction_NonReplay( player, "ServerCallback_AT_AnnouncePreParty", 0.0, waveId )
+ {
+ Remote_CallFunction_NonReplay(
+ player,
+ "ServerCallback_AT_AnnouncePreParty",
+ 0.0, // endTime ( not used )
+ waveId // waveNum
+ )
+ }
}
- wait WAVE_STATE_TRANSITION_TIME
+ wait AT_WAVE_TRANSITION_DELAY
- // run the wave
+ // Run the wave
+ thread AT_CampSpawnThink( waveId, isBossWave )
+
+ if ( !isBossWave )
+ {
+ svGlobal.levelEnt.WaitSignal( "ATAllCampsClean" ) // signaled when all camps cleaned in spawn functions
+ }
+ else
+ {
+ wait AT_BOUNTY_TITAN_CHECK_DELAY
+ // wait until all bounty titans killed
+ while ( IsAlive( GetGlobalNetEnt( "camp1Ent" ) ) || IsAlive( GetGlobalNetEnt( "camp2Ent" ) ) )
+ WaitFrame()
+ }
+
+ // wave end, prebank phase
+ svGlobal.levelEnt.Signal( "ATWaveEnd" ) // defensive fix, destroy existing campEnts
+ SetGlobalNetBool( "preBankPhase", true )
+
+ wait AT_WAVE_END_ANNOUNCEMENT_DELAY
- AT_WaveData wave = GetWaveData( waveId )
- array< array<AT_SpawnData> > campSpawnData
+ // announce wave end
+ foreach ( entity player in GetPlayerArray() )
+ {
+ Remote_CallFunction_NonReplay(
+ player,
+ "ServerCallback_AT_AnnounceWaveOver",
+ waveId, // waveNum ( not used )
+ 0, // militiaDamageTotal ( not used )
+ 0, // imcDamageTotal ( not used )
+ 0, // milMVP ( not used )
+ 0, // imcMVP ( not used )
+ 0, // milMVPDamage ( not used )
+ 0 // imcMVPDamage ( not used )
+ )
+ }
+
+ wait AT_WAVE_TRANSITION_DELAY
- if ( isBossWave )
- campSpawnData = wave.bossSpawnData
- else
- campSpawnData = wave.spawnDataArrays
+ // banking phase
+ SetGlobalNetBool( "preBankPhase", false )
+ SetGlobalNetTime( "AT_bankStartTime", Time() )
+ SetGlobalNetTime( "AT_bankEndTime", Time() + AT_BANKS_OPEN_DURATION )
+ SetGlobalNetBool( "banksOpen", true )
+
+ foreach ( entity player in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_AT_BankOpen" )
+
+ foreach ( entity bank in file.banks )
+ thread AT_BankActiveThink( bank )
+
- // initialise pending spawns
- foreach ( array< AT_SpawnData > campData in campSpawnData )
+ float endTime = Time() + AT_BANKS_OPEN_DURATION
+ bool forceCloseTriggered = false
+ // wait until no player is holding bonus, or max wait time
+ while ( Time() <= endTime )
{
- foreach ( AT_SpawnData spawnData in campData )
- spawnData.pendingSpawns = spawnData.totalToSpawn
+ // If everyone has deposited their bonuses close the banks early
+ if ( !ATAnyPlayerHasBonus() && !forceCloseTriggered )
+ {
+ forceCloseTriggered = true
+ endTime = Time() + AT_BANK_FORCE_CLOSE_DELAY
+ }
+
+ WaitFrame()
}
- // clear tracked spawns
- file.trackedCampNPCSpawns = {}
- while ( true )
- {
- // if this is ever 0 by the end of this loop, wave is complete
- int numActiveCampSpawners = 0
+ SetGlobalNetBool( "banksOpen", false )
+ foreach ( entity player in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_AT_BankClose" )
+ }
+}
+
+bool function ATAnyPlayerHasBonus()
+{
+ foreach ( entity player in GetPlayerArray() )
+ {
+ if ( AT_GetPlayerBonusPoints( player ) )
+ return true
+ }
+ return false
+}
+
+//////////////////////////////////
+///// GAMELOOP FUNCTIONS END /////
+//////////////////////////////////
+
+
+
+//////////////////////////
+///// CAMP FUNCTIONS /////
+//////////////////////////
+
+void function AT_CampSpawnThink( int waveId, bool isBossWave )
+{
+ AT_WaveData wave = GetWaveData( waveId )
+ array< array<AT_SpawnData> > campSpawnData
+
+ if ( isBossWave )
+ campSpawnData = wave.bossSpawnData
+ else
+ campSpawnData = wave.spawnDataArrays
+
+ array<AT_WaveOrigin> allCampsToUse
+ foreach ( AT_WaveOrigin campStruct in file.camps )
+ {
+ if ( campStruct.phaseAllowed[ waveId ] )
+ allCampsToUse.append( campStruct )
+ }
+
+ // HACK
+ // There's too many phase3 camps on exoplanet and rise, make sure we always have the correct count
+ int maxCampsForWave = waveId == 0 ? 1 : 2
+ while( allCampsToUse.len() > maxCampsForWave )
+ {
+ // Get the required number of camps
+ array<AT_WaveOrigin> tempCamps
+ for( int i = 0; i < maxCampsForWave; i++ )
+ tempCamps.append( allCampsToUse[RandomInt( allCampsToUse.len() )] )
- // iterate over camp data for wave
- for ( int campIdx = 0; campIdx < campSpawnData.len() && campIdx < file.camps.len(); campIdx++ )
+
+ // Check if they're intersecting, if they are, try again
+ bool intersecting = false
+ for( int i = 0; i < tempCamps.len(); i++ )
+ {
+ AT_WaveOrigin campA = tempCamps[i]
+ for( int j = 0; j < tempCamps.len(); j++ )
{
- if ( !( campIdx in file.trackedCampNPCSpawns ) )
- file.trackedCampNPCSpawns[ campIdx ] <- {}
+ // Don't compare the same two camps
+ if( j == i )
+ continue
+
+ AT_WaveOrigin campB = tempCamps[j]
+
+ if( Distance( campA.origin, campB.origin ) < campA.radius + campB.radius )
+ intersecting = true
+ }
+ }
+
+ if( !intersecting )
+ allCampsToUse = tempCamps
+
+ // If we ever get really unlucky just wait a frame
+ WaitFrame()
+ }
+
+ foreach ( int spawnId, AT_WaveOrigin curCampData in allCampsToUse )
+ {
+ array<AT_SpawnData> curSpawnData = campSpawnData[ spawnId ]
+
+ int totalNPCsToSpawn = 0
+ // initialise pending spawns and get total npcs
+ foreach ( AT_SpawnData spawnData in curSpawnData )
+ {
+ spawnData.pendingSpawns = spawnData.totalToSpawn
+ // add to network variables
+ string npcNetVar = GetNPCNetVarName( spawnData.aitype, spawnId )
+ SetGlobalNetInt( npcNetVar, spawnData.totalToSpawn )
+
+ totalNPCsToSpawn += spawnData.totalToSpawn
+ }
+
+ if ( !isBossWave )
+ {
+ // camp Ent, boss wave will use boss themselves as campEnt
+ string campEntVarName = "camp" + string( spawnId + 1 ) + "Ent"
+ bool waveNotActive = GetGlobalNetBool( "preBankPhase" ) || GetGlobalNetBool( "banksOpen" )
+ if ( !IsValid( GetGlobalNetEnt( campEntVarName ) ) && !waveNotActive )
+ SetGlobalNetEnt( campEntVarName, CreateCampTracker( curCampData, spawnId ) )
- // iterate over ai spawn data for camp
- foreach ( AT_SpawnData spawnData in campSpawnData[ campIdx ] )
+ array<AT_SpawnData> minionSquadDatas
+ foreach ( AT_SpawnData data in curSpawnData )
+ {
+ switch ( data.aitype )
{
- if ( !( spawnData.aitype in file.trackedCampNPCSpawns[ campIdx ] ) )
- file.trackedCampNPCSpawns[ campIdx ][ spawnData.aitype ] <- 0
-
- if ( spawnData.pendingSpawns > 0 || file.trackedCampNPCSpawns[ campIdx ][ spawnData.aitype ] > 0 )
- numActiveCampSpawners++
+ case "npc_soldier":
+ case "npc_spectre":
+ case "npc_stalker":
+ if ( !AT_USE_TOTAL_ALLOWED_ON_FIELD_CHECK )
+ minionSquadDatas.append( data )
+ else
+ thread AT_DroppodSquadEvent_Single( curCampData, spawnId, data )
+ break
+
+ case "npc_super_spectre":
+ thread AT_ReaperEvent( curCampData, spawnId, data )
+ break
+ }
+ }
+
+ // minions squad spawn
+ if ( !AT_USE_TOTAL_ALLOWED_ON_FIELD_CHECK )
+ {
+ if ( minionSquadDatas.len() > 0 )
+ thread AT_DroppodSquadEvent( curCampData, spawnId, minionSquadDatas )
+ }
+
+ // use campProgressThink for handling wave state
+ thread CampProgressThink( spawnId, totalNPCsToSpawn )
+ }
+ else // bosswave spawn
+ {
+ foreach ( AT_SpawnData data in curSpawnData )
+ {
+ if( data.aitype != "npc_titan" )
+ continue
- // try to spawn as many ai as we can, as long as the camp doesn't already have too many spawned
- int spawnCount
- for ( spawnCount = 0; spawnCount < spawnData.pendingSpawns && spawnCount < spawnData.totalAllowedOnField - file.trackedCampNPCSpawns[ campIdx ][ spawnData.aitype ]; )
- {
- // not doing this in a generic way atm, but could be good for the future if we want to support more ai
- switch ( spawnData.aitype )
- {
- case "npc_soldier":
- case "npc_spectre":
- case "npc_stalker":
- thread AT_SpawnDroppodSquad( campIdx, spawnData.aitype )
- spawnCount += 4
- break
-
- case "npc_super_spectre":
- thread AT_SpawnReaper( campIdx )
- spawnCount += 1
- break
-
- case "npc_titan":
- thread AT_SpawnBountyTitan( campIdx )
- spawnCount += 1
- break
-
- default:
- print( "BOUNTY HUNT: Tried to spawn unsupported ai of type \"" + "\" at camp " + campIdx )
- }
+ thread AT_BountyTitanEvent( curCampData, spawnId, data )
+ break
+ }
+ }
+ }
+}
+
+void function CampProgressThink( int spawnId, int totalNPCsToSpawn )
+{
+ string campLetter = GetCampLetter( spawnId )
+ string campProgressName = campLetter + "campProgress"
+ string campEntVarName = "camp" + string( spawnId + 1 ) + "Ent"
+
+ // initial wait
+ SetGlobalNetFloat( campProgressName, 1.0 )
+
+ // TODO: random wait, make this a constant ??
+ wait 3.0
+
+ float cleanUpTime = -1.0
+
+ while ( true )
+ {
+ int npcsLeft
+ // get all npcs might be in this camp
+ for ( int i = 0; i < 5; i++ )
+ {
+ string netVarName = string( i + 1 ) + campLetter + "campCount"
+ int netVarValue = GetGlobalNetInt( netVarName )
+ if ( netVarValue >= 0 ) // uninitialized network var starts from -1, avoid checking them
+ npcsLeft += netVarValue
+ }
+
+ float campLeft = float( npcsLeft ) / float( totalNPCsToSpawn )
+ SetGlobalNetFloat( campProgressName, campLeft )
+
+ if( npcsLeft <= AT_CAMP_BORED_NPCS_LEFT_TO_START_CLEANUP && cleanUpTime < 0.0 )
+ {
+ cleanUpTime = Time() + AT_CAMP_BORED_CLEANUP_WAIT
+ print("Cleanup timer started!")
+ }
+
+ if( Time() > cleanUpTime && cleanUpTime > 0.0 && spawnId in file.campScriptEntArrays )
+ {
+ foreach( int handle in file.campScriptEntArrays[spawnId] )
+ {
+ array<entity> entities = GetScriptManagedEntArray( handle )
+ entities.removebyvalue( null )
+ foreach ( entity ent in entities )
+ {
+ if ( IsAlive( ent ) && ent.IsNPC() )
+ {
+ printt( "Killing bored AI " + ent.GetClassName() + " at " + ent.GetOrigin() )
+ ent.Die()
}
-
- // track spawns
- file.trackedCampNPCSpawns[ campIdx ][ spawnData.aitype ] += spawnCount
- spawnData.pendingSpawns -= spawnCount
}
}
+ }
+
+ if ( campLeft <= 0.0 ) // camp wiped!
+ {
+ PlayFactionDialogueToTeam( "bh_cleared" + campLetter, TEAM_IMC )
+ PlayFactionDialogueToTeam( "bh_cleared" + campLetter, TEAM_MILITIA )
+
+ entity campEnt = GetGlobalNetEnt( campEntVarName )
+ if ( IsValid( campEnt ) )
+ campEnt.Signal( "ATCampClean" ) // destroy the camp ent
+
+ // check if both camps being destroyed
+ if ( !IsValid( GetGlobalNetEnt( "camp1Ent" ) ) && !IsValid( GetGlobalNetEnt( "camp2Ent" ) ) )
+ svGlobal.levelEnt.Signal( "ATAllCampsClean" ) // end the wave
- if ( numActiveCampSpawners == 0 )
- break
-
- wait 0.5
+ return
}
-
- wait WAVE_STATE_TRANSITION_TIME
-
- // banking phase
+
+ WaitFrame()
}
}
// entity funcs
+// camp
+entity function CreateCampTracker( AT_WaveOrigin campData, int spawnId )
+{
+ // store data
+ vector campOrigin = campData.origin
+ float campRadius = campData.radius
+ float campHeight = campData.height
+ // add a minimap icon
+ entity mapIconEnt = CreateEntity( "prop_script" )
+ DispatchSpawn( mapIconEnt )
+
+ mapIconEnt.SetOrigin( campOrigin )
+ mapIconEnt.DisableHibernation()
+ SetTeam( mapIconEnt, AT_AI_TEAM )
+ mapIconEnt.Minimap_AlwaysShow( TEAM_IMC, null )
+ mapIconEnt.Minimap_AlwaysShow( TEAM_MILITIA, null )
+
+ mapIconEnt.Minimap_SetCustomState( GetCampMinimapState( spawnId ) )
+ mapIconEnt.Minimap_SetAlignUpright( true )
+ mapIconEnt.Minimap_SetZOrder( MINIMAP_Z_OBJECT )
+ mapIconEnt.Minimap_SetObjectScale( campRadius / 16000.0 ) // proper icon on the map
+
+ // attach a location tracker
+ entity tracker = GetAvailableLocationTracker()
+ tracker.SetOwner( mapIconEnt ) // needs a owner to show up
+ tracker.SetOrigin( campOrigin )
+ SetLocationTrackerRadius( tracker, campRadius )
+ SetLocationTrackerID( tracker, spawnId )
+ DispatchSpawn( tracker )
+
+ thread TrackWaveEndForCampInfo( tracker, mapIconEnt )
+ return tracker
+}
+
+void function TrackWaveEndForCampInfo( entity tracker, entity mapIconEnt )
+{
+ tracker.EndSignal( "OnDestroy" )
+ tracker.EndSignal( "ATCampClean" )
+
+ OnThreadEnd
+ (
+ function(): ( tracker, mapIconEnt )
+ {
+ // camp cleaned, wave or game ended, destroy the camp info
+ if ( IsValid( tracker ) )
+ tracker.Destroy()
+
+ if ( IsValid( mapIconEnt ) )
+ mapIconEnt.Destroy()
+ }
+ )
+
+ WaitSignal( svGlobal.levelEnt, "GameStateChanged", "ATWaveEnd" )
+}
+
+string function GetCampLetter( int spawnId )
+{
+ return spawnId == 0 ? "A" : "B"
+}
+
+int function GetCampMinimapState( int id )
+{
+ switch ( id )
+ {
+ case 0:
+ return eMinimapObject_prop_script.AT_DROPZONE_A
+ case 1:
+ return eMinimapObject_prop_script.AT_DROPZONE_B
+ case 2:
+ return eMinimapObject_prop_script.AT_DROPZONE_C
+ }
+
+ unreachable
+}
+
+//////////////////////////////
+///// CAMP FUNCTIONS END /////
+//////////////////////////////
+
+
+
+//////////////////////////
+///// BANK FUNCTIONS /////
+//////////////////////////
+
+void function AT_BankActiveThink( entity bank )
+{
+ svGlobal.levelEnt.EndSignal( "GameStateChanged" )
+ bank.EndSignal( "OnDestroy" )
+
+ // Banks closed
+ OnThreadEnd
+ (
+ function(): ( bank )
+ {
+ if ( IsValid( bank ) )
+ {
+ // Update use prompt
+ if ( GetGameState() != eGameState.Playing )
+ bank.UnsetUsable()
+ else
+ bank.SetUsePrompts( "#AT_USE_BANK_CLOSED", "#AT_USE_BANK_CLOSED" )
+
+ thread PlayAnim( bank, "mh_active_2_inactive" )
+ FadeOutSoundOnEntity( bank, "Mobile_Hardpoint_Idle", 0.5 )
+ bank.Minimap_Hide( TEAM_IMC, null )
+ bank.Minimap_Hide( TEAM_MILITIA, null )
+ }
+ }
+ )
+
+ // Update use prompt to usable
+ bank.SetUsable()
+ bank.SetUsePrompts( "#AT_USE_BANK", "#AT_USE_BANK_PC" )
+
+ thread PlayAnim( bank, "mh_inactive_2_active" )
+ EmitSoundOnEntity( bank, "Mobile_Hardpoint_Idle" )
+
+ // Show minimap icon for bank
+ bank.Minimap_AlwaysShow( TEAM_IMC, null )
+ bank.Minimap_AlwaysShow( TEAM_MILITIA, null )
+ bank.Minimap_SetCustomState( eMinimapObject_prop_script.AT_BANK )
+
+ // Wait for bank close or game end
+ while ( GetGlobalNetBool( "banksOpen" ) )
+ WaitFrame()
+}
+
+function OnPlayerUseBank( bank, player )
+{
+ // Banks are always usable so that we can show the use prompt
+ // Only allow deposit when banks are open
+ if ( !GetGlobalNetBool( "banksOpen" ) )
+ return
+
+ expect entity( bank )
+ expect entity( player )
+
+ // bank.SetUsableByGroup( "pilot" ) didn't seem to work so we just
+ // exit here if player is in a titan
+ if( player.IsTitan() )
+ return
+
+ // Player has no bonus, try to send a tip using SendHUDMessage
+ if ( AT_GetPlayerBonusPoints( player ) == 0 )
+ {
+ ATSendDepositTipToPlayer( player, "#AT_USE_BANK_NO_BONUS_HINT" )
+ return
+ }
+
+ // Prevent more than one instance of this thread running
+ if ( !file.playerBankUploading[ player ] )
+ thread PlayerUploadingBonus_Threaded( bank, player )
+}
+
+bool function ATSendDepositTipToPlayer( entity player, string message )
+{
+ if ( Time() < file.playerHudMessageAllowedTime[ player ] )
+ return false
+
+ SendHudMessage( player, message, -1, 0.4, 255, 255, 255, 255, 0.5, 1.0, 0.5 )
+ file.playerHudMessageAllowedTime[ player ] = Time() + AT_PLAYER_HUD_MESSAGE_COOLDOWN
+
+ return true
+}
+
+struct AT_playerUploadStruct
+{
+ bool uploadSuccess = false
+ int depositedPoints = 0
+}
+
+void function PlayerUploadingBonus_Threaded( entity bank, entity player )
+{
+ bank.EndSignal( "OnDestroy" )
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "OnDeath" )
+
+ file.playerBankUploading[ player ] = true
+
+ // this literally only exists because structs are passed by ref,
+ // and primitives like ints and bools are passed by val
+ // which meant that the OnThreadEnd was just getting 0 and false
+ AT_playerUploadStruct uploadInfo
+
+ // Cleanup and call finish deposit func
+ OnThreadEnd
+ (
+ function(): ( player, uploadInfo )
+ {
+ if ( IsValid( player ) )
+ {
+ file.playerBankUploading[ player ] = false
+
+ // Clean up looping sound
+ StopSoundOnEntity( player, "HUD_MP_BountyHunt_BankBonusPts_Ticker_Loop_1P" )
+ StopSoundOnEntity( player, "HUD_MP_BountyHunt_BankBonusPts_Ticker_Loop_3P" )
+
+ // Do medal event
+ // TODO: Check if vanilla actually do.s this every time you finish depositing???
+ AddPlayerScore( player, "AttritionCashedBonus" )
+
+ // Do server callback
+ Remote_CallFunction_NonReplay(
+ player,
+ "ServerCallback_AT_FinishDeposit",
+ uploadInfo.depositedPoints // deposit
+ )
+
+ player.SetPlayerNetBool( "AT_playerUploading", false )
+
+ if ( uploadInfo.uploadSuccess ) // Player deposited all remaining bonus
+ {
+ // Emit uploading successful sound
+ EmitSoundOnEntityOnlyToPlayer( player, player, "HUD_MP_BountyHunt_BankBonusPts_Deposit_End_Successful_1P" )
+ EmitSoundOnEntityExceptToPlayer( player, player, "HUD_MP_BountyHunt_BankBonusPts_Deposit_End_Successful_3P" )
+
+ // player is MVP
+ int ourScore = player.GetPlayerGameStat( PGS_ASSAULT_SCORE )
+ bool isMVP = true
+ foreach(teamPlayer in GetPlayerArrayOfTeam(player.GetTeam()))
+ {
+ if (ourScore < teamPlayer.GetPlayerGameStat( PGS_ASSAULT_SCORE ))
+ {
+ isMVP = false
+ break
+ }
+ }
+ if (isMVP)
+ PlayFactionDialogueToPlayer( "bh_mvp", player )
+ }
+ else // Player was killed or left the bank radius
+ {
+ // Emit uploading failed sound
+ EmitSoundOnEntityOnlyToPlayer( player, player, "HUD_MP_BountyHunt_BankBonusPts_Deposit_End_Unsuccessful_1P" )
+ EmitSoundOnEntityExceptToPlayer( player, player, "HUD_MP_BountyHunt_BankBonusPts_Deposit_End_Unsuccessful_3P" )
+ }
+ }
+ }
+ )
+
+ // Uploading start sound
+ EmitSoundOnEntityOnlyToPlayer( player, player, "HUD_MP_BountyHunt_BankBonusPts_Deposit_Start_1P" )
+ EmitSoundOnEntityExceptToPlayer( player, player, "HUD_MP_BountyHunt_BankBonusPts_Deposit_Start_3P" )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "HUD_MP_BountyHunt_BankBonusPts_Ticker_Loop_1P" )
+ EmitSoundOnEntityExceptToPlayer( player, player, "HUD_MP_BountyHunt_BankBonusPts_Ticker_Loop_3P" )
+
+ player.SetPlayerNetBool( "AT_playerUploading", true )
+
+ // Upload bonus while the player is within range of the bank
+ while ( Distance( player.GetOrigin(), bank.GetOrigin() ) <= AT_BANK_DEPOSIT_RADIUS && GetGlobalNetBool( "banksOpen" ) )
+ {
+ // Calling this moves the "Uploading..." graphic to the same place it is
+ // in vanilla
+ Remote_CallFunction_NonReplay( player, "ServerCallback_AT_ShowRespawnBonusLoss" )
+
+ int bonusToUpload = int( min( AT_BANK_DEPOSIT_RATE, AT_GetPlayerBonusPoints( player ) ) )
+ // No more bonus to upload, return
+ if ( bonusToUpload == 0 )
+ {
+ uploadInfo.uploadSuccess = true
+ return
+ }
+
+ // Remove bonus points and add them to total poins
+ AT_AddPlayerBonusPoints( player, -bonusToUpload )
+ AT_AddPlayerTotalPoints( player, bonusToUpload )
+
+ uploadInfo.depositedPoints += bonusToUpload
+ WaitFrame()
+ }
+}
+
+//////////////////////////////
+///// BANK FUNCTIONS END /////
+//////////////////////////////
-void function AT_SpawnDroppodSquad( int camp, string aiType )
+
+
+/////////////////////////
+///// NPC FUNCTIONS /////
+/////////////////////////
+
+int function GetScriptManagedNPCArrayLength_Alive( int scriptManagerId )
+{
+ array<entity> entities = GetScriptManagedEntArray( scriptManagerId )
+ entities.removebyvalue( null )
+ int npcsAlive = 0
+ foreach ( entity ent in entities )
+ {
+ if ( IsAlive( ent ) && ent.IsNPC() )
+ npcsAlive += 1
+ }
+ return npcsAlive
+}
+
+void function AT_DroppodSquadEvent( AT_WaveOrigin campData, int spawnId, array<AT_SpawnData> minionDatas )
+{
+ svGlobal.levelEnt.EndSignal( "GameStateChanged" )
+ // create a script managed array for all handled minions
+ int eventManager = CreateScriptManagedEntArray()
+
+ if( !(spawnId in file.campScriptEntArrays) )
+ file.campScriptEntArrays[spawnId] <- []
+
+ file.campScriptEntArrays[spawnId].append(eventManager)
+
+ int totalAllowedOnField = SQUAD_SIZE * AT_DROPPOD_SQUADS_ALLOWED_ON_FIELD
+ while ( true )
+ {
+ foreach ( AT_SpawnData data in minionDatas )
+ {
+ string ent = data.aitype
+ waitthread AT_SpawnDroppodSquad( campData, spawnId, ent, eventManager )
+ data.pendingSpawns -= SQUAD_SIZE
+ if ( data.pendingSpawns <= 0 ) // current spawn data has reached max spawn amount
+ minionDatas.removebyvalue( data ) // remove this data
+ if ( GetScriptManagedNPCArrayLength_Alive( eventManager ) >= totalAllowedOnField ) // we have enough npcs on field?
+ break // stop following spawning functions
+ }
+ if ( minionDatas.len() == 0 ) // all spawn data has finished spawn
+ return
+
+ int npcOnFieldCount = GetScriptManagedNPCArrayLength_Alive( eventManager )
+ while ( npcOnFieldCount >= totalAllowedOnField - SQUAD_SIZE ) // wait until we have lost more than 1 squad
+ {
+ WaitFrame()
+ npcOnFieldCount = GetScriptManagedNPCArrayLength_Alive( eventManager )
+ }
+ }
+}
+
+// for AT_USE_TOTAL_ALLOWED_ON_FIELD_CHECK, handles a single spawndata
+void function AT_DroppodSquadEvent_Single( AT_WaveOrigin campData, int spawnId, AT_SpawnData data )
+{
+ svGlobal.levelEnt.EndSignal( "GameStateChanged" )
+
+ // get ent and create a script managed array for current event
+ string ent = data.aitype
+ int eventManager = CreateScriptManagedEntArray()
+
+ if( !(spawnId in file.campScriptEntArrays) )
+ file.campScriptEntArrays[spawnId] <- []
+
+ file.campScriptEntArrays[spawnId].append(eventManager)
+
+ int totalAllowedOnField = data.totalAllowedOnField // mostly 12 for grunts and spectres, too much!
+ // start spawner
+ while ( true )
+ {
+ waitthread AT_SpawnDroppodSquad( campData, spawnId, ent, eventManager )
+ data.pendingSpawns -= SQUAD_SIZE
+ if ( data.pendingSpawns <= 0 ) // we have reached max npcs
+ return // stop any spawning functions
+
+ int npcOnFieldCount = GetScriptManagedNPCArrayLength_Alive( eventManager )
+ while ( npcOnFieldCount >= totalAllowedOnField - SQUAD_SIZE ) // wait until we have less npcs than allowed count
+ {
+ WaitFrame()
+ npcOnFieldCount = GetScriptManagedNPCArrayLength_Alive( eventManager )
+ }
+ }
+}
+
+void function AT_SpawnDroppodSquad( AT_WaveOrigin campData, int spawnId, string aiType, int scriptManagerId )
{
entity spawnpoint
- if ( file.camps[ camp ].dropPodSpawnPoints.len() == 0 )
- spawnpoint = file.camps[ camp ].ent
+ if ( campData.dropPodSpawnPoints.len() == 0 )
+ spawnpoint = campData.ent
else
- spawnpoint = file.camps[ camp ].dropPodSpawnPoints.getrandom()
+ spawnpoint = campData.dropPodSpawnPoints.getrandom()
+ // anti-crash
+ if ( !IsValid( spawnpoint ) )
+ spawnpoint = campData.ent
// add variation to spawns
wait RandomFloat( 1.0 )
- AiGameModes_SpawnDropPod( spawnpoint.GetOrigin(), spawnpoint.GetAngles(), BH_AI_TEAM, aiType, void function( array<entity> guys ) : ( camp, aiType )
- {
- AT_HandleSquadSpawn( guys, camp, aiType )
- })
+ AiGameModes_SpawnDropPod(
+ spawnpoint.GetOrigin(),
+ spawnpoint.GetAngles(),
+ AT_AI_TEAM,
+ aiType,
+ // squad handler
+ void function( array<entity> guys ) : ( campData, spawnId, aiType, scriptManagerId )
+ {
+ AT_HandleSquadSpawn( guys, campData, spawnId, aiType, scriptManagerId )
+ },
+ eDropPodFlag.DISSOLVE_AFTER_DISEMBARKS
+ )
}
-void function AT_HandleSquadSpawn( array<entity> guys, int camp, string aiType )
+void function AT_HandleSquadSpawn( array<entity> guys, AT_WaveOrigin campData, int spawnId, string aiType, int scriptManagerId )
{
foreach ( entity guy in guys )
{
- guy.EnableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE | NPC_ALLOW_HAND_SIGNALS | NPC_ALLOW_FLEE )
-
- // untrack them on death
- thread AT_WaitToUntrackNPC( guy, camp, aiType )
+ // TODO: NPCs still seem to go outside their camp ???
+ //guy.EnableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_HAND_SIGNALS | NPC_ALLOW_FLEE )
+
+ // tracking lifetime
+ AddToScriptManagedEntArray( scriptManagerId, guy )
+ thread AT_TrackNPCLifeTime( guy, spawnId, aiType )
+
+ thread AT_ForceAssaultAroundCamp( guy, campData )
+ }
+}
+
+void function AT_ForceAssaultAroundCamp( entity guy, AT_WaveOrigin campData )
+{
+ guy.EndSignal( "OnDestroy" )
+ guy.EndSignal( "OnDeath" )
+
+ // goal check
+ vector ornull goalPos = NavMesh_ClampPointForAI(campData.origin, guy)
+ goalPos = goalPos == null ? campData.origin : goalPos
+ expect vector(goalPos)
+
+ float goalRadius = campData.radius / 4
+ float guyGoalRadius = guy.GetMinGoalRadius()
+ if ( guyGoalRadius > goalRadius ) // this npc cannot use forced goal radius?
+ goalRadius = guyGoalRadius
+
+ while( true )
+ {
+ guy.AssaultPoint( goalPos )
+ guy.AssaultSetGoalRadius( goalRadius )
+ guy.AssaultSetFightRadius( 0 )
+ guy.AssaultSetArrivalTolerance( int(goalRadius) )
+
+ wait RandomFloatRange( 1, 5 )
+ }
+}
+
+void function AT_ReaperEvent( AT_WaveOrigin campData, int spawnId, AT_SpawnData data )
+{
+ svGlobal.levelEnt.EndSignal( "GameStateChanged" )
+
+ // create a script managed array for current event
+ int eventManager = CreateScriptManagedEntArray()
+
+ if( !(spawnId in file.campScriptEntArrays) )
+ file.campScriptEntArrays[spawnId] <- []
+
+ file.campScriptEntArrays[spawnId].append(eventManager)
+
+ int totalAllowedOnField = 1 // 1 allowed at the same time for heavy armor units
+ if ( AT_USE_TOTAL_ALLOWED_ON_FIELD_CHECK )
+ totalAllowedOnField = data.totalAllowedOnField
+
+ while ( true )
+ {
+ waitthread AT_SpawnReaper( campData, spawnId, eventManager )
+ data.pendingSpawns -= 1
+ if ( data.pendingSpawns <= 0 ) // we have reached max npcs
+ return // stop any spawning functions
+
+ int npcOnFieldCount = GetScriptManagedNPCArrayLength_Alive( eventManager )
+ while ( npcOnFieldCount >= totalAllowedOnField ) // wait until we have less npcs than allowed count
+ {
+ WaitFrame()
+ npcOnFieldCount = GetScriptManagedNPCArrayLength_Alive( eventManager )
+ }
}
}
-void function AT_SpawnReaper( int camp )
+void function AT_SpawnReaper( AT_WaveOrigin campData, int spawnId, int scriptManagerId )
{
entity spawnpoint
- if ( file.camps[ camp ].dropPodSpawnPoints.len() == 0 )
- spawnpoint = file.camps[ camp ].ent
+ if ( campData.dropPodSpawnPoints.len() == 0 )
+ spawnpoint = campData.ent
else
- spawnpoint = file.camps[ camp ].titanSpawnPoints.getrandom()
+ spawnpoint = campData.dropPodSpawnPoints.getrandom()
+ // anti-crash
+ if ( !IsValid( spawnpoint ) )
+ spawnpoint = campData.ent
// add variation to spawns
wait RandomFloat( 1.0 )
- AiGameModes_SpawnReaper( spawnpoint.GetOrigin(), spawnpoint.GetAngles(), BH_AI_TEAM, "npc_super_spectre",void function( entity reaper ) : ( camp )
+ AiGameModes_SpawnReaper(
+ spawnpoint.GetOrigin(),
+ spawnpoint.GetAngles(),
+ AT_AI_TEAM,
+ "npc_super_spectre_aitdm",
+ // reaper handler
+ void function( entity reaper ) : ( campData, spawnId, scriptManagerId )
+ {
+ AT_HandleReaperSpawn( reaper, campData, spawnId, scriptManagerId )
+ }
+ )
+}
+
+void function AT_HandleReaperSpawn( entity reaper, AT_WaveOrigin campData, int spawnId, int scriptManagerId )
+{
+ // tracking lifetime
+ AddToScriptManagedEntArray( scriptManagerId, reaper )
+ thread AT_TrackNPCLifeTime( reaper, spawnId, "npc_super_spectre" )
+
+ thread AT_ForceAssaultAroundCamp( reaper, campData )
+}
+
+void function AT_BountyTitanEvent( AT_WaveOrigin campData, int spawnId, AT_SpawnData data )
+{
+ svGlobal.levelEnt.EndSignal( "GameStateChanged" )
+
+ // create a script managed array for current event
+ int eventManager = CreateScriptManagedEntArray()
+
+ int totalAllowedOnField = 1 // 1 allowed at the same time for heavy armor units
+ if ( AT_USE_TOTAL_ALLOWED_ON_FIELD_CHECK )
+ totalAllowedOnField = data.totalAllowedOnField
+ while ( true )
{
- thread AT_WaitToUntrackNPC( reaper, camp, "npc_super_spectre" )
- })
+ waitthread AT_SpawnBountyTitan( campData, spawnId, eventManager )
+ data.pendingSpawns -= 1
+ if ( data.pendingSpawns <= 0 ) // we have reached max npcs
+ return // stop any spawning functions
+
+ int npcOnFieldCount = GetScriptManagedNPCArrayLength_Alive( eventManager )
+ while ( npcOnFieldCount >= totalAllowedOnField ) // wait until we have less npcs than allowed count
+ {
+ WaitFrame()
+ npcOnFieldCount = GetScriptManagedNPCArrayLength_Alive( eventManager )
+ }
+ }
}
-void function AT_SpawnBountyTitan( int camp )
+void function AT_SpawnBountyTitan( AT_WaveOrigin campData, int spawnId, int scriptManagerId )
{
entity spawnpoint
- if ( file.camps[ camp ].dropPodSpawnPoints.len() == 0 )
- spawnpoint = file.camps[ camp ].ent
+ if ( campData.titanSpawnPoints.len() == 0 )
+ spawnpoint = campData.ent
else
- spawnpoint = file.camps[ camp ].titanSpawnPoints.getrandom()
+ spawnpoint = campData.titanSpawnPoints.getrandom()
+ // anti-crash
+ if ( !IsValid( spawnpoint ) )
+ spawnpoint = campData.ent
// add variation to spawns
wait RandomFloat( 1.0 )
@@ -320,57 +1630,178 @@ void function AT_SpawnBountyTitan( int camp )
int bountyID = 0
try
{
- bountyID = ReserveBossID( VALID_BOUNTY_TITAN_SETTINGS.getrandom() )
+ bountyID = ReserveBossID( AT_BOUNTY_TITANS_AI_SETTINGS.getrandom() )
}
catch ( ex ) {} // if we go above the expected wave count that vanilla supports, there's basically no way to ensure that this func won't error, so default 0 after that point
string aisettings = GetTypeFromBossID( bountyID )
string titanClass = expect string( Dev_GetAISettingByKeyField_Global( aisettings, "npc_titan_player_settings" ) )
+ AiGameModes_SpawnTitan(
+ spawnpoint.GetOrigin(),
+ spawnpoint.GetAngles(),
+ AT_AI_TEAM,
+ titanClass,
+ aisettings,
+ // titan handler
+ void function( entity titan ) : ( campData, spawnId, bountyID, scriptManagerId )
+ {
+ AT_HandleBossTitanSpawn( titan, campData, spawnId, bountyID, scriptManagerId )
+ }
+ )
+}
+
+void function AT_HandleBossTitanSpawn( entity titan, AT_WaveOrigin campData, int spawnId, int bountyID, int scriptManagerId )
+{
+ // set the bounty to be campEnt, for client tracking
+ SetGlobalNetEnt( "camp" + string( spawnId + 1 ) + "Ent", titan )
+ // set up health
+ titan.SetMaxHealth( titan.GetMaxHealth() * AT_BOUNTY_TITAN_HEALTH_MULTIPLIER )
+ titan.SetHealth( titan.GetMaxHealth() )
+ // make minimap always show them and highlight them
+ titan.Minimap_AlwaysShow( TEAM_IMC, null )
+ titan.Minimap_AlwaysShow( TEAM_MILITIA, null )
+ thread BountyBossHighlightThink( titan )
+
+ // set up titan-specific death callbacks, mark it as bounty boss
+ file.titanIsBountyBoss[ titan ] <- true
+ file.bountyTitanRewards[ titan ] <- ATTRITION_SCORE_BOSS_DAMAGE
+ AddEntityCallback_OnPostDamaged( titan, OnBountyTitanPostDamage )
+ AddEntityCallback_OnKilled( titan, OnBountyTitanKilled )
- AiGameModes_SpawnTitan( spawnpoint.GetOrigin(), spawnpoint.GetAngles(), BH_AI_TEAM, titanClass, aisettings, void function( entity titan ) : ( camp, bountyID )
+ titan.GetTitanSoul().soul.skipDoomState = true
+ // i feel like this should be localised, but there's nothing for it in r1_english?
+ titan.SetTitle( GetNameFromBossID( bountyID ) )
+
+ // tracking lifetime
+ AddToScriptManagedEntArray( scriptManagerId, titan )
+ thread AT_TrackNPCLifeTime( titan, spawnId, "npc_titan" )
+}
+
+void function BountyBossHighlightThink( entity titan )
+{
+ titan.EndSignal( "OnDestroy" )
+ titan.EndSignal( "OnDeath" )
+
+ while ( true )
{
- // set up titan-specific death/damage callbacks
- AddEntityCallback_OnDamaged( titan, OnBountyDamaged)
- AddEntityCallback_OnKilled( titan, OnBountyKilled )
-
- titan.GetTitanSoul().soul.skipDoomState = true
- // i feel like this should be localised, but there's nothing for it in r1_english?
- titan.SetTitle( GetNameFromBossID( bountyID ) )
- thread AT_WaitToUntrackNPC( titan, camp, "npc_titan" )
- } )
+ Highlight_SetEnemyHighlight( titan, "enemy_boss_bounty" )
+ titan.WaitSignal( "StopPhaseShift" ) // prevent phase shift mess up highlights
+ }
}
-// Tracked entities will require their own "wallet"
-// for titans it should be used for rounding error compenstation
-// for infantry it sould be used to store money if the npc kills a player
-void function OnBountyDamaged( entity titan, var damageInfo )
+void function OnBountyTitanPostDamage( entity titan, var damageInfo )
{
entity attacker = DamageInfo_GetAttacker( damageInfo )
+ if ( !IsValid( attacker ) ) // delayed by projectile shots
+ return
+ // damaged by npc or something?
if ( !attacker.IsPlayer() )
- attacker = GetLatestAssistingPlayerInfo( titan ).player
-
- if ( IsValid( attacker ) && attacker.IsPlayer() )
{
- int reward = int ( BOUNTY_TITAN_DAMAGE_POOL * DamageInfo_GetDamage( damageInfo ) / titan.GetMaxHealth() )
- printt ( titan.GetMaxHealth(), DamageInfo_GetDamage( damageInfo ) )
-
- AT_AddPlayerCash( attacker, reward )
+ attacker = GetBountyBossDamageOwner( attacker, titan )
+ if ( !IsValid( attacker ) || !attacker.IsPlayer() )
+ return
}
+
+ int rewardSegment = ATTRITION_SCORE_BOSS_DAMAGE
+ int healthSegment = titan.GetMaxHealth() / rewardSegment
+
+ // sometimes damage is not enough to add 1 point, we save the damage for player's next attack
+ if ( !( titan in file.playerSavedBountyDamage[ attacker ] ) )
+ file.playerSavedBountyDamage[ attacker ][ titan ] <- 0
+
+ file.playerSavedBountyDamage[ attacker ][ titan ] += int( DamageInfo_GetDamage( damageInfo ) )
+ if ( file.playerSavedBountyDamage[ attacker ][ titan ] < healthSegment )
+ return // they can't earn reward from this shot
+
+ int damageSegment = file.playerSavedBountyDamage[ attacker ][ titan ] / healthSegment
+ int savedDamageLeft = file.playerSavedBountyDamage[ attacker ][ titan ] % healthSegment
+ file.playerSavedBountyDamage[ attacker ][ titan ] = savedDamageLeft
+
+ float damageFrac = float( damageSegment ) / rewardSegment
+ int rewardLeft = file.bountyTitanRewards[ titan ]
+ int reward = int( ATTRITION_SCORE_BOSS_DAMAGE * damageFrac )
+ if ( reward >= rewardLeft ) // overloaded shot?
+ reward = rewardLeft
+ file.bountyTitanRewards[ titan ] -= reward
+
+ if ( reward > 0 )
+ AT_AddPlayerBonusPointsForBossDamaged( attacker, titan, reward, damageInfo )
}
-void function OnBountyKilled( entity titan, var damageInfo )
+void function OnBountyTitanKilled( entity titan, var damageInfo )
{
entity attacker = DamageInfo_GetAttacker( damageInfo )
+ if ( !IsValid( attacker ) ) // delayed by projectile shots
+ return
+ // damaged by npc or something?
if ( !attacker.IsPlayer() )
- attacker = GetLatestAssistingPlayerInfo( titan ).player
+ {
+ attacker = GetBountyBossDamageOwner( attacker, titan )
+ if ( !IsValid( attacker ) || !attacker.IsPlayer() )
+ return
+ }
+
+ // add all remaining reward to attacker
+ // bounty killed bonus handled by AT_PlayerOrNPCKilledScoreEvent()
+ int rewardLeft = file.bountyTitanRewards[ titan ]
+ delete file.bountyTitanRewards[ titan ]
+ if ( rewardLeft > 0 )
+ AT_AddPlayerBonusPointsForBossDamaged( attacker, titan, rewardLeft, damageInfo )
+
+ // remove this bounty's damage saver
+ foreach ( entity player in GetPlayerArray() )
+ {
+ if ( titan in file.playerSavedBountyDamage[ player ] )
+ delete file.playerSavedBountyDamage[ player ][ titan ]
+ }
+
+ // faction dialogue
+ int team = attacker.GetTeam()
+ PlayFactionDialogueToPlayer( "bh_playerKilledBounty", attacker )
+ PlayFactionDialogueToTeamExceptPlayer( "bh_bountyClaimedByFriendly", team, attacker )
+ PlayFactionDialogueToTeam( "bh_bountyClaimedByEnemy", GetOtherTeam( team ) )
+}
+
+entity function GetBountyBossDamageOwner( entity attacker, entity titan )
+{
+ if ( attacker.IsPlayer() ) // already a player
+ return attacker
- if ( IsValid( attacker ) && attacker.IsPlayer() )
- AT_AddPlayerCash( attacker, BOUNTY_TITAN_KILL_REWARD )
+ if ( attacker.IsTitan() ) // attacker is a npc titan
+ {
+ // try to find it's pet titan owner
+ if ( IsValid( GetPetTitanOwner( attacker ) ) )
+ return GetPetTitanOwner( attacker )
+ }
+
+ // other damages or non-owner npcs, not sure how it happens, just use this titan's last attacker
+ return GetLatestAssistingPlayerInfo( titan ).player
}
-void function AT_WaitToUntrackNPC( entity guy, int camp, string aiType )
+void function AT_TrackNPCLifeTime( entity guy, int spawnId, string aiType )
{
guy.WaitSignal( "OnDeath", "OnDestroy" )
- file.trackedCampNPCSpawns[ camp ][ aiType ]--
+
+ string npcNetVar = GetNPCNetVarName( aiType, spawnId )
+ SetGlobalNetInt( npcNetVar, GetGlobalNetInt( npcNetVar ) - 1 )
+}
+
+
+// network var
+string function GetNPCNetVarName( string className, int spawnId )
+{
+ string npcId = string( GetAiTypeInt( className ) + 1 )
+ string campLetter = GetCampLetter( spawnId )
+ if ( npcId == "0" ) // cannot find this ai support!
+ {
+ if ( className == "npc_super_spectre" ) // stupid, reapers are not handled by GetAiTypeInt(), but it must be 4
+ return "4" + campLetter + "campCount"
+ return ""
+ }
+ return npcId + campLetter + "campCount"
}
+
+/////////////////////////////
+///// NPC FUNCTIONS END /////
+/////////////////////////////
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut
index 705b7836..d8b0c9bd 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut
@@ -29,6 +29,8 @@ struct {
array<HardpointStruct> hardpoints
array<CP_PlayerStruct> players
+ table<entity,int> playerAssaultPoints
+ table<entity,int> playerDefensePoints
} file
void function GamemodeCP_Init()
@@ -112,11 +114,13 @@ void function GamemodeCP_OnPlayerKilled(entity victim, entity attacker, var dama
{
AddPlayerScore( attacker , "HardpointDefense", victim )
attacker.AddToPlayerGameStat(PGS_DEFENSE_SCORE,POINTVALUE_HARDPOINT_DEFENSE)
+ UpdatePlayerScoreForChallenge(attacker,0,POINTVALUE_HARDPOINT_DEFENSE)
}
else if((victimCP.hardpoint.GetTeam()==victim.GetTeam())||(GetHardpointCappingTeam(victimCP)==victim.GetTeam()))
{
AddPlayerScore( attacker, "HardpointAssault", victim )
attacker.AddToPlayerGameStat(PGS_ASSAULT_SCORE,POINTVALUE_HARDPOINT_ASSAULT)
+ UpdatePlayerScoreForChallenge(attacker,POINTVALUE_HARDPOINT_ASSAULT,0)
}
}
}
@@ -127,10 +131,12 @@ void function GamemodeCP_OnPlayerKilled(entity victim, entity attacker, var dama
{
AddPlayerScore( attacker , "HardpointSnipe", victim )
attacker.AddToPlayerGameStat(PGS_ASSAULT_SCORE,POINTVALUE_HARDPOINT_SNIPE)
+ UpdatePlayerScoreForChallenge(attacker,POINTVALUE_HARDPOINT_SNIPE,0)
}
else{
AddPlayerScore( attacker , "HardpointSiege", victim )
attacker.AddToPlayerGameStat(PGS_ASSAULT_SCORE,POINTVALUE_HARDPOINT_SIEGE)
+ UpdatePlayerScoreForChallenge(attacker,POINTVALUE_HARDPOINT_SIEGE,0)
}
}
else if(attackerCP.hardpoint!=null)//Perimeter Defense
@@ -138,6 +144,7 @@ void function GamemodeCP_OnPlayerKilled(entity victim, entity attacker, var dama
if(attackerCP.hardpoint.GetTeam()==attacker.GetTeam())
AddPlayerScore( attacker , "HardpointPerimeterDefense", victim)
attacker.AddToPlayerGameStat(PGS_DEFENSE_SCORE,POINTVALUE_HARDPOINT_PERIMETER_DEFENSE)
+ UpdatePlayerScoreForChallenge(attacker,0,POINTVALUE_HARDPOINT_PERIMETER_DEFENSE)
}
foreach(CP_PlayerStruct player in file.players) //Reset Victim Holdtime Counter
@@ -308,6 +315,7 @@ void function CapturePointForTeam(HardpointStruct hardpoint, int Team)
if(player.IsPlayer()){
AddPlayerScore(player,"ControlPointCapture")
player.AddToPlayerGameStat(PGS_ASSAULT_SCORE,POINTVALUE_HARDPOINT_CAPTURE)
+ UpdatePlayerScoreForChallenge(player,POINTVALUE_HARDPOINT_CAPTURE,0)
}
}
}
@@ -319,12 +327,17 @@ void function GamemodeCP_InitPlayer(entity player)
playerStruct.timeOnPoints = [0.0,0.0,0.0]
playerStruct.isOnHardpoint = false
file.players.append(playerStruct)
+ file.playerAssaultPoints[player] <- 0
+ file.playerDefensePoints[player] <- 0
thread PlayerThink(playerStruct)
}
void function GamemodeCP_RemovePlayer(entity player)
{
-
+ if(player in file.playerAssaultPoints)
+ delete file.playerAssaultPoints[player]
+ if(player in file.playerDefensePoints)
+ delete file.playerDefensePoints[player]
foreach(index,CP_PlayerStruct playerStruct in file.players)
{
if(playerStruct.player==player)
@@ -376,11 +389,13 @@ void function PlayerThink(CP_PlayerStruct player)
{
AddPlayerScore(player.player,"ControlPointAmpedHold")
player.player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, POINTVALUE_HARDPOINT_AMPED_HOLD )
+ UpdatePlayerScoreForChallenge(player.player,0,POINTVALUE_HARDPOINT_AMPED_HOLD)
}
else
{
AddPlayerScore(player.player,"ControlPointHold")
player.player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, POINTVALUE_HARDPOINT_HOLD )
+ UpdatePlayerScoreForChallenge(player.player,0,POINTVALUE_HARDPOINT_HOLD)
}
}
break
@@ -471,8 +486,10 @@ void function HardpointThink( HardpointStruct hardpoint )
}
else if(cappingTeam==TEAM_UNASSIGNED) // nobody on point
{
- if((GetHardpointState(hardpoint)==CAPTURE_POINT_STATE_AMPED)||(GetHardpointState(hardpoint)==CAPTURE_POINT_STATE_AMPING))
+ if((GetHardpointState(hardpoint)>=CAPTURE_POINT_STATE_AMPED) || (GetHardpointState(hardpoint)==CAPTURE_POINT_STATE_SELF_UNAMPING))
{
+ if (GetHardpointState(hardpoint) == CAPTURE_POINT_STATE_AMPED)
+ SetHardpointState(hardpoint,CAPTURE_POINT_STATE_SELF_UNAMPING) // plays a pulsating effect on the UI only when the hardpoint is amped
SetHardpointCappingTeam(hardpoint,hardpointEnt.GetTeam())
SetHardpointCaptureProgress(hardpoint,max(1.0,GetHardpointCaptureProgress(hardpoint)-(deltaTime/HARDPOINT_AMPED_DELAY)))
if(GetHardpointCaptureProgress(hardpoint)<=1.001) // unamp
@@ -546,8 +563,10 @@ void function HardpointThink( HardpointStruct hardpoint )
}
else if(file.ampingEnabled)//amping or reamping
{
- if(GetHardpointState(hardpoint)<CAPTURE_POINT_STATE_AMPING)
- SetHardpointState(hardpoint,CAPTURE_POINT_STATE_AMPING)
+ // i have no idea why but putting it CAPTURE_POINT_STATE_AMPING will say 'CONTESTED' on the UI
+ // since whether the point is contested is checked above, putting the hardpoint state to a value of 8 fixes it somehow
+ if(GetHardpointState(hardpoint)<=CAPTURE_POINT_STATE_AMPING)
+ SetHardpointState( hardpoint, 8 )
SetHardpointCaptureProgress( hardpoint, min( 2.0, GetHardpointCaptureProgress( hardpoint ) + ( deltaTime / HARDPOINT_AMPED_DELAY * capperAmount ) ) )
if(GetHardpointCaptureProgress(hardpoint)==2.0&&!(GetHardpointState(hardpoint)==CAPTURE_POINT_STATE_AMPED))
{
@@ -570,6 +589,7 @@ void function HardpointThink( HardpointStruct hardpoint )
{
AddPlayerScore(player,"ControlPointAmped")
player.AddToPlayerGameStat(PGS_DEFENSE_SCORE,POINTVALUE_HARDPOINT_AMPED)
+ UpdatePlayerScoreForChallenge(player,0,POINTVALUE_HARDPOINT_AMPED)
}
}
}
@@ -645,7 +665,10 @@ void function OnHardpointEntered( entity trigger, entity player )
hardpoint.militiaCappers.append( player )
foreach(CP_PlayerStruct playerStruct in file.players)
if(playerStruct.player == player)
+ {
playerStruct.isOnHardpoint = true
+ player.SetPlayerNetInt( "playerHardpointID", hardpoint.hardpoint.GetHardpointID() )
+ }
}
void function OnHardpointLeft( entity trigger, entity player )
@@ -661,7 +684,10 @@ void function OnHardpointLeft( entity trigger, entity player )
FindAndRemove( hardpoint.militiaCappers, player )
foreach(CP_PlayerStruct playerStruct in file.players)
if(playerStruct.player == player)
+ {
playerStruct.isOnHardpoint = false
+ player.SetPlayerNetInt( "playerHardpointID", 69 ) // an arbitary number to remove the hud from the player
+ }
}
string function CaptureStateToString( int state )
@@ -675,6 +701,7 @@ string function CaptureStateToString( int state )
case CAPTURE_POINT_STATE_CAPTURED:
return "CAPTURED"
case CAPTURE_POINT_STATE_AMPING:
+ case 8:
return "AMPING"
case CAPTURE_POINT_STATE_AMPED:
return "AMPED"
@@ -703,3 +730,26 @@ string function GetHardpointGroup(entity hardpoint) //Hardpoint Entity B on Home
return string(hardpoint.kv.hardpointGroup)
}
+
+void function UpdatePlayerScoreForChallenge(entity player,int assaultpoints = 0,int defensepoints = 0)
+{
+ if(player in file.playerAssaultPoints)
+ {
+ file.playerAssaultPoints[player] += assaultpoints
+ if( file.playerAssaultPoints[player] >= 1000 && !HasPlayerCompletedMeritScore(player) )
+ {
+ AddPlayerScore(player,"ChallengeCPAssault")
+ SetPlayerChallengeMeritScore(player)
+ }
+ }
+
+ if(player in file.playerDefensePoints)
+ {
+ file.playerDefensePoints[player] += defensepoints
+ if( file.playerDefensePoints[player] >= 500 && !HasPlayerCompletedMeritScore(player) )
+ {
+ AddPlayerScore(player,"ChallengeCPDefense")
+ SetPlayerChallengeMeritScore(player)
+ }
+ }
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
index 99f34164..97addc24 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
@@ -26,6 +26,8 @@ void function CaptureTheFlag_Init()
{
PrecacheModel( CTF_FLAG_MODEL )
PrecacheModel( CTF_FLAG_BASE_MODEL )
+ PrecacheParticleSystem( FLAG_FX_FRIENDLY )
+ PrecacheParticleSystem( FLAG_FX_ENEMY )
CaptureTheFlagShared_Init()
SetSwitchSidesBased( true )
@@ -73,27 +75,20 @@ void function RateSpawnpoints_CTF( int checkClass, array<entity> spawnpoints, in
bool function VerifyCTFSpawnpoint( entity spawnpoint, int team )
{
// ensure spawnpoints aren't too close to enemy base
+ vector allyFlagSpot
+ vector enemyFlagSpot
+ foreach ( entity spawn in GetEntArrayByClass_Expensive( "info_spawnpoint_flag" ) )
+ {
+ if( spawn.GetTeam() == team )
+ allyFlagSpot = spawn.GetOrigin()
+ else
+ enemyFlagSpot = spawn.GetOrigin()
+ }
- if ( HasSwitchedSides() )
- team = GetOtherTeam( team )
-
- array<entity> startSpawns = SpawnPoints_GetPilotStart( team )
- array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( team ) )
-
- vector averageFriendlySpawns
- vector averageEnemySpawns
-
- foreach ( entity spawn in startSpawns )
- averageFriendlySpawns += spawn.GetOrigin()
-
- averageFriendlySpawns /= startSpawns.len()
-
- foreach ( entity spawn in enemyStartSpawns )
- averageEnemySpawns += spawn.GetOrigin()
-
- averageEnemySpawns /= startSpawns.len()
+ if( Distance2D( spawnpoint.GetOrigin(), allyFlagSpot ) > Distance2D( spawnpoint.GetOrigin(), enemyFlagSpot ) )
+ return false
- return Distance2D( spawnpoint.GetOrigin(), averageEnemySpawns ) / Distance2D( averageFriendlySpawns, averageEnemySpawns ) > 0.35
+ return true
}
void function CTFInitPlayer( entity player )
@@ -164,6 +159,9 @@ void function CreateFlags()
flag.SetValueForModelKey( CTF_FLAG_MODEL )
SetTeam( flag, flagTeam )
flag.MarkAsNonMovingAttachment()
+ flag.Minimap_AlwaysShow( TEAM_IMC, null ) // show flag icon on minimap
+ flag.Minimap_AlwaysShow( TEAM_MILITIA, null )
+ flag.Minimap_SetAlignUpright( true )
DispatchSpawn( flag )
flag.SetModel( CTF_FLAG_MODEL )
flag.SetOrigin( spawn.GetOrigin() + < 0, 0, base.GetBoundingMaxs().z * 2 > ) // ensure flag doesn't spawn clipped into geometry
@@ -291,7 +289,7 @@ void function GiveFlag( entity player, entity flag )
PlayFactionDialogueToTeamExceptPlayer( "ctf_flagPickupFriendly", player.GetTeam(), player )
MessageToTeam( flag.GetTeam(), eEventNotifications.PlayerHasFriendlyFlag, player, player )
- EmitSoundOnEntityToTeam( flag, "UI_CTF_EnemyGrabFlag", flag.GetTeam() )
+ EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyGrabFlag", flag.GetTeam() )
SetFlagStateForTeam( flag.GetTeam(), eFlagState.Away ) // used for held
}
@@ -339,14 +337,13 @@ void function DropFlag( entity player, bool realDrop = true )
file.imcCaptureAssistList.append( player )
else
file.militiaCaptureAssistList.append( player )
-
+
// do notifications
MessageToPlayer( player, eEventNotifications.YouDroppedTheEnemyFlag )
EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_FlagDrop" )
-
+
MessageToTeam( player.GetTeam(), eEventNotifications.PlayerDroppedEnemyFlag, player, player )
// todo need a sound here maybe
-
MessageToTeam( GetOtherTeam( player.GetTeam() ), eEventNotifications.PlayerDroppedFriendlyFlag, player, player )
// todo need a sound here maybe
}
@@ -417,20 +414,33 @@ void function CaptureFlag( entity player, entity flag )
assistList = file.militiaCaptureAssistList
foreach( entity assistPlayer in assistList )
+ {
if ( player != assistPlayer )
AddPlayerScore( assistPlayer, "FlagCaptureAssist", player )
+ if( !HasPlayerCompletedMeritScore( assistPlayer ) )
+ {
+ AddPlayerScore( assistPlayer, "ChallengeCTFCapAssist" )
+ SetPlayerChallengeMeritScore( assistPlayer )
+ }
+ }
assistList.clear()
-
+
// notifs
MessageToPlayer( player, eEventNotifications.YouCapturedTheEnemyFlag )
EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_PlayerScore" )
+ if( !HasPlayerCompletedMeritScore( player ) )
+ {
+ AddPlayerScore( player, "ChallengeCTFRetAssist" )
+ SetPlayerChallengeMeritScore( player )
+ }
+
MessageToTeam( team, eEventNotifications.PlayerCapturedEnemyFlag, player, player )
EmitSoundOnEntityToTeamExceptPlayer( flag, "UI_CTF_3P_TeamScore", player.GetTeam(), player )
MessageToTeam( GetOtherTeam( team ), eEventNotifications.PlayerCapturedFriendlyFlag, player, player )
- EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyScore", flag.GetTeam() )
+ EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyScores", flag.GetTeam() )
if ( GameRules_GetTeamScore( team ) == GameMode_GetRoundScoreLimit( GAMETYPE ) - 1 )
{
@@ -446,6 +456,9 @@ void function OnPlayerEntersFlagReturnTrigger( entity trigger, entity player )
flag = file.imcFlag
else
flag = file.militiaFlag
+
+ if( !IsValid( flag ) || !IsValid( player ) )
+ return
if ( !player.IsPlayer() || player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null )
return
@@ -481,6 +494,7 @@ void function TryReturnFlag( entity player, entity flag )
})
player.EndSignal( "FlagReturnEnded" )
+ flag.EndSignal( "FlagReturnEnded" ) // avoid multiple players to return one flag at once
player.EndSignal( "OnDeath" )
wait CTF_GetFlagReturnTime()
@@ -488,12 +502,19 @@ void function TryReturnFlag( entity player, entity flag )
// flag return succeeded
// return flag
ResetFlag( flag )
-
+ flag.Signal( "FlagReturnEnded" )
+
// do notifications for return
MessageToPlayer( player, eEventNotifications.YouReturnedFriendlyFlag )
AddPlayerScore( player, "FlagReturn", player )
player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, 1 )
+ if( !HasPlayerCompletedMeritScore( player ) )
+ {
+ AddPlayerScore( player, "ChallengeCTFRetAssist" )
+ SetPlayerChallengeMeritScore( player )
+ }
+
MessageToTeam( flag.GetTeam(), eEventNotifications.PlayerReturnedFriendlyFlag, null, player )
EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_TeamReturnsFlag", flag.GetTeam() )
PlayFactionDialogueToTeam( "ctf_flagReturnedFriendly", flag.GetTeam() )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut
index 27eef177..4bff6038 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut
@@ -6,6 +6,9 @@ void function FFA_Init()
ScoreEvent_SetupEarnMeterValuesForMixedModes()
AddCallback_OnPlayerKilled( OnPlayerKilled )
+
+ // modified for northstar
+ AddCallback_OnClientConnected( OnClientConnected )
}
void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
@@ -16,4 +19,18 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
// why isn't this PGS_SCORE? odd game
attacker.AddToPlayerGameStat( PGS_ASSAULT_SCORE, 1 )
}
+}
+
+// modified for northstar
+void function OnClientConnected( entity player )
+{
+ thread FFAPlayerScoreThink( player ) // good to have this! instead of DisconnectCallback this could handle a null player
+}
+
+void function FFAPlayerScoreThink( entity player )
+{
+ int team = player.GetTeam()
+
+ player.WaitSignal( "OnDestroy" ) // this can handle disconnecting
+ AddTeamScore( team, -GameRules_GetTeamScore( team ) )
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut
index 9d8f84b5..6d0fd3c7 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut
@@ -17,6 +17,7 @@ void function GamemodeFRA_Init()
ScoreEvent_SetEarnMeterValues( "PilotBatteryPickup", 0.0, 0.34 )
EarnMeterMP_SetPassiveMeterGainEnabled( false )
PilotBattery_SetMaxCount( 3 )
+ SetupGenericFFAChallenge()
AddCallback_OnPlayerKilled( FRARemoveEarnMeter )
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut
index 31c85a57..8999231d 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut
@@ -8,6 +8,8 @@ struct {
float lastDamageInfoTime
bool shouldDoHighlights
+
+ table< entity, int > pilotstreak
} file
void function GamemodeLts_Init()
@@ -34,6 +36,37 @@ void function GamemodeLts_Init()
ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() )
ClassicMP_ForceDisableEpilogue( true )
AddCallback_GameStateEnter( eGameState.Playing, WaitForThirtySecondsLeft )
+
+ AddCallback_OnClientConnected( SetupPlayerLTSChallenges ) //Just to make up the Match Goals tracking
+ AddCallback_OnClientDisconnected( RemovePlayerLTSChallenges ) //Safety removal of data to prevent crashes
+ AddCallback_OnPlayerKilled( LTSChallengeForPlayerKilled )
+}
+
+void function SetupPlayerLTSChallenges( entity player )
+{
+ file.pilotstreak[ player ] <- 0
+}
+
+void function RemovePlayerLTSChallenges( entity player )
+{
+ if( player in file.pilotstreak )
+ delete file.pilotstreak[ player ]
+}
+
+void function LTSChallengeForPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim == attacker || !attacker.IsPlayer() || GetGameState() != eGameState.Playing )
+ return
+
+ if ( victim.IsPlayer() && attacker in file.pilotstreak )
+ {
+ file.pilotstreak[attacker]++
+ if( file.pilotstreak[attacker] >= 2 && !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengeLTS" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
}
void function WaitForThirtySecondsLeft()
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut
index 659dbb7a..768bbde1 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut
@@ -180,6 +180,12 @@ void function MarkPlayers( entity imcMark, entity militiaMark )
entity livingMark = GetMarked( GetOtherTeam( deadMark.GetTeam() ) )
livingMark.SetPlayerGameStat( PGS_DEFENSE_SCORE, livingMark.GetPlayerGameStat( PGS_DEFENSE_SCORE ) + 1 )
+ if( !HasPlayerCompletedMeritScore( livingMark ) )
+ {
+ AddPlayerScore( livingMark, "ChallengeMFD" )
+ SetPlayerChallengeMeritScore( livingMark )
+ }
+
// thread this so we don't kill our own thread
thread AddTeamScore( livingMark.GetTeam(), 1 )
}
@@ -188,10 +194,22 @@ void function UpdateMarksForKill( entity victim, entity attacker, var damageInfo
{
if ( victim == GetMarked( victim.GetTeam() ) )
{
- MessageToAll( eEventNotifications.MarkedForDeathKill, null, victim, attacker.GetEncodedEHandle() )
+ // handle suicides. Not sure what the actual message is that vanilla shows for this
+ // but this will prevent crashing for now
+ bool isSuicide = IsSuicide( victim, attacker, DamageInfo_GetDamageSourceIdentifier( damageInfo ) )
+ entity actualAttacker = isSuicide ? victim : attacker
+
+ MessageToAll( eEventNotifications.MarkedForDeathKill, null, victim, actualAttacker.GetEncodedEHandle() )
svGlobal.levelEnt.Signal( "MarkKilled", { mark = victim } )
- if ( attacker.IsPlayer() )
+ if ( !isSuicide && attacker.IsPlayer() )
+ {
attacker.SetPlayerGameStat( PGS_ASSAULT_SCORE, attacker.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 )
+ if( !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengeMFD" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
}
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut
index 57355ad8..c91c27d1 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut
@@ -19,6 +19,7 @@ void function GamemodePs_Init()
AddCallback_OnPlayerKilled( GiveScoreForPlayerKill )
ScoreEvent_SetupEarnMeterValuesForMixedModes()
SetTimeoutWinnerDecisionFunc( CheckScoreForDraw )
+ SetupGenericFFAChallenge()
// spawnzone stuff
SetShouldCreateMinimapSpawnZones( true )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
index cb277b00..4617476e 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
@@ -18,6 +18,7 @@ void function GamemodeSpeedball_Init()
Riff_ForceTitanAvailability( eTitanAvailability.Never )
Riff_ForceSetEliminationMode( eEliminationMode.Pilots )
ScoreEvent_SetupEarnMeterValuesForMixedModes()
+ SetupGenericFFAChallenge()
AddSpawnCallbackEditorClass( "script_ref", "info_speedball_flag", CreateFlag )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut
index 5c0e6fec..61ede2d4 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut
@@ -6,6 +6,7 @@ void function GamemodeTdm_Init()
AddCallback_OnPlayerKilled( GiveScoreForPlayerKill )
ScoreEvent_SetupEarnMeterValuesForMixedModes()
SetTimeoutWinnerDecisionFunc( CheckScoreForDraw )
+ SetupGenericTDMChallenge()
}
void function GiveScoreForPlayerKill( entity victim, entity attacker, var damageInfo )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut
index 6b30a399..3ba84394 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut
@@ -2,6 +2,11 @@ global function GamemodeTTDM_Init
const float TTDMIntroLength = 15.0
+struct
+{
+ table< entity, int > challengeCount
+} file
+
void function GamemodeTTDM_Init()
{
Riff_ForceSetSpawnAsTitan( eSpawnAsTitan.Always )
@@ -14,6 +19,8 @@ void function GamemodeTTDM_Init()
ClassicMP_ForceDisableEpilogue( true )
SetTimeoutWinnerDecisionFunc( CheckScoreForDraw )
+ AddCallback_OnClientConnected( SetupPlayerTTDMChallenges ) //Just to make up the Match Goals tracking
+ AddCallback_OnClientDisconnected( RemovePlayerTTDMChallenges ) //Safety removal of data to prevent crashes
AddCallback_OnPlayerKilled( AddTeamScoreForPlayerKilled ) // dont have to track autotitan kills since you cant leave your titan in this mode
// probably needs scoreevent earnmeter values
@@ -56,6 +63,17 @@ void function TTDMIntroShowIntermissionCam( entity player )
thread PlayerWatchesTTDMIntroIntermissionCam( player )
}
+void function SetupPlayerTTDMChallenges( entity player )
+{
+ file.challengeCount[ player ] <- 0
+}
+
+void function RemovePlayerTTDMChallenges( entity player )
+{
+ if( player in file.challengeCount )
+ delete file.challengeCount[ player ]
+}
+
void function PlayerWatchesTTDMIntroIntermissionCam( entity player )
{
player.EndSignal( "OnDestroy" )
@@ -79,6 +97,19 @@ void function AddTeamScoreForPlayerKilled( entity victim, entity attacker, var d
if ( victim == attacker || !victim.IsPlayer() || !attacker.IsPlayer() && GetGameState() == eGameState.Playing )
return
+ if( victim in file.challengeCount )
+ file.challengeCount[victim] = 0
+
+ if( attacker in file.challengeCount )
+ {
+ file.challengeCount[attacker]++
+ if( file.challengeCount[attacker] >= 2 && !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengeTTDM" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
+
AddTeamScore( GetOtherTeam( victim.GetTeam() ), 1 )
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut b/Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut
index 4e8f85ac..9057f7d8 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/item_inventory/sv_item_inventory.gnut
@@ -8,6 +8,7 @@ global function PlayerInventory_EndCriticalSectionForWeaponOnEndFrame
global function PlayerInventory_PushInventoryItem
global function PlayerInventory_PushInventoryItemByBurnRef
global function PlayerInventory_PopInventoryItem
+global function PlayerInventory_TakeAllInventoryItems
global function PlayerInventory_CountBurnRef
struct
@@ -19,6 +20,7 @@ void function Sv_ItemInventory_Init()
{
AddCallback_OnClientConnected( Sv_ItemInventory_OnClientConnected )
AddCallback_OnPlayerGetsNewPilotLoadout( Sv_ItemInventory_OnPlayerGetsNewPilotLoadout )
+ AddCallback_GameStateEnter( eGameState.Prematch, PrematchClearInventory )
}
void function Sv_ItemInventory_OnClientConnected( entity player )
@@ -26,13 +28,21 @@ void function Sv_ItemInventory_OnClientConnected( entity player )
file.playerInventoryStacks[ player ] <- []
}
+void function PrematchClearInventory() // vanilla behavior
+{
+ foreach( entity player in GetPlayerArray() )
+ {
+ PlayerInventory_TakeAllInventoryItems( player )
+ }
+}
+
void function Sv_ItemInventory_OnPlayerGetsNewPilotLoadout( entity player, PilotLoadoutDef newPilotLoadout )
{
array<InventoryItem> playerInventoryStack = file.playerInventoryStacks[ player ]
if (playerInventoryStack.len() > 0) {
InventoryItem topInventoryItem = playerInventoryStack[playerInventoryStack.len() - 1]
- PlayerInventory_GiveInventoryItem(player, topInventoryItem)
+ thread PlayerInventory_GiveInventoryItem(player, topInventoryItem)
}
return
@@ -68,13 +78,25 @@ int function PlayerInventory_CountBurnRef( entity player, string burnRef )
void function PlayerInventory_TakeInventoryItem( entity player )
{
+ player.EndSignal( "OnDestroy" )
entity preexistingWeapon = player.GetOffhandWeapon( OFFHAND_INVENTORY )
- if ( IsValid( preexistingWeapon ) )
- player.TakeWeaponNow( preexistingWeapon.GetWeaponClassName() )
+
+ if( !IsValid( preexistingWeapon ) )
+ return
+ preexistingWeapon.EndSignal( "OnDestroy" )
+ if( preexistingWeapon.GetWeaponClassName() == "mp_ability_burncardweapon" )
+ {
+ var fireTime = preexistingWeapon.GetWeaponInfoFileKeyField( "fire_anim_rate" )
+ if( fireTime )
+ wait fireTime
+ }
+ player.TakeWeaponNow( preexistingWeapon.GetWeaponClassName() )
}
void function PlayerInventory_GiveInventoryItem( entity player, InventoryItem inventoryItem )
{
+ player.EndSignal( "OnDestroy" )
+
array<string> mods = []
if ( inventoryItem.itemType == eInventoryItemType.burnmeter ) {
@@ -84,7 +106,10 @@ void function PlayerInventory_GiveInventoryItem( entity player, InventoryItem in
}
// ensure inventory slot isn't full to avoid crash
- PlayerInventory_TakeInventoryItem( player )
+ waitthread PlayerInventory_TakeInventoryItem( player )
+ entity preexistingWeapon = player.GetOffhandWeapon( OFFHAND_INVENTORY ) // defensive fix
+ if( IsValid( preexistingWeapon ) )
+ player.TakeWeaponNow( preexistingWeapon.GetWeaponClassName() )
player.GiveOffhandWeapon( inventoryItem.weaponRef, OFFHAND_INVENTORY, mods )
}
@@ -94,7 +119,7 @@ void function PlayerInventory_PushInventoryItem( entity player, InventoryItem in
file.playerInventoryStacks[ player ].append(inventoryItem)
player.SetPlayerNetInt( "itemInventoryCount", file.playerInventoryStacks[ player ].len() )
- PlayerInventory_GiveInventoryItem(player, inventoryItem)
+ thread PlayerInventory_GiveInventoryItem(player, inventoryItem)
}
void function PlayerInventory_PushInventoryItemByBurnRef( entity player, string burnRef )
@@ -117,15 +142,23 @@ void function PlayerInventory_PopInventoryItem( entity player )
if (playerInventoryStack.len() > 0) {
InventoryItem nextInventoryItem = playerInventoryStack[playerInventoryStack.len() - 1]
- PlayerInventory_GiveInventoryItem(player, nextInventoryItem)
+ thread PlayerInventory_GiveInventoryItem( player, nextInventoryItem )
} else {
- PlayerInventory_TakeInventoryItem( player )
+ waitthread PlayerInventory_TakeInventoryItem( player )
}
}
return
}
+void function PlayerInventory_TakeAllInventoryItems( entity player )
+{
+ file.playerInventoryStacks[ player ].clear()
+ waitthread PlayerInventory_TakeInventoryItem( player )
+ player.SetPlayerNetInt( "itemInventoryCount", 0 )
+ return
+}
+
void function PlayerInventory_RefreshEquippedState( entity player )
{
@@ -139,4 +172,4 @@ void function PlayerInventory_StartCriticalSection( entity player )
void function PlayerInventory_EndCriticalSectionForWeaponOnEndFrame( entity weapon )
{
-} \ No newline at end of file
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_lobby.gnut b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_lobby.gnut
index ae933b71..8b65ec93 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_lobby.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_lobby.gnut
@@ -14,6 +14,7 @@ void function Lobby_Init()
{
// non-private lobby clientcommands
AddClientCommandCallback( "StartPrivateMatchSearch", ClientCommandCallback_StartPrivateMatchSearch )
+ AddClientCommandCallback( "SetAnnouncementVersionSeen", ClientCommandCallback_SetAnnouncementVersionSeen )
}
}
@@ -37,3 +38,14 @@ bool function ClientCommandCallback_StartPrivateMatchSearch( entity player, arra
return true
}
+
+bool function ClientCommandCallback_SetAnnouncementVersionSeen( entity player, array<string> args )
+{
+ if ( args.len() < 1 )
+ return false
+
+ int version = int( args[0] )
+
+ player.SetPersistentVar( "announcementVersionSeen", version )
+ return true
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut
index c410869e..0a28031a 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut
@@ -38,14 +38,14 @@ void function SetupPrivateMatchUIVarsWhenReady()
bool function ClientCommandCallback_PrivateMatchLaunch( entity player, array<string> args )
{
if ( GetConVarBool( "ns_private_match_only_host_can_start" ) )
- if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
+ if ( !NSIsPlayerLocalPlayer( player ) )
return true
- PlayerChangedTheGame( player , " changed the game state." , args )
+ LogPrivateMatchChange( player , " changed the game state." , args )
if ( file.startState == ePrivateMatchStartState.STARTING )
{
- PlayerChangedTheGame( player , " canceled the game countdown." , args )
+ LogPrivateMatchChange( player , " canceled the game countdown." , args )
// cancel start if we're already mid-countdown
file.startState = ePrivateMatchStartState.READY
@@ -54,7 +54,7 @@ bool function ClientCommandCallback_PrivateMatchLaunch( entity player, array<str
}
else
{
- PlayerChangedTheGame( player , " started the game countdown." , args )
+ LogPrivateMatchChange( player , " started the game countdown." , args )
// start match
file.startState = ePrivateMatchStartState.STARTING
@@ -73,10 +73,10 @@ bool function ClientCommandCallback_PrivateMatchSetMode( entity player, array<st
return true
if ( GetConVarInt( "ns_private_match_only_host_can_change_settings" ) == 2 )
- if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
+ if ( !NSIsPlayerLocalPlayer( player ) )
return true
- PlayerChangedTheGame( player , " changed the mode to " , args )
+ LogPrivateMatchChange( player , " changed the mode to " , args )
// todo: need to verify this value
file.mode = args[0]
@@ -97,10 +97,10 @@ bool function ClientCommandCallback_SetCustomMap( entity player, array<string> a
return true
if ( GetConVarInt( "ns_private_match_only_host_can_change_settings" ) == 2 )
- if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
+ if ( !NSIsPlayerLocalPlayer( player ) )
return true
- PlayerChangedTheGame( player , " changed the map to " , args )
+ LogPrivateMatchChange( player , " changed the map to " , args )
// todo: need to verify this value
file.map = args[0]
@@ -217,10 +217,10 @@ bool function ClientCommandCallback_PrivateMatchSetPlaylistVarOverride( entity p
return true
if ( GetConVarInt( "ns_private_match_only_host_can_change_settings" ) >= 1 )
- if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
+ if ( !NSIsPlayerLocalPlayer( player ) )
return true
- PlayerChangedTheGame( player , " override the setting " , args )
+ LogPrivateMatchChange( player , " override the setting " , args )
bool found = false
foreach ( string category in GetPrivateMatchSettingCategories() )
@@ -244,23 +244,21 @@ bool function ClientCommandCallback_PrivateMatchSetPlaylistVarOverride( entity p
bool function ClientCommandCallback_ResetMatchSettingsToDefault( entity player, array<string> args )
{
if ( GetConVarInt( "ns_private_match_only_host_can_change_settings" ) >= 1 )
- if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
+ if ( !NSIsPlayerLocalPlayer( player ) )
return true
- PlayerChangedTheGame( player , " reset to default" , args )
+ LogPrivateMatchChange( player , " reset to default" , args )
ClearPlaylistVarOverrides()
return true
}
-void function PlayerChangedTheGame( entity player , string step , array<string> args ){
- if( step.find( "mode" ) || step.find( "map" )){
+void function LogPrivateMatchChange( entity player , string step , array<string> args )
+{
+ if( step.find( "mode" ) || step.find( "map" ) )
print( player.GetPlayerName() + step + args[ 0 ] + ".---" + "UID:" +player.GetUID() )
- }
- else if(step.find("setting")){
+ else if ( step.find( "setting" ) )
print( player.GetPlayerName() + step + args[ 0 ] + " to "+ args[ 1 ] + ".---" + "UID:" +player.GetUID() )
- }
- else{
+ else
print( player.GetPlayerName() + step + ".---" + "UID:" + player.GetUID())
- }
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/lobby/sh_private_lobby_modes_init.gnut b/Northstar.CustomServers/mod/scripts/vscripts/lobby/sh_private_lobby_modes_init.gnut
index ccccefaf..d2be2ab4 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/lobby/sh_private_lobby_modes_init.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/lobby/sh_private_lobby_modes_init.gnut
@@ -17,6 +17,7 @@ void function PrivateMatchModesInit()
AddPrivateMatchModeSettingEnum( "#MODE_SETTING_CATEGORY_PILOT", "boosts_enabled", [ "#SETTING_DEFAULT", "#SETTING_DISABLED" ], "1" )
AddPrivateMatchModeSettingEnum( "#MODE_SETTING_CATEGORY_PILOT", "earn_meter_pilot_overdrive", [ "#SETTING_DISABLED", "#SETTING_ENABLED", "Only" ], "1" )
AddPrivateMatchModeSettingArbitrary( "#MODE_SETTING_CATEGORY_PILOT", "earn_meter_pilot_multiplier", "1.0" )
+ AddPrivateMatchModeSettingArbitrary( "#MODE_SETTING_CATEGORY_PILOT", "player_force_respawn", "5" )
AddPrivateMatchModeSettingArbitrary( "#MODE_SETTING_CATEGORY_TITAN", "earn_meter_titan_multiplier", "1.0" )
AddPrivateMatchModeSettingEnum( "#MODE_SETTING_CATEGORY_TITAN", "aegis_upgrades", [ "#SETTING_DISABLED", "#SETTING_ENABLED" ], "0" )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype.gnut
index a4c6e187..362407b3 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype.gnut
@@ -1412,15 +1412,28 @@ void function CodeCallback_WeaponFireInCloak( entity player )
//player.SetCloakFlicker( 1.0, 2.0 )
DisableCloak( player, 0.5 )
- entity weapon = player.GetOffhandWeapon( OFFHAND_LEFT )
- //printt( "weapon", weapon.GetWeaponClassName() )
- // JFS; need code feature to properly reset next attack time/cooldown stuff
- if ( IsValid( weapon ) && weapon.GetWeaponClassName() == "mp_ability_cloak" )
+ entity cloakWeapon
+ int offhandSlot
+ for ( int i = 0; i <= OFFHAND_MELEE; i++ ) // OFFHAND_MELEE is the largest
{
- player.TakeOffhandWeapon( OFFHAND_LEFT )
- player.GiveOffhandWeapon( "mp_ability_cloak", OFFHAND_LEFT )
- weapon = player.GetOffhandWeapon( OFFHAND_LEFT )
- weapon.SetWeaponPrimaryClipCountAbsolute( 0 )
+ entity nowWeapon = player.GetOffhandWeapon( i )
+ if( IsValid( nowWeapon ))
+ {
+ if( nowWeapon.GetWeaponClassName() == "mp_ability_cloak" )
+ {
+ cloakWeapon = nowWeapon
+ offhandSlot = i
+ }
+ }
+ }
+ if( IsValid( cloakWeapon ) )
+ {
+ array<string> mods = cloakWeapon.GetMods()
+ // can't reset cooldown properly in script, let's give player a empty one
+ player.TakeWeapon( "mp_ability_cloak" ) // not using TakeWeaponNow() to fit vanilla behavior
+ player.GiveOffhandWeapon( "mp_ability_cloak", offhandSlot, mods )
+ cloakWeapon = player.GetOffhandWeapon( offhandSlot )
+ cloakWeapon.SetWeaponPrimaryClipCountAbsolute( 0 )
}
}
else
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
index 1c53167f..b77a37b2 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
@@ -19,6 +19,8 @@ global function ShouldEntTakeDamage_SPMP
global function GetTitanBuildTime
global function TitanPlayerHotDropsIntoLevel
+global function SetRecalculateRespawnAsTitanStartPointCallback
+
struct {
bool killcamsEnabled = true
bool playerDeathsHidden = false
@@ -26,6 +28,8 @@ struct {
entity intermissionCamera
array<entity> specCams
+
+ entity functionref( entity player, entity basePoint ) recalculateRespawnAsTitanStartPointCallback
} file
void function BaseGametype_Init_MPSP()
@@ -39,6 +43,8 @@ void function BaseGametype_Init_MPSP()
AddCallback_OnPlayerKilled( CheckForAutoTitanDeath )
RegisterSignal( "PlayerRespawnStarted" )
RegisterSignal( "KillCamOver" )
+
+ FlagInit( "WeaponDropsAllowed", true )
}
void function SetIntermissionCamera( entity camera )
@@ -130,6 +136,15 @@ void function CodeCallback_OnClientConnectionCompleted( entity player )
Lobby_OnClientConnectionCompleted( player )
return
}
+ else if ( !IsFDMode( GAMETYPE ) )
+ {
+ // reset this for non-fd modes
+ // for some reason the postgame scoreboard uses this to
+ // determine if it should show the FD aegis rank one
+ // FD should either set this in their own mode, or add an else
+ // to this if statement when it releases
+ player.SetPersistentVar( "lastFDTitanRef", "" )
+ }
player.hasConnected = true
@@ -270,6 +285,9 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga
ClearRespawnAvailable( player )
+ // reset this so that we default to pilot spawn
+ player.SetPersistentVar( "spawnAsTitan", false )
+
OnThreadEnd( function() : ( player )
{
if ( !IsValid( player ) )
@@ -279,6 +297,10 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga
})
entity attacker = DamageInfo_GetAttacker( damageInfo )
+ entity inflictor = DamageInfo_GetInflictor( damageInfo )
+ int eHandle = attacker.GetEncodedEHandle()
+ if ( inflictor && ShouldTryUseProjectileReplay( player, attacker, damageInfo, false ) )
+ eHandle = inflictor.GetEncodedEHandle()
int methodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo )
table<int, bool> alreadyAssisted
@@ -298,6 +320,9 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga
attackerInfo.attacker.AddToPlayerGameStat( PGS_ASSISTS, 1 )
}
}
+
+ if( attacker.IsPlayer() )
+ Highlight_SetDeathRecapHighlight( attacker, "killer_outline" )
}
player.p.rematchOrigin = player.p.deathOrigin
@@ -355,7 +380,7 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga
if ( "respawnTime" in attacker.s )
respawnTime = Time() - expect float ( attacker.s.respawnTime )
- thread PlayerWatchesKillReplayWrapper( player, attacker, respawnTime, timeOfDeath, beforeTime, replayTracker )
+ thread PlayerWatchesKillReplayWrapper( player, attacker, eHandle, respawnTime, timeOfDeath, beforeTime, replayTracker )
}
player.SetPlayerSettings( "spectator" ) // prevent a crash with going from titan => pilot on respawn
@@ -372,6 +397,13 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga
SetRespawnAvailable( player )
wait respawnDelay
+
+ int forceRespawn = GetCurrentPlaylistVarInt( "player_force_respawn", -1 )
+
+ // -1 is disabled, anything over is the time we wait in seconds
+ // before respawning the player
+ if( forceRespawn >= 0 )
+ thread ForceRespawnMeSignalAfterDelay( player, forceRespawn )
player.WaitSignal( "RespawnMe" ) // set in base_gametype: ClientCommand_RespawnPlayer
@@ -391,7 +423,22 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga
}
}
-void function PlayerWatchesKillReplayWrapper( entity player, entity attacker, float timeSinceAttackerSpawned, float timeOfDeath, float beforeTime, table replayTracker )
+// idk if this is a good delay or if it matches vanilla
+void function ForceRespawnMeSignalAfterDelay( entity player, int delay = 5 )
+{
+ player.EndSignal( "RespawnMe" )
+ player.EndSignal( "OnDestroy" )
+
+ if( player.IsWatchingKillReplay() )
+ player.WaitSignal( "KillCamOver" )
+
+ wait delay
+
+ printt( format( "Forcing player respawn for player %s (took >%d seconds to respawn)", player.GetPlayerName(), delay ) )
+ player.Signal( "RespawnMe" )
+}
+
+void function PlayerWatchesKillReplayWrapper( entity player, entity attacker, int eHandle, float timeSinceAttackerSpawned, float timeOfDeath, float beforeTime, table replayTracker )
{
player.EndSignal( "RespawnMe" )
player.EndSignal( "OnRespawned" )
@@ -414,7 +461,7 @@ void function PlayerWatchesKillReplayWrapper( entity player, entity attacker, fl
})
player.SetPredictionEnabled( false )
- PlayerWatchesKillReplay( player, attacker.GetEncodedEHandle(), attacker.GetIndexForEntity(), timeSinceAttackerSpawned, timeOfDeath, beforeTime, replayTracker )
+ PlayerWatchesKillReplay( player, eHandle, attacker.GetIndexForEntity(), timeSinceAttackerSpawned, timeOfDeath, beforeTime, replayTracker )
}
void function DecideRespawnPlayer( entity player )
@@ -424,16 +471,25 @@ void function DecideRespawnPlayer( entity player )
void function RespawnAsPilot( entity player )
{
+ // respawn crash exploit hotfix
+ if(IsAlive( player )) return
+
player.RespawnPlayer( FindSpawnPoint( player, false, ( ShouldStartSpawn( player ) || Flag( "ForceStartSpawn" ) ) && !IsFFAGame() ) )
}
void function RespawnAsTitan( entity player, bool manualPosition = false )
{
+ // respawn crash exploit hotfix
+ if(IsAlive( player )) return
+
player.Signal( "PlayerRespawnStarted" )
player.isSpawning = true
entity spawnpoint = FindSpawnPoint( player, true, ( ShouldStartSpawn( player ) || Flag( "ForceStartSpawn" ) ) && !IsFFAGame() )
+ if ( file.recalculateRespawnAsTitanStartPointCallback != null )
+ spawnpoint = file.recalculateRespawnAsTitanStartPointCallback( player, spawnpoint )
+
TitanLoadoutDef titanLoadout = GetTitanLoadoutForPlayer( player )
asset model = GetPlayerSettingsAssetForClassName( titanLoadout.setFile, "bodymodel" )
@@ -447,7 +503,20 @@ void function RespawnAsTitan( entity player, bool manualPosition = false )
AddCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) // hide hud
// do titanfall scoreevent
- AddPlayerScore( player, "Titanfall", player )
+ if ( !level.firstTitanfall )
+ {
+ AddPlayerScore( player, "FirstTitanfall", player )
+
+ #if HAS_STATS
+ UpdatePlayerStat( player, "misc_stats", "titanFallsFirst" )
+ #endif
+
+ level.firstTitanfall = true
+ }
+ else
+ {
+ AddPlayerScore( player, "Titanfall", player )
+ }
entity camera = CreateTitanDropCamera( spawnpoint.GetAngles(), < 90, titan.GetAngles().y, 0 > )
camera.SetParent( titan )
@@ -478,7 +547,7 @@ void function RespawnAsTitan( entity player, bool manualPosition = false )
titan.Destroy() // pilotbecomestitan leaves an npc titan that we need to delete
else
RespawnAsPilot( player ) // this is 100% an edgecase, just avoid softlocking if we ever hit it in playable gamestates
-
+
camera.Fire( "Disable", "!activator", 0, player )
camera.Destroy()
})
@@ -487,6 +556,7 @@ void function RespawnAsTitan( entity player, bool manualPosition = false )
player.RespawnPlayer( null ) // spawn player as pilot so they get their pilot loadout on embark
player.SetOrigin( titan.GetOrigin() )
+ ClearTitanAvailable( player ) // titanfall succeed, clear titan availability
// don't make player titan when entity batteryContainer is not valid.
// This will prevent a servercrash that sometimes occur when evac is disabled and somebody is calling a titan in the defeat screen.
@@ -555,10 +625,19 @@ void function CheckForAutoTitanDeath( entity victim, entity attacker, var damage
}
}
+void function SetRecalculateRespawnAsTitanStartPointCallback( entity functionref( entity player, entity basePoint ) callbackFunc )
+{
+ file.recalculateRespawnAsTitanStartPointCallback = callbackFunc
+}
+
// stuff to change later
bool function ShouldEntTakeDamage_SPMP( entity ent, var damageInfo )
{
+ // dropships are immune to being crushed
+ if ( ( IsDropship( ent ) || IsEvacDropship( ent ) ) && IsTitanCrushDamage( damageInfo ) )
+ return false
+
return true
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_battery_port.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_battery_port.gnut
index 37b89169..ea88c1bc 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_battery_port.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_battery_port.gnut
@@ -1 +1,219 @@
-//fuck \ No newline at end of file
+untyped
+global function InitTurretBatteryPort // only for fw turrets!
+
+void function InitTurretBatteryPort( entity batteryPort )
+{
+
+ batteryPort.s.beingUsed <- false // bool
+ batteryPort.s.hackAvaliable <- true // bool, for controlling hacking avaliablity
+
+ // SetUsableByGroup() updates is done in TurretStateWatcher()
+ batteryPort.SetUsableByGroup( "pilot" ) // show hind to any pilots
+ batteryPort.SetUsePrompts( "#RODEO_APPLY_BATTERY_HINT", "#RODEO_APPLY_BATTERY_HINT" ) // don't know what to use
+ AddCallback_OnUseEntity( batteryPort, OnUseTurretBatteryPort )
+}
+
+function OnUseTurretBatteryPort( entBeingUse, user )
+{
+ expect entity( entBeingUse )
+ expect entity( user )
+
+ //print( "try to use batteryPort" )
+ thread TryUseTurretBatteryPort( user, entBeingUse )
+}
+
+void function TryUseTurretBatteryPort( entity player, entity batteryPort )
+{
+ if( batteryPort.s.beingUsed ) // already being using
+ return
+
+ player.EndSignal( "OnDeath" )
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "ScriptAnimStop" ) // so you can jump off animation
+ AddButtonPressedPlayerInputCallback( player, IN_JUMP, ForceStopUseBatteryPort )
+
+ OnThreadEnd(
+ function():( player )
+ {
+ RemoveButtonPressedPlayerInputCallback( player, IN_JUMP, ForceStopUseBatteryPort )
+ }
+ )
+
+
+ var BatteryPortUsable = batteryPort.s.isUsable
+
+ if( expect bool( BatteryPortUsable( batteryPort, player ) ) )
+ {
+ // friendly try to apply one, or enemy try to hack this turret
+ waitthread PlayerApplesBatteryPackToPort( player, batteryPort )
+ }
+}
+
+void function ForceStopUseBatteryPort( entity player )
+{
+ player.Signal( "ScriptAnimStop" )
+}
+
+void function PlayerApplesBatteryPackToPort( entity player, entity batteryPort )
+{
+ table result = {}
+ result.success <- false
+ batteryPort.s.beingUsed = true
+
+ BatteryPortSequenceStruct dataStruct = DisableCloakBeforeBatteryPortSequence( player )
+
+ // these are from _rodeo_titan.gnut
+ entity battery = GetBatteryOnBack( player )
+ battery.Hide() //Hide it because the animation has a battery model already
+ Battery_StopFX( battery )
+
+ entity tempBattery3p
+ tempBattery3p = CreatePropDynamic( RODEO_BATTERY_MODEL_FOR_RODEO_ANIMS )
+ tempBattery3p.SetParent( player, "R_HAND", false, 0.0 )
+ tempBattery3p.RemoveFromSpatialPartition()
+
+ entity tempBattery1p
+ tempBattery1p = CreatePropDynamic( RODEO_BATTERY_MODEL_FOR_RODEO_ANIMS )
+ tempBattery1p.SetParent( player.GetFirstPersonProxy(), "R_HAND", false, 0.0 )
+ tempBattery1p.RemoveFromSpatialPartition()
+
+ player.p.rodeoAnimTempProps.append( tempBattery3p )
+ player.p.rodeoAnimTempProps.append( tempBattery1p )
+
+ OnThreadEnd(
+ function() : ( battery, batteryPort, player, result, dataStruct )
+ {
+ if ( IsValid( battery ) ) // animation interrupted, otherwise the battery will be destroyed
+ {
+ battery.Show()
+ Battery_StartFX( battery )
+ }
+
+ if ( IsValid( batteryPort ) )
+ {
+ batteryPort.s.beingUsed = false
+ batteryPort.Anim_Stop()
+ }
+
+ if ( IsValid( player ) )
+ {
+ // restore control
+ DeployAndEnableWeapons( player )
+ //ViewConeFree( player ) // no need to lock viewcone
+
+ // clean up
+ ClearBatteryAnimTempProps( player )
+ PutEntityInSafeSpot( player, player, null, player.GetOrigin() + <0, 0, 32>, player.GetOrigin() )
+
+ CleanUpBatterySequenceForPlayer( player )
+ RestoreCloakAfterBatteryPortSequence( player, dataStruct )
+ }
+ }
+ )
+
+ FirstPersonSequenceStruct sequence
+ sequence.attachment = "REF" // only ref the batteryPort has
+
+ sequence.thirdPersonAnim = "pt_mp_battery_port_insert" //"pt_rodeo_ride_r_return_battery"
+ sequence.firstPersonAnim = "ptpov_mp_battery_port_insert" //"ptpov_rodeo_ride_r_return_battery"
+
+ // player stats
+ HolsterAndDisableWeapons( player )
+ //ViewConeZero( player ) // no need to lock viewcone
+
+ batteryPort.Anim_Play( "bp_mp_battery_port_insert" )
+
+ thread WaitForActivateBattery( player, battery, batteryPort )
+ waitthread FirstPersonSequence( sequence, player, batteryPort )
+}
+
+void function WaitForActivateBattery( entity player, entity battery, entity batteryPort )
+{
+ player.EndSignal( "OnDeath" )
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "ScriptAnimStop" ) // so you can jump off animation
+ battery.EndSignal( "OnDestroy" )
+
+ player.WaitSignal( "BatteryActivate" ) // this is registered in _gamemode_fw.nut!
+ ApplyBatteryToBatteryPort( player, batteryPort )
+}
+
+void function ApplyBatteryToBatteryPort( entity player, entity batteryPort )
+{
+ if ( player.GetPlayerNetInt( "batteryCount" ) <= 0 ) // player actually not carrying a battery
+ return
+
+ entity battery = Rodeo_TakeBatteryAwayFromPilot( player )
+ if ( !IsValid( battery ) )
+ return
+
+ // player can apply battery
+
+ // disable hacking
+ batteryPort.s.hackAvaliable = false // can't be hacked again until completely killed
+
+
+ var useBatteryPort = batteryPort.s.useBattery
+ useBatteryPort( batteryPort, player )
+
+ // all things done, destroy this batt
+ battery.Destroy()
+}
+
+// for disabling cloak
+struct BatteryPortSequenceStruct
+{
+ bool wasCloaked = false
+ float cloakEndTime = 0.0
+}
+
+BatteryPortSequenceStruct function DisableCloakBeforeBatteryPortSequence( entity player )
+{
+ BatteryPortSequenceStruct dataStruct
+ if ( !IsCloaked( player ) )
+ return dataStruct // empty struct!
+
+ dataStruct.wasCloaked = true
+ dataStruct.cloakEndTime = player.GetCloakEndTime()
+ DisableCloak( player, 0.0 )
+
+ return dataStruct
+}
+
+bool function RestoreCloakAfterBatteryPortSequence( entity player, BatteryPortSequenceStruct dataStruct )
+{
+ if ( !IsAlive( player ) )
+ return false
+
+ if ( !dataStruct.wasCloaked )
+ return false
+
+ if ( dataStruct.cloakEndTime <= 0.0 )
+ return false
+
+ float remainingCloakDuration = max( 0.0, dataStruct.cloakEndTime - Time() )
+ if ( remainingCloakDuration <= CLOAK_FADE_IN ) //Has to be greater than 1.0 fade in duration, otherwise will cloak forever
+ return false
+
+ EnableCloak( player, remainingCloakDuration, CLOAK_FADE_IN )
+ return true
+}
+
+void function CleanUpBatterySequenceForPlayer( entity player )
+{
+ ClearPlayerAnimViewEntity( player )
+ player.AnimViewEntity_SetLerpOutTime( 0.4 ) // blend out the clear anim view entity
+ player.ClearParent()
+ player.Anim_Stop()
+}
+
+void function ClearBatteryAnimTempProps( entity player )
+{
+ foreach( tempProp in player.p.rodeoAnimTempProps )
+ {
+ if ( IsValid( tempProp ) )
+ tempProp.Destroy()
+ }
+
+ player.p.rodeoAnimTempProps.clear()
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut
index 466a5042..016097f2 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_challenges.gnut
@@ -1,6 +1,276 @@
global function InitChallenges
+global function SetPlayerChallengeEvacState //Hooked in _evac.gnut
+global function SetPlayerChallengeMatchWon //Hooked in _score.nut
+global function SetPlayerChallengeMatchComplete //Hooked in _score.nut
+global function SetPlayerChallengeMeritScore //Up to gamemodes to use this directly if needed
+global function IncrementPlayerChallengeTitanLeveledUp //Hooked in titan_xp.gnut
+global function IncrementPlayerChallengeWeaponLeveledUp //Hooked in weapon_xp.gnut
+global function IncrementPlayerChallengeFactionLeveledUp //Hooked in faction_xp.gnut (invisible but necessary for post-summary menu)
+global function RegisterChallenges_OnMatchEnd //Hooked in _gamestate_mp.gnut
+
+global function HasPlayerCompletedMeritScore //Check from gamemodes to not reapply SetPlayerChallengeMeritScore
+global function SetupGenericTDMChallenge //Used by gamemodes which simply adopts the: "Kill 3 Pilots without dying." Challenge
+global function SetupGenericFFAChallenge //Used by gamemodes which simply adopts the: "Kill 5 Pilots." Challenge
+
+struct
+{
+ table< entity, int > playerTotalMeritCount
+ table< entity, bool > playerChallenge
+ table< entity, int > pilotstreak
+ bool isHappyHourActive
+} file
+
+
+
+
+
+
+/*=============================================================================================================
+ __ __ _ _ ____ _ _ _
+ | \/ | __ _ | |_ ___ | |__ / ___|| |__ __ _ | || | ___ _ __ __ _ ___ ___
+ | |\/| | / _` || __|/ __|| '_ \ | | | '_ \ / _` || || | / _ \| '_ \ / _` | / _ \/ __|
+ | | | || (_| || |_| (__ | | | | | |___ | | | || (_| || || || __/| | | || (_| || __/\__ \
+ |_| |_| \__,_| \__|\___||_| |_| \____||_| |_| \__,_||_||_| \___||_| |_| \__, | \___||___/
+ |___/
+=============================================================================================================*/
void function InitChallenges()
{
+#if (UI && CLIENT)
+
+ SCB_SetCompleteMeritState( 4 )
+ SCB_SetEvacMeritState( 4 )
+ SCB_SetMeritCount( 4 )
+ SCB_SetScoreMeritState( 4 )
+ SCB_SetWinMeritState( 4 )
+ SCB_SetWeaponMeritCount( -1 )
+ SCB_SetTitanMeritCount( -1 )
+
+#elseif (SERVER && MP)
+
+ AddCallback_OnClientConnected( SetupPlayerMenuChallenges )
+ AddCallback_OnClientDisconnected( RemovePlayerFromChallengePool )
+
+#endif
+}
+
+void function SetupPlayerMenuChallenges( entity player )
+{
+ file.playerTotalMeritCount[ player ] <- 0
+ file.pilotstreak[ player ] <- 0
+ file.playerChallenge[ player ] <- false
+
+ thread SetupChallenges_Threaded( player )
+}
+void function SetupChallenges_Threaded( entity player )
+{
+ player.EndSignal( "OnDestroy" )
+
+ WaitFrame()
+
+ Remote_CallFunction_UI( player, "SCB_SetCompleteMeritState", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 4 ) //4 tells RUI to hide it
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetScoreMeritState", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetWinMeritState", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetWeaponMeritCount", 0 )
+ Remote_CallFunction_UI( player, "SCB_SetTitanMeritCount", 0 )
+}
+
+void function SetupGenericTDMChallenge()
+{
+ AddCallback_OnPlayerKilled( TDMChallenges_OnPlayerKilled )
+}
+
+void function SetupGenericFFAChallenge()
+{
+ AddCallback_OnPlayerKilled( FFAChallenges_OnPlayerKilled )
+}
+
+void function RemovePlayerFromChallengePool( entity player )
+{
+ if( player in file.playerChallenge )
+ delete file.playerChallenge[ player ]
+ if( player in file.playerTotalMeritCount )
+ delete file.playerTotalMeritCount[ player ]
+ if( player in file.pilotstreak )
+ delete file.pilotstreak[ player ]
+}
+
+void function RegisterChallenges_OnMatchEnd()
+{
+ bool eliteWarpaintRNG = false
+
+ if( RandomIntRange( 0, 100 ) <= 30 ) //30% Chance to trigger akin to vanilla, apply always since all players have paid cosmetics unlocked
+ eliteWarpaintRNG = true
+
+ foreach( player in GetPlayerArray() )
+ {
+ player.SetPersistentVar( "isPostGameScoreboardValid", true )
+ player.SetPersistentVar( "isFDPostGameScoreboardValid", false ) //FD itself overrides this right after when match ends
+ SetUIVar( level, "showGameSummary", true )
+
+ if( eliteWarpaintRNG )
+ SetPlayerChallengeSquadLeader( player )
+
+ if( ShouldAwardHappyHourBonus( player ) )
+ {
+ AddPlayerScore( player, "HappyHourBonus" )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.HAPPY_HOUR + "]", 5 ) //The XP Given from Happy Hour Score is 5 merits
+ }
+ }
+}
+
+void function TDMChallenges_OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim == attacker || !attacker.IsPlayer() || GetGameState() != eGameState.Playing )
+ return
+
+ if ( victim.IsPlayer() )
+ {
+ if( victim in file.pilotstreak )
+ file.pilotstreak[victim] = 0
+ if( attacker in file.pilotstreak )
+ {
+ file.pilotstreak[attacker]++
+ if( file.pilotstreak[attacker] >= 3 && !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengeTDM" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
+ }
+}
+
+void function FFAChallenges_OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim == attacker || !attacker.IsPlayer() || GetGameState() != eGameState.Playing )
+ return
+
+ if ( victim.IsPlayer() && attacker in file.pilotstreak )
+ {
+ file.pilotstreak[attacker]++
+ if( file.pilotstreak[attacker] >= 5 && !HasPlayerCompletedMeritScore( attacker ) )
+ {
+ AddPlayerScore( attacker, "ChallengePVPKillCount" )
+ SetPlayerChallengeMeritScore( attacker )
+ }
+ }
+}
+
+bool function HasPlayerCompletedMeritScore( entity player )
+{
+ Assert( player in file.playerChallenge, player + " is not registered in the challenge pool hooks." )
+ return file.playerChallenge[ player ]
+}
+
+
+
+
+
+
+
+/*=============================================================================================================
+ ____ _ _ _ _
+ / ___| __ _ _ __ ___ ___ _ __ ___ ___ __| | ___ | | | | ___ ___ | | __ ___
+ | | _ / _` || '_ ` _ \ / _ \| '_ ` _ \ / _ \ / _` | / _ \ | |_| | / _ \ / _ \ | |/ // __|
+ | |_| || (_| || | | | | || __/| | | | | || (_) || (_| || __/ | _ || (_) || (_) || < \__ \
+ \____| \__,_||_| |_| |_| \___||_| |_| |_| \___/ \__,_| \___| |_| |_| \___/ \___/ |_|\_\|___/
+
+=============================================================================================================*/
+
+void function SetPlayerChallengeEvacState( entity player, int successEvac = 0 )
+{
+ if( successEvac == 0 ) //Evac Ship destroyed
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 2 )
+
+ else if( successEvac == 1 ) //Player itself managed to evac
+ {
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 1 )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.EVAC + "]", 1 )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+ }
+
+ else if( successEvac == 2 ) //Team managed to evac
+ Remote_CallFunction_UI( player, "SCB_SetEvacMeritState", 3 )
+}
+
+void function SetPlayerChallengeMatchWon( entity player, bool playerWon )
+{
+ if( playerWon )
+ {
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetWinMeritState", 1 )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.MATCH_VICTORY + "]", 1 )
+ player.SetPersistentVar( "matchWin", true )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+ }
+ else
+ Remote_CallFunction_UI( player, "SCB_SetWinMeritState", -1 )
+}
+
+void function SetPlayerChallengeMatchComplete( entity player )
+{
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetCompleteMeritState", 1 )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.MATCH_COMPLETED + "]", 1 )
+ player.SetPersistentVar( "matchComplete", true )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+}
+
+void function SetPlayerChallengeSquadLeader( entity player )
+{
+ if( !ProgressionEnabledForPlayer( player ) )
+ return
+
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SquadLeaderDoubleXP" )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SquadLeaderBonus", player.GetEncodedEHandle() )
+ player.SetPersistentVar( "matchSquadBonus", true )
+ Player_GiveDoubleXP( player, 1 )
+ foreach( entity teamplayer in GetPlayerArrayOfTeam( player.GetTeam() ) )
+ {
+ if( teamplayer == player )
+ continue
+
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SquadLeaderBonus", teamplayer.GetEncodedEHandle() )
+ }
+}
+
+void function SetPlayerChallengeMeritScore( entity player )
+{
+ if( !HasPlayerCompletedMeritScore( player ) )
+ {
+ file.playerChallenge[ player ] = true
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetScoreMeritState", 1 )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.SCORE_MILESTONE + "]", 1 )
+ player.SetPersistentVar( "matchScoreEvent", true )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+ }
+}
+
+void function IncrementPlayerChallengeTitanLeveledUp( entity player )
+{
+ player.p.meritData.titanMerits++
+ file.playerTotalMeritCount[ player ]++
+
+ Remote_CallFunction_UI( player, "SCB_SetTitanMeritCount", player.p.meritData.titanMerits++ )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+}
+
+void function IncrementPlayerChallengeWeaponLeveledUp( entity player )
+{
+ player.p.meritData.weaponMerits++
+ file.playerTotalMeritCount[ player ]++
+
+ Remote_CallFunction_UI( player, "SCB_SetWeaponMeritCount", player.p.meritData.weaponMerits )
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
+}
+
+void function IncrementPlayerChallengeFactionLeveledUp( entity player )
+{
+ file.playerTotalMeritCount[ player ]++
+ Remote_CallFunction_UI( player, "SCB_SetMeritCount", file.playerTotalMeritCount[ player ] )
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_changemap.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_changemap.nut
index 16a3ce92..0ababfc7 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_changemap.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_changemap.nut
@@ -58,7 +58,9 @@ void function CodeCallback_MatchIsOver()
#if MP
void function PopulatePostgameData()
{
- // something's busted here because this isn't showing automatically on match end, ag
+ // show the postgame scoreboard summary
+ SetUIVar( level, "showGameSummary", true )
+
foreach ( entity player in GetPlayerArray() )
{
int teams = GetCurrentPlaylistVarInt( "max_teams", 2 )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
index 0555df9b..c3bdf01c 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
@@ -26,18 +26,9 @@ const int MAX_DROPSHIP_PLAYERS = 4
global const float DROPSHIP_INTRO_LENGTH = 15.0 // TODO tweak this
-struct IntroDropship
-{
- entity dropship
-
- int playersInDropship
- entity[MAX_DROPSHIP_PLAYERS] players
-}
-
struct {
- // these used to be IntroDropship[2]s but i wanted to be able to use array.getrandom so they have to be actual arrays
- array<IntroDropship> militiaDropships
- array<IntroDropship> imcDropships
+ table< entity, array<entity> > militiaDropships
+ table< entity, array<entity> > imcDropships
float introStartTime
} file
@@ -52,7 +43,12 @@ void function ClassicMP_DefaultDropshipIntro_Setup()
void function DropshipIntro_OnClientConnected( entity player )
{
if ( GetGameState() == eGameState.Prematch )
- thread SpawnPlayerIntoDropship( player )
+ {
+ if( PlayerCanSpawn( player ) )
+ DoRespawnPlayer( player, null )
+
+ PutPlayerInDropship( player )
+ }
}
void function OnPrematchStart()
@@ -62,11 +58,11 @@ void function OnPrematchStart()
print( "starting dropship intro!" )
file.introStartTime = Time()
- // make 2 empty dropship structs per team
- IntroDropship emptyDropship
+ // Clear Dropship arrays of Teams for Match Restarts (i.e Half-Times)
file.militiaDropships.clear()
file.imcDropships.clear()
+ // Try to gather all possible Dropship spawn points for Team
array<entity> validDropshipSpawns
array<entity> dropshipSpawns = GetEntArrayByClass_Expensive( "info_spawnpoint_dropship_start" )
foreach ( entity dropshipSpawn in dropshipSpawns )
@@ -78,47 +74,47 @@ void function OnPrematchStart()
validDropshipSpawns.append( dropshipSpawn )
}
- // if no dropship spawns for this mode, just allow any dropship spawns
+ // Use any spawn point if not enough valid for this Gamemode exists
if ( validDropshipSpawns.len() < 2 )
validDropshipSpawns = dropshipSpawns
// spawn dropships
foreach ( entity dropshipSpawn in validDropshipSpawns )
{
- // todo: possibly make this only spawn dropships if we've got enough players to need them
int createTeam = HasSwitchedSides() ? GetOtherTeam( dropshipSpawn.GetTeam() ) : dropshipSpawn.GetTeam()
- array<IntroDropship> teamDropships = createTeam == TEAM_MILITIA ? file.militiaDropships : file.imcDropships
+ table< entity, array<entity> > teamDropships = createTeam == TEAM_MILITIA ? file.militiaDropships : file.imcDropships
if ( teamDropships.len() >= 2 )
- continue
+ break
- // create entity
entity dropship = CreateDropship( createTeam, dropshipSpawn.GetOrigin(), dropshipSpawn.GetAngles() )
-
- teamDropships.append( clone emptyDropship )
- teamDropships[ teamDropships.len() - 1 ].dropship = dropship
-
AddAnimEvent( dropship, "dropship_warpout", WarpoutEffect )
+
dropship.SetValueForModelKey( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
- dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
+ if ( dropshipSpawn.GetTeam() == TEAM_IMC )
+ dropship.SetValueForModelKey( $"models/vehicle/goblin_dropship/goblin_dropship_hero.mdl" )
DispatchSpawn( dropship )
- // have to do this after dispatch otherwise it won't work for some reason
- // weirdly enough, tf2 actually does use different dropships for imc and militia, despite these concepts not really being a thing for players in tf2
- // probably was just missed by devs, but keeping it in for accuracy
+ dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
if ( dropshipSpawn.GetTeam() == TEAM_IMC )
dropship.SetModel( $"models/vehicle/goblin_dropship/goblin_dropship_hero.mdl" )
- else
- dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
+
+ teamDropships[ dropship ] <- [ null, null, null, null ]
thread PlayAnim( dropship, "dropship_classic_mp_flyin" )
}
+ // Populate Dropships
foreach ( entity player in GetPlayerArray() )
{
if ( !IsPrivateMatchSpectator( player ) )
- thread SpawnPlayerIntoDropship( player )
+ {
+ if( PlayerCanSpawn( player ) )
+ DoRespawnPlayer( player, null )
+
+ PutPlayerInDropship( player )
+ }
else
RespawnPrivateMatchSpectator( player )
}
@@ -128,75 +124,79 @@ void function OnPrematchStart()
void function EndIntroWhenFinished()
{
- wait 15.0
+ wait DROPSHIP_INTRO_LENGTH
ClassicMP_OnIntroFinished()
}
-void function SpawnPlayerIntoDropship( entity player )
+void function PutPlayerInDropship( entity player )
{
- player.EndSignal( "OnDestroy" )
+ //Find the player's dropship and seat
+ table< entity, array<entity> > teamDropships
+ if ( player.GetTeam() == TEAM_MILITIA )
+ teamDropships = file.militiaDropships
+ else
+ teamDropships = file.imcDropships
+
+ entity playerDropship
+ array< int > availableShipSlots
+ array< entity > introDropships
+ int playerDropshipIndex = RandomInt( MAX_DROPSHIP_PLAYERS )
+ foreach( dropship, playerslot in teamDropships )
+ {
+ introDropships.append( dropship )
+ for ( int i = 0; i < MAX_DROPSHIP_PLAYERS; i++ )
+ {
+ if ( !IsValidPlayer( playerslot[i] ) )
+ availableShipSlots.append( i )
+ }
+
+ if( !availableShipSlots.len() )
+ continue
+
+ int slotPick = availableShipSlots.getrandom()
+ playerslot[slotPick] = player
+ playerDropship = dropship
+ playerDropshipIndex = slotPick
+ break
+ }
+
+ if( !IsAlive( playerDropship ) ) //If we're at this point, we have more players than we do dropships, so just pick a random one
+ playerDropship = introDropships.getrandom()
+
+ thread SpawnPlayerIntoDropship( player, playerDropshipIndex, playerDropship )
+}
- if ( IsAlive( player ) )
- player.Die() // kill them so we don't have any issues respawning them later
+void function SpawnPlayerIntoDropship( entity player, int playerDropshipIndex, entity playerDropship )
+{
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "OnDeath" )
- player.s.dropshipIntroIsJumping <- false
- OnThreadEnd( function() : ( player )
+ OnThreadEnd( function() : ( player, playerDropshipIndex, playerDropship )
{
if ( IsValid( player ) )
{
player.ClearParent()
ClearPlayerAnimViewEntity( player )
-
- if ( !player.s.dropshipIntroIsJumping )
- {
- player.MovementEnable()
- player.EnableWeaponViewModel()
- RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
- }
+ }
+ if( IsAlive( playerDropship ) )
+ {
+ if ( playerDropship.GetTeam() == TEAM_MILITIA )
+ file.militiaDropships[ playerDropship ][ playerDropshipIndex ] = null
+ else
+ file.imcDropships[ playerDropship ][ playerDropshipIndex ] = null
}
})
- WaitFrame()
-
- player.EndSignal( "OnDeath" )
-
- // find the player's dropship and seat
- array<IntroDropship> teamDropships
- if ( player.GetTeam() == TEAM_MILITIA )
- teamDropships = file.militiaDropships
- else
- teamDropships = file.imcDropships
-
- IntroDropship playerDropship
- int playerDropshipIndex = -1
- foreach ( IntroDropship dropship in teamDropships )
- for ( int i = 0; i < dropship.players.len(); i++ )
- if ( dropship.players[ i ] == null )
- {
- playerDropship = dropship
- playerDropshipIndex = i
-
- dropship.players[ i ] = player
- break
- }
-
- if ( playerDropship.dropship == null )
- {
- // if we're at this point, we have more players than we do dropships, so just pick a random one
- playerDropship = teamDropships.getrandom()
- playerDropshipIndex = RandomInt( MAX_DROPSHIP_PLAYERS )
- }
-
- // respawn player and holster their weapons so they aren't out
- player.RespawnPlayer( null )
- HolsterAndDisableWeapons(player)
+ HolsterAndDisableWeapons( player )
player.DisableWeaponViewModel()
+ UnMuteAll( player )
+ StopSoundOnEntity( player, "Duck_For_FrontierDefenseTitanSelectScreen" )
// hide hud and fade screen out from black
AddCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
ScreenFadeFromBlack( player, 0.5, 0.5 )
// faction leaders are done clientside, spawn them here
- Remote_CallFunction_NonReplay( player, "ServerCallback_SpawnFactionCommanderInDropship", playerDropship.dropship.GetEncodedEHandle(), file.introStartTime )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SpawnFactionCommanderInDropship", playerDropship.GetEncodedEHandle(), file.introStartTime )
// do firstperson sequence
FirstPersonSequenceStruct idleSequence
@@ -207,9 +207,7 @@ void function SpawnPlayerIntoDropship( entity player )
idleSequence.viewConeFunction = ViewConeRampFree
idleSequence.hideProxy = true
idleSequence.setInitialTime = Time() - file.introStartTime
- thread FirstPersonSequence( idleSequence, player, playerDropship.dropship )
- WaittillAnimDone( player )
-
+ waitthread FirstPersonSequence( idleSequence, player, playerDropship )
// todo: possibly rework this to actually get the time the idle anim takes and start the starttime of the jump sequence for very late joiners using that
// jump sequence
@@ -217,13 +215,17 @@ void function SpawnPlayerIntoDropship( entity player )
jumpSequence.firstPersonAnim = DROPSHIP_JUMP_ANIMS_POV[ playerDropshipIndex ]
jumpSequence.thirdPersonAnim = DROPSHIP_JUMP_ANIMS[ playerDropshipIndex ]
jumpSequence.attachment = "ORIGIN"
+ jumpSequence.viewConeFunction = ViewConeFree
jumpSequence.setInitialTime = max( 0.0, Time() - ( file.introStartTime + 11.0 ) ) // pretty sure you should do this with GetScriptedAnimEventCycleFrac?
// idk unsure how to use that, all i know is getsequenceduration > the length it actually should be
- thread FirstPersonSequence( jumpSequence, player, playerDropship.dropship )
- WaittillAnimDone( player ) // somehow this is better than just waiting for the blocking FirstPersonSequence call?
+ #if BATTLECHATTER_ENABLED
+ if( playerDropshipIndex == 0 )
+ PlayBattleChatterLine( player, "bc_pIntroChat" )
+ #endif
+
+ waitthread FirstPersonSequence( jumpSequence, player, playerDropship )
- player.s.dropshipIntroIsJumping <- true
thread PlayerJumpsFromDropship( player )
}
@@ -239,20 +241,21 @@ void function PlayerJumpsFromDropship( entity player )
// show weapon viewmodel and hud and let them move again
player.MovementEnable()
player.EnableWeaponViewModel()
- DeployAndEnableWeapons(player)
+ DeployAndEnableWeapons( player )
RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
}
})
-
- // wait for intro timer to be fully done
- wait ( file.introStartTime + DROPSHIP_INTRO_LENGTH ) - Time()
- player.MovementDisable() // disable all movement but let them look around still
- player.ConsumeDoubleJump() // movementdisable doesn't prevent double jumps
// wait for player to hit the ground
- wait 0.1 // assume players will never actually hit ground before this
+ player.ClearParent()
+ WaitFrame()
+ player.SetVelocity( < 0, 0, -100 > ) // Toss players a bit down so it makes a smoother transition when jumping off the Dropship
+ player.MovementDisable() // Disable all movement but let them look around still
+ player.ConsumeDoubleJump() // MovementDisable doesn't prevent double jumps
+ WaitFrame()
while ( !player.IsOnGround() && !player.IsWallRunning() && !player.IsWallHanging() ) // todo this needs tweaking
WaitFrame()
- TryGameModeAnnouncement( player )
-} \ No newline at end of file
+ if ( GetRoundsPlayed() == 0 ) //Intro is announced only for the first round in Vanilla as certain gamemodes have different announcements for rounds restarts
+ TryGameModeAnnouncement( player )
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut
index 425a8b8b..0d1b42b7 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_codecallbacks.gnut
@@ -21,6 +21,8 @@ global function SetTitanMeterGainScale
#if MP
global function CodeCallback_OnServerAnimEvent
+global function CodeCallback_WeaponDropped
+global function AddCallback_OnWeaponDropped
#endif
struct AccumulatedDamageData
@@ -43,6 +45,7 @@ struct
]
table<entity, AccumulatedDamageData> playerAccumulatedDamageData
+ array< void functionref( entity ) > weaponDroppedCallbacks
} file
void function CodeCallback_Init()
@@ -283,12 +286,26 @@ void function CodeCallback_DamagePlayerOrNPC( entity ent, var damageInfo )
return
RunClassDamageFinalCallbacks( ent, damageInfo )
+
#if VERBOSE_DAMAGE_PRINTOUTS
printt( " after class damage final callbacks:", DamageInfo_GetDamage( damageInfo ) )
#endif
if ( DamageInfo_GetDamage( damageInfo ) == 0 )
return
+ // Added via AddEntityCallback_OnFinalDamaged
+ foreach ( callbackFunc in ent.e.entFinalDamageCallbacks )
+ {
+ callbackFunc( ent, damageInfo )
+ }
+
+ #if VERBOSE_DAMAGE_PRINTOUTS
+ printt( " afterAddEntityCallback_OnFinalDamaged callbacks:", DamageInfo_GetDamage( damageInfo ) )
+ #endif
+
+ if ( DamageInfo_GetDamage( damageInfo ) == 0 )
+ return
+
if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS )
DamageInfo_AddDamageFlags( damageInfo, DAMAGEFLAG_NOPAIN )
@@ -1016,4 +1033,26 @@ void function CodeCallback_OnServerAnimEvent( entity ent, string eventName )
PerfEnd( PerfIndexServer.CB_OnServerAnimEvent )
}
+
+void function AddCallback_OnWeaponDropped( void functionref( entity ) callback )
+{
+ file.weaponDroppedCallbacks.append( callback )
+}
+
+void function CodeCallback_WeaponDropped( entity weapon )
+{
+ // shamelessly taken form SP
+ if ( !IsValid( weapon ) )
+ return
+
+ // Might look a bit hacky to put it here, but thats how SP does it
+ if ( !Flag( "WeaponDropsAllowed" ) )
+ {
+ weapon.Destroy()
+ return
+ }
+
+ foreach( callback in file.weaponDroppedCallbacks )
+ callback( weapon )
+}
#endif // #if MP \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
index bfcd23e0..f7c398d9 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
@@ -13,6 +13,8 @@ global function SetTimerBased
global function SetShouldUseRoundWinningKillReplay
global function SetRoundWinningKillReplayKillClasses
global function SetRoundWinningKillReplayAttacker
+global function SetCallback_TryUseProjectileReplay
+global function ShouldTryUseProjectileReplay
global function SetWinner
global function SetTimeoutWinnerDecisionFunc
global function AddTeamScore
@@ -48,13 +50,28 @@ struct {
float roundWinningKillReplayTime
entity roundWinningKillReplayVictim
entity roundWinningKillReplayAttacker
+ int roundWinningKillReplayInflictorEHandle // this is either the inflictor or the attacker
int roundWinningKillReplayMethodOfDeath
float roundWinningKillReplayTimeOfDeath
float roundWinningKillReplayHealthFrac
array<void functionref()> roundEndCleanupCallbacks
+ bool functionref( entity victim, entity attacker, var damageInfo, bool isRoundEnd ) shouldTryUseProjectileReplayCallback
} file
+void function SetCallback_TryUseProjectileReplay( bool functionref( entity victim, entity attacker, var damageInfo, bool isRoundEnd ) callback )
+{
+ file.shouldTryUseProjectileReplayCallback = callback
+}
+
+bool function ShouldTryUseProjectileReplay( entity victim, entity attacker, var damageInfo, bool isRoundEnd )
+{
+ if ( file.shouldTryUseProjectileReplayCallback != null )
+ return file.shouldTryUseProjectileReplayCallback( victim, attacker, damageInfo, isRoundEnd )
+ // default to true (vanilla behaviour)
+ return true
+}
+
void function PIN_GameStart()
{
// todo: using the pin telemetry function here, weird and was done veeery early on before i knew how this all worked, should use a different one
@@ -79,6 +96,7 @@ void function PIN_GameStart()
AddCallback_OnPlayerKilled( OnPlayerKilled )
AddDeathCallback( "npc_titan", OnTitanKilled )
+ AddCallback_EntityChangedTeam( "player", OnPlayerChangedTeam )
RegisterSignal( "CleanUpEntitiesForRoundEnd" )
}
@@ -184,6 +202,14 @@ void function GameStateEnter_Prematch()
if ( !GetClassicMPMode() && !ClassicMP_ShouldTryIntroAndEpilogueWithoutClassicMP() )
thread StartGameWithoutClassicMP()
+
+ // Initialise any spectators. Hopefully they are all initialised already in CodeCallback_OnClientConnectionCompleted
+ // (_base_gametype_mp.gnut) but for modes like LTS this doesn't seem to happen late enough to work properly.
+ foreach ( player in GetPlayerArray() )
+ {
+ if ( IsPrivateMatchSpectator( player ) )
+ InitialisePrivateMatchSpectatorPlayer( player )
+ }
}
void function StartGameWithoutClassicMP()
@@ -220,6 +246,8 @@ void function GameStateEnter_Playing_Threaded()
{
WaitFrame() // ensure timelimits are all properly set
+ thread DialoguePlayNormal() // runs dialogue play function
+
while ( GetGameState() == eGameState.Playing )
{
// could cache these, but what if we update it midgame?
@@ -268,6 +296,8 @@ void function GameStateEnter_WinnerDetermined_Threaded()
// do win announcement
int winningTeam = GetWinningTeamWithFFASupport()
+ DialoguePlayWinnerDetermined() // play a faction dialogue when winner is determined
+
foreach ( entity player in GetPlayerArray() )
{
int announcementSubstr
@@ -278,6 +308,9 @@ void function GameStateEnter_WinnerDetermined_Threaded()
Remote_CallFunction_NonReplay( player, "ServerCallback_AnnounceRoundWinner", winningTeam, announcementSubstr, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME, GameRules_GetTeamScore2( TEAM_MILITIA ), GameRules_GetTeamScore2( TEAM_IMC ) )
else
Remote_CallFunction_NonReplay( player, "ServerCallback_AnnounceWinner", winningTeam, announcementSubstr, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME )
+
+ if ( player.GetTeam() == winningTeam )
+ UnlockAchievement( player, achievements.MP_WIN )
}
WaitFrame() // wait a frame so other scripts can setup killreplay stuff
@@ -312,6 +345,7 @@ void function GameStateEnter_WinnerDetermined_Threaded()
WaitFrame() // prevent a race condition with PlayerWatchesRoundWinningKillReplay
file.roundWinningKillReplayAttacker = null // clear this
+ file.roundWinningKillReplayInflictorEHandle = -1
if ( killcamsWereEnabled )
SetKillcamsEnabled( true )
@@ -362,6 +396,7 @@ void function GameStateEnter_WinnerDetermined_Threaded()
}
else
{
+ RegisterChallenges_OnMatchEnd()
if ( ClassicMP_ShouldRunEpilogue() )
{
ClassicMP_SetupEpilogue()
@@ -384,11 +419,14 @@ void function PlayerWatchesRoundWinningKillReplay( entity player, float replayLe
player.SetPredictionEnabled( false ) // prediction fucks with replays
entity attacker = file.roundWinningKillReplayAttacker
- player.SetKillReplayDelay( Time() - replayLength, THIRD_PERSON_KILL_REPLAY_ALWAYS )
- player.SetKillReplayInflictorEHandle( attacker.GetEncodedEHandle() )
- player.SetKillReplayVictim( file.roundWinningKillReplayVictim )
- player.SetViewIndex( attacker.GetIndexForEntity() )
- player.SetIsReplayRoundWinning( true )
+ if ( IsValid( attacker ) )
+ {
+ player.SetKillReplayDelay( Time() - replayLength, THIRD_PERSON_KILL_REPLAY_ALWAYS )
+ player.SetKillReplayInflictorEHandle( file.roundWinningKillReplayInflictorEHandle )
+ player.SetKillReplayVictim( file.roundWinningKillReplayVictim )
+ player.SetViewIndex( attacker.GetIndexForEntity() )
+ player.SetIsReplayRoundWinning( true )
+ }
if ( replayLength >= ROUND_WINNING_KILL_REPLAY_LENGTH_OF_REPLAY - 0.5 ) // only do fade if close to full length replay
{
@@ -450,6 +488,7 @@ void function GameStateEnter_SwitchingSides_Threaded()
svGlobal.levelEnt.Signal( "RoundEnd" ) // might be good to get a new signal for this? not 100% necessary tho i think
SetServerVar( "switchedSides", 1 )
file.roundWinningKillReplayAttacker = null // reset this after replay
+ file.roundWinningKillReplayInflictorEHandle = -1
if ( file.usePickLoadoutScreen )
SetGameState( eGameState.PickLoadout )
@@ -473,7 +512,7 @@ void function PlayerWatchesSwitchingSidesKillReplay( entity player, bool doRepla
entity attacker = file.roundWinningKillReplayAttacker
player.SetKillReplayDelay( Time() - replayLength, THIRD_PERSON_KILL_REPLAY_ALWAYS )
- player.SetKillReplayInflictorEHandle( attacker.GetEncodedEHandle() )
+ player.SetKillReplayInflictorEHandle( file.roundWinningKillReplayInflictorEHandle )
player.SetKillReplayVictim( file.roundWinningKillReplayVictim )
player.SetViewIndex( attacker.GetIndexForEntity() )
player.SetIsReplayRoundWinning( true )
@@ -503,6 +542,20 @@ void function GameStateEnter_SuddenDeath()
{
// disable respawns, suddendeath calling is done on a kill callback
SetRespawnsEnabled( false )
+
+ // defensive fixes, so game won't stuck in SuddenDeath forever
+ bool mltElimited = false
+ bool imcElimited = false
+ if( GetPlayerArrayOfTeam_Alive( TEAM_MILITIA ).len() < 1 )
+ mltElimited = true
+ if( GetPlayerArrayOfTeam_Alive( TEAM_IMC ).len() < 1 )
+ imcElimited = true
+ if( mltElimited && imcElimited )
+ SetWinner( TEAM_UNASSIGNED )
+ else if( mltElimited )
+ SetWinner( TEAM_IMC )
+ else if( imcElimited )
+ SetWinner( TEAM_MILITIA )
}
@@ -554,6 +607,9 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
return
}
+ entity inflictor = DamageInfo_GetInflictor( damageInfo )
+ bool shouldUseInflictor = IsValid( inflictor ) && ShouldTryUseProjectileReplay( victim, attacker, damageInfo, true )
+
// set round winning killreplay info here if we're tracking pilot kills
// todo: make this not count environmental deaths like falls, unsure how to prevent this
if ( file.roundWinningKillReplayTrackPilotKills && victim != attacker && attacker != svGlobal.worldspawn && IsValid( attacker ) )
@@ -563,6 +619,7 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
file.roundWinningKillReplayTime = Time()
file.roundWinningKillReplayVictim = victim
file.roundWinningKillReplayAttacker = attacker
+ file.roundWinningKillReplayInflictorEHandle = ( shouldUseInflictor ? inflictor : attacker ).GetEncodedEHandle()
file.roundWinningKillReplayMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo )
file.roundWinningKillReplayTimeOfDeath = Time()
file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker )
@@ -613,6 +670,9 @@ void function OnTitanKilled( entity victim, var damageInfo )
return
}
+ entity inflictor = DamageInfo_GetInflictor( damageInfo )
+ bool shouldUseInflictor = IsValid( inflictor ) && ShouldTryUseProjectileReplay( victim, DamageInfo_GetAttacker( damageInfo ), damageInfo, true )
+
// set round winning killreplay info here if we're tracking titan kills
// todo: make this not count environmental deaths like falls, unsure how to prevent this
entity attacker = DamageInfo_GetAttacker( damageInfo )
@@ -623,6 +683,7 @@ void function OnTitanKilled( entity victim, var damageInfo )
file.roundWinningKillReplayTime = Time()
file.roundWinningKillReplayVictim = victim
file.roundWinningKillReplayAttacker = attacker
+ file.roundWinningKillReplayInflictorEHandle = ( shouldUseInflictor ? inflictor : attacker ).GetEncodedEHandle()
file.roundWinningKillReplayMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo )
file.roundWinningKillReplayTimeOfDeath = Time()
file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker )
@@ -737,11 +798,12 @@ void function SetRoundWinningKillReplayKillClasses( bool pilot, bool titan )
file.roundWinningKillReplayTrackTitanKills = titan // player kills in titans should get tracked anyway, might be worth renaming this
}
-void function SetRoundWinningKillReplayAttacker( entity attacker )
+void function SetRoundWinningKillReplayAttacker( entity attacker, int inflictorEHandle = -1 )
{
file.roundWinningKillReplayTime = Time()
file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker )
file.roundWinningKillReplayAttacker = attacker
+ file.roundWinningKillReplayInflictorEHandle = inflictorEHandle == -1 ? attacker.GetEncodedEHandle() : inflictorEHandle
file.roundWinningKillReplayTimeOfDeath = Time()
}
@@ -773,9 +835,45 @@ void function SetWinner( int team, string winningReason = "", string losingReaso
}
SetGameState( eGameState.WinnerDetermined )
+ ScoreEvent_RoundComplete( team )
}
else
+ {
SetGameState( eGameState.WinnerDetermined )
+ ScoreEvent_MatchComplete( team )
+
+ array<entity> players = GetPlayerArray()
+ int functionref( entity, entity ) compareFunc = GameMode_GetScoreCompareFunc( GAMETYPE )
+ if ( compareFunc != null )
+ {
+ players.sort( compareFunc )
+ int playerCount = players.len()
+ int currentPlace = 1
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( i >= playerCount )
+ continue
+
+ if ( i > 0 && compareFunc( players[i - 1], players[i] ) != 0 )
+ currentPlace += 1
+
+ switch( currentPlace )
+ {
+ case 1:
+ UpdatePlayerStat( players[i], "game_stats", "mvp" )
+ UpdatePlayerStat( players[i], "game_stats", "mvp_total" )
+ UpdatePlayerStat( players[i], "game_stats", "top3OnTeam" )
+ break
+ case 2:
+ UpdatePlayerStat( players[i], "game_stats", "top3OnTeam" )
+ break
+ case 3:
+ UpdatePlayerStat( players[i], "game_stats", "top3OnTeam" )
+ break
+ }
+ }
+ }
+ }
}
}
@@ -868,3 +966,108 @@ float function GetTimeLimit_ForGameMode()
// default to 10 mins, because that seems reasonable
return GetCurrentPlaylistVarFloat( playlistString, 10 )
}
+
+// faction dialogue
+
+void function DialoguePlayNormal()
+{
+ int totalScore = GameMode_GetScoreLimit( GameRules_GetGameMode() )
+ int winningTeam
+ int losingTeam
+ float diagIntervel = 71 // play a faction dailogue every 70 + 1s to prevent play together with winner dialogue
+
+ while( GetGameState() == eGameState.Playing )
+ {
+ wait diagIntervel
+ if( GameRules_GetTeamScore( TEAM_MILITIA ) < GameRules_GetTeamScore( TEAM_IMC ) )
+ {
+ winningTeam = TEAM_IMC
+ losingTeam = TEAM_MILITIA
+ }
+ if( GameRules_GetTeamScore( TEAM_MILITIA ) > GameRules_GetTeamScore( TEAM_IMC ) )
+ {
+ winningTeam = TEAM_MILITIA
+ losingTeam = TEAM_IMC
+ }
+ if( GameRules_GetTeamScore( winningTeam ) - GameRules_GetTeamScore( losingTeam ) >= totalScore * 0.4 )
+ {
+ PlayFactionDialogueToTeam( "scoring_winningLarge", winningTeam )
+ PlayFactionDialogueToTeam( "scoring_losingLarge", losingTeam )
+ }
+ else if( GameRules_GetTeamScore( winningTeam ) - GameRules_GetTeamScore( losingTeam ) <= totalScore * 0.2 )
+ {
+ PlayFactionDialogueToTeam( "scoring_winningClose", winningTeam )
+ PlayFactionDialogueToTeam( "scoring_losingClose", losingTeam )
+ }
+ else if( GameRules_GetTeamScore( winningTeam ) == GameRules_GetTeamScore( losingTeam ) )
+ {
+ continue
+ }
+ else
+ {
+ PlayFactionDialogueToTeam( "scoring_winning", winningTeam )
+ PlayFactionDialogueToTeam( "scoring_losing", losingTeam )
+ }
+ }
+}
+
+void function DialoguePlayWinnerDetermined()
+{
+ int totalScore = GameMode_GetScoreLimit( GameRules_GetGameMode() )
+ int winningTeam
+ int losingTeam
+
+ if( GameRules_GetTeamScore( TEAM_MILITIA ) < GameRules_GetTeamScore( TEAM_IMC ) )
+ {
+ winningTeam = TEAM_IMC
+ losingTeam = TEAM_MILITIA
+ }
+ if( GameRules_GetTeamScore( TEAM_MILITIA ) > GameRules_GetTeamScore( TEAM_IMC ) )
+ {
+ winningTeam = TEAM_MILITIA
+ losingTeam = TEAM_IMC
+ }
+ if( IsRoundBased() ) // check for round based modes
+ {
+ if( GameRules_GetTeamScore( winningTeam ) != GameMode_GetRoundScoreLimit( GAMETYPE ) ) // no winner dialogue till game really ends
+ return
+ }
+ if( GameRules_GetTeamScore( winningTeam ) - GameRules_GetTeamScore( losingTeam ) >= totalScore * 0.4 )
+ {
+ PlayFactionDialogueToTeam( "scoring_wonMercy", winningTeam )
+ PlayFactionDialogueToTeam( "scoring_lostMercy", losingTeam )
+ }
+ else if( GameRules_GetTeamScore( winningTeam ) - GameRules_GetTeamScore( losingTeam ) <= totalScore * 0.2 )
+ {
+ PlayFactionDialogueToTeam( "scoring_wonClose", winningTeam )
+ PlayFactionDialogueToTeam( "scoring_lostClose", losingTeam )
+ }
+ else if( GameRules_GetTeamScore( winningTeam ) == GameRules_GetTeamScore( losingTeam ) )
+ {
+ PlayFactionDialogueToTeam( "scoring_tied", winningTeam )
+ PlayFactionDialogueToTeam( "scoring_tied", losingTeam )
+ }
+ else
+ {
+ PlayFactionDialogueToTeam( "scoring_won", winningTeam )
+ PlayFactionDialogueToTeam( "scoring_lost", losingTeam )
+ }
+}
+
+/// This is to move all NPCs that a player owns from one team to the other during a match
+/// Auto-Titans, Turrets, Ticks and Hacked Spectres will all move along together with the player to the new Team
+/// Also possibly prevents mods that spawns other types of NPCs that players can own from breaking when switching (i.e Drones, Hacked Reapers)
+void function OnPlayerChangedTeam( entity player )
+{
+ if ( !player.hasConnected ) // Prevents players who just joined to trigger below code, as server always pre setups their teams
+ return
+
+ NotifyClientsOfTeamChange( player, GetOtherTeam( player.GetTeam() ), player.GetTeam() )
+
+ foreach( npc in GetNPCArray() )
+ {
+ entity bossPlayer = npc.GetBossPlayer()
+ if ( IsValidPlayer( bossPlayer ) && bossPlayer == player && IsAlive( npc ) )
+ SetTeam( npc, player.GetTeam() )
+ }
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_model_viewer.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_model_viewer.nut
new file mode 100644
index 00000000..c33f4ef0
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_model_viewer.nut
@@ -0,0 +1,180 @@
+untyped
+
+
+global function ModelViewer_Init
+
+global function ToggleModelViewer
+
+global modelViewerModels = []
+
+#if DEV
+struct
+{
+ bool initialized
+ bool active
+ entity gameUIFreezeControls
+ array<string> playerWeapons
+ array<string> playerOffhands
+ bool dpadUpPressed = true
+ bool dpadDownPressed = true
+ var lastTitanAvailability
+} file
+#endif // DEV
+
+function ModelViewer_Init()
+{
+ #if DEV
+ if ( reloadingScripts )
+ return
+ AddClientCommandCallback( "ModelViewer", ClientCommand_ModelViewer )
+ #endif
+}
+
+function ToggleModelViewer()
+{
+ #if DEV
+ entity player = GetPlayerArray()[ 0 ]
+ if ( !file.active )
+ {
+ file.active = true
+
+ DisablePrecacheErrors()
+ wait 0.5
+
+ ModelViewerDisableConflicts()
+ Remote_CallFunction_NonReplay( player, "ServerCallback_ModelViewerDisableConflicts" )
+
+ ReloadShared()
+
+ if ( !file.initialized )
+ {
+ file.initialized = true
+ ControlsInit()
+ }
+
+ Remote_CallFunction_NonReplay( player, "ServerCallback_MVEnable" )
+
+ file.lastTitanAvailability = level.nv.titanAvailability
+ Riff_ForceTitanAvailability( eTitanAvailability.Never )
+
+ WeaponsRemove()
+ thread UpdateModelBounds()
+ }
+ else
+ {
+ file.active = false
+
+ Remote_CallFunction_NonReplay( player, "ServerCallback_MVDisable" )
+ RestorePrecacheErrors()
+
+ Riff_ForceTitanAvailability( file.lastTitanAvailability )
+
+ WeaponsRestore()
+ }
+ #endif
+}
+
+#if DEV
+function ModelViewerDisableConflicts()
+{
+ disable_npcs() //Just disable_npcs() for now, will probably add things later
+}
+
+function ReloadShared()
+{
+ modelViewerModels = GetModelViewerList()
+}
+
+function ControlsInit()
+{
+ file.gameUIFreezeControls = CreateEntity( "game_ui" )
+ file.gameUIFreezeControls.kv.spawnflags = 32
+ file.gameUIFreezeControls.kv.FieldOfView = -1.0
+
+ DispatchSpawn( file.gameUIFreezeControls )
+}
+
+bool function ClientCommand_ModelViewer( entity player, array<string> args )
+{
+ string command = args[ 0 ]
+ switch ( command )
+ {
+ case "freeze_player":
+ file.gameUIFreezeControls.Fire( "Activate", "!player", 0 )
+ break
+
+ case "unfreeze_player":
+ file.gameUIFreezeControls.Fire( "Deactivate", "!player", 0 )
+ break
+ }
+
+ return true
+}
+
+function UpdateModelBounds()
+{
+ wait( 0.3 )
+
+ foreach ( index, modelName in modelViewerModels )
+ {
+ entity model = CreatePropDynamic( expect asset( modelName ) )
+ local mins = model.GetBoundingMins()
+ local maxs = model.GetBoundingMaxs()
+
+ mins.x = min( -8.0, mins.x )
+ mins.y = min( -8.0, mins.y )
+ mins.z = min( -8.0, mins.z )
+
+ maxs.x = max( 8.0, maxs.x )
+ maxs.y = max( 8.0, maxs.y )
+ maxs.z = max( 8.0, maxs.z )
+
+ Remote_CallFunction_NonReplay( GetPlayerArray()[ 0 ], "ServerCallback_MVUpdateModelBounds", index, mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z )
+ model.Destroy()
+ }
+}
+
+function WeaponsRemove()
+{
+ entity player = GetPlayerArray()[0]
+ if ( !IsValid( player ) )
+ return
+
+ file.playerWeapons.clear()
+ file.playerOffhands.clear()
+
+ array<entity> weapons = player.GetMainWeapons()
+ foreach ( weaponEnt in weapons )
+ {
+ string weapon = weaponEnt.GetWeaponClassName()
+ file.playerWeapons.append( weapon )
+ player.TakeWeapon( weapon )
+ }
+
+ array<entity> offhands = player.GetOffhandWeapons()
+ foreach ( index, offhandEnt in offhands )
+ {
+ string offhand = offhandEnt.GetWeaponClassName()
+ file.playerOffhands.append( offhand )
+ player.TakeOffhandWeapon( index )
+ }
+}
+
+function WeaponsRestore()
+{
+ entity player = GetPlayerArray()[0]
+ if ( !IsValid( player ) )
+ return
+
+ foreach ( weapon in file.playerWeapons )
+ {
+ player.GiveWeapon( weapon )
+ }
+
+ foreach ( index, offhand in file.playerOffhands )
+ {
+ player.GiveOffhandWeapon( offhand, index )
+ }
+}
+
+#endif // DEV
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut
index 2d1ff074..2a4c4282 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_score.nut
@@ -7,6 +7,8 @@ global function ScoreEvent_PlayerKilled
global function ScoreEvent_TitanDoomed
global function ScoreEvent_TitanKilled
global function ScoreEvent_NPCKilled
+global function ScoreEvent_MatchComplete
+global function ScoreEvent_RoundComplete
global function ScoreEvent_SetEarnMeterValues
global function ScoreEvent_SetupEarnMeterValuesForMixedModes
@@ -27,6 +29,10 @@ void function InitPlayerForScoreEvents( entity player )
player.s.currentKillstreak <- 0
player.s.lastKillTime <- 0.0
player.s.currentTimedKillstreak <- 0
+ player.s.lastKillTime_Mayhem <- 0.0
+ player.s.currentTimedKillstreak_Mayhem <- 0
+ player.s.lastKillTime_Onslaught <- 0.0
+ player.s.currentTimedKillstreak_Onslaught <- 0
}
void function AddPlayerScore( entity targetPlayer, string scoreEventName, entity associatedEnt = null, string noideawhatthisis = "", int pointValueOverride = -1 )
@@ -92,6 +98,7 @@ void function ScoreEvent_PlayerKilled( entity victim, entity attacker, var damag
victim.s.currentTimedKillstreak = 0
victim.p.numberOfDeathsSinceLastKill++ // this is reset on kill
+ victim.p.lastKiller = attacker
// have to do this early before we reset victim's player killstreaks
// nemesis when you kill a player that is dominating you
@@ -130,12 +137,20 @@ void function ScoreEvent_PlayerKilled( entity victim, entity attacker, var damag
attacker.p.numberOfDeathsSinceLastKill = 0
}
+ // revenge + quick revenge
+ if ( attacker.p.lastKiller == victim )
+ {
+ if ( Time() - GetPlayerLastRespawnTime( attacker ) < QUICK_REVENGE_TIME_LIMIT )
+ AddPlayerScore( attacker, "QuickRevenge" )
+ else
+ AddPlayerScore( attacker, "Revenge" )
+ }
// untimed killstreaks
attacker.s.currentKillstreak++
- if ( attacker.s.currentKillstreak == 3 )
+ if ( attacker.s.currentKillstreak == KILLINGSPREE_KILL_REQUIREMENT )
AddPlayerScore( attacker, "KillingSpree" )
- else if ( attacker.s.currentKillstreak == 5 )
+ else if ( attacker.s.currentKillstreak == RAMPAGE_KILL_REQUIREMENT )
AddPlayerScore( attacker, "Rampage" )
// increment untimed killstreaks against specific players
@@ -187,24 +202,42 @@ void function ScoreEvent_TitanKilled( entity victim, entity attacker, var damage
return
if ( attacker.IsTitan() )
- AddPlayerScore( attacker, "TitanKillTitan", victim.GetTitanSoul().GetOwner() )
+ {
+ if( victim.GetBossPlayer() || victim.IsPlayer() ) // to confirm this is a pet titan or player titan
+ AddPlayerScore( attacker, "TitanKillTitan", attacker ) // this will show the "Titan Kill" callsign event
+ else
+ AddPlayerScore( attacker, "TitanKillTitan" )
+ }
else
- AddPlayerScore( attacker, "KillTitan", victim.GetTitanSoul().GetOwner() )
+ {
+ if( victim.GetBossPlayer() || victim.IsPlayer() )
+ AddPlayerScore( attacker, "KillTitan", attacker )
+ else
+ AddPlayerScore( attacker, "KillTitan" )
+ }
- table<int, bool> alreadyAssisted
- foreach( DamageHistoryStruct attackerInfo in victim.e.recentDamageHistory )
+ entity soul = victim.GetTitanSoul()
+ if ( IsValid( soul ) )
{
- if ( !IsValid( attackerInfo.attacker ) || !attackerInfo.attacker.IsPlayer() || attackerInfo.attacker == victim )
- continue
-
- bool exists = attackerInfo.attacker.GetEncodedEHandle() in alreadyAssisted ? true : false
- if( attackerInfo.attacker != attacker && !exists )
+ table<int, bool> alreadyAssisted
+
+ foreach( DamageHistoryStruct attackerInfo in soul.e.recentDamageHistory )
{
- alreadyAssisted[attackerInfo.attacker.GetEncodedEHandle()] <- true
- AddPlayerScore(attackerInfo.attacker, "TitanAssist" )
- Remote_CallFunction_NonReplay( attackerInfo.attacker, "ServerCallback_SetAssistInformation", attackerInfo.damageSourceId, attacker.GetEncodedEHandle(), victim.GetEncodedEHandle(), attackerInfo.time )
+ if ( !IsValid( attackerInfo.attacker ) || !attackerInfo.attacker.IsPlayer() || attackerInfo.attacker == soul )
+ continue
+
+ bool exists = attackerInfo.attacker.GetEncodedEHandle() in alreadyAssisted ? true : false
+ if( attackerInfo.attacker != attacker && !exists )
+ {
+ alreadyAssisted[attackerInfo.attacker.GetEncodedEHandle()] <- true
+ AddPlayerScore(attackerInfo.attacker, "TitanAssist" )
+ Remote_CallFunction_NonReplay( attackerInfo.attacker, "ServerCallback_SetAssistInformation", attackerInfo.damageSourceId, attacker.GetEncodedEHandle(), soul.GetEncodedEHandle(), attackerInfo.time )
+ }
}
}
+
+ if( !victim.IsNPC() ) // don't let killing a npc titan plays dialogue
+ KilledPlayerTitanDialogue( attacker, victim )
}
void function ScoreEvent_NPCKilled( entity victim, entity attacker, var damageInfo )
@@ -215,9 +248,66 @@ void function ScoreEvent_NPCKilled( entity victim, entity attacker, var damageIn
AddPlayerScore( attacker, ScoreEventForNPCKilled( victim, damageInfo ), victim )
}
catch ( ex ) {}
+
+ if ( !attacker.IsPlayer() )
+ return
+
+ // mayhem/onslaught (timed killstreaks vs AI)
+
+ // reset before checking
+ if ( Time() - attacker.s.lastKillTime_Mayhem > MAYHEM_REQUIREMENT_TIME )
+ {
+ attacker.s.currentTimedKillstreak_Mayhem = 0
+ attacker.s.lastKillTime_Mayhem = Time()
+ }
+ if ( Time() - attacker.s.lastKillTime_Mayhem <= MAYHEM_REQUIREMENT_TIME )
+ {
+ attacker.s.currentTimedKillstreak_Mayhem++
+
+ if ( attacker.s.currentTimedKillstreak_Mayhem == MAYHEM_REQUIREMENT_KILLS )
+ AddPlayerScore( attacker, "Mayhem" )
+ }
+
+ // reset before checking
+ if ( Time() - attacker.s.lastKillTime_Onslaught > ONSLAUGHT_REQUIREMENT_TIME )
+ {
+ attacker.s.currentTimedKillstreak_Onslaught = 0
+ attacker.s.lastKillTime_Onslaught = Time()
+ }
+ if ( Time() - attacker.s.lastKillTime_Onslaught <= ONSLAUGHT_REQUIREMENT_TIME )
+ {
+ attacker.s.currentTimedKillstreak_Onslaught++
+
+ if ( attacker.s.currentTimedKillstreak_Onslaught == ONSLAUGHT_REQUIREMENT_KILLS )
+ AddPlayerScore( attacker, "Onslaught" )
+ }
}
+void function ScoreEvent_MatchComplete( int winningTeam )
+{
+ foreach( entity player in GetPlayerArray() )
+ {
+ AddPlayerScore( player, "MatchComplete" )
+ SetPlayerChallengeMatchComplete( player )
+ if ( player.GetTeam() == winningTeam )
+ {
+ AddPlayerScore( player, "MatchVictory" )
+ SetPlayerChallengeMatchWon( player, true )
+ }
+ else
+ SetPlayerChallengeMatchWon( player, false )
+ }
+}
+void function ScoreEvent_RoundComplete( int winningTeam )
+{
+ foreach( entity player in GetPlayerArray() )
+ {
+ AddPlayerScore( player, "RoundComplete" )
+ if ( player.GetTeam() == winningTeam )
+ AddPlayerScore( player, "RoundVictory" )
+ }
+}
void function ScoreEvent_SetEarnMeterValues( string eventName, float earned, float owned, float coreScale = 1.0 )
{
@@ -231,7 +321,7 @@ void function ScoreEvent_SetupEarnMeterValuesForMixedModes() // mixed modes in t
{
// todo needs earn/overdrive values
// player-controlled stuff
- ScoreEvent_SetEarnMeterValues( "KillPilot", 0.07, 0.15 )
+ ScoreEvent_SetEarnMeterValues( "KillPilot", 0.07, 0.15, 0.33 ) // 5% for titan cores
ScoreEvent_SetEarnMeterValues( "KillTitan", 0.0, 0.15 )
ScoreEvent_SetEarnMeterValues( "TitanKillTitan", 0.0, 0.0 ) // unsure
ScoreEvent_SetEarnMeterValues( "PilotBatteryStolen", 0.0, 0.35 ) // this actually just doesn't have overdrive in vanilla even
@@ -251,3 +341,42 @@ void function ScoreEvent_SetupEarnMeterValuesForTitanModes()
{
// relatively sure we don't have to do anything here but leaving this function for consistency
}
+
+// faction dialogue
+void function KilledPlayerTitanDialogue( entity attacker, entity victim )
+{
+ if( !attacker.IsPlayer() )
+ return
+ entity titan
+ if ( victim.IsTitan() )
+ titan = victim
+
+ if( !IsValid( titan ) )
+ return
+ string titanCharacterName = GetTitanCharacterName( titan )
+
+ switch( titanCharacterName )
+ {
+ case "ion":
+ PlayFactionDialogueToPlayer( "kc_pilotkillIon", attacker )
+ return
+ case "tone":
+ PlayFactionDialogueToPlayer( "kc_pilotkillTone", attacker )
+ return
+ case "legion":
+ PlayFactionDialogueToPlayer( "kc_pilotkillLegion", attacker )
+ return
+ case "scorch":
+ PlayFactionDialogueToPlayer( "kc_pilotkillScorch", attacker )
+ return
+ case "ronin":
+ PlayFactionDialogueToPlayer( "kc_pilotkillRonin", attacker )
+ return
+ case "northstar":
+ PlayFactionDialogueToPlayer( "kc_pilotkillNorthstar", attacker )
+ return
+ default:
+ PlayFactionDialogueToPlayer( "kc_pilotkilltitan", attacker )
+ return
+ }
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_spectator.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_spectator.gnut
index aa2fc108..510a9b7e 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_spectator.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_spectator.gnut
@@ -170,6 +170,9 @@ void function SpectatorFunc_Default( entity player )
{
player.SetObserverTarget( target )
player.StartObserverMode( OBS_MODE_CHASE )
+ // the delay of 0.1 seems to fix the spec_mode command not working
+ // when using the keybind
+ player.SetSpecReplayDelay( 0.1 )
}
catch ( ex ) { }
}
@@ -215,9 +218,12 @@ bool function ClientCommandCallback_spec_mode( entity player, array<string> args
else if ( player.GetObserverMode() == OBS_MODE_IN_EYE )
{
// set to third person spectate
- player.SetSpecReplayDelay( 0.0 )
+
+ // the delay of 0.1 seems to fix the spec_mode command not working
+ // when using the keybind
+ player.SetSpecReplayDelay( 0.1 )
player.StartObserverMode( OBS_MODE_CHASE )
}
return true
-} \ No newline at end of file
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut
index 0e8b58f4..74a9088b 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_stats.nut
@@ -1,3 +1,5 @@
+untyped // because entity.s
+
global function Stats_Init
global function AddStatCallback
global function Stats_SaveStatDelayed
@@ -12,67 +14,1105 @@ global function PreScoreEventUpdateStats
global function PostScoreEventUpdateStats
global function Stats_OnPlayerDidDamage
+struct {
+ table< string, array<string> > refs
+ table< string, array< void functionref( entity, float, string ) > > callbacks
+
+ table< entity, table< string, int > > cachedIntStatChanges
+ table< table< string, float > > cachedFloatStatChanges
+
+ table< entity, float > playerKills
+ table< entity, float > playerKillsPvp
+ table< entity, float > playerDeaths
+ table< entity, float > playerDeathsPvp
+
+ bool isFirstStrike = true
+} file
+
void function Stats_Init()
{
+ AddCallback_OnPlayerKilled( OnPlayerOrNPCKilled )
+ AddCallback_OnNPCKilled( OnPlayerOrNPCKilled )
+ AddCallback_OnPlayerRespawned( OnPlayerRespawned )
+ AddCallback_OnClientConnected( OnClientConnected )
+ AddCallback_OnClientDisconnected( OnClientDisconnected )
+ AddCallback_GameStateEnter( eGameState.WinnerDetermined, OnWinnerDetermined )
+ thread HandleDistanceAndTimeStats_Threaded()
+ thread SaveStatsPeriodically_Threaded()
}
-void function AddStatCallback(string statCategory, string statAlias, string statSubAlias, void functionref(entity, float, string) callback, string subRef)
+void function AddStatCallback( string statCategory, string statAlias, string statSubAlias, void functionref( entity, float, string ) callback, string subRef )
{
+ if ( !IsValidStat( statCategory, statAlias, statSubAlias ) )
+ throw format( "INVALID STAT: %s : %s : %s", statCategory, statAlias, statSubAlias )
+
+
+ string statVar = GetStatVar( statCategory, statAlias, statSubAlias )
+ if ( statVar in file.refs )
+ {
+ file.refs[ statVar ].append( subRef )
+ file.callbacks[ statVar ].append( callback )
+ }
+ else
+ {
+ file.refs[ statVar ] <- [ subRef ]
+ file.callbacks[ statVar ] <- [ callback ]
+ }
}
-void function Stats_SaveStatDelayed(entity player, string statCategory, string statAlias, string statSubAlias)
+// a lot of this file seems to be doing caching of stats in some way
+void function Stats_SaveStatDelayed( entity player, string statCategory, string statAlias, string statSubAlias, float delay = 0.1 )
{
+ // idk how long the delay is meant to be but whatever
+ wait delay
+
+ if ( !IsValid( player ) )
+ return
+
+ Stats_SaveStat( player, statCategory, statAlias, statSubAlias )
+}
+
+void function Stats_SaveAllStats( entity player )
+{
+ if ( player in file.cachedIntStatChanges )
+ {
+ foreach( string key, int val in file.cachedIntStatChanges[ player ] )
+ {
+ player.SetPersistentVar( key, player.GetPersistentVarAsInt( key ) + val )
+ }
+
+ delete file.cachedIntStatChanges[ player ]
+ }
+ // save cached float stat change
+ if ( player in file.cachedFloatStatChanges )
+ {
+ foreach( string key, float val in file.cachedFloatStatChanges[ player ] )
+ {
+ player.SetPersistentVar( key, expect float( player.GetPersistentVar( key ) ) + val )
+ }
+ delete file.cachedFloatStatChanges[ player ]
+ }
}
-int function PlayerStat_GetCurrentInt(entity player, string statCategory, string statAlias, string statSubAlias)
+void function Stats_SaveStat( entity player, string statCategory, string statAlias, string statSubAlias )
{
+ string stat = GetStatVar( statCategory, statAlias, statSubAlias )
+ // save cached int stat change
+ if ( player in file.cachedIntStatChanges && stat in file.cachedIntStatChanges[ player ] )
+ {
+ player.SetPersistentVar( stat, player.GetPersistentVarAsInt( stat ) + file.cachedIntStatChanges[ player ][ stat ] )
+ delete file.cachedIntStatChanges[ player ][ stat ]
+ return
+ }
+ // save cached float stat change
+ if ( player in file.cachedFloatStatChanges && stat in file.cachedFloatStatChanges[ player ] )
+ {
+ player.SetPersistentVar( stat, expect float( player.GetPersistentVar( stat ) ) + file.cachedFloatStatChanges[ player ][ stat ] )
+ delete file.cachedFloatStatChanges[ player ][ stat ]
+ return
+ }
+}
+
+// this gets the cached change, not the actual value
+int function PlayerStat_GetCurrentInt( entity player, string statCategory, string statAlias, string statSubAlias )
+{
+ string str = GetStatVar( statCategory, statAlias, statSubAlias )
+
+ if ( player in file.cachedIntStatChanges && str in file.cachedIntStatChanges[ player ] )
+ return file.cachedIntStatChanges[ player ][ str ]
return 0
}
-float function PlayerStat_GetCurrentFloat(entity player, string statCategory, string statAlias, string statSubAlias)
+// this gets the cached change, not the actual value
+float function PlayerStat_GetCurrentFloat( entity player, string statCategory, string statAlias, string statSubAlias )
{
+ string str = GetStatVar( statCategory, statAlias, statSubAlias )
+
+ if ( player in file.cachedFloatStatChanges && str in file.cachedFloatStatChanges[ player ] )
+ return file.cachedFloatStatChanges[ player ][ str ]
return 0
}
-void function UpdatePlayerStat(entity player, string statCategory, string subStat, int count = 0)
+void function UpdatePlayerStat( entity player, string statCategory, string subStat, int count = 1, string statAlias = "" )
{
+ if ( !IsValid( player ) )
+ return
+
+ Stats_IncrementStat( player, statCategory, subStat, statAlias, count.tofloat() )
+}
+
+void function IncrementPlayerDidPilotExecutionWhileCloaked( entity player )
+{
+ UpdatePlayerStat( player, "kills_stats", "pilotExecutePilotWhileCloaked" )
+}
+
+void function UpdateTitanDamageStat( entity attacker, float savedDamage, var damageInfo )
+{
+ if ( !IsValid( attacker ) )
+ return
+
+ Stats_IncrementStat( attacker, "titan_stats", "titanDamage", GetTitanCharacterName( attacker ), savedDamage )
+}
+
+void function UpdateTitanWeaponDamageStat( entity attacker, float savedDamage, var damageInfo )
+{
+ if ( !IsValid( attacker ) )
+ return
+
+ string weaponName = GetPersistenceRefFromDamageInfo( damageInfo )
+ if ( weaponName == "" )
+ return
+
+ Stats_IncrementStat( attacker, "weapon_stats", "titanDamage", weaponName, savedDamage )
+}
+
+void function UpdateTitanCoreEarnedStat( entity player, entity titan, int count = 1 )
+{
+ if ( !IsValid( player ) )
+ return
+
+ if ( !IsValid( titan ) )
+ return
+
+ Stats_IncrementStat( player, "titan_stats", "coresEarned", GetTitanCharacterName( titan ), count.tofloat() )
+}
+
+void function PreScoreEventUpdateStats( entity attacker, entity ent )
+{
+ // used to track kill streaks ending i think ( that stuff gets reset during score event update )
+}
+
+void function PostScoreEventUpdateStats( entity attacker, entity ent )
+{
+ if ( !attacker.IsPlayer() )
+ return
+ // used to track kill streaks starting maybe
+ if ( attacker.s.currentKillstreak == KILLINGSPREE_KILL_REQUIREMENT )
+ {
+ // killingSpressAs_<chassis>
+ if ( attacker.IsTitan() )
+ Stats_IncrementStat( attacker, "kills_stats", "killingSpressAs_" + GetTitanCharacterName( attacker ), "", 1.0 )
+
+ entity weapon = attacker.GetActiveWeapon()
+ // I guess if you dont have a valid active weapon when you get awarded a killing spree
+ // you dont get the stat. Too bad!
+ if ( !IsValid( weapon ) )
+ return
+ Stats_IncrementStat( attacker, "weapon_kill_stats", "killingSprees", weapon.GetWeaponClassName(), 1.0 )
+ }
+}
+
+void function Stats_OnPlayerDidDamage( entity victim, var damageInfo )
+{
+ // try and get the player
+ entity attacker = DamageInfo_GetAttacker( damageInfo )
+ // get the player from their titan
+ if ( attacker.IsTitan() && IsPetTitan( attacker ) )
+ attacker = attacker.GetTitanSoul().GetBossPlayer()
+
+ if ( !attacker.IsPlayer() )
+ return
+
+ string weaponName = GetPersistenceRefFromDamageInfo( damageInfo )
+ if ( weaponName == "" )
+ return
+
+ Stats_IncrementStat( attacker, "weapon_stats", "shotsHit", weaponName, 1.0 )
+
+ if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_CRITICAL )
+ Stats_IncrementStat( attacker, "weapon_stats", "critHits", weaponName, 1.0 )
+ if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_HEADSHOT )
+ Stats_IncrementStat( attacker, "weapon_stats", "headshots", weaponName, 1.0 )
+}
+
+void function Stats_IncrementStat( entity player, string statCategory, string statAlias, string statSubAlias, float amount )
+{
+ if ( !IsValidStat( statCategory, statAlias, statSubAlias ) )
+ {
+ printt( "invalid stat: " + statCategory + " : " + statAlias + " : " + statSubAlias )
+ return
+ }
+
+ string str = GetStatVar( statCategory, statAlias, statSubAlias )
+ int type = GetStatVarType( statCategory, statAlias, statSubAlias )
+
+ // stupid exception because respawn set this up as an int in script
+ // but it is actually a float, so the game will crash if we don't fix it somewhere
+ // i dont feel like committing all of sh_stats.gnut so im doing this instead
+ if ( str == "mapStats[%mapname%].hoursPlayed[%gamemode%]" )
+ type = ePlayerStatType.FLOAT
+
+ // this is rather hacky
+ string mode = GAMETYPE
+ int difficulty = GetDifficultyLevel()
+ if ( difficulty >= 5 )
+ return
+
+ string saveVar = Stats_GetFixedSaveVar( str, GetMapName(), mode, difficulty.tostring() )
+ // check if the map and mode exist in persistence
+ try
+ {
+ PersistenceGetEnumIndexForItemName( "gamemodes", mode )
+ PersistenceGetEnumIndexForItemName( "maps", GetMapName() )
+ }
+ catch( ex )
+ {
+ // if we have an invalid mode or map for persistence, and it is used in the
+ // persistence string, we can't save the persistence so we have to just return
+ if ( str != saveVar )
+ {
+ //printt( ex, str, GetMapName(), mode ) // Commented out due to spamming logs on invalid modes (e.g. Gun Game, Infection, ...)
+ return
+ }
+ }
+ str = saveVar
+
+ switch ( type )
+ {
+ case ePlayerStatType.INT:
+ // populate table if needed
+ if ( !( player in file.cachedIntStatChanges ) )
+ file.cachedIntStatChanges[ player ] <- {}
+ if ( !( str in file.cachedIntStatChanges[ player ] ) )
+ file.cachedIntStatChanges[ player ][ str ] <- 0
+
+ file.cachedIntStatChanges[ player ][ str ] += amount.tointeger()
+ break
+ case ePlayerStatType.FLOAT:
+ // populate table if needed
+ if ( !( player in file.cachedFloatStatChanges ) )
+ file.cachedFloatStatChanges[ player ] <- {}
+ if ( !( str in file.cachedFloatStatChanges[ player ] ) )
+ file.cachedFloatStatChanges[ player ][ str ] <- 0.0
+
+ file.cachedFloatStatChanges[ player ][ str ] += amount
+ break
+ default:
+ throw "UNIMPLEMENTED STAT TYPE: " + type
+ }
+
+ // amount here is never used
+ Stats_RunCallbacks( str, player, amount )
+}
+
+void function Stats_RunCallbacks( string statVar, entity player, float change )
+{
+ if ( !( statVar in file.refs ) )
+ return
+
+ for( int i = 0; i < file.refs[ statVar ].len(); i++ )
+ {
+ string ref = file.refs[ statVar ][ i ]
+ void functionref( entity, float, string ) callback = file.callbacks[ statVar ][ i ]
+
+ callback( player, change, ref )
+ }
+}
+
+void function OnClientConnected( entity player )
+{
+ Stats_IncrementStat( player, "game_stats", "game_joined", "", 1.0 )
+}
+
+void function OnClientDisconnected( entity player )
+{
+ Stats_SaveAllStats( player )
+ // maybe we can save this stuff, but idk if we can access persistence in this callback
+ if ( player in file.cachedIntStatChanges )
+ delete file.cachedIntStatChanges[ player ]
+
+ if ( player in file.cachedFloatStatChanges )
+ delete file.cachedFloatStatChanges[ player ]
+}
+
+void function OnPlayerOrNPCKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim.IsPlayer() )
+ thread SetLastPosForDistanceStatValid_Threaded( victim, false )
+
+ HandleDeathStats( victim, attacker, damageInfo )
+
+ if( victim == attacker ) //Suicides are registering stats, afaik vanilla ignores them
+ return
+
+ HandleKillStats( victim, attacker, damageInfo )
+ HandleWeaponKillStats( victim, attacker, damageInfo )
+ HandleTitanStats( victim, attacker, damageInfo )
+}
+
+void function HandleDeathStats( entity player, entity attacker, var damageInfo )
+{
+ if ( !IsValid( player ) || !player.IsPlayer() )
+ return
+
+ if ( player in file.playerDeaths )
+ file.playerDeaths[ player ]++
+ else
+ file.playerDeaths[ player ] <- 1.0
+ // total
+ Stats_IncrementStat( player, "deaths_stats", "total", "", 1.0 )
+
+ // these all rely on the attacker being valid
+ if ( IsValid( attacker ) )
+ {
+ // totalPVP
+ // note: I'm not sure if owned entities count towards totalPVP
+ // such as auto-titans, turrets, etc.
+ if ( attacker.IsPlayer() || attacker.GetBossPlayer() )
+ {
+ if ( player in file.playerDeathsPvp )
+ file.playerDeathsPvp[ player ]++
+ else
+ file.playerDeathsPvp[ player ] <- 1.0
+ Stats_IncrementStat( player, "deaths_stats", "totalPVP", "", 1.0 )
+ }
+
+ // byPilots
+ if ( IsPilot( attacker ) )
+ Stats_IncrementStat( player, "deaths_stats", "byPilots", "", 1.0 )
+
+ // byTitan_<chassis>
+ if ( attacker.IsTitan() && attacker.IsPlayer() )
+ Stats_IncrementStat( player, "deaths_stats", "byTitan_" + GetTitanCharacterName( attacker ), "", 1.0 )
+
+ // bySpectres
+ if ( IsSpectre( attacker ) )
+ Stats_IncrementStat( player, "deaths_stats", "bySpectres", "", 1.0 )
+
+ // byGrunts
+ if ( IsGrunt( attacker ) )
+ Stats_IncrementStat( player, "deaths_stats", "byGrunts", "", 1.0 )
+
+ // byNPCTitans_<chassis>
+ if ( attacker.IsTitan() && attacker.IsNPC() )
+ Stats_IncrementStat( player, "deaths_stats", "byNPCTitans_" + GetTitanCharacterName( attacker ), "", 1.0 )
+ }
+
+ // asPilot
+ if ( IsPilot( player ) )
+ Stats_IncrementStat( player, "deaths_stats", "asPilot", "", 1.0 )
+
+ // asTitan_<chassis>
+ if ( player.IsTitan() )
+ Stats_IncrementStat( player, "deaths_stats", "asTitan_" + GetTitanCharacterName( player ), "", 1.0 )
+
+ // suicides
+ if ( IsSuicide( attacker, player, DamageInfo_GetDamageSourceIdentifier( damageInfo ) ) )
+ Stats_IncrementStat( player, "deaths_stats", "suicides", "", 1.0 )
+
+ // whileEjecting
+ if ( player.p.pilotEjecting )
+ Stats_IncrementStat( player, "deaths_stats", "whileEjecting", "", 1.0 )
+}
+
+void function HandleWeaponKillStats( entity victim, entity attacker, var damageInfo )
+{
+ if ( !IsValid( attacker ) )
+ return
+
+ // get the player and it's pet titan
+ entity player
+ entity playerPetTitan
+ if ( attacker.IsPlayer() )
+ {
+ // the player is just the attacker
+ player = attacker
+ playerPetTitan = player.GetPetTitan()
+ }
+ else if ( attacker.IsTitan() && IsPetTitan( attacker ) )
+ {
+ // the attacker is the player's auto titan
+ player = attacker.GetTitanSoul().GetBossPlayer()
+ playerPetTitan = attacker
+ }
+ else
+ {
+ // attacker could be something like an NPC, or worldspawn
+ return
+ }
+
+ string damageSourceStr = GetPersistenceRefFromDamageInfo( damageInfo )
+ // cant do weapon stats for no weapon
+ if ( damageSourceStr == "" )
+ return
+
+ // check things once, for performance
+ int damageSource = DamageInfo_GetDamageSourceIdentifier( damageInfo )
+ bool victimIsPlayer = victim.IsPlayer()
+ bool victimIsNPC = victim.IsNPC()
+ bool victimIsPilot = IsPilot( victim )
+ bool victimIsTitan = victim.IsTitan()
+
+ // total
+ Stats_IncrementStat( player, "weapon_kill_stats", "total", damageSourceStr, 1.0 )
+
+ // pilots
+ if ( victimIsPilot )
+ Stats_IncrementStat( player, "weapon_kill_stats", "pilots", damageSourceStr, 1.0 )
+
+ // ejecting_pilots
+ if ( victimIsPilot && victim.p.pilotEjecting )
+ Stats_IncrementStat( player, "weapon_kill_stats", "ejecting_pilots", damageSourceStr, 1.0 )
+
+ // titansTotal
+ if ( victimIsTitan )
+ Stats_IncrementStat( player, "weapon_kill_stats", "titansTotal", damageSourceStr, 1.0 )
+ // spectres
+ if ( IsSpectre( victim ) )
+ Stats_IncrementStat( player, "weapon_kill_stats", "spectres", damageSourceStr, 1.0 )
+
+ // marvins
+ if ( IsMarvin( victim ) )
+ Stats_IncrementStat( player, "weapon_kill_stats", "marvins", damageSourceStr, 1.0 )
+
+ // grunts
+ if ( IsGrunt( victim ) )
+ Stats_IncrementStat( player, "weapon_kill_stats", "grunts", damageSourceStr, 1.0 )
+
+ // ai
+ if ( victimIsNPC )
+ Stats_IncrementStat( player, "weapon_kill_stats", "ai", damageSourceStr, 1.0 )
+
+ // titans_<chassis>
+ if ( victimIsPlayer && victimIsTitan )
+ Stats_IncrementStat( player, "weapon_kill_stats", "titans_" + GetTitanCharacterName( victim ), damageSourceStr, 1.0 )
+
+ // npcTitans_<chassis>
+ if ( victimIsNPC && victimIsTitan )
+ Stats_IncrementStat( player, "weapon_kill_stats", "npcTitans_" + GetTitanCharacterName( victim ), damageSourceStr, 1.0 )
}
-void function IncrementPlayerDidPilotExecutionWhileCloaked(entity player)
+void function HandleKillStats( entity victim, entity attacker, var damageInfo )
{
+ if ( !IsValid( attacker ) )
+ return
+ // get the player and it's pet titan
+ entity player
+ entity playerPetTitan
+ entity inflictor = DamageInfo_GetInflictor( damageInfo )
+
+ if ( IsValid( inflictor ) )
+ {
+ if ( inflictor.IsProjectile() && IsValid( inflictor.GetOwner() ) ) // Attackers are always the final entity in the owning hierarchy, projectile owners though migh be a player's NPC minion (i.e Auto-Titans)
+ attacker = inflictor.GetOwner()
+
+ else if ( inflictor.IsNPC() ) // NPCs are bypassed as Attackers if they are owned by players, instead they become just inflictors
+ attacker = inflictor
+ }
+
+ if ( attacker.IsNPC() )
+ {
+ if ( !attacker.IsTitan() ) // Normal NPCs case
+ return
+
+ if ( !IsPetTitan( attacker ) ) // NPC Titans case
+ return
+
+ player = attacker.GetTitanSoul().GetBossPlayer()
+ playerPetTitan = attacker
+ }
+ else if ( attacker.IsPlayer() ) // Still checks this because worldspawn might be the attacker
+ player = attacker
+ else
+ return
+
+ // check things once, for performance
+ int damageSource = DamageInfo_GetDamageSourceIdentifier( damageInfo )
+ bool victimIsPlayer = victim.IsPlayer()
+ bool victimIsNPC = victim.IsNPC()
+ bool victimIsPilot = IsPilot( victim )
+ bool victimIsTitan = victim.IsTitan()
+
+ if ( player in file.playerKills )
+ file.playerKills[ player ]++
+ else
+ file.playerKills[ player ] <- 1.0
+ // total
+ Stats_IncrementStat( player, "kills_stats", "total", "", 1.0 )
+
+ // totalPVP
+ if ( victimIsPlayer )
+ {
+ if ( player in file.playerKillsPvp )
+ file.playerKillsPvp[ player ]++
+ else
+ file.playerKillsPvp[ player ] <- 1.0
+ Stats_IncrementStat( player, "kills_stats", "totalPVP", "", 1.0 )
+ }
+
+ // pilots
+ if ( victimIsPilot )
+ Stats_IncrementStat( player, "kills_stats", "pilots", "", 1.0 )
+
+ // spectres
+ if ( IsSpectre( victim ) )
+ Stats_IncrementStat( player, "kills_stats", "spectres", "", 1.0 )
+
+ // marvins
+ if ( IsMarvin( victim ) )
+ Stats_IncrementStat( player, "kills_stats", "marvins", "", 1.0 )
+
+ // grunts
+ if ( IsGrunt( victim ) )
+ Stats_IncrementStat( player, "kills_stats", "grunts", "", 1.0 )
+
+ // totalTitans
+ if ( victimIsTitan )
+ Stats_IncrementStat( player, "kills_stats", "totalTitans", "", 1.0 )
+
+ // totalPilots
+ if ( victimIsPilot )
+ Stats_IncrementStat( player, "kills_stats", "totalPilots", "", 1.0 )
+
+ // totalNPC
+ if ( victimIsNPC )
+ Stats_IncrementStat( player, "kills_stats", "totalNPC", "", 1.0 )
+
+ // totalTitansWhileDoomed
+ if ( victimIsTitan && attacker.IsTitan() && GetDoomedState( attacker ) )
+ Stats_IncrementStat( player, "kills_stats", "totalTitansWhileDoomed", "", 1.0 )
+
+ // asPilot
+ if ( IsPilot( attacker ) )
+ Stats_IncrementStat( player, "kills_stats", "asPilot", "", 1.0 )
+
+ // totalAssists
+ // assistsTotal ( weapon_kill_stats )
+ // note: eww
+ table<int, bool> alreadyAssisted
+ // titans store their recentDamageHistory in the soul
+ entity assistVictim = ( victim.IsTitan() && IsValid( victim.GetTitanSoul() ) ) ? victim.GetTitanSoul() : victim
+ foreach( DamageHistoryStruct attackerInfo in assistVictim.e.recentDamageHistory )
+ {
+ if ( !IsValid( attackerInfo.attacker ) || !attackerInfo.attacker.IsPlayer() || attackerInfo.attacker == assistVictim )
+ continue
+
+ bool exists = attackerInfo.attacker.GetEncodedEHandle() in alreadyAssisted ? true : false
+ if( attackerInfo.attacker != attacker && !exists )
+ {
+ alreadyAssisted[ attackerInfo.attacker.GetEncodedEHandle() ] <- true
+ Stats_IncrementStat( attackerInfo.attacker, "kills_stats", "totalAssists", "", 1.0 )
+
+ string source = DamageSourceIDToString( attackerInfo.damageSourceId )
+
+ if ( IsValidStatItemString( source ) )
+ Stats_IncrementStat( attackerInfo.attacker, "weapon_kill_stats", "assistsTotal", source, 1.0 )
+ }
+ }
+
+ // asTitan_<chassis>
+ if ( player.IsTitan() )
+ Stats_IncrementStat( player, "kills_stats", "asTitan_" + GetTitanCharacterName( player ), "", 1.0 )
+
+ // firstStrikes
+ if ( file.isFirstStrike && attacker.IsPlayer() && victimIsPlayer )
+ {
+ Stats_IncrementStat( player, "kills_stats", "firstStrikes", "", 1.0 )
+ file.isFirstStrike = false
+ }
+
+ // ejectingPilots
+ if ( victimIsPilot && victim.p.pilotEjecting )
+ Stats_IncrementStat( player, "kills_stats", "ejectingPilots", "", 1.0 )
+
+ // whileEjecting
+ if ( attacker.IsPlayer() && attacker.p.pilotEjecting )
+ Stats_IncrementStat( player, "kills_stats", "whileEjecting", "", 1.0 )
+
+ // cloakedPilots
+ if ( victimIsPilot && IsCloaked( victim ) )
+ Stats_IncrementStat( player, "kills_stats", "cloakedPilots", "", 1.0 )
+
+ // whileCloaked
+ if ( attacker == player && IsCloaked( attacker ) )
+ Stats_IncrementStat( player, "kills_stats", "whileCloaked", "", 1.0 )
+
+ // wallrunningPilots
+ if ( victimIsPilot && victim.IsWallRunning() )
+ Stats_IncrementStat( player, "kills_stats", "wallrunningPilots", "", 1.0 )
+
+ // whileWallrunning
+ if ( attacker == player && attacker.IsWallRunning() )
+ Stats_IncrementStat( player, "kills_stats", "whileWallrunning", "", 1.0 )
+
+ // wallhangingPilots
+ if ( victimIsPilot && victim.IsWallHanging() )
+ Stats_IncrementStat( player, "kills_stats", "wallhangingPilots", "", 1.0 )
+
+ // whileWallhanging
+ if ( attacker == player && attacker.IsWallHanging() )
+ Stats_IncrementStat( player, "kills_stats", "whileWallhanging", "", 1.0 )
+
+ // pilotExecution
+ if ( damageSource == eDamageSourceId.human_execution )
+ Stats_IncrementStat( player, "kills_stats", "pilotExecution", "", 1.0 )
+
+ // pilotExecutePilot
+ if ( victimIsPilot && damageSource == eDamageSourceId.human_execution )
+ Stats_IncrementStat( player, "kills_stats", "pilotExecutePilot", "", 1.0 )
+
+ // pilotKillsWithHoloPilotActive
+ if ( victimIsPilot && GetDecoyActiveCountForPlayer( player ) > 0 )
+ Stats_IncrementStat( player, "kills_stats", "pilotKillsWithHoloPilotActive", "", 1.0 )
+
+ // pilotKillsWithAmpedWallActive
+ if ( victimIsPilot && GetAmpedWallsActiveCountForPlayer( player ) > 0 )
+ Stats_IncrementStat( player, "kills_stats", "pilotKillsWithAmpedWallActive", "", 1.0 )
+
+ // pilotExecutePilotUsing_<execution>
+ if ( victimIsPilot && damageSource == eDamageSourceId.human_execution )
+ Stats_IncrementStat( player, "kills_stats", "pilotExecutePilotUsing_" + player.p.lastExecutionUsed, "", 1.0 )
+
+ // pilotKickMelee
+ if ( damageSource == eDamageSourceId.human_melee )
+ Stats_IncrementStat( player, "kills_stats", "pilotKickMelee", "", 1.0 )
+
+ // pilotKickMeleePilot
+ if ( victimIsPilot && damageSource == eDamageSourceId.human_melee )
+ Stats_IncrementStat( player, "kills_stats", "pilotKickMeleePilot", "", 1.0 )
+
+ // titanMelee
+ if ( DamageIsTitanMelee( damageSource ) )
+ Stats_IncrementStat( player, "kills_stats", "titanMelee", "", 1.0 )
+
+ // titanMeleePilot
+ if ( victimIsPilot && DamageIsTitanMelee( damageSource ) )
+ Stats_IncrementStat( player, "kills_stats", "titanMeleePilot", "", 1.0 )
+
+ // titanStepCrush
+ if ( IsTitanCrushDamage( damageInfo ) )
+ Stats_IncrementStat( player, "kills_stats", "titanStepCrush", "", 1.0 )
+
+ // titanStepCrushPilot
+ if ( victimIsPilot && IsTitanCrushDamage( damageInfo ) )
+ Stats_IncrementStat( player, "kills_stats", "titanStepCrushPilot", "", 1.0 )
+
+ // titanExocution<capitalisedChassis>
+ // note: RESPAWN WHY? EXPLAIN
+ if ( damageSource == eDamageSourceId.titan_execution )
+ {
+ string titanName = GetTitanCharacterName( player )
+ titanName = titanName.slice( 0, 1 ).toupper() + titanName.slice( 1, titanName.len() )
+ Stats_IncrementStat( player, "kills_stats", "titanExocution" + titanName, "", 1.0 )
+ }
+
+ // titanFallKill
+ if ( damageSource == eDamageSourceId.damagedef_titan_fall )
+ Stats_IncrementStat( player, "kills_stats", "titanFallKill", "", 1.0 )
+
+ // petTitanKillsFollowMode
+ if ( attacker == playerPetTitan && player.GetPetTitanMode() == eNPCTitanMode.FOLLOW )
+ Stats_IncrementStat( player, "kills_stats", "petTitanKillsFollowMode", "", 1.0 )
+
+ // petTitanKillsGuardMode
+ if ( attacker == playerPetTitan && player.GetPetTitanMode() == eNPCTitanMode.STAY )
+ Stats_IncrementStat( player, "kills_stats", "petTitanKillsGuardMode", "", 1.0 )
+
+ // rodeo_total
+ if ( damageSource == eDamageSourceId.rodeo_battery_removal )
+ Stats_IncrementStat( player, "kills_stats", "rodeo_total", "", 1.0 )
+
+ // pilot_headshots_total
+ if ( victimIsPilot && DamageInfo_GetCustomDamageType( damageInfo ) & DF_HEADSHOT )
+ Stats_IncrementStat( player, "kills_stats", "pilot_headshots_total", "", 1.0 )
+
+ // evacShips
+ if ( IsEvacDropship( victim ) )
+ Stats_IncrementStat( player, "kills_stats", "evacShips", "", 1.0 )
+
+ // nuclearCore
+ if ( damageSource == eDamageSourceId.damagedef_nuclear_core )
+ Stats_IncrementStat( player, "kills_stats", "nuclearCore", "", 1.0 )
+
+ // meleeWhileCloaked
+ if ( IsCloaked( attacker ) && damageSource == eDamageSourceId.human_melee )
+ Stats_IncrementStat( player, "kills_stats", "meleeWhileCloaked", "", 1.0 )
+
+ // titanKillsAsPilot
+ if ( victimIsTitan && IsPilot( attacker ) )
+ Stats_IncrementStat( player, "kills_stats", "titanKillsAsPilot", "", 1.0 )
+
+ // pilotKillsWhileStimActive
+ if ( victimIsPilot && StatusEffect_Get( attacker, eStatusEffect.stim_visual_effect ) <= 0 )
+ Stats_IncrementStat( player, "kills_stats", "pilotKillsWhileStimActive", "", 1.0 )
+
+ // pilotKillsAsTitan
+ if ( victimIsPilot && attacker.IsTitan() )
+ Stats_IncrementStat( player, "kills_stats", "pilotKillsAsTitan", "", 1.0 )
+
+ // pilotKillsAsPilot
+ if ( victimIsPilot && IsPilot( attacker ) )
+ Stats_IncrementStat( player, "kills_stats", "pilotKillsAsPilot", "", 1.0 )
+
+ // titanKillsAsTitan
+ if ( victimIsTitan && attacker.IsTitan() )
+ Stats_IncrementStat( player, "kills_stats", "titanKillsAsTitan", "", 1.0 )
}
-void function UpdateTitanDamageStat(entity attacker, float savedDamage, var damageInfo)
+void function HandleTitanStats( entity victim, entity attacker, var damageInfo )
{
+ if ( !IsValid( attacker ) )
+ return
+
+ // get the player and it's pet titan
+ entity player
+ entity playerPetTitan
+ if ( attacker.IsPlayer() )
+ {
+ // the player is just the attacker
+ player = attacker
+ playerPetTitan = player.GetPetTitan()
+ }
+ else if ( attacker.IsTitan() && IsPetTitan( attacker ) )
+ {
+ // the attacker is the player's auto titan
+ player = attacker.GetTitanSoul().GetBossPlayer()
+ playerPetTitan = attacker
+ }
+ else
+ {
+ // attacker could be something like an NPC, or worldspawn
+ return
+ }
+
+ int damageSource = DamageInfo_GetDamageSourceIdentifier( damageInfo )
+ bool victimIsPlayer = victim.IsPlayer()
+ bool victimIsNPC = victim.IsNPC()
+ bool victimIsPilot = IsPilot( victim )
+ bool victimIsTitan = victim.IsTitan()
+ bool titanIsPrime = IsTitanPrimeTitan( player )
+ // pilots
+ if ( victimIsPilot && attacker.IsTitan() )
+ Stats_IncrementStat( player, "titan_stats", "pilots", GetTitanCharacterName( attacker ), 1.0 )
+
+ // titansTotal
+ if ( victimIsTitan && attacker.IsTitan() )
+ Stats_IncrementStat( player, "titan_stats", "titansTotal", GetTitanCharacterName( attacker ), 1.0 )
+
+ // pilotsAsPrime
+ if ( victimIsPilot && attacker.IsTitan() && titanIsPrime )
+ Stats_IncrementStat( player, "titan_stats", "pilotsAsPrime", GetTitanCharacterName( attacker ), 1.0 )
+
+ // titansAsPrime
+ if ( victimIsTitan && attacker.IsTitan() && titanIsPrime )
+ Stats_IncrementStat( player, "titan_stats", "titansAsPrime", GetTitanCharacterName( attacker ), 1.0 )
+
+ // executionsAsPrime
+ if ( damageSource == eDamageSourceId.titan_execution && attacker.IsTitan() && titanIsPrime )
+ Stats_IncrementStat( player, "titan_stats", "executionsAsPrime", GetTitanCharacterName( attacker ), 1.0 )
}
-void function UpdateTitanWeaponDamageStat(entity attacker, float savedDamage, var damageInfo)
+void function OnPlayerRespawned( entity player )
{
+ thread SetLastPosForDistanceStatValid_Threaded( player, true )
+}
+
+void function OnWinnerDetermined()
+{
+ // award players for match completed, wins, and losses
+ foreach ( entity player in GetPlayerArray() )
+ {
+ Stats_IncrementStat( player, "game_stats", "game_completed", "", 1.0 )
+
+ if ( player.GetTeam() == GetWinningTeam() )
+ Stats_IncrementStat( player, "game_stats", "game_won", "", 1.0 )
+ else
+ Stats_IncrementStat( player, "game_stats", "game_lost", "", 1.0 )
+ }
+
+ if ( IsValidGamemodeString( GAMETYPE ) )
+ {
+ // award players with matches played on the mode
+ foreach ( entity player in GetPlayerArray() )
+ {
+ Stats_IncrementStat( player, "game_stats", "mode_played", GAMETYPE, 1.0 )
+
+ if ( player.GetTeam() == GetWinningTeam() )
+ Stats_IncrementStat( player, "game_stats", "mode_won", GAMETYPE, 1.0 )
+ }
+ }
+
+ // update player's KD
+ foreach ( entity player in GetPlayerArray() )
+ {
+ // kd stats
+ // index 0 is most recent game
+ // index 9 is least recent game
+ float playerKills = ( player in file.playerKills ) ? file.playerKills[ player ] : 0.0
+ float playerDeaths = ( player in file.playerDeaths ) ? file.playerDeaths[ player ] : 0.0
+ float kdratio_match
+ if ( playerDeaths == 0.0 )
+ kdratio_match = playerKills
+ else
+ kdratio_match = playerKills / playerDeaths
+
+ float playerKillsPvp = ( player in file.playerKillsPvp ) ? file.playerKillsPvp[ player ] : 0.0
+ float playerDeathsPvp = ( player in file.playerDeathsPvp ) ? file.playerDeathsPvp[ player ] : 0.0
+ float kdratiopvp_match
+ if ( playerDeathsPvp == 0.0 )
+ kdratiopvp_match = playerKillsPvp
+ else
+ kdratiopvp_match = playerKillsPvp / playerDeathsPvp
+
+ float totalDeaths = player.GetPersistentVarAsInt( "deathStats.total" ).tofloat()
+ float totalKills = player.GetPersistentVarAsInt( "killStats.total" ).tofloat()
+ float totalDeathsPvp = player.GetPersistentVarAsInt( "deathStats.totalPVP" ).tofloat()
+ float totalKillsPvp = player.GetPersistentVarAsInt( "killStats.totalPVP" ).tofloat()
+ float kdratio_lifetime
+ if ( totalDeaths == 0.0 )
+ kdratio_lifetime = totalKills
+ else
+ kdratio_lifetime = totalKills / totalDeaths
+ float kdratio_lifetimepvp
+ if ( totalDeathsPvp == 0.0 )
+ kdratio_lifetimepvp = totalKillsPvp
+ else
+ kdratio_lifetimepvp = totalKillsPvp / totalDeathsPvp
+
+ // shift stats by 1 to make room for new game data
+ for ( int i = NUM_GAMES_TRACK_KDRATIO - 2; i >= 0; --i )
+ {
+ player.SetPersistentVar( format( "kdratio_match[%i]", ( i + 1 ) ), player.GetPersistentVar( format("kdratio_match[%i]", i ) ) )
+ player.SetPersistentVar( format( "kdratiopvp_match[%i]", ( i + 1 ) ), player.GetPersistentVar( format( "kdratiopvp_match[%i]", i ) ) )
+ }
+ // add new game data
+ player.SetPersistentVar( "kdratio_match[0]", kdratio_match )
+ player.SetPersistentVar( "kdratiopvp_match[0]", kdratiopvp_match )
+ player.SetPersistentVar( "kdratio_lifetime", kdratio_lifetime )
+ player.SetPersistentVar( "kdratio_lifetime_pvp", kdratio_lifetimepvp )
+ }
+
+ // award mvp and top 3 in each team
+ if ( !IsFFAGame() )
+ {
+ string gamemode = GameRules_GetGameMode()
+ int functionref( entity, entity ) compareFunc = GameMode_GetScoreCompareFunc( gamemode )
+
+ for( int team = 0; team < MAX_TEAMS; team++ )
+ {
+ array<entity> players = GetPlayerArrayOfTeam( team )
+ if ( compareFunc == null )
+ {
+ printt( "gamemode doesn't have a compare func to get the top 3" )
+ return
+ }
+ players.sort( compareFunc )
+ int maxAwards = int( min( players.len(), 3 ) )
+ for ( int i = 0; i < maxAwards; i++ )
+ {
+ if ( i == 0 )
+ Stats_IncrementStat( players[ i ], "game_stats", "mvp", "", 1.0 )
+ Stats_IncrementStat( players[ i ], "game_stats", "top3OnTeam", "", 1.0 )
+ }
+ }
+
+ }
+}
+void function SetLastPosForDistanceStatValid_Threaded( entity player, bool val )
+{
+ WaitFrame()
+ if ( !IsValid( player ) )
+ return
+ player.p.lastPosForDistanceStatValid = val
}
-void function UpdateTitanCoreEarnedStat( entity player, entity titan )
+// Respawn did this through stuff found in _entitystructs.gnut (stuff like stats_wallrunTime)
+// but their implementation seems kinda bad. The advantage it has over this method is not polling
+// every 0.25 seconds, and using movement callbacks and stuff instead. However, since i can't find
+// callbacks for things like changing weapon, i would have to poll for that *anyway* and thus,
+// there is no point in doing things Respawn's way here
+void function HandleDistanceAndTimeStats_Threaded()
{
+ // just to be safe
+ if ( IsLobby() )
+ return
+
+ while ( GetGameState() < eGameState.Playing )
+ WaitFrame()
+
+ float lastTickTime = Time()
+
+ while( true )
+ {
+ // track distance stats
+ foreach ( entity player in GetPlayerArray() )
+ {
+ if ( !IsValid( player ) )
+ continue
+
+ if ( player.p.lastPosForDistanceStatValid )
+ {
+ // not 100% sure on using Distance2D over Distance tbh
+ float distInches = Distance2D( player.p.lastPosForDistanceStat, player.GetOrigin() )
+ float distMiles = distInches / 63360.0
+
+ // more generic distance stats
+ Stats_IncrementStat( player, "distance_stats", "total", "", distMiles )
+ if ( player.IsTitan() )
+ {
+ Stats_IncrementStat( player, "distance_stats", "asTitan_" + GetTitanCharacterName( player ), "", distMiles )
+ Stats_IncrementStat( player, "distance_stats", "asTitan", "", distMiles )
+ }
+ else
+ Stats_IncrementStat( player, "distance_stats", "asPilot", "", distMiles )
+
+
+ string state = ""
+ // specific distance stats
+ if ( player.IsWallRunning() )
+ state = "wallrunning"
+ else if ( PlayerIsRodeoingTitan( player ) )
+ {
+ if ( player.GetTitanSoulBeingRodeoed().GetTeam() == player.GetTeam() )
+ state = "onFriendlyTitan"
+ else
+ state = "onEnemyTitan"
+ }
+ else if ( player.IsZiplining() )
+ state = "ziplining"
+ else if ( !player.IsOnGround() )
+ state = "inAir"
+
+ if ( state != "" )
+ Stats_IncrementStat( player, "distance_stats", state, "", distMiles )
+ }
+
+ player.p.lastPosForDistanceStat = player.GetOrigin()
+ }
+
+ float timeSeconds = Time() - lastTickTime
+ float timeHours = timeSeconds / 3600.0
+
+ // track time stats
+ foreach ( entity player in GetPlayerArray() )
+ {
+ // first tick i dont count
+ if ( timeSeconds == 0 )
+ break
+
+ // more generic time stats
+ Stats_IncrementStat( player, "time_stats", "hours_total", "", timeHours )
+ if ( player.IsTitan() )
+ {
+ Stats_IncrementStat( player, "time_stats", "hours_as_titan_" + GetTitanCharacterName( player ), "", timeHours )
+ Stats_IncrementStat( player, "time_stats", "hours_as_titan", "", timeHours )
+ }
+ else
+ Stats_IncrementStat( player, "time_stats", "hours_as_pilot", "", timeHours )
+
+ string state = ""
+ // specific time stats
+ if ( !IsAlive( player ) )
+ state = "hours_dead"
+ else if ( player.IsWallHanging() )
+ state = "hours_wallhanging"
+ else if ( player.IsWallRunning() )
+ state = "hours_wallrunning"
+ else if ( !player.IsOnGround() )
+ state = "hours_inAir"
+ if ( state != "" )
+ Stats_IncrementStat( player, "time_stats", state, "", timeHours )
+
+ // weapon time stats
+ entity activeWeapon = player.GetActiveWeapon()
+ if ( IsValid( activeWeapon ) )
+ {
+ if ( IsValidStatItemString( activeWeapon.GetWeaponClassName() ) )
+ Stats_IncrementStat( player, "weapon_stats", "hoursUsed", activeWeapon.GetWeaponClassName(), timeHours )
+
+ foreach( entity weapon in player.GetMainWeapons() )
+ {
+ if ( IsValidStatItemString( weapon.GetWeaponClassName() ) )
+ Stats_IncrementStat( player, "weapon_stats", "hoursEquipped", weapon.GetWeaponClassName(), timeHours )
+ }
+ }
+
+ // map time stats
+ Stats_IncrementStat( player, "game_stats", "hoursPlayed", "", timeHours )
+ }
+
+ lastTickTime = Time()
+ // not rly worth doing this every frame, just a couple of times per second should be fine
+ wait 0.25
+ }
+}
+
+// this is kinda shit
+void function SaveStatsPeriodically_Threaded()
+{
+ while( true )
+ {
+ foreach( entity player in GetPlayerArray() )
+ {
+ if ( IsValid( player ) )
+ Stats_SaveAllStats( player )
+ }
+ wait 5
+ }
}
-void function PreScoreEventUpdateStats(entity attacker, entity ent)
+bool function IsValidGamemodeString( string mode )
{
+ int gameModeCount = PersistenceGetEnumCount( "gameModes" )
+ for ( int modeIndex = 0; modeIndex < gameModeCount; modeIndex++ )
+ {
+ string gameModeName = PersistenceGetEnumItemNameForIndex( "gameModes", modeIndex )
+
+ if ( gameModeName == mode )
+ return true
+ }
+ return false
}
-void function PostScoreEventUpdateStats(entity attacker, entity ent)
+bool function IsValidStatItemString( string item )
{
+ foreach( str in shGlobalMP.statsItemsList )
+ {
+ if ( str == item )
+ return true
+ }
+ return false
}
-void function Stats_OnPlayerDidDamage(entity player, var damageInfo)
+string function GetPersistenceRefFromDamageInfo( var damageInfo )
{
+ string damageSourceString = DamageSourceIDToString( DamageInfo_GetDamageSourceIdentifier( damageInfo ) )
+
+ foreach( str in shGlobalMP.statsItemsList )
+ {
+ if ( str == damageSourceString )
+ return damageSourceString
+ }
+ return ""
+}
+
+bool function DamageIsTitanMelee( int damageSourceId )
+{
+ switch( damageSourceId )
+ {
+ case eDamageSourceId.melee_titan_punch:
+ case eDamageSourceId.melee_titan_punch_ion:
+ case eDamageSourceId.melee_titan_punch_legion:
+ case eDamageSourceId.melee_titan_punch_tone:
+ case eDamageSourceId.melee_titan_punch_scorch:
+ case eDamageSourceId.melee_titan_punch_northstar:
+ case eDamageSourceId.melee_titan_punch_fighter:
+ case eDamageSourceId.melee_titan_sword:
+ case eDamageSourceId.melee_titan_sword_aoe:
+ return true
+ default:
+ return false
+ }
+ unreachable
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
index 341493ba..8d859ba6 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
@@ -339,7 +339,8 @@ void function PlayerWatchesWargamesIntro( entity player )
void function DelayedGamemodeAnnouncement( entity player )
{
wait 1.0
- TryGameModeAnnouncement( player )
+ if ( IsValid( player ) && IsAlive( player ) )
+ TryGameModeAnnouncement( player )
}
void function PlaySound_SimPod_DoorShut( entity playerFirstPersonProxy ) // stolen from sp_training
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
index 5bf150c0..d64e3a5b 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
@@ -130,7 +130,7 @@ entity function FindSpawnPoint( entity player, bool isTitan, bool useStartSpawnp
{
int team = player.GetTeam()
if ( HasSwitchedSides() )
- team = GetOtherTeam( team )
+ team = ( team == TEAM_MILITIA ) ? TEAM_IMC : TEAM_MILITIA
array<entity> spawnpoints
if ( useStartSpawnpoint )
@@ -181,29 +181,19 @@ entity function GetBestSpawnpoint( entity player, array<entity> spawnpoints )
foreach ( entity spawnpoint in spawnpoints )
{
if ( IsSpawnpointValid( spawnpoint, player.GetTeam() ) )
- {
validSpawns.append( spawnpoint )
-
- if ( validSpawns.len() == 3 ) // arbitrary small sample size
- break
- }
}
- if ( validSpawns.len() == 0 )
+ if ( !validSpawns.len() )
{
// no valid spawns, very bad, so dont care about spawns being valid anymore
print( "found no valid spawns! spawns may be subpar!" )
foreach ( entity spawnpoint in spawnpoints )
- {
validSpawns.append( spawnpoint )
-
- if ( validSpawns.len() == 3 ) // arbitrary small sample size
- break
- }
}
// last resort
- if ( validSpawns.len() == 0 )
+ if ( !validSpawns.len() )
{
print( "map has literally 0 spawnpoints, as such everything is fucked probably, attempting to use info_player_start if present" )
entity start = GetEnt( "info_player_start" )
@@ -215,7 +205,7 @@ entity function GetBestSpawnpoint( entity player, array<entity> spawnpoints )
}
}
- return validSpawns[ RandomInt( validSpawns.len() ) ] // slightly randomize it
+ return validSpawns.getrandom() // slightly randomize it
}
bool function IsSpawnpointValid( entity spawnpoint, int team )
@@ -232,15 +222,18 @@ bool function IsSpawnpointValid( entity spawnpoint, int team )
return false
}
+ if( IsFFAGame() && !spawnpoint.IsVisibleToEnemies( team ) )
+ return true
+
int compareTeam = spawnpoint.GetTeam()
- if ( HasSwitchedSides() && ( compareTeam == TEAM_MILITIA || compareTeam == TEAM_IMC ) )
- compareTeam = GetOtherTeam( compareTeam )
-
+ if ( HasSwitchedSides() )
+ compareTeam = ( compareTeam == TEAM_MILITIA ) ? TEAM_IMC : TEAM_MILITIA
+
foreach ( bool functionref( entity, int ) customValidationRule in file.customSpawnpointValidationRules )
if ( !customValidationRule( spawnpoint, team ) )
return false
- if ( spawnpoint.GetTeam() > 0 && compareTeam != team && !IsFFAGame() )
+ if ( spawnpoint.GetTeam() > 0 && compareTeam != team )
return false
if ( spawnpoint.IsOccupied() )
@@ -261,10 +254,12 @@ bool function IsSpawnpointValid( entity spawnpoint, int team )
return false
}
- array<entity> projectiles = GetProjectileArrayEx( "any", TEAM_ANY, TEAM_ANY, spawnpoint.GetOrigin(), 600 )
- foreach ( entity projectile in projectiles )
- if ( projectile.GetTeam() != team )
- return false
+ const minEnemyDist = 1200.0
+ array< entity > spawnBlockers = GetPlayerArrayEx( "any", TEAM_ANY, spawnpoint.GetTeam(), spawnpoint.GetOrigin(), minEnemyDist )
+ spawnBlockers.extend( GetProjectileArrayEx( "any", TEAM_ANY, spawnpoint.GetTeam(), spawnpoint.GetOrigin(), minEnemyDist ) )
+ spawnBlockers.extend( GetNPCArrayEx( "any", TEAM_ANY, spawnpoint.GetTeam(), spawnpoint.GetOrigin(), minEnemyDist ) )
+ if ( spawnBlockers.len() )
+ return false
// los check
return !spawnpoint.IsVisibleToEnemies( team )
@@ -396,11 +391,9 @@ void function InitPreferSpawnNodes()
// frontline
void function RateSpawnpoints_Frontline( int checkClass, array<entity> spawnpoints, int team, entity player )
{
+ float rating = RandomFloatRange( 0.0, 100.0 )
foreach ( entity spawnpoint in spawnpoints )
- {
- float rating = spawnpoint.CalculateFrontlineRating()
- spawnpoint.CalculateRating( checkClass, player.GetTeam(), rating, rating > 0 ? rating * 0.25 : rating )
- }
+ spawnpoint.CalculateRating( checkClass, player.GetTeam(), rating, rating )
}
// spawnzones
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/rodeo/_rodeo_titan.gnut b/Northstar.CustomServers/mod/scripts/vscripts/rodeo/_rodeo_titan.gnut
index 9f05a0cd..78cfdb27 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/rodeo/_rodeo_titan.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/rodeo/_rodeo_titan.gnut
@@ -41,6 +41,9 @@ global function Battery_StopFXAndHideIconForPlayer
global function RemovePlayerAirControl //This function should really be in a server only SP & MP utility script file. No such file exists as of right now.
global function RestorePlayerAirControl //This function should really be in a server only SP & MP utility script file. No such file exists as of right now.
+// needs these
+global function Rodeo_TakeBatteryAwayFromPilot
+
#if DEV
global function SetDebugRodeoPrint
global function GetDebugRodeoPrint
@@ -336,7 +339,7 @@ void function RodeoBatteryRemoval( entity pilot )
if ( !playerHadBattery )
{
- AddPlayerScore( pilot, "PilotBatteryStolen" )
+ AddPlayerScore( pilot, "PilotBatteryStolen", pilot )
entity battery = Rodeo_CreateBatteryPack( titan )
Rodeo_PilotPicksUpBattery( pilot, battery )
thread BatteryThiefHighlight( pilot )
@@ -1853,7 +1856,7 @@ void function Rodeo_OnTouchBatteryPack_Internal( entity player, entity batteryPa
Battery_StopFX( batteryPack ) //Will be turned on again when player loses cloak
Rodeo_PilotPicksUpBattery( player, batteryPack )
- AddPlayerScore( player, "PilotBatteryPickup" )
+ AddPlayerScore( player, "PilotBatteryPickup", player )
// MessageToPlayer( player, eEventNotifications.Rodeo_PilotPickedUpBattery )
return
}
@@ -1878,7 +1881,7 @@ void function Rodeo_PilotAddsBatteryToFriendlyTitan( entity rider, entity titan
Rodeo_ApplyBatteryToTitan( battery, titan ) //This destroys the battery
- AddPlayerScore( rider, "PilotBatteryApplied" )
+ AddPlayerScore( rider, "PilotBatteryApplied", rider )
EmitSoundOnEntityOnlyToPlayer( rider, rider, PILOT_APPLIES_BATTERY_TO_TITAN_HEALTH_RESTORED_SOUND )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut b/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut
index d15220e4..7a7498b8 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut
@@ -785,7 +785,7 @@ bool function IsSettingPrimeTitanWithoutSetFile( entity player, string loadoutTy
bool function SkipItemLockedCheck( entity player, string ref, string parentRef, string loadoutProperty ) //Hack: Skip entitlement related unlock checks for now. Can fail.
{
- if ( DevEverythingUnlocked() )
+ if ( DevEverythingUnlocked( player ) )
return true
//if ( IsItemInEntitlementUnlock( ref ) && IsLobby() ) //TODO: Look into restricting this to lobby only? But entitlement checks can fail randomly...
@@ -2379,10 +2379,8 @@ bool function IsValidPilotLoadoutProperty( string propertyName )
case "weapon3Mod2":
case "weapon3Mod3":
case "ordnance":
- case "special":
case "passive1":
case "passive2":
- case "melee":
case "skinIndex":
case "camoIndex":
case "primarySkinIndex":
@@ -2403,7 +2401,6 @@ bool function IsValidTitanLoadoutProperty( string propertyName )
{
case "name":
case "titanClass":
- case "setFile":
case "primaryMod":
case "special":
case "antirodeo":
@@ -3266,6 +3263,24 @@ string function Loadouts_GetSetFileForRequestedClass( entity player )
return loadout.race
}
+ #if DEV
+ // these are #if DEV'd until they work as their function names describe they should
+ // atm these only exist to allow the #if DEV'd calls to them for bot code in this file to compile on retail
+ // bots don't work in retail at all, so this doesn't matter for us really, but these should be unDEV'd and api'd properly once they are functional
+
+ PilotLoadoutDef function GetRandomPilotLoadout()
+ {
+ PilotLoadoutDef loadout
+ return loadout
+ }
+
+ TitanLoadoutDef function GetRandomTitanLoadout( string setFile )
+ {
+ TitanLoadoutDef loadout
+ return loadout
+ }
+ #endif
+
bool function Loadouts_TryGivePilotLoadout( entity player )
{
if ( !Loadouts_CanGivePilotLoadout( player ) )
@@ -3978,7 +3993,7 @@ bool function IsValidTitanLoadoutIndex( int loadoutIndex )
bool function HasPrimeToMatchExecutionType( entity player, int itemType )
{
- if ( DevEverythingUnlocked() )
+ if ( DevEverythingUnlocked( player ) )
return true
switch( itemType )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_northstar_utils.gnut b/Northstar.CustomServers/mod/scripts/vscripts/sh_northstar_utils.gnut
index b26e48ca..f8597744 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/sh_northstar_utils.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_northstar_utils.gnut
@@ -1,11 +1,5 @@
globalize_all_functions
-// whether the server is a modded, northstar server
-bool function IsNorthstarServer()
-{
- return GetConVarBool( "ns_is_modded_server" )
-}
-
// whether the game should return to the lobby on GameRules_EndMatch()
bool function ShouldReturnToLobby()
{
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_powerup.gnut b/Northstar.CustomServers/mod/scripts/vscripts/sh_powerup.gnut
new file mode 100644
index 00000000..c390b5f5
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_powerup.gnut
@@ -0,0 +1,294 @@
+global function SH_PowerUp_Init
+global function GetPowerUpFromIndex
+global function GetPowerUpFromItemRef
+
+//Proto Use Functions
+global function PowerUp_Func_GiveEPG
+global function PowerUp_Func_GiveHELL
+global function PowerUp_Func_GiveLSTAR
+global function PowerUp_Func_GiveSHOTGUN
+global function PowerUp_Func_GiveArmorSmall
+global function PowerUp_Func_GiveArmorMedium
+global function PowerUp_Func_GiveArmorLarge
+global function PowerUp_Func_TitanBuildTime
+global function PowerUp_Func_PilotUpgrade
+global function PowerUp_Func_GiveTicks
+
+global struct PowerUp
+{
+ int index
+ string name
+ asset icon
+ asset model
+ asset baseModel
+ string itemRef
+ vector modelOffset
+ vector modelAngles
+ float respawnDelay
+ vector glowColor
+ bool titanPickup
+ int maxInWorld
+ void functionref( entity ) destroyFunc
+ bool functionref() spawnFunc
+}
+
+const bool TITAN_PICKUP = true
+const bool PILOT_PICKUP = false
+
+struct
+{
+ array<PowerUp> powerUps
+}file
+
+const TEST_MODEL = $"models/communication/terminal_com_station.mdl"
+const TEST_ICON = $"vgui/HUD/coop/minimap_coop_nuke_titan"
+
+void function SH_PowerUp_Init()
+{
+ #if SERVER || CLIENT
+ PrecacheWeapon( "mp_weapon_epg" )
+ PrecacheWeapon( "mp_weapon_arena1" )
+ PrecacheWeapon( "mp_weapon_arena2" )
+ PrecacheWeapon( "mp_weapon_arena3" )
+ PrecacheWeapon( "mp_weapon_lstar" )
+ PrecacheWeapon( "mp_weapon_shotgun_doublebarrel" )
+ PrecacheWeapon( "mp_weapon_frag_drone" )
+ #endif
+
+ file.powerUps.resize( ePowerUps.count )
+ CreatePowerUp( ePowerUps.weaponEPG, "mp_weapon_epg", "EPG", 60.0, 0, DefaultShouldSpawnPowerUp, PowerUp_Func_GiveEPG, <255,0,0>, PILOT_PICKUP, $"vgui/HUD/op_ammo_mini", $"models/weapons/auto_rocket_launcher_ARL/w_ARL.mdl", $"models/communication/flag_base.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.weaponHELL, "mp_weapon_arena3", "HELL", 90.0, 0, DefaultShouldSpawnPowerUp, PowerUp_Func_GiveHELL, <255,0,0>, PILOT_PICKUP, $"vgui/HUD/op_ammo_mini", $"models/weapons/defender/w_defender.mdl", $"models/communication/flag_base.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.weaponLSTAR, "mp_weapon_lstar", "LSTAR", 45.0, 0, DefaultShouldSpawnPowerUp, PowerUp_Func_GiveLSTAR, <255,0,0>, PILOT_PICKUP, $"vgui/HUD/op_ammo_mini", $"models/weapons/lstar/w_lstar.mdl", $"models/communication/flag_base.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.weaponSHOTGUN, "mp_weapon_shotgun_doublebarrel", "Shrapnel Shotgun", 30.0, 0, DefaultShouldSpawnPowerUp, PowerUp_Func_GiveSHOTGUN, <255,0,0>, PILOT_PICKUP, $"vgui/HUD/op_ammo_mini", $"models/weapons/mastiff_stgn/w_mastiff.mdl", $"models/communication/flag_base.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.armorSmall, "mp_loot_armor_small", "Armor +5", 30.0, 0, DefaultShouldSpawnPowerUp, PowerUp_Func_GiveArmorSmall, <0,0,255>, PILOT_PICKUP, $"vgui/HUD/op_health_mini", $"models/gameplay/health_pickup_small.mdl", $"models/containers/plastic_pallet_01.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.armorMedium, "mp_loot_armor_medium", "Armor +25", 60.0, 0, DefaultShouldSpawnPowerUp, PowerUp_Func_GiveArmorMedium, <0,0,255>, PILOT_PICKUP, $"vgui/HUD/op_health_mini", $"models/gameplay/health_pickup_small.mdl", $"models/containers/plastic_pallet_01.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.armorLarge, "mp_loot_armor_large", "Armor +50", 120.0, 0, DefaultShouldSpawnPowerUp, PowerUp_Func_GiveArmorLarge, <0,0,255>, PILOT_PICKUP, $"vgui/HUD/op_health_mini", $"models/gameplay/health_pickup_large.mdl", $"models/containers/plastic_pallet_01.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.titanTimeReduction, "mp_loot_titan_build_credit", "Titan Build Time", 20.0, 2, FRAShouldSpawnPowerUp, PowerUp_Func_TitanBuildTime, <0,255,0>, PILOT_PICKUP, $"vgui/HUD/op_drone_mini", $"models/titans/medium/titan_medium_battery_static.mdl", $"models/communication/flag_base.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.LTS_TitanTimeReduction, "mp_loot_titan_build_credit_lts", "Titan Build Time", 60.0, 0, LTSShouldSpawnPowerUp, PowerUp_Func_TitanBuildTime, <0,255,0>, PILOT_PICKUP, $"vgui/HUD/op_drone_mini", $"models/titans/medium/titan_medium_battery_static.mdl", $"models/communication/flag_base.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.pilotUpgrade, "mp_loot_pilot_upgrade", "Can of Spinach", 120.0, 0, DefaultShouldSpawnPowerUp, PowerUp_Func_PilotUpgrade, <0,255,0>, PILOT_PICKUP, $"vgui/HUD/op_drone_mini", $"models/humans/pilots/pilot_light_ged_m.mdl", $"models/communication/flag_base.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+ CreatePowerUp( ePowerUps.ticks, "mp_weapon_frag_drone", "Ticks", 60.0, 0, DefaultShouldSpawnPowerUp, PowerUp_Func_GiveTicks, <255,0,0>, PILOT_PICKUP, $"vgui/HUD/op_ammo_mini", $"models/robots/drone_frag/frag_drone_proj.mdl", $"models/robots/drone_frag/frag_drone_proj.mdl", < 0, 0, 32 >, < 0, 0, 0 > )
+}
+
+bool function FRAShouldSpawnPowerUp()
+{
+ return GAMETYPE == FREE_AGENCY
+}
+
+bool function LTSShouldSpawnPowerUp()
+{
+ if ( HasIronRules() )
+ return false
+
+ // modified for fw
+ //return ( GAMETYPE == LAST_TITAN_STANDING || GAMETYPE == LTS_BOMB )
+ return ( GAMETYPE == LAST_TITAN_STANDING || GAMETYPE == LTS_BOMB || GAMETYPE == FORT_WAR )
+}
+
+bool function DefaultShouldSpawnPowerUp()
+{
+ return GetCurrentPlaylistVarInt( "power_ups_enabled", 0 ) == 1
+}
+
+void function CreatePowerUp( int enumIndex, string item, string displayName, float respawnTime, int worldLimit, bool functionref() shouldSpawnFunction, void functionref( entity ) destroyFunction, vector color, bool canTitanPickup, asset worldIcon, asset worldModel, asset worldBase, vector worldModelOffset, vector worldModelAngle )
+{
+ PowerUp power
+ power.index = enumIndex
+ power.name = displayName
+ power.icon = worldIcon
+ power.model = worldModel
+ power.baseModel = worldBase
+ power.itemRef = item
+ power.modelOffset = worldModelOffset
+ power.modelAngles = worldModelAngle
+ power.respawnDelay = respawnTime
+ power.destroyFunc = destroyFunction
+ power.spawnFunc = shouldSpawnFunction
+ power.glowColor = color
+ power.titanPickup = canTitanPickup
+ power.maxInWorld = worldLimit
+ file.powerUps[enumIndex] = power
+
+ #if CLIENT
+ PrecacheHUDMaterial( worldIcon )
+ #else
+ PrecacheModel( worldModel )
+ PrecacheModel( worldBase )
+ #if R1_VGUI_MINIMAP
+ Minimap_PrecacheMaterial( worldIcon )
+ #endif
+ #endif
+}
+
+PowerUp function GetPowerUpFromIndex( int index )
+{
+ return file.powerUps[index]
+}
+
+PowerUp function GetPowerUpFromItemRef( string ref )
+{
+ foreach( power in file.powerUps )
+ {
+ if ( power.itemRef == ref )
+ return power
+ }
+
+ Assert( false, "Power Up not found")
+ unreachable
+}
+
+//////////////////////////////////////////////
+// PROTO USE FUNCTIONS - Maybe should be a bunch of new item_ classes with their own healthkit callbacks?
+//////////////////////////////////////////////
+void function PowerUp_Func_GiveEPG( entity player )
+{
+ #if SERVER
+ if ( player.IsTitan() )
+ return
+ GiveWeaponPowerUp( player, "mp_weapon_arena2" )
+ #endif
+}
+
+void function PowerUp_Func_GiveHELL( entity player )
+{
+ #if SERVER
+ if ( player.IsTitan() )
+ return
+ GiveWeaponPowerUp( player, "mp_weapon_arena3" )
+ #endif
+}
+
+void function PowerUp_Func_GiveLSTAR( entity player )
+{
+ #if SERVER
+ if ( player.IsTitan() )
+ return
+ GiveWeaponPowerUp( player, "mp_weapon_arena1" )
+ #endif
+}
+
+void function PowerUp_Func_GiveSHOTGUN( entity player )
+{
+ #if SERVER
+ if ( player.IsTitan() )
+ return
+ GiveWeaponPowerUp( player, "mp_weapon_shotgun_doublebarrel" )
+ #endif
+}
+
+void function PowerUp_Func_GiveTicks( entity player )
+{
+ #if SERVER
+ if ( player.IsTitan() )
+ return
+ player.TakeOffhandWeapon( OFFHAND_ORDNANCE )
+ player.GiveOffhandWeapon( "mp_weapon_frag_drone", OFFHAND_ORDNANCE )
+ thread RestoreDefaultOffhandWeapon( player )
+ #endif
+}
+
+#if SERVER
+void function RestoreDefaultOffhandWeapon( entity player )
+{
+ player.EndSignal( "OnDeath" )
+ player.EndSignal( "OnDestroy" )
+
+ while( true )
+ {
+ player.WaitSignal( "ThrowGrenade" )
+
+ if ( player.IsTitan() )
+ continue
+
+ entity weapon = player.GetOffhandWeapon( OFFHAND_ORDNANCE )
+ if ( weapon.GetWeaponPrimaryClipCount() == 0 )
+ {
+ player.TakeOffhandWeapon( OFFHAND_ORDNANCE )
+ int loadoutIndex = GetActivePilotLoadoutIndex( player )
+ PilotLoadoutDef loadout = GetPilotLoadoutFromPersistentData( player, loadoutIndex )
+ player.GiveOffhandWeapon( loadout.ordnance, OFFHAND_ORDNANCE )
+ return
+ }
+ }
+}
+
+void function GiveWeaponPowerUp( entity player, string newWeapon )
+{
+ array<entity> weapons = player.GetMainWeapons()
+ string weaponToSwitch = player.GetLatestPrimaryWeapon().GetWeaponClassName()
+
+ if ( player.GetActiveWeapon() != player.GetAntiTitanWeapon() )
+ {
+ foreach ( weapon in weapons )
+ {
+ string weaponClassName = weapon.GetWeaponClassName()
+ if ( weaponClassName == newWeapon )
+ {
+ weaponToSwitch = weaponClassName
+ break
+ }
+ }
+ }
+
+ player.TakeWeaponNow( weaponToSwitch )
+ player.GiveWeapon( newWeapon )
+ player.SetActiveWeaponByName( newWeapon )
+}
+#endif
+
+void function PowerUp_Func_GiveArmorSmall( entity player )
+{
+ GiveArmor( player, 5 )
+}
+
+void function PowerUp_Func_GiveArmorMedium( entity player )
+{
+ GiveArmor( player, 25 )
+}
+
+void function PowerUp_Func_GiveArmorLarge( entity player )
+{
+ GiveArmor( player, 50 )
+}
+
+void function GiveArmor( entity player, int amount )
+{
+ #if SERVER
+ if ( player.IsTitan() )
+ return
+ int currentShieldHealth = player.GetShieldHealth()
+ int currentMaxShieldHealth = player.GetShieldHealthMax()
+ player.SetShieldHealth( min( 200, amount + currentShieldHealth ) )
+ player.SetShieldHealthMax( min( 200, amount + currentMaxShieldHealth ) )
+ #endif
+}
+
+void function PowerUp_Func_TitanBuildTime( entity player )
+{
+ #if SERVER
+ entity battery = Rodeo_CreateBatteryPack()
+ battery.SetOrigin( player.GetOrigin() )
+ #endif
+}
+
+
+
+void function PowerUp_Func_PilotUpgrade( entity player )
+{
+ #if SERVER
+ if ( player.IsTitan() )
+ return
+
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "pilot" )
+
+ PilotLoadoutDef loadout = GetPilotLoadoutFromPersistentData( player, loadoutIndex )
+
+ loadout.primary = "mp_weapon_arena2"
+ loadout.secondary = "mp_weapon_mgl"
+ loadout.ordnance = "mp_weapon_grenade_emp"
+
+ UpdateDerivedPilotLoadoutData( loadout )
+
+ GivePilotLoadout( player, loadout )
+ SetActivePilotLoadoutIndex( player, loadoutIndex )
+ #endif
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_progression.nut b/Northstar.CustomServers/mod/scripts/vscripts/sh_progression.nut
new file mode 100644
index 00000000..3297643e
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_progression.nut
@@ -0,0 +1,1154 @@
+global function Progression_Init
+global function ProgressionEnabledForPlayer
+#if CLIENT || UI
+global function Progression_SetPreference
+global function Progression_GetPreference
+global function UpdateCachedLoadouts_Delayed
+#endif
+
+#if SP // literally just stub the global functions and call it a day
+
+void function Progression_Init() {}
+bool function ProgressionEnabledForPlayer( entity player ) { return false }
+#if CLIENT || UI
+void function Progression_SetPreference( bool enabled ) {}
+bool function Progression_GetPreference() { return false }
+void function UpdateCachedLoadouts_Delayed() {}
+#endif // CLIENT || UI
+
+#else // MP || UI basically
+
+// SO FOR SOME GOD DAMN REASON, PUTTING THESE INTO ONE STRUCT
+// AND PUTTING THE #if STUFF AROUND THE VARS CAUSES A COMPILE
+// ERROR, SO I HAVE TO DO THIS AWFULNESS
+
+#if SERVER
+struct {
+ table<entity, bool> progressionEnabled
+} file
+#else // UI || CLIENT
+struct {
+ bool isUpdatingCachedLoadouts = false
+} file
+#endif
+
+
+void function Progression_Init()
+{
+ #if SERVER
+ AddCallback_OnClientDisconnected( OnClientDisconnected )
+ AddClientCommandCallback( "ns_progression", ClientCommand_SetProgression )
+ AddClientCommandCallback( "ns_resettitanaegis", ClientCommand_ResetTitanAegis )
+ AddCallback_GameStateEnter( eGameState.Playing, OnPlaying )
+ #elseif CLIENT
+ AddCallback_OnClientScriptInit( OnClientScriptInit )
+ #endif
+}
+
+bool function ProgressionEnabledForPlayer( entity player )
+{
+ #if SERVER
+ if ( player in file.progressionEnabled )
+ return file.progressionEnabled[player]
+
+ return false
+ #else // CLIENT || UI
+ return GetConVarBool( "ns_progression_enabled" )
+ #endif
+}
+
+#if SERVER
+void function OnPlaying()
+{
+ SetUIVar( level, "penalizeDisconnect", false ) // dont show the "you will lose merits thing"
+}
+
+void function OnClientDisconnected( entity player )
+{
+ // cleanup table when player leaves
+ if ( player in file.progressionEnabled )
+ delete file.progressionEnabled[player]
+}
+
+bool function ClientCommand_SetProgression( entity player, array<string> args )
+{
+ if ( args.len() != 1 )
+ return false
+ if ( args[0] != "0" && args[0] != "1" )
+ return false
+
+ file.progressionEnabled[player] <- args[0] == "1"
+
+ // loadout validation when progression is turned on
+ if ( file.progressionEnabled[player] )
+ ValidateEquippedItems( player )
+
+ return true
+}
+
+/// Resets a specific Titan's Aegis rank back to `0`
+/// * `player` - The player entity to perform the action on
+/// * `args` - The arguments passed from the client command. `args[0]` should be a string corresponding to the chassis name of the Titan to reset.
+/// Valid chassis are: ion, tone, vanguard, northstar, ronin, legion, and scorch.
+///
+/// Returns `true` on success and `false` on missing args.
+bool function ClientCommand_ResetTitanAegis( entity player, array<string> args )
+{
+ if ( !args.len() )
+ return false
+
+ string titanRef = args[0].tolower()
+ if( !PersistenceEnumValueIsValid( "titanClasses", titanRef ) )
+ return false
+
+ int suitIndex = PersistenceGetEnumIndexForItemName( "titanClasses", titanRef )
+
+ player.SetPersistentVar( "titanFDUnlockPoints[" + suitIndex + "]", 0 )
+ player.SetPersistentVar( "previousFDUnlockPoints[" + suitIndex + "]", 0 )
+ player.SetPersistentVar( "fdTitanXP[" + suitIndex + "]", 0 )
+ player.SetPersistentVar( "fdPreviousTitanXP[" + suitIndex + "]", 0 )
+
+ // Refresh Highest Aegis Titan since we might get all of them back to 1 if players wants
+ RecalculateHighestTitanFDLevel( player )
+
+ return true
+}
+#endif
+
+#if CLIENT
+void function OnClientScriptInit( entity player )
+{
+ // unsure if this is needed, just being safe
+ if ( player != GetLocalClientPlayer() )
+ return
+
+ Progression_SetPreference( GetConVarBool( "ns_progression_enabled" ) )
+ UpdateCachedLoadouts_Delayed()
+}
+#endif
+
+#if CLIENT || UI
+void function Progression_SetPreference( bool enabled )
+{
+ SetConVarBool( "ns_progression_enabled", enabled )
+
+ #if CLIENT
+ GetLocalClientPlayer().ClientCommand( "ns_progression " + enabled.tointeger() )
+ #else // UI
+ ClientCommand( "ns_progression " + enabled.tointeger() )
+ #endif
+}
+
+bool function Progression_GetPreference()
+{
+ return GetConVarBool( "ns_progression_enabled" )
+}
+
+void function UpdateCachedLoadouts_Delayed()
+{
+ if ( file.isUpdatingCachedLoadouts )
+ return
+
+ file.isUpdatingCachedLoadouts = true
+
+ #if UI
+ RunClientScript( "UpdateCachedLoadouts_Delayed" ) // keep client and UI synced
+ #else // CLIENT
+ RunUIScript( "UpdateCachedLoadouts_Delayed" ) // keep client and UI synced
+ #endif
+
+ thread UpdateCachedLoadouts_Threaded()
+}
+
+void function UpdateCachedLoadouts_Threaded()
+{
+ wait 1.0 // give the server time to network our new persistence
+
+ UpdateCachedLoadouts()
+
+ // below here is just making all the menu models update properly and such
+
+ #if UI
+ uiGlobal.pilotSpawnLoadoutIndex = GetPersistentSpawnLoadoutIndex( GetUIPlayer(), "pilot" )
+ uiGlobal.titanSpawnLoadoutIndex = GetPersistentSpawnLoadoutIndex( GetUIPlayer(), "titan" )
+ #endif
+
+ #if CLIENT
+ entity player = GetLocalClientPlayer()
+ ClearAllTitanPreview( player )
+ ClearAllPilotPreview( player )
+ UpdateTitanModel( player, GetPersistentSpawnLoadoutIndex( player, "titan" ) )
+ UpdatePilotModel( player, GetPersistentSpawnLoadoutIndex( player, "pilot" ) )
+ #endif
+
+ file.isUpdatingCachedLoadouts = false
+}
+#endif
+
+#if SERVER
+void function ValidateEquippedItems( entity player )
+{
+ printt( "VALIDATING EQUIPPED ITEMS FOR PLAYER: " + player.GetPlayerName() )
+
+ // banner
+ CallingCard card = PlayerCallingCard_GetActive( player )
+ if ( IsItemLocked( player, card.ref ) )
+ {
+ printt( "- BANNER CARD IS LOCKED, RESETTING" )
+ PlayerCallingCard_SetActiveByRef( player, "callsign_16_col" ) // copied from _persistentdata.gnut
+ }
+
+ // patch
+ CallsignIcon icon = PlayerCallsignIcon_GetActive( player )
+ if ( IsItemLocked( player, icon.ref ) )
+ {
+ printt( "- BANNER PATCH IS LOCKED, RESETTING" )
+ PlayerCallsignIcon_SetActiveByRef( player, "gc_icon_titanfall" ) // copied from _persistentdata.gnut
+ }
+
+ // faction
+ int factionIndex = player.GetPersistentVarAsInt( "factionChoice" )
+ string factionRef = PersistenceGetEnumItemNameForIndex( "faction", factionIndex )
+ if ( IsItemLocked( player, factionRef ) )
+ {
+ printt( "- FACTION IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "factionChoice", "faction_marauder" ) // im so sorry that i am setting you to use sarah, you don't deserve this
+ }
+
+ // boost
+ BurnReward reward = BurnReward_GetById( player.GetPersistentVarAsInt( "burnmeterSlot" ) )
+ if ( IsItemLocked( player, reward.ref ) )
+ {
+ printt( "- BOOST IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "burnmeterSlot", BurnReward_GetByRef( "burnmeter_amped_weapons" ).id )
+ }
+
+ // titan loadouts
+ int selectedTitanLoadoutIndex = player.GetPersistentVarAsInt( "titanSpawnLoadout.index" )
+ for ( int titanLoadoutIndex = 0; titanLoadoutIndex < NUM_PERSISTENT_TITAN_LOADOUTS; titanLoadoutIndex++ )
+ {
+ printt( "- VALIDATING TITAN LOADOUT: " + titanLoadoutIndex )
+
+ bool isSelected = titanLoadoutIndex == selectedTitanLoadoutIndex
+ TitanLoadoutDef loadout = GetTitanLoadout( player, titanLoadoutIndex )
+ TitanLoadoutDef defaultLoadout = shGlobal.defaultTitanLoadouts[titanLoadoutIndex]
+
+ printt( " - CHASSIS: " + loadout.titanClass )
+
+ // passive1 - "Titan Kit" (things like overcore)
+ if ( loadout.passive1 != defaultLoadout.passive1 && IsSubItemLocked( player, loadout.passive1, loadout.titanClass ) )
+ {
+ printt( " - TITAN KIT EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].passive1", defaultLoadout.passive1 )
+ }
+
+ // passive2 - "<chassis> Kit" (things like zero point tripwire)
+ if ( loadout.passive2 != defaultLoadout.passive2 && IsSubItemLocked( player, loadout.passive2, loadout.titanClass ) )
+ {
+ printt( " - CHASSIS KIT EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].passive2", defaultLoadout.passive2 )
+ }
+
+ // passive3 - "Titanfall Kit" (warpfall/dome shield)
+ if ( loadout.passive3 != defaultLoadout.passive3 && IsSubItemLocked( player, loadout.passive3, loadout.titanClass ) )
+ {
+ printt( " - TITANFALL KIT EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].passive3", defaultLoadout.passive3 )
+ }
+
+ // passive4 - monarch core 1
+ if ( loadout.passive4 != defaultLoadout.passive4 && IsSubItemLocked( player, loadout.passive4, loadout.titanClass ) )
+ {
+ printt( " - MONARCH CORE 1 KIT EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].passive4", defaultLoadout.passive4 )
+ }
+
+ // passive5 - monarch core 2
+ if ( loadout.passive5 != defaultLoadout.passive5 && IsSubItemLocked( player, loadout.passive5, loadout.titanClass ) )
+ {
+ printt( " - MONARCH CORE 2 KIT EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].passive5", defaultLoadout.passive5 )
+ }
+
+ // passive6 - monarch core 3
+ if ( loadout.passive6 != defaultLoadout.passive6 && IsSubItemLocked( player, loadout.passive6, loadout.titanClass ) )
+ {
+ printt( " - MONARCH CORE 3 KIT EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].passive6", defaultLoadout.passive6 )
+ }
+
+ // titanExecution
+ if ( !IsRefValid( loadout.titanExecution ) || !IsValidTitanExecution( titanLoadoutIndex, "titanExecution", "", loadout.titanExecution ) )
+ {
+ printt( " - TITAN EXECUTION IS INVALID FOR CHASSIS, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].titanExecution", defaultLoadout.titanExecution )
+ }
+ else if ( IsItemLocked( player, loadout.titanExecution ) )
+ {
+ printt( " - TITAN EXECUTION EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].titanExecution", defaultLoadout.titanExecution )
+ }
+ else if ( GetItemData( loadout.titanExecution ).reqPrime && IsItemLocked( player, loadout.primeTitanRef ) )
+ {
+ printt( " - PRIME TITAN EXECUTION EQUIPPED WHEN PRIME TITAN IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].titanExecution", defaultLoadout.titanExecution )
+ }
+
+ // skinIndex
+ // camoIndex
+ if ( loadout.skinIndex == TITAN_SKIN_INDEX_CAMO )
+ {
+ array<ItemData> camoSkins = GetAllItemsOfType( eItemTypes.CAMO_SKIN_TITAN )
+ if ( loadout.camoIndex >= camoSkins.len() || loadout.camoIndex < 0 )
+ {
+ printt( " - INVALID TITAN CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].skinIndex", defaultLoadout.skinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].camoIndex", defaultLoadout.camoIndex )
+ }
+ else
+ {
+ ItemData camoSkin = camoSkins[loadout.camoIndex]
+ if ( IsSubItemLocked( player, camoSkin.ref, loadout.titanClass ) )
+ {
+ printt( " - TITAN CAMO/SKIN EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].skinIndex", defaultLoadout.skinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].camoIndex", defaultLoadout.camoIndex )
+ }
+ }
+ }
+ else if ( loadout.skinIndex == 0 )
+ {
+ if ( loadout.camoIndex != 0 )
+ {
+ printt( " - INVALID TITAN CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].skinIndex", defaultLoadout.skinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].camoIndex", defaultLoadout.camoIndex )
+ }
+ }
+ else
+ {
+ string ref = GetSkinRefFromTitanClassAndPersistenceValue( loadout.titanClass, loadout.skinIndex )
+ if ( ref == INVALID_REF )
+ {
+ printt( " - INVALID TITAN WARPAINT, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].skinIndex", defaultLoadout.skinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].camoIndex", defaultLoadout.camoIndex )
+ }
+ else if ( IsSubItemLocked( player, ref, loadout.titanClass ) )
+ {
+ printt( " - TITAN WARPAINT EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].skinIndex", defaultLoadout.skinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].camoIndex", defaultLoadout.camoIndex )
+ }
+ }
+
+ // decalIndex
+ string noseArtRef = GetNoseArtRefFromTitanClassAndPersistenceValue( loadout.titanClass, loadout.decalIndex )
+ if ( loadout.decalIndex != defaultLoadout.decalIndex && IsSubItemLocked( player, noseArtRef, loadout.titanClass ) )
+ {
+ printt( " - NOSE ART EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].decalIndex", defaultLoadout.decalIndex )
+ }
+
+ // primarySkinIndex
+ // primaryCamoIndex
+ if ( loadout.primarySkinIndex == WEAPON_SKIN_INDEX_CAMO )
+ {
+ array<ItemData> camoSkins = GetAllItemsOfType( eItemTypes.CAMO_SKIN )
+ if ( loadout.primaryCamoIndex >= camoSkins.len() || loadout.primaryCamoIndex < 0 )
+ {
+ printt( " - INVALID WEAPON CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primarySkinIndex", defaultLoadout.primarySkinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primaryCamoIndex", defaultLoadout.primaryCamoIndex )
+ }
+ else
+ {
+ ItemData camoSkin = camoSkins[loadout.primaryCamoIndex]
+ if ( IsSubItemLocked( player, camoSkin.ref, loadout.titanClass ) )
+ {
+ printt( " - WEAPON CAMO/SKIN EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primarySkinIndex", defaultLoadout.primarySkinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primaryCamoIndex", defaultLoadout.primaryCamoIndex )
+ }
+ }
+ }
+ else if ( loadout.primarySkinIndex == 0 && loadout.primaryCamoIndex != 0 )
+ {
+ // titan weapons do not have skins, if we ever do add them lots of stuff will
+ //need a refactor outside of here so with that being said, i cannot be bothered
+ printt( " - INVALID WEAPON CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primarySkinIndex", defaultLoadout.primarySkinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primaryCamoIndex", defaultLoadout.primaryCamoIndex )
+ }
+
+
+ // isPrime
+ if ( loadout.isPrime == "titan_is_prime" && IsItemLocked( player, loadout.primeTitanRef ) )
+ {
+ printt( " - PRIME TITAN EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].isPrime", defaultLoadout.isPrime )
+ }
+
+ // primeSkinIndex
+ // primeCamoIndex
+ if ( loadout.primeSkinIndex == TITAN_SKIN_INDEX_CAMO )
+ {
+ array<ItemData> camoSkins = GetAllItemsOfType( eItemTypes.CAMO_SKIN_TITAN )
+ if ( loadout.primeCamoIndex >= camoSkins.len() || loadout.primeCamoIndex < 0 )
+ {
+ printt( " - INVALID TITAN CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primeSkinIndex", defaultLoadout.primeSkinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primeCamoIndex", defaultLoadout.primeCamoIndex )
+ }
+ else
+ {
+ ItemData camoSkin = camoSkins[loadout.primeCamoIndex]
+ if ( IsSubItemLocked( player, camoSkin.ref, loadout.titanClass ) )
+ {
+ printt( " - TITAN CAMO/SKIN EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primeSkinIndex", defaultLoadout.primeSkinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primeCamoIndex", defaultLoadout.primeCamoIndex )
+ }
+ }
+ }
+ else if ( loadout.primeSkinIndex == 0 )
+ {
+ if ( loadout.primeCamoIndex != 0 )
+ {
+ printt( " - INVALID TITAN CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primeSkinIndex", defaultLoadout.primeSkinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primeCamoIndex", defaultLoadout.primeCamoIndex )
+ }
+ }
+ else
+ {
+ printt( " - INVALID PRIME TITAN SKIN, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primeSkinIndex", defaultLoadout.primeSkinIndex )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primeCamoIndex", defaultLoadout.primeCamoIndex )
+ }
+
+ // primeDecalIndex
+ string primeNoseArtRef = GetNoseArtRefFromTitanClassAndPersistenceValue( loadout.titanClass, loadout.primeDecalIndex )
+ if ( loadout.primeDecalIndex != defaultLoadout.primeDecalIndex && IsSubItemLocked( player, primeNoseArtRef, loadout.titanClass ) )
+ {
+ printt( " - PRIME NOSE ART EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].primeDecalIndex", defaultLoadout.primeDecalIndex )
+ }
+
+ // showArmBadge - equipped and shouldnt be able to
+ if ( loadout.showArmBadge && !CanEquipArmBadge( player, loadout.titanClass ) )
+ {
+ printt( " - ARM BADGE EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanLoadouts[" + titanLoadoutIndex + "].showArmBadge", defaultLoadout.showArmBadge )
+ }
+
+ // equipped titan loadout - equipped titan class is locked
+ if ( isSelected && IsItemLocked( player, loadout.titanClass ) )
+ {
+ printt( " - SELECTED TITAN CLASS IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "titanSpawnLoadout.index", 0 )
+ selectedTitanLoadoutIndex = 0
+ }
+ }
+
+ if ( selectedTitanLoadoutIndex < 0 || selectedTitanLoadoutIndex >= NUM_PERSISTENT_TITAN_LOADOUTS )
+ {
+ printt( "- SELECTED TITAN CLASS IS INVALID, RESETTING" )
+ player.SetPersistentVar( "titanSpawnLoadout.index", 0 )
+ selectedTitanLoadoutIndex = 0
+ }
+
+ Remote_CallFunction_NonReplay( player, "ServerCallback_UpdateTitanModel", selectedTitanLoadoutIndex )
+
+ // pilot loadouts
+ for ( int pilotLoadoutIndex = 0; pilotLoadoutIndex < NUM_PERSISTENT_PILOT_LOADOUTS; pilotLoadoutIndex++ )
+ {
+ printt( "- VALIDATING PILOT LOADOUT: " + pilotLoadoutIndex )
+
+ bool isSelected = pilotLoadoutIndex == player.GetPersistentVarAsInt( "pilotSpawnLoadout.index" )
+ PilotLoadoutDef loadout = GetPilotLoadout( player, pilotLoadoutIndex )
+ PilotLoadoutDef defaultLoadout = shGlobal.defaultPilotLoadouts[pilotLoadoutIndex]
+
+ // note: for readability, I have added {} around the different items,
+ // so that you can collapse them in visual studio code (and other good IDEs)
+
+ // tactical
+ {
+ if ( !IsRefValid( loadout.suit ) )
+ {
+ printt( " - TACTICAL IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].suit", defaultLoadout.suit )
+ }
+ else if ( IsItemLocked( player, loadout.suit ) )
+ {
+ printt( " - TACTICAL IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].suit", defaultLoadout.suit )
+ }
+ }
+
+ // ordnance
+ {
+ if ( !IsRefValid( loadout.ordnance ) )
+ {
+ printt( " - ORDNANCE IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].ordnance", defaultLoadout.ordnance )
+ }
+ else if ( IsItemLocked( player, loadout.ordnance ) )
+ {
+ printt( " - ORDNANCE IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].ordnance", defaultLoadout.ordnance )
+ }
+ }
+
+ // race ( gender )
+ {
+ if ( !IsRefValid( loadout.race ) )
+ {
+ printt( " - GENDER IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].race", defaultLoadout.race )
+ }
+ else if ( IsItemLocked( player, loadout.race ) )
+ {
+ printt( " - GENDER IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].race", defaultLoadout.race )
+ }
+ }
+
+ // camoIndex
+ // skinIndex
+ {
+ if ( loadout.skinIndex == PILOT_SKIN_INDEX_CAMO )
+ {
+ array<ItemData> camoSkins = GetAllItemsOfType( eItemTypes.CAMO_SKIN_PILOT )
+ if ( loadout.camoIndex >= camoSkins.len() || loadout.camoIndex < 0 )
+ {
+ printt( " - INVALID PILOT CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].skinIndex", defaultLoadout.skinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].camoIndex", defaultLoadout.camoIndex )
+ }
+ else
+ {
+ ItemData camoSkin = camoSkins[loadout.camoIndex]
+ if ( IsItemLocked( player, camoSkin.ref ) )
+ {
+ printt( " - PILOT CAMO/SKIN EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].skinIndex", defaultLoadout.skinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].camoIndex", defaultLoadout.camoIndex )
+ }
+ }
+ }
+ else if ( loadout.skinIndex == 0 )
+ {
+ if ( loadout.camoIndex != 0 )
+ {
+ printt( " - INVALID PILOT CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].skinIndex", defaultLoadout.skinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].camoIndex", defaultLoadout.camoIndex )
+ }
+ }
+ else
+ {
+ // pilots can't have skins other than 0 and 1 right?
+ printt( " - INVALID PILOT SKIN, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].skinIndex", defaultLoadout.skinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].camoIndex", defaultLoadout.camoIndex )
+ }
+ }
+
+ // primary weapon
+ {
+ if ( !IsRefValid( loadout.primary ) || GetItemType( loadout.primary ) != eItemTypes.PILOT_PRIMARY )
+ {
+ printt( " - PRIMARY WEAPON IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primary", defaultLoadout.primary )
+ }
+ else if ( IsItemLocked( player, loadout.primary ) )
+ {
+ printt( " - PRIMARY WEAPON IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primary", defaultLoadout.primary )
+ }
+ }
+
+ // primary weapon mods
+ {
+ // mod1
+ if ( loadout.primaryMod1 == "" )
+ {
+ // do nothing
+ }
+ else if ( !HasSubitem( loadout.primary, loadout.primaryMod1 ) )
+ {
+ printt( " - PRIMARY WEAPON MOD 1 IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryMod1", defaultLoadout.primaryMod1 )
+ }
+ else if ( IsSubItemLocked( player, loadout.primaryMod1, loadout.primary ) )
+ {
+ printt( " - PRIMARY WEAPON MOD 1 IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryMod1", defaultLoadout.primaryMod1 )
+ }
+ // mod2
+ if ( loadout.primaryMod2 == "" )
+ {
+ // do nothing
+ }
+ else if ( IsSubItemLocked( player, "primarymod2", loadout.primary ) )
+ {
+ printt( " - PRIMARY WEAPON MOD 2 SLOT IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryMod2", defaultLoadout.primaryMod2 )
+ }
+ else if ( !HasSubitem( loadout.primary, loadout.primaryMod2 ) )
+ {
+ printt( " - PRIMARY WEAPON MOD 2 IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryMod2", defaultLoadout.primaryMod2 )
+ }
+ else if ( IsSubItemLocked( player, loadout.primaryMod2, loadout.primary ) )
+ {
+ printt( " - PRIMARY WEAPON MOD 2 IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryMod2", defaultLoadout.primaryMod2 )
+ }
+ else if ( loadout.primaryMod2 == loadout.primaryMod1 && loadout.primaryMod2 != "" )
+ {
+ printt( " - PRIMARY WEAPON MOD 2 IS DUPLICATE, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryMod2", defaultLoadout.primaryMod2 )
+ }
+ else if ( loadout.primaryAttachment == "threat_scope" )
+ {
+ printt( " - PRIMARY WEAPON MOD 2 IS SET WITH THREAT SCOPE, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryMod2", defaultLoadout.primaryMod2 )
+ }
+ // attachment
+ if ( loadout.primaryAttachment == "" )
+ {
+ // do nothing
+ }
+ else if ( !HasSubitem( loadout.primary, loadout.primaryAttachment ) )
+ {
+ printt( " - PRIMARY WEAPON ATTACHMENT IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryAttachment", defaultLoadout.primaryAttachment )
+ }
+ else if ( IsSubItemLocked( player, loadout.primaryAttachment, loadout.primary ) )
+ {
+ printt( " - PRIMARY WEAPON ATTACHMENT IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryAttachment", defaultLoadout.primaryAttachment )
+ }
+ // mod3 (pro screen)
+ if ( loadout.primaryMod3 == "" )
+ {
+ // do nothing
+ }
+ else if ( loadout.primaryMod3 == "pro_screen" )
+ {
+ // fuck you and your three mod slot stuff
+ printt( " - PRIMARY WEAPON PRO SCREEN IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryMod3", defaultLoadout.primaryMod3 )
+ }
+ else if ( IsSubItemLocked( player, loadout.primaryMod3, loadout.primary ) )
+ {
+ printt( " - PRIMARY WEAPON PRO SCREEN IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryMod3", defaultLoadout.primaryMod3 )
+ }
+ }
+
+ // primary weapon camoIndex
+ // primary weapon skinIndex
+ {
+ if ( loadout.primarySkinIndex == WEAPON_SKIN_INDEX_CAMO )
+ {
+ array<ItemData> camoSkins = GetAllItemsOfType( eItemTypes.CAMO_SKIN )
+ if ( loadout.primaryCamoIndex >= camoSkins.len() || loadout.primaryCamoIndex < 0 )
+ {
+ printt( " - INVALID PRIMARY WEAPON CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primarySkinIndex", defaultLoadout.primarySkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryCamoIndex", defaultLoadout.primaryCamoIndex )
+ }
+ else
+ {
+ ItemData camoSkin = camoSkins[loadout.primaryCamoIndex]
+ if ( IsSubItemLocked( player, camoSkin.ref, loadout.primary ) )
+ {
+ printt( " - PRIMARY WEAPON CAMO/SKIN EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primarySkinIndex", defaultLoadout.primarySkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryCamoIndex", defaultLoadout.primaryCamoIndex )
+ }
+ }
+ }
+ else if ( loadout.primarySkinIndex == 0 )
+ {
+ if ( loadout.primaryCamoIndex != 0 )
+ {
+ printt( " - INVALID PRIMARY WEAPON CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primarySkinIndex", defaultLoadout.primarySkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryCamoIndex", defaultLoadout.primaryCamoIndex )
+ }
+ }
+ else
+ {
+ string warpaintRef = GetWeaponWarpaintRefByIndex( loadout.primarySkinIndex, loadout.primary )
+ if ( warpaintRef == INVALID_REF || IsSubItemLocked( player, warpaintRef, loadout.primary ) )
+ {
+ printt( " - PRIMARY WEAPON SKIN LOCKED/INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primarySkinIndex", defaultLoadout.primarySkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].primaryCamoIndex", defaultLoadout.primaryCamoIndex )
+ }
+ }
+ }
+
+ // secondary weapon
+ {
+ if ( !IsRefValid( loadout.secondary ) || GetItemType( loadout.secondary ) != eItemTypes.PILOT_SECONDARY )
+ {
+ printt( " - SECONDARY WEAPON IS LOCKED, RESETTING" )
+ string ref = defaultLoadout.secondary
+ if ( loadout.secondary == ref ) // item dupes swap
+ {
+ ref = defaultLoadout.weapon3
+ }
+ else if ( ItemsInSameMenuCategory( loadout.secondary, ref ) ) // category dupes assign value to other slot and swap
+ {
+ ref = defaultLoadout.weapon3
+ }
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondary", ref )
+ }
+ else if ( IsItemLocked( player, loadout.secondary ) )
+ {
+ printt( " - SECONDARY WEAPON IS LOCKED, RESETTING" )
+ string ref = defaultLoadout.secondary
+ if ( loadout.weapon3 == ref ) // item dupes swap
+ {
+ ref = defaultLoadout.weapon3
+ }
+ else if ( ItemsInSameMenuCategory( loadout.weapon3, ref ) ) // category dupes assign value to other slot and swap
+ {
+ ref = defaultLoadout.weapon3
+ }
+
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondary", ref )
+ }
+ }
+
+ // secondary weapon mods
+ {
+ // mod1
+ if ( loadout.secondaryMod1 == "" )
+ {
+ // do nothing
+ }
+ else if ( !HasSubitem( loadout.secondary, loadout.secondaryMod1 ) )
+ {
+ printt( " - SECONDARY WEAPON MOD 1 IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryMod1", defaultLoadout.secondaryMod1 )
+ }
+ else if ( IsSubItemLocked( player, loadout.secondaryMod1, loadout.secondary ) )
+ {
+ printt( " - SECONDARY WEAPON MOD 1 IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryMod1", defaultLoadout.secondaryMod1 )
+ }
+ // mod2
+ if ( loadout.secondaryMod2 == "" )
+ {
+ // do nothing
+ }
+ else if ( IsSubItemLocked( player, "secondarymod2", loadout.secondary ) )
+ {
+ printt( " - SECONDARY WEAPON MOD 2 SLOT IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryMod2", defaultLoadout.secondaryMod2 )
+ }
+ else if ( !HasSubitem( loadout.secondary, loadout.secondaryMod2 ) )
+ {
+ printt( " - SECONDARY WEAPON MOD 2 IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryMod2", defaultLoadout.secondaryMod2 )
+ }
+ else if ( IsSubItemLocked( player, loadout.secondaryMod2, loadout.secondary ) )
+ {
+ printt( " - SECONDARY WEAPON MOD 2 IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryMod2", defaultLoadout.secondaryMod2 )
+ }
+ else if ( loadout.secondaryMod2 == loadout.secondaryMod1 && loadout.secondaryMod2 != "" )
+ {
+ printt( " - SECONDARY WEAPON MOD 2 IS DUPLICATE, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryMod2", defaultLoadout.secondaryMod2 )
+ }
+ // mod3 (pro screen)
+ if ( loadout.secondaryMod3 == "" )
+ {
+ // do nothing
+ }
+ else if ( loadout.secondaryMod3 == "pro_screen" )
+ {
+ // fuck you and your three mod slot stuff
+ printt( " - SECONDARY WEAPON PRO SCREEN IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryMod3", defaultLoadout.secondaryMod3 )
+ }
+ else if ( IsSubItemLocked( player, "secondarymod3", loadout.secondary ) )
+ {
+ printt( " - SECONDARY WEAPON PRO SCREEN IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryMod3", defaultLoadout.secondaryMod3 )
+ }
+ }
+
+ // secondary weapon camoIndex
+ // secondary weapon skinIndex
+ {
+ if ( loadout.secondarySkinIndex == WEAPON_SKIN_INDEX_CAMO )
+ {
+ array<ItemData> camoSkins = GetAllItemsOfType( eItemTypes.CAMO_SKIN )
+ if ( loadout.secondaryCamoIndex >= camoSkins.len() || loadout.secondaryCamoIndex < 0 )
+ {
+ printt( " - INVALID SECONDARY WEAPON CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondarySkinIndex", defaultLoadout.secondarySkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryCamoIndex", defaultLoadout.secondaryCamoIndex )
+ }
+ else
+ {
+ ItemData camoSkin = camoSkins[loadout.secondaryCamoIndex]
+ if ( IsSubItemLocked( player, camoSkin.ref, loadout.secondary ) )
+ {
+ printt( " - SECONDARY WEAPON CAMO/SKIN EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondarySkinIndex", defaultLoadout.secondarySkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryCamoIndex", defaultLoadout.secondaryCamoIndex )
+ }
+ }
+ }
+ else if ( loadout.secondarySkinIndex == 0 )
+ {
+ if ( loadout.secondaryCamoIndex != 0 )
+ {
+ printt( " - INVALID SECONDARY WEAPON CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondarySkinIndex", defaultLoadout.secondarySkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryCamoIndex", defaultLoadout.secondaryCamoIndex )
+ }
+ }
+ else
+ {
+ string warpaintRef = GetWeaponWarpaintRefByIndex( loadout.secondarySkinIndex, loadout.secondary )
+ if ( warpaintRef == INVALID_REF || IsSubItemLocked( player, warpaintRef, loadout.secondary ) )
+ {
+ printt( " - SECONDARY WEAPON SKIN LOCKED/INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondarySkinIndex", defaultLoadout.secondarySkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].secondaryCamoIndex", defaultLoadout.secondaryCamoIndex )
+ }
+ }
+ }
+
+ // weapon3
+ // note: these are always eItemTypes.PILOT_SECONDARY
+ {
+ if ( !IsRefValid( loadout.weapon3 ) || GetItemType( loadout.weapon3 ) != eItemTypes.PILOT_SECONDARY )
+ {
+ printt( " - WEAPON3 WEAPON IS LOCKED, RESETTING" )
+ string ref = defaultLoadout.weapon3
+ if ( loadout.weapon3 == ref ) // item dupes swap
+ {
+ ref = defaultLoadout.secondary
+ }
+ else if ( ItemsInSameMenuCategory( loadout.weapon3, ref ) ) // category dupes assign value to other slot and swap
+ {
+ ref = defaultLoadout.secondary
+ }
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3", ref )
+ }
+ else if ( IsItemLocked( player, loadout.weapon3 ) )
+ {
+ printt( " - TERTIARY WEAPON IS LOCKED, RESETTING" )
+ string ref = defaultLoadout.weapon3
+ if ( loadout.secondary == ref ) // item dupes swap
+ {
+ ref = defaultLoadout.secondary
+ }
+ else if ( ItemsInSameMenuCategory( loadout.secondary, ref ) ) // category dupes assign value to other slot and swap
+ {
+ ref = defaultLoadout.secondary
+ }
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3", ref )
+ }
+ }
+
+ // weapon3 mods
+ {
+ // mod1
+ if ( loadout.weapon3Mod1 == "" )
+ {
+ // do nothing
+ }
+ else if ( !HasSubitem( loadout.weapon3, loadout.weapon3Mod1 ) )
+ {
+ printt( " - WEAPON3 MOD 1 IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3Mod1", defaultLoadout.weapon3Mod1 )
+ }
+ else if ( IsSubItemLocked( player, loadout.weapon3Mod1, loadout.weapon3 ) )
+ {
+ printt( " - WEAPON3 MOD 1 IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3Mod1", defaultLoadout.weapon3Mod1 )
+ }
+ // mod2
+ if ( loadout.weapon3Mod2 == "" )
+ {
+ // do nothing
+ }
+ else if ( IsSubItemLocked( player, "secondarymod2", loadout.weapon3 ) )
+ {
+ printt( " - WEAPON3 MOD 2 SLOT IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3Mod2", defaultLoadout.weapon3Mod2 )
+ }
+ else if ( !HasSubitem( loadout.weapon3, loadout.weapon3Mod2 ) )
+ {
+ printt( " - WEAPON3 MOD 2 IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3Mod2", defaultLoadout.weapon3Mod2 )
+ }
+ else if ( IsSubItemLocked( player, loadout.weapon3Mod2, loadout.weapon3 ) )
+ {
+ printt( " - WEAPON3 MOD 2 IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3Mod2", defaultLoadout.weapon3Mod2 )
+ }
+ else if ( loadout.weapon3Mod2 == loadout.weapon3Mod1 && loadout.weapon3Mod2 != "" )
+ {
+ printt( " - WEAPON3 MOD 2 IS DUPLICATE, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3Mod2", defaultLoadout.weapon3Mod2 )
+ }
+ // mod3 (pro screen)
+ if ( loadout.weapon3Mod3 == "" )
+ {
+ // do nothing
+ }
+ else if ( loadout.weapon3Mod3 != "pro_screen" )
+ {
+ // fuck you and your three mod slot stuff
+ printt( " - WEAPON3 PRO SCREEN IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3Mod3", defaultLoadout.weapon3Mod3 )
+ }
+ else if ( IsSubItemLocked( player, "secondarymod3", loadout.weapon3 ) )
+ {
+ printt( " - WEAPON3 PRO SCREEN IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3Mod3", defaultLoadout.weapon3Mod3 )
+ }
+ }
+
+ // weapon3 camoIndex
+ // weapon3 skinIndex
+ {
+ if ( loadout.weapon3SkinIndex == WEAPON_SKIN_INDEX_CAMO )
+ {
+ array<ItemData> camoSkins = GetAllItemsOfType( eItemTypes.CAMO_SKIN )
+ if ( loadout.weapon3CamoIndex >= camoSkins.len() || loadout.weapon3CamoIndex < 0 )
+ {
+ printt( " - INVALID TERTIARY WEAPON CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3SkinIndex", defaultLoadout.weapon3SkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3CamoIndex", defaultLoadout.weapon3CamoIndex )
+ }
+ else
+ {
+ ItemData camoSkin = camoSkins[loadout.weapon3CamoIndex]
+ if ( IsSubItemLocked( player, camoSkin.ref, loadout.weapon3 ) )
+ {
+ printt( " - TERTIARY WEAPON CAMO/SKIN EQUIPPED WHEN LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3SkinIndex", defaultLoadout.weapon3SkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3CamoIndex", defaultLoadout.weapon3CamoIndex )
+ }
+ }
+ }
+ else if ( loadout.weapon3SkinIndex == 0 )
+ {
+ if ( loadout.weapon3CamoIndex != 0 )
+ {
+ printt( " - INVALID TERTIARY WEAPON CAMO/SKIN, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3SkinIndex", defaultLoadout.weapon3SkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3CamoIndex", defaultLoadout.weapon3CamoIndex )
+ }
+ }
+ else
+ {
+ string warpaintRef = GetWeaponWarpaintRefByIndex( loadout.weapon3SkinIndex, loadout.weapon3 )
+ if ( warpaintRef == INVALID_REF || IsSubItemLocked( player, warpaintRef, loadout.weapon3 ) )
+ {
+ printt( " - TERTIARY WEAPON SKIN LOCKED/INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3SkinIndex", defaultLoadout.weapon3SkinIndex )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].weapon3CamoIndex", defaultLoadout.weapon3CamoIndex )
+ }
+ }
+ }
+
+ // kit 1
+ {
+ if ( !IsRefValid( loadout.passive1 ) || GetItemType( loadout.passive1 ) != eItemTypes.PILOT_PASSIVE1 )
+ {
+ printt( " - KIT 1 IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].passive1", defaultLoadout.passive1 )
+ }
+ else if ( IsItemLocked( player, loadout.passive1 ) )
+ {
+ printt( " - KIT 1 IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].passive1", defaultLoadout.passive1 )
+ }
+ }
+
+ // kit 2
+ {
+ if ( !IsRefValid( loadout.passive2 ) || GetItemType( loadout.passive2 ) != eItemTypes.PILOT_PASSIVE2 )
+ {
+ printt( " - KIT 2 IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].passive2", defaultLoadout.passive2 )
+ }
+ else if ( IsItemLocked( player, loadout.passive2 ) )
+ {
+ printt( " - KIT 2 IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].passive2", defaultLoadout.passive2 )
+ }
+ }
+
+ // execution
+ // note: not sure why defaultLoadout has this set to "", but neck snap should be default
+ {
+ if ( !IsRefValid( loadout.execution ) || GetItemType( loadout.execution ) != eItemTypes.PILOT_EXECUTION )
+ {
+ printt( " - EXECUTION IS INVALID, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].execution", "execution_neck_snap" )
+ }
+ else if ( IsItemLocked( player, loadout.execution ) )
+ {
+ printt( " - EXECUTION IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotLoadouts[" + pilotLoadoutIndex + "].execution", "execution_neck_snap" )
+ }
+ }
+
+ // equipped pilot loadout
+ {
+ if ( isSelected && IsItemLocked( player, "pilot_loadout_" + ( pilotLoadoutIndex + 1 ) ) )
+ {
+ printt( " - SELECTED PILOT LOADOUT IS LOCKED, RESETTING" )
+ player.SetPersistentVar( "pilotSpawnLoadout.index", 0 )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_UpdatePilotModel", 0 )
+ }
+ }
+ }
+
+ Remote_CallFunction_NonReplay( player, "ServerCallback_UpdatePilotModel", player.GetPersistentVarAsInt( "pilotSpawnLoadout.index" ) )
+
+ printt( "ITEM VALIDATION COMPLETE FOR PLAYER: " + player.GetPlayerName() )
+}
+
+// basically just PopulateTitanLoadoutFromPersistentData but without validation, we are doing the validation in a better way
+// that doesnt just kick the player and reset the entire loadout, since we want to only reset parts of the loadout that we need
+TitanLoadoutDef function GetTitanLoadout( entity player, int loadoutIndex )
+{
+ TitanLoadoutDef loadout
+
+ loadout.name = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "name" )
+ loadout.titanClass = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "titanClass" )
+ loadout.primaryMod = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "primaryMod" )
+ loadout.special = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "special" )
+ loadout.antirodeo = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "antirodeo" )
+ loadout.passive1 = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive1" )
+ loadout.passive2 = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive2" )
+ loadout.passive3 = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive3" )
+ loadout.passive4 = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive4" )
+ loadout.passive5 = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive5" )
+ loadout.passive6 = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive6" )
+ loadout.camoIndex = GetPersistentLoadoutValueInt( player, "titan", loadoutIndex, "camoIndex" )
+ loadout.skinIndex = GetPersistentLoadoutValueInt( player, "titan", loadoutIndex, "skinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.decalIndex = GetPersistentLoadoutValueInt( player, "titan", loadoutIndex, "decalIndex" )
+ loadout.primaryCamoIndex = GetPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primaryCamoIndex" )
+ loadout.primarySkinIndex = GetPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primarySkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.titanExecution = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "titanExecution" )
+ loadout.showArmBadge = GetPersistentLoadoutValueInt( player, "titan", loadoutIndex, "showArmBadge" )
+
+ //Prime Titan related vars
+ loadout.isPrime = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "isPrime" )
+ loadout.primeCamoIndex = GetPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primeCamoIndex" )
+ loadout.primeSkinIndex = GetPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primeSkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.primeDecalIndex = GetPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primeDecalIndex" )
+
+ UpdateDerivedTitanLoadoutData( loadout )
+ OverwriteLoadoutWithDefaultsForSetFile( loadout )
+
+ return loadout
+}
+
+// basically just PopulatePilotLoadoutFromPersistentData but without validation, we are doing the validation in a better way
+// that doesnt just kick the player and reset the entire loadout, since we want to only reset parts of the loadout that we need
+PilotLoadoutDef function GetPilotLoadout( entity player, int loadoutIndex )
+{
+ PilotLoadoutDef loadout
+
+ loadout.name = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "name" )
+ loadout.suit = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "suit" )
+ loadout.race = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "race" )
+ loadout.execution = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "execution" )
+ loadout.primary = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primary" )
+ loadout.primaryAttachment = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryAttachment" )
+ loadout.primaryMod1 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod1" )
+ loadout.primaryMod2 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod2" )
+ loadout.primaryMod3 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod3" )
+ loadout.secondary = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondary" )
+ loadout.secondaryMod1 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod1" )
+ loadout.secondaryMod2 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod2" )
+ loadout.secondaryMod3 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod3" )
+ loadout.weapon3 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3" )
+ loadout.weapon3Mod1 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod1" )
+ loadout.weapon3Mod2 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod2" )
+ loadout.weapon3Mod3 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod3" )
+ loadout.ordnance = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "ordnance" )
+ loadout.passive1 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive1" )
+ loadout.passive2 = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive2" )
+ loadout.camoIndex = GetPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "camoIndex" )
+ loadout.skinIndex = GetPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "skinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.primaryCamoIndex = GetPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "primaryCamoIndex" )
+ loadout.primarySkinIndex = GetPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "primarySkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.secondaryCamoIndex = GetPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "secondaryCamoIndex" )
+ loadout.secondarySkinIndex = GetPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "secondarySkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.weapon3CamoIndex = GetPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "weapon3CamoIndex" )
+ loadout.weapon3SkinIndex = GetPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "weapon3SkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+
+ UpdateDerivedPilotLoadoutData( loadout )
+
+ return loadout
+}
+
+bool function CanEquipArmBadge( entity player, string titanClass )
+{
+ string skinRef
+ switch ( titanClass )
+ {
+ case "ion":
+ skinRef = "ion_skin_fd"
+ break
+ case "scorch":
+ skinRef = "scorch_skin_fd"
+ break
+ case "northstar":
+ skinRef = "northstar_skin_fd"
+ break
+ case "ronin":
+ skinRef = "ronin_skin_fd"
+ break
+ case "tone":
+ skinRef = "tone_skin_fd"
+ break
+ case "legion":
+ skinRef = "legion_skin_fd"
+ break
+ case "vanguard":
+ skinRef = "monarch_skin_fd"
+ break
+ }
+
+ return !IsSubItemLocked( player, skinRef, titanClass )
+}
+
+string function GetWeaponWarpaintRefByIndex( int skinIndex, string parentRef )
+{
+ ItemData parentItem = GetItemData( parentRef )
+ foreach ( subItem in parentItem.subitems )
+ {
+ if ( GetSubitemType( parentRef, subItem.ref ) != eItemTypes.WEAPON_SKIN )
+ continue
+ if ( subItem.i.skinIndex != skinIndex )
+ continue
+
+ return subItem.ref
+ }
+
+ return INVALID_REF
+}
+#endif // SERVER
+
+#endif // MP
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_server_to_client_stringcommands.gnut b/Northstar.CustomServers/mod/scripts/vscripts/sh_server_to_client_stringcommands.gnut
index a51e528f..18df6a6f 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/sh_server_to_client_stringcommands.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_server_to_client_stringcommands.gnut
@@ -1,6 +1,4 @@
#if CLIENT
-global function ServerToClientStringCommands_Init
-
global function AddServerToClientStringCommandCallback
global function NSClientCodeCallback_RecievedServerToClientStringCommand
#endif
@@ -14,11 +12,6 @@ struct {
table< string, array< void functionref( array<string> args ) > > callbacks
} file
-void function ServerToClientStringCommands_Init()
-{
- getroottable().rawset( "NSClientCodeCallback_RecievedServerToClientStringCommand", NSClientCodeCallback_RecievedServerToClientStringCommand )
-}
-
void function AddServerToClientStringCommandCallback( string command, void functionref( array<string> args ) callback )
{
if ( !( command in file.callbacks ) )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_store.gnut b/Northstar.CustomServers/mod/scripts/vscripts/sh_store.gnut
new file mode 100644
index 00000000..371cc1c7
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_store.gnut
@@ -0,0 +1,933 @@
+
+global function Store_Init
+global function Store_GetCustomizationRefs
+global function Store_GetPatchRefs
+global function Store_GetBannerRefs
+global function Store_GetCamoRefs
+
+global struct CamoRef
+{
+ string ref
+ string pilotRef
+ string titanRef
+}
+
+struct RefData
+{
+ string ref
+ string parentRef
+}
+
+struct
+{
+ table< int, array<string> > customizationRefs
+ table< int, array<string> > patchRefs
+ table< int, array<string> > bannerRefs
+ table< int, array<CamoRef> > camoRefs
+ table< int, array<RefData> > limitedEditionFDRefData
+} file
+
+void function Store_Init()
+{
+ #if SERVER
+ AddClientCommandCallback( "SetHasSeenStore", ClientCommand_SetHasSeenStore )
+ AddClientCommandCallback( "StoreSetNewItemStatus", ClientCommand_StoreSetNewItemStatus )
+ #endif
+
+ file.customizationRefs[ ET_DLC1_ION ] <- []
+ file.customizationRefs[ ET_DLC1_ION ].append( "ion_skin_10" )
+ file.customizationRefs[ ET_DLC1_ION ].append( "ion_nose_art_17" )
+ file.customizationRefs[ ET_DLC1_ION ].append( "ion_nose_art_18" )
+ file.customizationRefs[ ET_DLC1_ION ].append( "ion_nose_art_19" )
+ file.customizationRefs[ ET_DLC1_ION ].append( "ion_nose_art_20" )
+ file.customizationRefs[ ET_DLC1_ION ].append( "ion_nose_art_21" )
+ file.customizationRefs[ ET_DLC3_ION ] <- []
+ file.customizationRefs[ ET_DLC3_ION ].append( "ion_skin_11" )
+ file.customizationRefs[ ET_DLC3_ION ].append( "ion_nose_art_22" )
+ file.customizationRefs[ ET_DLC3_ION ].append( "ion_nose_art_23" )
+ file.customizationRefs[ ET_DLC3_ION ].append( "ion_nose_art_24" )
+ file.customizationRefs[ ET_DLC3_ION ].append( "ion_nose_art_25" )
+ file.customizationRefs[ ET_DLC3_ION ].append( "ion_nose_art_26" )
+ file.customizationRefs[ ET_DLC5_ION ] <- []
+ file.customizationRefs[ ET_DLC5_ION ].append( "ion_skin_07" )
+ file.customizationRefs[ ET_DLC5_ION ].append( "ion_nose_art_27" )
+ file.customizationRefs[ ET_DLC5_ION ].append( "ion_nose_art_28" )
+ file.customizationRefs[ ET_DLC5_ION ].append( "ion_nose_art_29" )
+ file.customizationRefs[ ET_DLC5_ION ].append( "ion_nose_art_30" )
+ file.customizationRefs[ ET_DLC5_ION ].append( "ion_nose_art_31" )
+
+ file.customizationRefs[ ET_DLC1_SCORCH ] <- []
+ file.customizationRefs[ ET_DLC1_SCORCH ].append( "scorch_skin_07" )
+ file.customizationRefs[ ET_DLC1_SCORCH ].append( "scorch_nose_art_15" )
+ file.customizationRefs[ ET_DLC1_SCORCH ].append( "scorch_nose_art_16" )
+ file.customizationRefs[ ET_DLC1_SCORCH ].append( "scorch_nose_art_17" )
+ file.customizationRefs[ ET_DLC1_SCORCH ].append( "scorch_nose_art_18" )
+ file.customizationRefs[ ET_DLC1_SCORCH ].append( "scorch_nose_art_19" )
+ file.customizationRefs[ ET_DLC3_SCORCH ] <- []
+ file.customizationRefs[ ET_DLC3_SCORCH ].append( "scorch_skin_08" )
+ file.customizationRefs[ ET_DLC3_SCORCH ].append( "scorch_nose_art_20" )
+ file.customizationRefs[ ET_DLC3_SCORCH ].append( "scorch_nose_art_21" )
+ file.customizationRefs[ ET_DLC3_SCORCH ].append( "scorch_nose_art_22" )
+ file.customizationRefs[ ET_DLC3_SCORCH ].append( "scorch_nose_art_23" )
+ file.customizationRefs[ ET_DLC3_SCORCH ].append( "scorch_nose_art_24" )
+ file.customizationRefs[ ET_DLC5_SCORCH ] <- []
+ file.customizationRefs[ ET_DLC5_SCORCH ].append( "scorch_skin_06" )
+ file.customizationRefs[ ET_DLC5_SCORCH ].append( "scorch_nose_art_25" )
+ file.customizationRefs[ ET_DLC5_SCORCH ].append( "scorch_nose_art_26" )
+ file.customizationRefs[ ET_DLC5_SCORCH ].append( "scorch_nose_art_27" )
+ file.customizationRefs[ ET_DLC5_SCORCH ].append( "scorch_nose_art_28" )
+ file.customizationRefs[ ET_DLC5_SCORCH ].append( "scorch_nose_art_29" )
+
+ file.customizationRefs[ ET_DLC1_NORTHSTAR ] <- []
+ file.customizationRefs[ ET_DLC1_NORTHSTAR ].append( "northstar_skin_10" )
+ file.customizationRefs[ ET_DLC1_NORTHSTAR ].append( "northstar_nose_art_18" )
+ file.customizationRefs[ ET_DLC1_NORTHSTAR ].append( "northstar_nose_art_19" )
+ file.customizationRefs[ ET_DLC1_NORTHSTAR ].append( "northstar_nose_art_20" )
+ file.customizationRefs[ ET_DLC1_NORTHSTAR ].append( "northstar_nose_art_21" )
+ file.customizationRefs[ ET_DLC1_NORTHSTAR ].append( "northstar_nose_art_22" )
+ file.customizationRefs[ ET_DLC3_NORTHSTAR ] <- []
+ file.customizationRefs[ ET_DLC3_NORTHSTAR ].append( "northstar_skin_11" )
+ file.customizationRefs[ ET_DLC3_NORTHSTAR ].append( "northstar_nose_art_23" )
+ file.customizationRefs[ ET_DLC3_NORTHSTAR ].append( "northstar_nose_art_24" )
+ file.customizationRefs[ ET_DLC3_NORTHSTAR ].append( "northstar_nose_art_25" )
+ file.customizationRefs[ ET_DLC3_NORTHSTAR ].append( "northstar_nose_art_26" )
+ file.customizationRefs[ ET_DLC3_NORTHSTAR ].append( "northstar_nose_art_27" )
+ file.customizationRefs[ ET_DLC5_NORTHSTAR ] <- []
+ file.customizationRefs[ ET_DLC5_NORTHSTAR ].append( "northstar_skin_06" )
+ file.customizationRefs[ ET_DLC5_NORTHSTAR ].append( "northstar_nose_art_28" )
+ file.customizationRefs[ ET_DLC5_NORTHSTAR ].append( "northstar_nose_art_29" )
+ file.customizationRefs[ ET_DLC5_NORTHSTAR ].append( "northstar_nose_art_30" )
+ file.customizationRefs[ ET_DLC5_NORTHSTAR ].append( "northstar_nose_art_31" )
+ file.customizationRefs[ ET_DLC5_NORTHSTAR ].append( "northstar_nose_art_32" )
+
+ file.customizationRefs[ ET_DLC1_RONIN ] <- []
+ file.customizationRefs[ ET_DLC1_RONIN ].append( "ronin_skin_10" )
+ file.customizationRefs[ ET_DLC1_RONIN ].append( "ronin_nose_art_16" )
+ file.customizationRefs[ ET_DLC1_RONIN ].append( "ronin_nose_art_17" )
+ file.customizationRefs[ ET_DLC1_RONIN ].append( "ronin_nose_art_18" )
+ file.customizationRefs[ ET_DLC1_RONIN ].append( "ronin_nose_art_19" )
+ file.customizationRefs[ ET_DLC1_RONIN ].append( "ronin_nose_art_20" )
+ file.customizationRefs[ ET_DLC3_RONIN ] <- []
+ file.customizationRefs[ ET_DLC3_RONIN ].append( "ronin_skin_11" )
+ file.customizationRefs[ ET_DLC3_RONIN ].append( "ronin_nose_art_21" )
+ file.customizationRefs[ ET_DLC3_RONIN ].append( "ronin_nose_art_22" )
+ file.customizationRefs[ ET_DLC3_RONIN ].append( "ronin_nose_art_23" )
+ file.customizationRefs[ ET_DLC3_RONIN ].append( "ronin_nose_art_24" )
+ file.customizationRefs[ ET_DLC3_RONIN ].append( "ronin_nose_art_25" )
+ file.customizationRefs[ ET_DLC5_RONIN ] <- []
+ file.customizationRefs[ ET_DLC5_RONIN ].append( "ronin_skin_07" )
+ file.customizationRefs[ ET_DLC5_RONIN ].append( "ronin_nose_art_26" )
+ file.customizationRefs[ ET_DLC5_RONIN ].append( "ronin_nose_art_27" )
+ file.customizationRefs[ ET_DLC5_RONIN ].append( "ronin_nose_art_28" )
+ file.customizationRefs[ ET_DLC5_RONIN ].append( "ronin_nose_art_29" )
+ file.customizationRefs[ ET_DLC5_RONIN ].append( "ronin_nose_art_30" )
+
+ file.customizationRefs[ ET_DLC1_TONE ] <- []
+ file.customizationRefs[ ET_DLC1_TONE ].append( "tone_skin_06" )
+ file.customizationRefs[ ET_DLC1_TONE ].append( "tone_nose_art_17" )
+ file.customizationRefs[ ET_DLC1_TONE ].append( "tone_nose_art_18" )
+ file.customizationRefs[ ET_DLC1_TONE ].append( "tone_nose_art_19" )
+ file.customizationRefs[ ET_DLC1_TONE ].append( "tone_nose_art_20" )
+ file.customizationRefs[ ET_DLC1_TONE ].append( "tone_nose_art_21" )
+ file.customizationRefs[ ET_DLC3_TONE ] <- []
+ file.customizationRefs[ ET_DLC3_TONE ].append( "tone_skin_07" )
+ file.customizationRefs[ ET_DLC3_TONE ].append( "tone_nose_art_22" )
+ file.customizationRefs[ ET_DLC3_TONE ].append( "tone_nose_art_23" )
+ file.customizationRefs[ ET_DLC3_TONE ].append( "tone_nose_art_24" )
+ file.customizationRefs[ ET_DLC3_TONE ].append( "tone_nose_art_25" )
+ file.customizationRefs[ ET_DLC3_TONE ].append( "tone_nose_art_26" )
+ file.customizationRefs[ ET_DLC5_TONE ] <- []
+ file.customizationRefs[ ET_DLC5_TONE ].append( "tone_skin_08" )
+ file.customizationRefs[ ET_DLC5_TONE ].append( "tone_nose_art_27" )
+ file.customizationRefs[ ET_DLC5_TONE ].append( "tone_nose_art_28" )
+ file.customizationRefs[ ET_DLC5_TONE ].append( "tone_nose_art_29" )
+ file.customizationRefs[ ET_DLC5_TONE ].append( "tone_nose_art_30" )
+ file.customizationRefs[ ET_DLC5_TONE ].append( "tone_nose_art_31" )
+
+ file.customizationRefs[ ET_DLC1_LEGION ] <- []
+ file.customizationRefs[ ET_DLC1_LEGION ].append( "legion_skin_07" )
+ file.customizationRefs[ ET_DLC1_LEGION ].append( "legion_nose_art_17" )
+ file.customizationRefs[ ET_DLC1_LEGION ].append( "legion_nose_art_18" )
+ file.customizationRefs[ ET_DLC1_LEGION ].append( "legion_nose_art_19" )
+ file.customizationRefs[ ET_DLC1_LEGION ].append( "legion_nose_art_20" )
+ file.customizationRefs[ ET_DLC1_LEGION ].append( "legion_nose_art_21" )
+ file.customizationRefs[ ET_DLC3_LEGION ] <- []
+ file.customizationRefs[ ET_DLC3_LEGION ].append( "legion_skin_08" )
+ file.customizationRefs[ ET_DLC3_LEGION ].append( "legion_nose_art_22" )
+ file.customizationRefs[ ET_DLC3_LEGION ].append( "legion_nose_art_23" )
+ file.customizationRefs[ ET_DLC3_LEGION ].append( "legion_nose_art_24" )
+ file.customizationRefs[ ET_DLC3_LEGION ].append( "legion_nose_art_25" )
+ file.customizationRefs[ ET_DLC3_LEGION ].append( "legion_nose_art_26" )
+ file.customizationRefs[ ET_DLC5_LEGION ] <- []
+ file.customizationRefs[ ET_DLC5_LEGION ].append( "legion_skin_09" )
+ file.customizationRefs[ ET_DLC5_LEGION ].append( "legion_nose_art_27" )
+ file.customizationRefs[ ET_DLC5_LEGION ].append( "legion_nose_art_28" )
+ file.customizationRefs[ ET_DLC5_LEGION ].append( "legion_nose_art_29" )
+ file.customizationRefs[ ET_DLC5_LEGION ].append( "legion_nose_art_30" )
+ file.customizationRefs[ ET_DLC5_LEGION ].append( "legion_nose_art_31" )
+
+ file.patchRefs[ ET_DLC1_CALLSIGN ] <- []
+ file.patchRefs[ ET_DLC3_CALLSIGN ] <- []
+ file.patchRefs[ ET_DLC5_CALLSIGN ] <- []
+
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_64" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_aces" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_alien" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_apex" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_ares" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_controller" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_drone" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_heartbreaker" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_hexes" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_kodai" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_lastimosa" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_lawai" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_mcor" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_phoenix" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_pilot" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_robot" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_sentry" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_super_spectre" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_vinson" )
+ file.patchRefs[ ET_DLC1_CALLSIGN ].append( "gc_icon_wonyeon" )
+
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_balance" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_boot" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_bt_eye" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_buzzsaw" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_crossed_lighting" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_flying_bullet" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_hammer2" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_keyboard" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_lightbulb" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_narwhal" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_peace" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_pilot2" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_robot_eye" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_srs" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_starline" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_taco" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_thumbdown" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_thumbup" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_treble" )
+ file.patchRefs[ ET_DLC3_CALLSIGN ].append( "gc_icon_vanguard" )
+
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_monarch_dlc5" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_militia" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_militia_alt" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_imc" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_hammond" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_tri_chevron" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_pilot_circle" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_x" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_nessie" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_spicy" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_crown" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_pawn" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_excite" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_duck" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_sock" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_rabbit" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_peanut" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_clock" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_shamrock" )
+ file.patchRefs[ ET_DLC5_CALLSIGN ].append( "gc_icon_trident" )
+
+ file.bannerRefs[ ET_DLC1_CALLSIGN ] <- []
+ file.bannerRefs[ ET_DLC3_CALLSIGN ] <- []
+ file.bannerRefs[ ET_DLC5_CALLSIGN ] <- []
+
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_106_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_107_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_108_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_109_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_110_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_111_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_112_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_113_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_114_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_115_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_116_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_117_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_118_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_119_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_120_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_121_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_122_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_123_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_124_col" )
+ file.bannerRefs[ ET_DLC1_CALLSIGN ].append( "callsign_125_col" )
+
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_143_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_144_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_145_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_146_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_147_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_148_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_149_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_150_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_151_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_152_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_153_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_154_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_155_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_156_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_157_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_158_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_159_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_160_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_161_col" )
+ file.bannerRefs[ ET_DLC3_CALLSIGN ].append( "callsign_162_col" )
+
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_166_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_167_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_168_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_169_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_170_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_171_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_172_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_173_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_174_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_175_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_176_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_177_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_178_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_179_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_180_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_181_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_182_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_183_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_184_col" )
+ file.bannerRefs[ ET_DLC5_CALLSIGN ].append( "callsign_185_col" )
+
+ file.camoRefs[ ET_DLC1_CAMO ] <- []
+ file.camoRefs[ ET_DLC3_CAMO ] <- []
+ file.camoRefs[ ET_DLC5_CAMO ] <- []
+
+ for ( int i = 101; i <= 120; i++ )
+ {
+ AddCamoRef( ET_DLC1_CAMO, i )
+ }
+
+ for ( int i = 121; i <= 140; i++ )
+ {
+ AddCamoRef( ET_DLC3_CAMO, i )
+ }
+
+ // You did it reddit!
+ int numRefs = file.camoRefs[ ET_DLC3_CAMO ].len()
+ CamoRef tempRef = file.camoRefs[ ET_DLC3_CAMO ][numRefs - 1]
+ file.camoRefs[ ET_DLC3_CAMO ][numRefs - 1] = file.camoRefs[ ET_DLC3_CAMO ][numRefs - 2]
+ file.camoRefs[ ET_DLC3_CAMO ][numRefs - 2] = tempRef
+
+ for ( int i = 141; i <= 160; i++ )
+ {
+ AddCamoRef( ET_DLC5_CAMO, i )
+ }
+
+ file.limitedEditionFDRefData[ ET_DLC7_ION_WARPAINT ] <- []
+ file.limitedEditionFDRefData[ ET_DLC7_ION_WARPAINT ].append( CreateRefData( "ion_skin_fd", "ion" ) )
+ file.limitedEditionFDRefData[ ET_DLC7_ION_WARPAINT ].append( CreateRefData( "callsign_fd_ion_dynamic" ) )
+
+ file.limitedEditionFDRefData[ ET_DLC7_SCORCH_WARPAINT ] <- []
+ file.limitedEditionFDRefData[ ET_DLC7_SCORCH_WARPAINT ].append( CreateRefData( "scorch_skin_fd", "scorch" ) )
+ file.limitedEditionFDRefData[ ET_DLC7_SCORCH_WARPAINT ].append( CreateRefData( "callsign_fd_scorch_dynamic" ) )
+
+ file.limitedEditionFDRefData[ ET_DLC7_NORTHSTAR_WARPAINT ] <- []
+ file.limitedEditionFDRefData[ ET_DLC7_NORTHSTAR_WARPAINT ].append( CreateRefData( "northstar_skin_fd", "northstar" ) )
+ file.limitedEditionFDRefData[ ET_DLC7_NORTHSTAR_WARPAINT ].append( CreateRefData( "callsign_fd_northstar_dynamic" ) )
+
+ file.limitedEditionFDRefData[ ET_DLC7_RONIN_WARPAINT ] <- []
+ file.limitedEditionFDRefData[ ET_DLC7_RONIN_WARPAINT ].append( CreateRefData( "ronin_skin_fd", "ronin" ) )
+ file.limitedEditionFDRefData[ ET_DLC7_RONIN_WARPAINT ].append( CreateRefData( "callsign_fd_ronin_dynamic" ) )
+
+ file.limitedEditionFDRefData[ ET_DLC7_TONE_WARPAINT ] <- []
+ file.limitedEditionFDRefData[ ET_DLC7_TONE_WARPAINT ].append( CreateRefData( "tone_skin_fd", "tone" ) )
+ file.limitedEditionFDRefData[ ET_DLC7_TONE_WARPAINT ].append( CreateRefData( "callsign_fd_tone_dynamic" ) )
+
+ file.limitedEditionFDRefData[ ET_DLC7_LEGION_WARPAINT ] <- []
+ file.limitedEditionFDRefData[ ET_DLC7_LEGION_WARPAINT ].append( CreateRefData( "legion_skin_fd", "legion" ) )
+ file.limitedEditionFDRefData[ ET_DLC7_LEGION_WARPAINT ].append( CreateRefData( "callsign_fd_legion_dynamic" ) )
+
+ file.limitedEditionFDRefData[ ET_DLC7_MONARCH_WARPAINT ] <- []
+ file.limitedEditionFDRefData[ ET_DLC7_MONARCH_WARPAINT ].append( CreateRefData( "monarch_skin_fd", "vanguard" ) )
+ file.limitedEditionFDRefData[ ET_DLC7_MONARCH_WARPAINT ].append( CreateRefData( "callsign_fd_monarch_dynamic" ) )
+}
+
+RefData function CreateRefData( string ref, string parentRef = "" )
+{
+ RefData data
+ data.ref = ref
+ data.parentRef = parentRef
+
+ return data
+}
+
+array<string> function Store_GetCustomizationRefs( int entitlementId )
+{
+ return file.customizationRefs[ entitlementId ]
+}
+
+array<string> function Store_GetPatchRefs( int entitlementId )
+{
+ return file.patchRefs[ entitlementId ]
+}
+
+array<string> function Store_GetBannerRefs( int entitlementId )
+{
+ return file.bannerRefs[ entitlementId ]
+}
+
+array<CamoRef> function Store_GetCamoRefs( int entitlementId )
+{
+ return file.camoRefs[ entitlementId ]
+}
+
+void function AddCamoRef( int entitlementId, int index )
+{
+ CamoRef cref
+ cref.ref = "camo_skin" + index
+ cref.pilotRef = "pilot_camo_skin" + index
+ cref.titanRef = "titan_camo_skin" + index
+ file.camoRefs[ entitlementId ].append( cref )
+}
+
+#if SERVER
+bool function ClientCommand_SetHasSeenStore( entity player, array<string> args )
+{
+ player.SetPersistentVar( "hasSeenStore", true )
+
+ return true
+}
+
+// TODO: refParam is problematic, because it assumes an entitlement maps to a single ref which is not the case for limited edition frontier defense entitlements
+void function StoreUpdatePIN( entity player, int entitlementId, string refParam )
+{
+ printt( "StoreUpdatePIN", entitlementId, refParam)
+
+ switch ( entitlementId )
+ {
+ case ET_DLC1_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc1_bundle", 0 )
+ break
+
+ case ET_DLC3_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc3_bundle", 0 )
+ break
+
+ case ET_DLC5_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc5_bundle", 0 )
+ break
+
+ case ET_PRIME_TITANS_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "sprime_titans_bundle", 0 )
+ break
+
+ case ET_DLC1_PRIME_ION:
+ case ET_DLC1_PRIME_SCORCH:
+ case ET_DLC3_PRIME_NORTHSTAR:
+ case ET_DLC3_PRIME_LEGION:
+ case ET_DLC5_PRIME_TONE:
+ case ET_DLC5_PRIME_RONIN:
+ Assert( refParam != "" )
+ if ( refParam == "" || !ItemDefined( refParam ) )
+ return
+
+ PIN_BuyItemWithRealMoney( player, false, refParam, 0 )
+ break
+
+ case ET_DLC1_ION:
+ case ET_DLC1_SCORCH:
+ case ET_DLC1_NORTHSTAR:
+ case ET_DLC1_RONIN:
+ case ET_DLC1_TONE:
+ case ET_DLC1_LEGION:
+ Assert( refParam != "" )
+ if ( refParam == "" )
+ return
+
+ PIN_BuyItemWithRealMoney( player, false, "customization_" + refParam, 0 )
+ break
+
+ case ET_DLC3_ION:
+ case ET_DLC3_SCORCH:
+ case ET_DLC3_NORTHSTAR:
+ case ET_DLC3_RONIN:
+ case ET_DLC3_TONE:
+ case ET_DLC3_LEGION:
+ Assert( refParam != "" )
+ if ( refParam == "" )
+ return
+
+ PIN_BuyItemWithRealMoney( player, false, "dlc3_customization_" + refParam, 0 )
+ break
+
+ case ET_DLC5_ION:
+ case ET_DLC5_SCORCH:
+ case ET_DLC5_NORTHSTAR:
+ case ET_DLC5_RONIN:
+ case ET_DLC5_TONE:
+ case ET_DLC5_LEGION:
+ Assert( refParam != "" )
+ if ( refParam == "" )
+ return
+
+ PIN_BuyItemWithRealMoney( player, false, "dlc5_customization_" + refParam, 0 )
+ break
+
+ case ET_DLC1_CAMO:
+ PIN_BuyItemWithRealMoney( player, false, "dlc_camos", 0 )
+ break
+
+ case ET_DLC3_CAMO:
+ PIN_BuyItemWithRealMoney( player, false, "dlc3_camos", 0 )
+ break
+
+ case ET_DLC5_CAMO:
+ PIN_BuyItemWithRealMoney( player, false, "dlc5_camos", 0 )
+ break
+
+ case ET_DLC1_CALLSIGN:
+ PIN_BuyItemWithRealMoney( player, false, "dlc_callsigns", 0 )
+ break
+
+ case ET_DLC3_CALLSIGN:
+ PIN_BuyItemWithRealMoney( player, false, "dlc3_callsigns", 0 )
+ break
+
+ case ET_DLC5_CALLSIGN:
+ PIN_BuyItemWithRealMoney( player, false, "dlc5_callsigns", 0 )
+ break
+
+ case ET_DLC7_TITAN_WARPAINT_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc7_frontier_titan_warpaint_bundle", 0 )
+ break
+
+ case ET_DLC7_ION_WARPAINT:
+ case ET_DLC7_SCORCH_WARPAINT:
+ case ET_DLC7_NORTHSTAR_WARPAINT:
+ case ET_DLC7_RONIN_WARPAINT:
+ case ET_DLC7_TONE_WARPAINT:
+ case ET_DLC7_LEGION_WARPAINT:
+ case ET_DLC7_MONARCH_WARPAINT:
+ PIN_BuyItemWithRealMoney( player, false, "dlc7_frontier_titan_warpaint_" + file.limitedEditionFDRefData[ entitlementId ][0].ref, 0 )
+ break
+
+ case ET_DLC7_WEAPON_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc7_weapon_warpaint_bundle", 0 )
+ break
+
+ case ET_DLC7_R201_WARPAINT:
+ case ET_DLC7_G2A5_WARPAINT:
+ case ET_DLC7_FLATLINE_WARPAINT:
+ case ET_DLC7_CAR_WARPAINT:
+ case ET_DLC7_ALTERNATOR_WARPAINT:
+ case ET_DLC7_EVA8_WARPAINT:
+ case ET_DLC7_WINGMAN_WARPAINT:
+ case ET_DLC7_ARCHER_WARPAINT:
+ Assert( refParam != "" )
+ if ( refParam == "" )
+ return
+
+ PIN_BuyItemWithRealMoney( player, false, "dlc7_weapon_warpaint_" + refParam, 0 )
+ break
+
+ case ET_DLC8_WEAPON_WARPAINT_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc8_weapon_warpaint_bundle", 0 )
+ break
+
+ case ET_DLC8_R201_WARPAINT:
+ case ET_DLC8_HEMLOK_WARPAINT:
+ case ET_DLC8_R97_WARPAINT:
+ case ET_DLC8_KRABER_WARPAINT:
+ case ET_DLC8_SPITFIRE_WARPAINT:
+ case ET_DLC8_DEVOTION_WARPAINT:
+ case ET_DLC8_MOZAMBIQUE_WARPAINT:
+ case ET_DLC8_THUNDERBOLT_WARPAINT:
+ Assert( refParam != "" )
+ if ( refParam == "" )
+ return
+
+ PIN_BuyItemWithRealMoney( player, false, "dlc8_weapon_warpaint_" + refParam, 0 )
+ break
+
+ case ET_JUMPSTARTERBUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc10_jump_starter_pack", 0 )
+
+ case ET_DLC9_WEAPON_WARPAINT_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc9_weapon_warpaint_bundle", 0 )
+ break
+
+ case ET_DLC9_LSTAR_WARPAINT:
+ case ET_DLC9_MASTIFF_WARPAINT:
+ case ET_DLC9_SIDEWINDER_WARPAINT:
+ case ET_DLC9_R201_WARPAINT:
+ case ET_DLC9_CAR_WARPAINT:
+ case ET_DLC9_SPITFIRE_WARPAINT:
+ Assert( refParam != "" )
+ if ( refParam == "" )
+ return
+
+ PIN_BuyItemWithRealMoney( player, false, "dlc9_weapon_warpaint_" + refParam, 0 )
+ break
+
+ case ET_DLC10_WEAPON_WARPAINT_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc10_weapon_warpaint_bundle", 0 )
+ break
+
+ case ET_DLC10_R101_WARPAINT:
+ case ET_DLC10_FLATLINE_WARPAINT:
+ case ET_DLC10_VOLT_WARPAINT:
+ case ET_DLC10_ALTERNATOR_WARPAINT:
+ case ET_DLC10_SOFTBALL_WARPAINT:
+ case ET_DLC10_EPG1_WARPAINT:
+ Assert( refParam != "" )
+ if ( refParam == "" )
+ return
+
+ PIN_BuyItemWithRealMoney( player, false, "dlc10_weapon_warpaint_" + refParam, 0 )
+ break
+
+ case ET_DLC11_WEAPON_WARPAINT_BUNDLE:
+ PIN_BuyItemWithRealMoney( player, false, "dlc11_weapon_warpaint_bundle", 0 )
+ break
+
+ case ET_DLC11_DMR_WARPAINT:
+ case ET_DLC11_DOUBLETAKE_WARPAINT:
+ case ET_DLC11_G2A5_WARPAINT:
+ case ET_DLC11_COLDWAR_WARPAINT:
+ case ET_DLC11_R97_WARPAINT:
+ case ET_DLC11_R101_WARPAINT:
+ Assert( refParam != "" )
+ if ( refParam == "" )
+ return
+
+ PIN_BuyItemWithRealMoney( player, false, "dlc11_weapon_warpaint_" + refParam, 0 )
+ break
+ }
+}
+
+bool function ClientCommand_StoreSetNewItemStatus( entity player, array<string> args )
+{
+ // fix crash
+ if( args.len() < 1 )
+ return true
+
+ int entitlementId = int( args[0] )
+ string refParam
+ string parentRefParam
+
+ if ( args.len() >= 2 )
+ refParam = args[1]
+
+ if ( args.len() >= 3 )
+ parentRefParam = args[2]
+
+ StoreUpdatePIN( player, entitlementId, refParam )
+ StoreSetNewItemStatus( player, entitlementId, refParam, parentRefParam )
+
+ return true
+}
+
+bool function StoreSetNewItemStatus( entity player, int entitlementId, string refParam, string parentRefParam )
+{
+ string e = entitlementId == ET_JUMPSTARTERBUNDLE ? "ET_JUMPSTARTERBUNDLE" : string( entitlementId )
+ printt( "!!!!!!!!!!! StoreSetNewItemStatus() running for entitlement:", e )
+
+ switch ( entitlementId )
+ {
+ case 3:
+ // Prime Titans
+ SetItemNewStatus( player, "ion_prime", "", true )
+ SetItemNewStatus( player, "scorch_prime", "", true )
+
+ // Customization Packs
+ table< string, array<string> > dlc1BundleCustomizationRefs
+ dlc1BundleCustomizationRefs[ "ion" ] <- file.customizationRefs[ ET_DLC1_ION ]
+ dlc1BundleCustomizationRefs[ "tone" ] <- file.customizationRefs[ ET_DLC1_TONE ]
+ dlc1BundleCustomizationRefs[ "scorch" ] <- file.customizationRefs[ ET_DLC1_SCORCH ]
+ dlc1BundleCustomizationRefs[ "legion" ] <- file.customizationRefs[ ET_DLC1_LEGION ]
+ dlc1BundleCustomizationRefs[ "ronin" ] <- file.customizationRefs[ ET_DLC1_RONIN ]
+ dlc1BundleCustomizationRefs[ "northstar" ] <- file.customizationRefs[ ET_DLC1_NORTHSTAR ]
+
+ foreach ( parentRef, childRefs in dlc1BundleCustomizationRefs )
+ {
+ foreach ( ref in childRefs )
+ {
+ if ( !SubitemDefined( parentRef, ref ) )
+ return false
+
+ SetItemNewStatus( player, ref, parentRef, true )
+ }
+ }
+
+ // Callsigns
+ foreach ( ref in file.patchRefs[ ET_DLC1_CALLSIGN ] )
+ {
+ SetItemNewStatus( player, ref, "", true )
+ }
+
+ break
+
+ case ET_DLC3_BUNDLE:
+ // Prime Titans
+ SetItemNewStatus( player, "northstar_prime", "", true )
+ SetItemNewStatus( player, "legion_prime", "", true )
+
+ // Customization Packs
+ table< string, array<string> > dlc3BundleCustomizationRefs
+ dlc3BundleCustomizationRefs[ "ion" ] <- file.customizationRefs[ ET_DLC3_ION ]
+ dlc3BundleCustomizationRefs[ "tone" ] <- file.customizationRefs[ ET_DLC3_TONE ]
+ dlc3BundleCustomizationRefs[ "scorch" ] <- file.customizationRefs[ ET_DLC3_SCORCH ]
+ dlc3BundleCustomizationRefs[ "legion" ] <- file.customizationRefs[ ET_DLC3_LEGION ]
+ dlc3BundleCustomizationRefs[ "ronin" ] <- file.customizationRefs[ ET_DLC3_RONIN ]
+ dlc3BundleCustomizationRefs[ "northstar" ] <- file.customizationRefs[ ET_DLC3_NORTHSTAR ]
+
+ foreach ( parentRef, childRefs in dlc3BundleCustomizationRefs )
+ {
+ foreach ( ref in childRefs )
+ {
+ if ( !SubitemDefined( parentRef, ref ) )
+ return false
+
+ SetItemNewStatus( player, ref, parentRef, true )
+ }
+ }
+
+ // Callsigns
+ foreach ( ref in file.patchRefs[ ET_DLC3_CALLSIGN ] )
+ {
+ SetItemNewStatus( player, ref, "", true )
+ }
+
+ break
+
+ case ET_DLC5_BUNDLE:
+ // Prime Titans
+ SetItemNewStatus( player, "ronin_prime", "", true )
+ SetItemNewStatus( player, "tone_prime", "", true )
+
+ // Customization Packs
+ table< string, array<string> > dlc5BundleCustomizationRefs
+ dlc5BundleCustomizationRefs[ "ion" ] <- file.customizationRefs[ ET_DLC5_ION ]
+ dlc5BundleCustomizationRefs[ "tone" ] <- file.customizationRefs[ ET_DLC5_TONE ]
+ dlc5BundleCustomizationRefs[ "scorch" ] <- file.customizationRefs[ ET_DLC5_SCORCH ]
+ dlc5BundleCustomizationRefs[ "legion" ] <- file.customizationRefs[ ET_DLC5_LEGION ]
+ dlc5BundleCustomizationRefs[ "ronin" ] <- file.customizationRefs[ ET_DLC5_RONIN ]
+ dlc5BundleCustomizationRefs[ "northstar" ] <- file.customizationRefs[ ET_DLC5_NORTHSTAR ]
+
+ foreach ( parentRef, childRefs in dlc5BundleCustomizationRefs )
+ {
+ foreach ( ref in childRefs )
+ {
+ if ( !SubitemDefined( parentRef, ref ) )
+ return false
+
+ SetItemNewStatus( player, ref, parentRef, true )
+ }
+ }
+
+ // Callsigns
+ foreach ( ref in file.patchRefs[ ET_DLC5_CALLSIGN ] )
+ {
+ SetItemNewStatus( player, ref, "", true )
+ }
+
+ break
+
+ case ET_PRIME_TITANS_BUNDLE:
+ SetItemNewStatus( player, "ion_prime", "", true )
+ SetItemNewStatus( player, "scorch_prime", "", true )
+ SetItemNewStatus( player, "northstar_prime", "", true )
+ SetItemNewStatus( player, "legion_prime", "", true )
+ SetItemNewStatus( player, "ronin_prime", "", true )
+ SetItemNewStatus( player, "tone_prime", "", true )
+ break
+
+ case ET_DLC1_PRIME_ION:
+ case ET_DLC1_PRIME_SCORCH:
+ case ET_DLC3_PRIME_NORTHSTAR:
+ case ET_DLC3_PRIME_LEGION:
+ case ET_DLC5_PRIME_TONE:
+ case ET_DLC5_PRIME_RONIN:
+ Assert( refParam != "" )
+ if ( refParam == "" || !ItemDefined( refParam ) )
+ return false
+
+ SetItemNewStatus( player, refParam, "", true )
+ break
+
+ case ET_DLC1_ION:
+ case ET_DLC3_ION:
+ case ET_DLC5_ION:
+ case ET_DLC1_SCORCH:
+ case ET_DLC3_SCORCH:
+ case ET_DLC5_SCORCH:
+ case ET_DLC1_NORTHSTAR:
+ case ET_DLC3_NORTHSTAR:
+ case ET_DLC5_NORTHSTAR:
+ case ET_DLC1_RONIN:
+ case ET_DLC3_RONIN:
+ case ET_DLC5_RONIN:
+ case ET_DLC1_TONE:
+ case ET_DLC3_TONE:
+ case ET_DLC5_TONE:
+ case ET_DLC1_LEGION:
+ case ET_DLC3_LEGION:
+ case ET_DLC5_LEGION:
+ Assert( refParam != "" )
+ if ( refParam == "" )
+ return false
+
+ foreach ( ref in file.customizationRefs[ entitlementId ] )
+ {
+ if ( !SubitemDefined( refParam, ref ) )
+ return false
+
+ SetItemNewStatus( player, ref, refParam, true )
+ }
+ break
+
+ case ET_DLC1_CAMO:
+ case ET_DLC3_CAMO:
+ case ET_DLC5_CAMO:
+ // Not implemented, way too many camos would show as new
+ break
+
+ case ET_DLC1_CALLSIGN:
+ case ET_DLC3_CALLSIGN:
+ case ET_DLC5_CALLSIGN:
+ foreach ( ref in file.patchRefs[ entitlementId ] )
+ {
+ SetItemNewStatus( player, ref, "", true )
+ }
+
+ foreach ( ref in file.bannerRefs[ entitlementId ] )
+ {
+ SetItemNewStatus( player, ref, "", true )
+ }
+ break
+
+ case ET_DLC7_TITAN_WARPAINT_BUNDLE:
+ array<int> childEntitlements = GetChildEntitlements( entitlementId )
+
+ foreach ( entitlement in childEntitlements )
+ {
+ foreach ( data in file.limitedEditionFDRefData[ entitlement ] )
+ SetItemNewStatus( player, data.ref, data.parentRef, true )
+ }
+ break
+
+ case ET_DLC7_ION_WARPAINT:
+ case ET_DLC7_SCORCH_WARPAINT:
+ case ET_DLC7_NORTHSTAR_WARPAINT:
+ case ET_DLC7_RONIN_WARPAINT:
+ case ET_DLC7_TONE_WARPAINT:
+ case ET_DLC7_LEGION_WARPAINT:
+ case ET_DLC7_MONARCH_WARPAINT:
+ foreach ( data in file.limitedEditionFDRefData[ entitlementId ] )
+ SetItemNewStatus( player, data.ref, data.parentRef, true )
+ break
+
+ case ET_DLC7_R201_WARPAINT:
+ case ET_DLC7_G2A5_WARPAINT:
+ case ET_DLC7_FLATLINE_WARPAINT:
+ case ET_DLC7_CAR_WARPAINT:
+ case ET_DLC7_ALTERNATOR_WARPAINT:
+ case ET_DLC7_EVA8_WARPAINT:
+ case ET_DLC7_WINGMAN_WARPAINT:
+ case ET_DLC7_ARCHER_WARPAINT:
+ case ET_DLC8_R201_WARPAINT:
+ case ET_DLC8_HEMLOK_WARPAINT:
+ case ET_DLC8_R97_WARPAINT:
+ case ET_DLC8_KRABER_WARPAINT:
+ case ET_DLC8_SPITFIRE_WARPAINT:
+ case ET_DLC8_DEVOTION_WARPAINT:
+ case ET_DLC8_MOZAMBIQUE_WARPAINT:
+ case ET_DLC8_THUNDERBOLT_WARPAINT:
+ case ET_DLC9_LSTAR_WARPAINT:
+ case ET_DLC9_MASTIFF_WARPAINT:
+ case ET_DLC9_SIDEWINDER_WARPAINT:
+ case ET_DLC9_R201_WARPAINT:
+ case ET_DLC9_CAR_WARPAINT:
+ case ET_DLC9_SPITFIRE_WARPAINT:
+ case ET_DLC10_R101_WARPAINT:
+ case ET_DLC10_FLATLINE_WARPAINT:
+ case ET_DLC10_VOLT_WARPAINT:
+ case ET_DLC10_ALTERNATOR_WARPAINT:
+ case ET_DLC10_SOFTBALL_WARPAINT:
+ case ET_DLC10_EPG1_WARPAINT:
+ case ET_DLC11_DMR_WARPAINT:
+ case ET_DLC11_DOUBLETAKE_WARPAINT:
+ case ET_DLC11_G2A5_WARPAINT:
+ case ET_DLC11_COLDWAR_WARPAINT:
+ case ET_DLC11_R97_WARPAINT:
+ case ET_DLC11_R101_WARPAINT:
+ Assert( refParam != "" )
+ if ( refParam == "" || !ItemDefined( refParam ) )
+ return false
+
+ Assert( parentRefParam != "" )
+ if ( parentRefParam == "" || !ItemDefined( parentRefParam ) )
+ return false
+
+ SetItemNewStatus( player, refParam, parentRefParam, true )
+ break
+
+ case ET_DLC7_WEAPON_BUNDLE:
+ SetItemNewStatus( player, "skin_rspn101_wasteland", "mp_weapon_rspn101", true )
+ SetItemNewStatus( player, "skin_g2_masterwork", "mp_weapon_g2", true )
+ SetItemNewStatus( player, "skin_vinson_blue_fade", "mp_weapon_vinson", true )
+ SetItemNewStatus( player, "skin_car_crimson_fury", "mp_weapon_car", true )
+ SetItemNewStatus( player, "skin_alternator_patriot", "mp_weapon_alternator_smg", true )
+ SetItemNewStatus( player, "skin_shotgun_badlands", "mp_weapon_shotgun", true )
+ SetItemNewStatus( player, "skin_wingman_aqua_fade", "mp_weapon_wingman", true )
+ SetItemNewStatus( player, "skin_rocket_launcher_psych_spectre", "mp_weapon_rocket_launcher", true )
+ break
+
+ case ET_DLC8_WEAPON_WARPAINT_BUNDLE:
+ SetItemNewStatus( player, "skin_rspn101_patriot", "mp_weapon_rspn101", true )
+ SetItemNewStatus( player, "skin_hemlok_mochi", "mp_weapon_hemlok", true )
+ SetItemNewStatus( player, "skin_r97_purple_fade", "mp_weapon_r97", true )
+ SetItemNewStatus( player, "skin_kraber_masterwork", "mp_weapon_sniper", true )
+ SetItemNewStatus( player, "skin_spitfire_lead_farmer", "mp_weapon_lmg", true )
+ SetItemNewStatus( player, "skin_devotion_rspn_customs", "mp_weapon_esaw", true )
+ SetItemNewStatus( player, "skin_mozambique_crimson_fury", "mp_weapon_shotgun_pistol", true )
+ SetItemNewStatus( player, "skin_thunderbolt_8bit", "mp_weapon_arc_launcher", true )
+ break
+
+ case ET_DLC9_WEAPON_WARPAINT_BUNDLE:
+ SetItemNewStatus( player, "skin_lstar_heatsink", "mp_weapon_lstar", true )
+ SetItemNewStatus( player, "skin_mastiff_crimson_fury", "mp_weapon_mastiff", true )
+ SetItemNewStatus( player, "skin_sidewinder_masterwork", "mp_weapon_smr", true )
+ SetItemNewStatus( player, "skin_rspn101_halloween", "mp_weapon_rspn101", true )
+ SetItemNewStatus( player, "skin_car_halloween", "mp_weapon_car", true )
+ SetItemNewStatus( player, "skin_spitfire_halloween", "mp_weapon_lmg", true )
+ break
+
+ case ET_DLC10_WEAPON_WARPAINT_BUNDLE:
+ SetItemNewStatus( player, "skin_rspn101_og_blue_fade", "mp_weapon_rspn101_og", true )
+ SetItemNewStatus( player, "skin_vinson_badlands", "mp_weapon_vinson", true )
+ SetItemNewStatus( player, "skin_volt_heatsink", "mp_weapon_hemlok_smg", true )
+ SetItemNewStatus( player, "skin_alternator_headhunter", "mp_weapon_alternator_smg", true )
+ SetItemNewStatus( player, "skin_softball_masterwork", "mp_weapon_softball", true )
+ SetItemNewStatus( player, "skin_epg_mrvn", "mp_weapon_epg", true )
+ break
+
+ case ET_DLC11_WEAPON_WARPAINT_BUNDLE:
+ SetItemNewStatus( player, "skin_dmr_phantom", "mp_weapon_dmr", true )
+ SetItemNewStatus( player, "skin_doubletake_masterwork", "mp_weapon_doubletake", true )
+ SetItemNewStatus( player, "skin_g2_purple_fade", "mp_weapon_g2", true )
+ SetItemNewStatus( player, "skin_coldwar_heatsink", "mp_weapon_pulse_lmg", true )
+ SetItemNewStatus( player, "skin_r97_sky", "mp_weapon_r97", true )
+ SetItemNewStatus( player, "skin_rspn101_crimson_fury", "mp_weapon_rspn101", true )
+ break
+
+ case ET_JUMPSTARTERBUNDLE:
+ UnlockUltimateEdition( player )
+ break
+ }
+
+ return true
+}
+#endif
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_utility_all.gnut b/Northstar.CustomServers/mod/scripts/vscripts/sh_utility_all.gnut
index 7f356a18..2ca051cf 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/sh_utility_all.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_utility_all.gnut
@@ -348,6 +348,14 @@ string function GetMapDisplayDesc( string mapname )
return "#" + mapname + "_CLASSIC_DESC"
}
+/// Sends a string message to player
+/// * `baseString` - The input string to search through
+/// * `searchString` - Find this substring...
+/// * `replaceString` - ...and replace with this substring
+/// * `replaceAll` - Whether to replace all occurences or just the first
+/// * `caseInsensitive` - Whether to consider casing (upper/lower)
+///
+/// Returns the updated string
string function StringReplace( string baseString, string searchString, string replaceString, bool replaceAll = false, bool caseInsensitive = false )
{
bool loopedOnce = false
@@ -362,7 +370,7 @@ string function StringReplace( string baseString, string searchString, string re
source = part1 + replaceString + part2
loopedOnce = true
- findResult = source.find( searchString )
+ findResult = source.find( searchString, findResult + replaceString.len() )
}
return baseString
@@ -386,8 +394,12 @@ float function RoundToNearestMultiplier( float value, float multiplier )
return value
}
-function DevEverythingUnlocked()
+function DevEverythingUnlocked( entity player = null )
{
+ // check if player has opted into progression or not
+ if ( player != null && ProgressionEnabledForPlayer( player ) )
+ return false
+
return EverythingUnlockedConVarEnabled()
}
@@ -1528,7 +1540,23 @@ array<string> function GetAvailableTitanRefs( entity player )
return availableTitanRefs
}
+/// Gets the highest Titan FD level and stores it in the corresponding persistent var.
+/// * `player` - The player entity to perform the action on
#if MP
+void function RecalculateHighestTitanFDLevel( entity player )
+{
+ int enumCount = PersistenceGetEnumCount( "titanClasses" )
+ int highestAegis = 0
+ for ( int i = 0; i < enumCount; i++ )
+ {
+ string enumName = PersistenceGetEnumItemNameForIndex( "titanClasses", i )
+ int aegisLevel = FD_TitanGetLevelForXP( enumName, FD_TitanGetXP( player, enumName ) )
+ if ( highestAegis < aegisLevel )
+ highestAegis = aegisLevel
+ }
+ player.SetPersistentVar( "fdStats.highestTitanFDLevel", highestAegis )
+}
+
string function GetTitanRefForLoadoutIndex( entity player, int loadoutIndex )
{
TitanLoadoutDef loadout = GetTitanLoadoutFromPersistentData( player, loadoutIndex )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans.gnut b/Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans.gnut
index c9d986bc..57361362 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans.gnut
@@ -21,7 +21,6 @@ global function ReplacementTitan
global function TryAnnounceTitanfallWarningToEnemyTeam
global function GetTitanForPlayer
-
global function ShouldSetTitanRespawnTimer
global function PauseTitanTimers
@@ -33,6 +32,7 @@ global function SetReplacementTitanGamemodeRules
global function SetRequestTitanGamemodeRules
global function CreateTitanForPlayerAndHotdrop
+global function SetRequestTitanAllowedCallback
struct {
array<int> ETATimeThresholds = [ 120, 60, 30, 15 ]
@@ -53,6 +53,8 @@ struct {
bool functionref( entity ) ReplacementTitanGamemodeRules
bool functionref( entity, vector ) RequestTitanGamemodeRules
+ bool functionref( entity player, array< string > args ) RequestTitanAllowedCallback
+
} file
const nagInterval = 40
@@ -87,6 +89,10 @@ function ReplacementTitans_Init()
FlagInit( "LevelHasRoof" )
}
+void function SetRequestTitanAllowedCallback( bool functionref( entity player, array<string> args ) RequestTitanAllowedCallback )
+{
+ file.RequestTitanAllowedCallback = RequestTitanAllowedCallback
+}
void function ReplacementTitan_InitPlayer( entity player )
{
@@ -424,6 +430,7 @@ function TryETATitanReadyAnnouncement( entity player )
TryReplacementTitanReadyAnnouncement( player )
return
}
+
//This entire loop is probably too complicated now for what it's doing. Simplify next game!
//Loop might be pretty hard to read, a particular iteration of the loop is written in comments below
@@ -524,6 +531,9 @@ function req()
bool function ClientCommand_RequestTitan( entity player, array<string> args )
{
+ if( file.RequestTitanAllowedCallback != null && !file.RequestTitanAllowedCallback( player, args ) )
+ return true
+
ReplacementTitan( player ) //Separate function because other functions will call ReplacementTitan
return true
}
@@ -877,6 +887,20 @@ void function CreateTitanForPlayerAndHotdrop( entity player, Point spawnPoint, T
player.Signal( "titan_impact" )
thread TitanNPC_WaitForBubbleShield_StartAutoTitanBehavior( titan )
+ thread PlayerEarnMeter_ReplacementTitanThink( player, titan )
+}
+
+void function PlayerEarnMeter_ReplacementTitanThink( entity player, entity titan )
+{
+ player.EndSignal( "OnDestroy" )
+ OnThreadEnd(
+ function(): ( player )
+ {
+ if( IsValid( player ) )
+ PlayerEarnMeter_Reset( player )
+ }
+ )
+ titan.WaitSignal( "OnDestroy" )
}
void function CleanupTitanFallDisablingEntity( entity titanFallDisablingEntity, entity titan )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans_drop.gnut b/Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans_drop.gnut
index 933e9988..6972d5ff 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans_drop.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/titan/_replacement_titans_drop.gnut
@@ -4,6 +4,7 @@ global function HullTraceDropPoint
global function DebugTitanfall
global function TitanFindDropNodes
global function TitanHulldropSpawnpoint
+global function SetRecalculateTitanReplacementPointCallback
global const TITANDROP_LOS_DIST = 2000 // 2D distance at which we do the line of sight check to see where the player wants to call in the titan
global const TITANDROP_MIN_FOV = 10
@@ -19,8 +20,15 @@ global const TITANDROP_FALLBACK_DIST = 150 // if the ground search hits, we go t
struct
{
int replacementSpawnpointsID
+ Point functionref(Point originalPoint, entity player) recalculateTitanReplacementPointCallback
} file
+
+void function SetRecalculateTitanReplacementPointCallback(Point functionref(Point originalPoint, entity player) recalculateTitanReplacementPointCallback)
+{
+ file.recalculateTitanReplacementPointCallback = recalculateTitanReplacementPointCallback
+}
+
void function ReplacementTitansDrop_Init()
{
AddSpawnCallback( "info_spawnpoint_titan", AddDroppoint )
@@ -117,7 +125,10 @@ Point function GetTitanReplacementPoint( entity player, bool forDebugging = fals
vector playerEyeAngles = player.EyeAngles()
vector playerOrg = player.GetOrigin()
- return CalculateTitanReplacementPoint( playerOrg, playerEyePos, playerEyeAngles, forDebugging )
+ Point tempPoint = CalculateTitanReplacementPoint( playerOrg, playerEyePos, playerEyeAngles, forDebugging)
+ if( file.recalculateTitanReplacementPointCallback != null )
+ tempPoint = file.recalculateTitanReplacementPointCallback( tempPoint, player )
+ return tempPoint
}
Point function CalculateTitanReplacementPoint( vector playerOrg, vector playerEyePos, vector playerEyeAngles, bool forDebugging = false )
@@ -165,6 +176,7 @@ Point function CalculateTitanReplacementPoint( vector playerOrg, vector playerEy
Point point
point.origin = dropPoint
point.angles = yawAngles
+
return point
}
}
@@ -215,7 +227,8 @@ Point function CalculateTitanReplacementPoint( vector playerOrg, vector playerEy
Point point
point.origin = nodeOrigin
point.angles = Vector( 0, yaw, 0 )
- return point
+
+ return point
}
vector function GetPathNodeSearchPosWithLookPos( vector playerOrg, vector playerEyePos, vector playerEyeForward, vector playerLookPos, bool debug )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_health.gnut b/Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_health.gnut
index d600cb03..396d5624 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_health.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_health.gnut
@@ -1010,7 +1010,7 @@ void function AddCreditToTitanCoreBuilder( entity titan, float credit )
if ( IsValid( bossPlayer ) && !coreWasAvailable && IsCoreChargeAvailable( bossPlayer, soul ) )
{
- AddPlayerScore( bossPlayer, "TitanCoreEarned" )
+ AddPlayerScore( bossPlayer, "TitanCoreEarned", bossPlayer ) // this will show the "Core Earned" callsign event
#if MP
UpdateTitanCoreEarnedStat( bossPlayer, titan )
PIN_PlayerAbilityReady( bossPlayer, "core" )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/titan/class_titan.gnut b/Northstar.CustomServers/mod/scripts/vscripts/titan/class_titan.gnut
index 5f72385e..d0a2d5e4 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/titan/class_titan.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/titan/class_titan.gnut
@@ -68,6 +68,11 @@ bool function ClientCommand_TitanEject( entity player, array<string> args )
if ( !PlayerCanEject( player ) )
return true
+ // check array length before accessing index to avoid oob access
+ // prevents crashing a server by just calling `TitanEject` without arguments
+ if( args.len() < 1 )
+ return true
+
int ejectPressCount = args[ 0 ].tointeger()
if ( ejectPressCount < 3 )
return true
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut
index 4bfeb4f8..847881b5 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/titan_xp.gnut
@@ -1,14 +1,39 @@
global function AddTitanXP
+global function AddFDTitanXP
void function AddTitanXP( entity player, int amount )
{
string titan = GetActiveTitanLoadout( player ).titanClass
int oldLevel = TitanGetLevel( player, titan )
+ int TitanXPMatch = player.GetPersistentVarAsInt( "xp_match[" + XP_TYPE.TITAN_LEVELED + "]" )
// increment xp
player.SetPersistentVar( "titanXP[" + titan + "]", min( TitanGetXP( player, titan ) + amount, TitanGetMaxXP( titan ) ) )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_TitanXPAdded", shTitanXP.titanClasses.find( titan ), TitanGetXP( player, titan ), amount )
// level up notif
if ( TitanGetLevel( player, titan ) != oldLevel )
+ {
Remote_CallFunction_NonReplay( player, "ServerCallback_TitanLeveledUp", shTitanXP.titanClasses.find( titan ), TitanGetGen( player, titan ), TitanGetLevel( player, titan ) )
+ AddPlayerScore( player, "TitanLevelUp" )
+ IncrementPlayerChallengeTitanLeveledUp( player )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.TITAN_LEVELED + "]", TitanXPMatch + 1 )
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForTitanLevels( player, titan, oldLevel, TitanGetLevel( player, titan ) )
+ }
+}
+
+void function AddFDTitanXP( entity player, int fdXPamount )
+{
+ string titanRef = GetActiveTitanLoadout( player ).titanClass
+
+ player.SetPersistentVar( "fdTitanXP[" + titanRef + "]", FD_TitanGetPreviousXP( player, titanRef ) + fdXPamount )
+ int startingLevel = FD_TitanGetLevelForXP( titanRef, FD_TitanGetPreviousXP( player, titanRef ) )
+ int endingLevel = FD_TitanGetLevelForXP( titanRef, FD_TitanGetXP( player, titanRef ) )
+
+ Player_GiveFDUnlockPoints( player, endingLevel - startingLevel )
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForFDTitanLevels( player, titanRef, startingLevel, endingLevel )
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut
index 8e100257..0b0084b3 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/weapon_xp.gnut
@@ -6,14 +6,24 @@ void function AddWeaponXP( entity player, int amount )
entity activeWeapon = player.GetActiveWeapon()
string weaponClassname = activeWeapon.GetWeaponClassName()
int oldLevel = WeaponGetLevel( player, weaponClassname )
+ int WeaponXPMatch = player.GetPersistentVarAsInt( "xp_match[" + XP_TYPE.WEAPON_LEVELED + "]" )
// increment xp
player.SetPersistentVar( GetItemPersistenceStruct( weaponClassname ) + ".weaponXP", min( WeaponGetXP( player, weaponClassname ) + amount, WeaponGetMaxXP( weaponClassname ) ) )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_WeaponXPAdded", shWeaponXP.weaponClassNames.find( weaponClassname ), WeaponGetXP( player, weaponClassname ), amount )
// level up notif
if ( WeaponGetLevel( player, weaponClassname ) != oldLevel )
+ {
Remote_CallFunction_NonReplay( player, "ServerCallback_WeaponLeveledUp", shWeaponXP.weaponClassNames.find( weaponClassname ), WeaponGetGen( player, weaponClassname ), WeaponGetLevel( player, weaponClassname ) )
-
+ AddPlayerScore( player, "WeaponLevelUp" )
+ IncrementPlayerChallengeWeaponLeveledUp( player )
+ player.SetPersistentVar( "xp_match[" + XP_TYPE.WEAPON_LEVELED + "]", WeaponXPMatch + 1 )
+
+ if( ProgressionEnabledForPlayer( player ) )
+ AwardRandomItemsForWeaponLevels( player, weaponClassname, oldLevel, WeaponGetLevel( player, weaponClassname ) )
+ }
+
// proscreen
if ( player == activeWeapon.GetProScreenOwner() )
{