boiii-scripts/mp/_contracts.gsc
2023-04-13 17:30:38 +02:00

992 lines
27 KiB
Plaintext

#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[ <stat_name> ]
// mp_stats.ddl, PlayerStatsList[ <stat_name> ]
// 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 );
}