#using scripts\codescripts\struct; #using scripts\shared\callbacks_shared; #using scripts\shared\challenges_shared; #using scripts\shared\drown; #using scripts\shared\scoreevents_shared; #using scripts\shared\system_shared; #using scripts\shared\util_shared; #using scripts\shared\weapons\_weapon_utils; #using scripts\mp\_util; #using scripts\mp\_challenges; #using scripts\mp\gametypes\_globallogic; #using scripts\mp\gametypes\_globallogic_score; #using scripts\mp\gametypes\_loadout; #using scripts\mp\_util; // look for dvar loot_cryptokeyCost // look for dvar loot_cryptokeyCost // should coincide with contracts array size on mp_stats.ddl // contract string table and columns // mp_stats.ddl, PlayerStatsList[ ] // mp_stats.ddl, PlayerStatsList[ ] // mp_stats.ddl // table index values from mp_contractTable.csv // weekly contracts // daily contracts // special contracts #namespace contracts; #precache( "eventstring", "mp_daily_challenge_complete" ); #precache( "eventstring", "mp_weekly_challenge_complete" ); #precache( "eventstring", "mp_special_contract_complete" ); function autoexec __init__sytem__() { system::register("contracts",&__init__,undefined,undefined); } function __init__() { callback::on_start_gametype( &start_gametype ); /# level thread watch_contract_debug(); #/ } function start_gametype() { if ( !isdefined( level.ChallengesCallbacks ) ) { level.ChallengesCallbacks = []; } util::init_player_contract_events(); // must always initialize waittillframeend; if ( can_process_contracts() ) { /# execdevgui( "devgui/mp/mp_devgui_contracts" ); #/ challenges::registerChallengesCallback( "playerKilled",&contract_kills ); challenges::registerChallengesCallback( "gameEnd",&contract_game_ended ); globallogic_score::registerContractWinEvent( &contract_win ); scoreevents::register_hero_ability_kill_event( &on_hero_ability_kill ); scoreevents::register_hero_ability_multikill_event( &on_hero_ability_multikill ); scoreevents::register_hero_weapon_multikill_event( &on_hero_weapon_multikill ); util::register_player_contract_event( "score", &on_player_score, 1 ); util::register_player_contract_event( "killstreak_score", &on_killstreak_score, 2 ); util::register_player_contract_event( "offender_kill", &on_offender_kill ); util::register_player_contract_event( "defender_kill", &on_defender_kill ); util::register_player_contract_event( "headshot", &on_headshot_kill ); util::register_player_contract_event( "killed_hero_ability_enemy", &on_killed_hero_ability_enemy ); util::register_player_contract_event( "killed_hero_weapon_enemy", &on_killed_hero_weapon_enemy ); util::register_player_contract_event( "earned_specialist_ability_medal", &on_hero_ability_medal ); // globallogic::registerOtherLootXPAwards( &award_loot_xp ); // intentionally commented out, will use mp_loot_xp_due instead for now } callback::on_connect( &on_player_connect ); } function on_killed_hero_ability_enemy() { self add_stat( 1014 ); } function on_killed_hero_weapon_enemy() { self add_stat( 1014 ); } function on_player_connect() { player = self; if ( can_process_contracts() ) { player setup_player_contracts(); } } function can_process_contracts() { if ( GetDvarInt( "contracts_enabled_mp", 1 ) == 0 ) // mp contracts kill switch return false; return challenges::canProcessChallenges(); } function setup_player_contracts() { player = self; player.pers["contracts"] = []; // no need to setup active contracts for bots if ( player util::is_bot() ) return; for( slot = 0; slot < 10; slot++ ) { if ( get_contract_stat( slot, "active" ) && !get_contract_stat( slot, "award_given" ) ) { contract_index = get_contract_stat( slot, "index" ); player.pers["contracts"][contract_index] = SpawnStruct(); player.pers["contracts"][contract_index].slot = slot; table_row = TableLookupRowNum( "gamedata/tables/mp/mp_contractTable.csv", 0, contract_index ); player.pers["contracts"][contract_index].table_row = table_row; player.pers["contracts"][contract_index].target_value = int( TableLookupColumnForRow( "gamedata/tables/mp/mp_contractTable.csv", table_row, 2 ) ); player.pers["contracts"][contract_index].calling_card_stat = TableLookupColumnForRow( "gamedata/tables/mp/mp_contractTable.csv", table_row, 7 ); player.pers["contracts"][contract_index].weapon_camo_stat = TableLookupColumnForRow( "gamedata/tables/mp/mp_contractTable.csv", table_row, 8 ); player.pers["contracts"][contract_index].absolute_stat_path = TableLookupColumnForRow( "gamedata/tables/mp/mp_contractTable.csv", table_row, 9 ); } } } /# function watch_contract_debug() { level notify( "watch_contract_debug_singleton" ); level endon( "watch_contract_debug_singleton" ); level endon( "game_ended" ); while( 1 ) { if ( GetDvarInt( "scr_contract_deactivate_all_slots" ) > 0 ) { if ( isdefined( level.players ) ) { new_index = GetDvarInt( "scr_contract_new_test_index", 0 ); foreach( player in level.players ) { if ( !isdefined( player ) ) continue; if ( player util::is_bot() ) continue; for ( slot = 0; slot < 10; slot++ ) { player set_contract_stat( slot, "active", 0 ); } IPrintLn( "All contract slots deactivated for " + player.name ); player setup_player_contracts(); } } SetDvar( "scr_contract_deactivate_all_slots", 0 ); } if ( GetDvarInt( "scr_contract_new_test_index", 0 ) > 0 ) { if ( isdefined( level.players ) ) { new_index = GetDvarInt( "scr_contract_new_test_index", 0 ); foreach( player in level.players ) { if ( !isdefined( player ) ) continue; if ( player util::is_bot() ) continue; test_slot = GetDvarInt( "scr_contract_debug_slot", 10 - 1 ); player set_contract_stat( test_slot, "active", 1 ); player set_contract_stat( test_slot, "index", new_index ); player set_contract_stat( test_slot, "progress", 0 ); player set_contract_stat( test_slot, "award_given", 0 ); player setup_player_contracts(); IPrintLn( "Contract Slot " + test_slot + " table index set to " + new_index + " for " + player.name + ". Progress reset." ); } } SetDvar( "scr_contract_new_test_index", 0 ); } if ( GetDvarInt( "scr_contract_deactivate_debug_slot", 0 ) > 0 ) { if ( isdefined( level.players ) ) { test_slot = GetDvarInt( "scr_contract_debug_slot", 10 - 1 ); IPrintLn( "Contract Debug Off" ); foreach( player in level.players ) { if ( !isdefined( player ) ) continue; if ( player util::is_bot() ) continue; if ( test_slot >= 3 ) { player set_contract_stat( test_slot, "active", 0 ); player setup_player_contracts(); IPrintLn( "Contract Slot " + test_slot + " deactivated for " + player.name ); } else { IPrintLn( "Contract Slot " + test_slot + " left active for " + player.name ); } } } SetDvar( "scr_contract_deactivate_debug_slot", 0 ); } if ( GetDvarInt( "scr_contract_msg_front_end_only", 0 ) > 0 ) { IPrintLn( "Assign Slot menu(s) only available in FrontEnd." ); SetDvar( "scr_contract_msg_front_end_only", 0 ); } if ( GetDvarInt( "scr_contract_msg_debug_on", 0 ) > 0 ) { IPrintLn( "Contracts Debug On. No slots altered." ); SetDvar( "scr_contract_msg_debug_on", 0 ); } wait 0.5; } } #/ function is_contract_active( challenge_index ) { if ( !isPlayer( self ) ) return false; if ( !isdefined( self.pers["contracts"] ) ) return false; if ( !isdefined( self.pers["contracts"][challenge_index] ) ) return false; // sanity check... better than causing an SRE if ( self.pers["contracts"][challenge_index].table_row == -1 ) return false; return true; } function on_hero_ability_kill( ability, victimAbility ) { player = self; if ( !isdefined( player ) || !isplayer( player ) ) return; } function on_hero_ability_medal() { player = self; if ( !isdefined( player ) || !isplayer( player ) ) return; player add_stat( 1013 ); player add_stat( 3 ); } function on_hero_ability_multikill( killcount, ability ) { player = self; if ( !isdefined( player ) || !isplayer( player ) ) return; } function on_hero_weapon_multikill( killcount, weapon ) { player = self; if ( !isdefined( player ) || !isplayer( player ) ) return; } function on_player_score( delta_score ) { self add_stat( 1009, delta_score ); self add_stat( 5, delta_score ); } function on_killstreak_score( delta_score, killstreak_purchased ) { if ( killstreak_purchased ) { self add_stat( 1011, delta_score ); } } function contract_kills( data ) { victim = data.victim; attacker = data.attacker; player = attacker; weapon = data.weapon; time = data.time; if ( !isdefined( weapon ) || ( weapon == level.weaponNone ) ) return; if ( !isdefined( player ) || !isplayer( player ) ) return; player add_stat( 1015 ); player add_stat( 4 ); if ( weapon.isHeroWeapon === true ) { player add_stat( 1012 ); player add_stat( 7 ); player add_stat( 3006 ); } isKillstreak = isdefined( data.eInflictor ) && isdefined( data.eInflictor.killstreakid ); if ( !isKillstreak && isdefined( level.isKillstreakWeapon ) ) { isKillstreakWeapon = [[level.isKillstreakWeapon]]( weapon ); } if ( isKillstreak || ( isKillstreakWeapon === true ) ) { player add_stat( 1010 ); player add_stat( 8 ); } statItemIndex = weapon.statIndex; if ( player isItemPurchased( statItemIndex ) ) { weaponClass = util::getWeaponClass( weapon ); switch ( weaponClass ) // aka group in mp_statsTable { case "weapon_assault": player add_stat( 1019 ); player add_stat( 3001 ); break; case "weapon_smg": player add_stat( 1020 ); player add_stat( 3000 ); break; case "weapon_sniper": player add_stat( 1021 ); player add_stat( 3004 ); break; case "weapon_lmg": player add_stat( 1022 ); player add_stat( 3003 ); break; case "weapon_cqb": player add_stat( 1023 ); player add_stat( 3002 ); break; case "weapon_pistol": player add_stat( 1024 ); break; case "weapon_knife": player add_stat( 3005 ); break; default: break; } total_unlocked = player GetTotalUnlockedWeaponAttachments( weapon ); if ( total_unlocked >= 4 ) { player add_stat( 1025 ); } } } function add_stat( contract_index, delta ) { if ( self is_contract_active( contract_index ) ) { self add_active_stat( contract_index, delta ); } } function add_active_stat( contract_index, delta = 1 ) { slot = self.pers["contracts"][contract_index].slot; target_value = self.pers["contracts"][contract_index].target_value; /# if ( GetDvarInt( "scr_contract_debug_multiplier", 0 ) > 0 ) delta *= GetDvarInt( "scr_contract_debug_multiplier", 1 ); #/ old_progress = get_contract_stat( slot, "progress" ); new_progress = old_progress + delta; if ( new_progress > target_value ) new_progress = target_value; if ( new_progress != old_progress ) self set_contract_stat( slot, "progress", new_progress ); just_completed = false; if ( old_progress < target_value && target_value <= new_progress ) { just_completed = true; // notify contract achieved event = &"mp_weekly_challenge_complete"; display_rewards = false; if ( slot == 2 ) { event = &"mp_daily_challenge_complete"; display_rewards = true; self award_loot_xp_due( award_daily_contract() ); self set_contract_stat( 2, "award_given", 1 ); } else if ( slot == 0 || slot == 1 ) { other_slot = 1; if ( slot == 1 ) { other_slot = 0; } foreach ( c_index,c_data in self.pers["contracts"] ) { if ( c_data.slot == other_slot ) { if ( c_data.target_value <= get_contract_stat( other_slot, "progress" ) ) { display_rewards = true; self award_loot_xp_due( award_weekly_contract() ); self set_contract_stat( 0, "award_given", 1 ); self set_contract_stat( 1, "award_given", 1 ); } break; } } } else if ( slot == 3 ) { event = &"mp_special_contract_complete"; display_rewards = true; absolute_stat_path = self.pers["contracts"][contract_index].absolute_stat_path; if ( absolute_stat_path != "" ) { set_contract_award_stat_from_path( absolute_stat_path, true ); } calling_card_stat = self.pers["contracts"][contract_index].calling_card_stat; if ( calling_card_stat != "" ) { set_contract_award_stat( "calling_card", calling_card_stat ); } weapon_camo_stat = self.pers["contracts"][contract_index].weapon_camo_stat; if ( weapon_camo_stat != "" ) { set_contract_award_stat( "weapon_camo", weapon_camo_stat ); } self set_contract_stat( 3, "award_given", 1 ); } /# test_slot = GetDvarInt( "scr_contract_debug_slot", 10 - 1 ); if ( slot == test_slot ) { if ( contract_index >= 1000 && contract_index <= 2999 ) { event = &"mp_daily_challenge_complete"; } display_rewards = true; } #/ self LUINotifyEvent( event, 2, contract_index, display_rewards ); } /# if ( GetDvarInt( "scr_contract_debug", 0 ) > 0 ) { IPrintLn( "Contract Slot "+ slot + " table index " + contract_index + " progress: " + new_progress + "/" + target_value ); } #/ } function get_contract_stat( slot, stat_name ) { return self GetDStat( "contracts", slot, stat_name ); } function set_contract_stat( slot, stat_name, stat_value ) { return self SetDStat( "contracts", slot, stat_name, stat_value ); } function set_contract_award_stat( award_type, stat_name, stat_value = 1 ) { // award_type is unused for now as we use PlayerStatsList for now return self AddPlayerStat( stat_name, stat_value ); } function set_contract_award_stat_from_path( stat_path, stat_value ) { stat_path_array = StrTok( stat_path, " " ); string_path_1 = ""; string_path_2 = ""; string_path_3 = ""; string_path_4 = ""; string_path_5 = ""; switch( stat_path_array.size ) { case 5: string_path_5 = stat_path_array[4]; if( StrIsNumber( string_path_5 ) ) { string_path_5 = Int( string_path_5 ); } case 4: string_path_4 = stat_path_array[3]; if( StrIsNumber( string_path_4 ) ) { string_path_4 = Int( string_path_4 ); } case 3: string_path_3 = stat_path_array[2]; if( StrIsNumber( string_path_3 ) ) { string_path_3 = Int( string_path_3 ); } case 2: string_path_2 = stat_path_array[1]; if( StrIsNumber( string_path_2 ) ) { string_path_2 = Int( string_path_2 ); } case 1: string_path_1 = stat_path_array[0]; if( StrIsNumber( string_path_1 ) ) { string_path_1 = Int( string_path_1 ); } } switch( stat_path_array.size ) { case 1: return self SetDStat( string_path_1, stat_value ); break; case 2: return self SetDStat( string_path_1, string_path_2, stat_value ); break; case 3: return self SetDStat( string_path_1, string_path_2, string_path_3, stat_value ); break; case 4: return self SetDStat( string_path_1, string_path_2, string_path_3, string_path_4, stat_value ); break; case 5: return self SetDStat( string_path_1, string_path_2, string_path_3, string_path_4, string_path_5, stat_value ); break; default: AssertMsg( "Stat path depth of " + stat_path_array.size + " is too large. Limit to 5 deep" ); break; } } function award_loot_xp_due( amount ) { if ( !isdefined( self ) ) return; if ( amount <= 0 ) return; current_amount = (isdefined(self GetDStat( "mp_loot_xp_due" ))?self GetDStat( "mp_loot_xp_due" ):0); new_amount = current_amount + amount; self SetDStat( "mp_loot_xp_due", new_amount ); } function get_hero_weapon_mask( attacker, weapon ) { if ( !isdefined( weapon ) ) return 0; if ( isdefined( weapon.isHeroWeapon ) && !weapon.isHeroWeapon ) return 0; switch( weapon.name ) { case "hero_minigun": case "hero_minigun_body3": return 1; // note: heroWeaponMask needs to stay unique and consistent for function across TUs and FFOTDs break; case "hero_flamethrower": return 1 << 1; break; case "hero_lightninggun": case "hero_lightninggun_arc": return 1 << 2; break; case "hero_chemicalgelgun": case "hero_firefly_swarm": return 1 << 3; break; case "hero_pineapplegun": case "hero_pineapple_grenade": return 1 << 4; break; case "hero_armblade": return 1 << 5; break; case "hero_bowlauncher": case "hero_bowlauncher2": case "hero_bowlauncher3": case "hero_bowlauncher4": return 1 << 6; break; case "hero_gravityspikes": return 1 << 7; break; case "hero_annihilator": return 1 << 8; break; default: return 0; } } function get_hero_ability_mask( ability ) { if ( !isdefined( ability ) ) return 0; switch( ability.name ) { case "gadget_clone": return 1; // note: heroAbilityMask needs to stay unique and consistent for functions across TUs and FFOTDs break; case "gadget_heat_wave": return 1 << 1; break; case "gadget_flashback": return 1 << 2; break; case "gadget_resurrect": return 1 << 3; break; case "gadget_armor": return 1 << 4; break; case "gadget_camo": return 1 << 5; break; case "gadget_vision_pulse": return 1 << 6; break; case "gadget_speed_burst": return 1 << 7; break; case "gadget_combat_efficiency": return 1 << 8; break; default: return 0; } } function contract_game_ended( data ) { // TODO: award challenge award here: maybe } function contract_win( winner ) { winner add_stat( 1000 ); winner add_stat( 1 ); winner add_stat( 3007 ); winner add_stat( 3008 ); winner add_stat( 3009 ); winner add_stat( 3010 ); winner add_stat( 3011 ); winner add_stat( 3012 ); winner add_stat( 3013 ); winner add_stat( 3014 ); winner add_stat( 3015 ); winner add_stat( 3016 ); winner add_stat( 3017 ); winner add_stat( 3018 ); winner add_stat( 3019 ); winner add_stat( 3020 ); winner add_stat( 3021 ); winner add_stat( 3022 ); winner add_stat( 3023 ); winner add_stat( 3024 ); winner add_stat( 3025 ); winner add_stat( 3026 ); winner add_stat( 3027 ); winner add_stat( 3028 ); if ( util::is_objective_game( level.gametype ) ) { winner add_stat( 2 ); } if ( IsArenaMode() ) { winner add_stat( 1001 ); } gametype_win( winner ); } function gametype_win( winner ) { switch ( level.gametype ) { case "tdm": winner add_stat( 1002 ); break; case "ball": winner add_stat( 1003 ); break; case "escort": winner add_stat( 1004 ); break; case "conf": winner add_stat( 1005 ); break; case "sd": winner add_stat( 1006 ); break; case "koth": winner add_stat( 1007 ); break; case "dom": winner add_stat( 1008 ); break; case "ctf": winner add_stat( 1026 ); break; case "dem": winner add_stat( 1027 ); break; case "dm": winner add_stat( 1028 ); break; case "clean": winner add_stat( 1029 ); break; default: break; } } function on_offender_kill() { self add_stat( 1018 ); self add_stat( 6 ); } function on_defender_kill() { self add_stat( 1017 ); self add_stat( 6 ); } function on_headshot_kill() { self add_stat( 1016, 1 ); } function award_loot_xp() { player = self; if ( !isdefined( player.pers["contracts"] ) ) return 0; loot_xp = 0; // // daily contract // daily_slot = 2; if ( get_contract_stat( daily_slot, "active" ) && !get_contract_stat( daily_slot, "award_given" ) ) { if ( contract_slot_met( daily_slot ) ) { loot_xp += player award_daily_contract(); player set_contract_stat( daily_slot, "award_given", 1 ); } } // // weekly contract // weekly_slot_A = 0; weekly_slot_B = 1; if ( get_contract_stat( weekly_slot_A, "active" ) && !get_contract_stat( weekly_slot_A, "award_given" ) && get_contract_stat( weekly_slot_B, "active" ) && !get_contract_stat( weekly_slot_B, "award_given" ) ) { if ( contract_slot_met( weekly_slot_A ) && contract_slot_met( weekly_slot_B ) ) { loot_xp += player award_weekly_contract(); player set_contract_stat( weekly_slot_A, "award_given", 1 ); player set_contract_stat( weekly_slot_B, "award_given", 1 ); } } return loot_xp; } function contract_slot_met( slot ) { player = self; contract_index = get_contract_stat( slot, "index" ); if ( !isdefined( player.pers["contracts"][contract_index] ) ) return false; progress = player get_contract_stat( slot, "progress" ); target_value = player.pers["contracts"][contract_index].target_value; return ( progress >= target_value ); } function award_daily_contract() { return GetDvarInt( "daily_contract_cryptokey_reward_count", 10 ) * GetDvarInt( "loot_cryptokeyCost", 100 ); } function award_weekly_contract() { self award_blackjack_contract(); return GetDvarInt( "weekly_contract_cryptokey_reward_count", 30 ) * GetDvarInt( "loot_cryptokeyCost", 100 ); } function award_blackjack_contract() { contract_count = self GetDStat( "blackjack_contract_count" ); reward_count = GetDvarInt( "weekly_contract_blackjack_contract_reward_count", 1 ); self SetDStat( "blackjack_contract_count", contract_count + reward_count ); }