diff --git a/TERMSOFSERVICE.md b/TERMSOFSERVICE.md deleted file mode 100644 index 640f400e..00000000 --- a/TERMSOFSERVICE.md +++ /dev/null @@ -1,35 +0,0 @@ -# Terms of Service - -Welcome to Slender Fortress Modified. These are the terms and service for use of the gamemode which you may use in many differnt ways, including but not limited to content creation, server hosting, and beta testing. By hosting Slender Fortress Modified, you are agreeing to be bound by these Terms. If you disagree with any part of the terms then you may not use Slender Fortress Modified. - -## 1. Server Hosting - -Slender Fortress Modified is a Sourcemod plugin offered by Mentrillum for free. If you plan to only use the gamemode for a private server that no one else can join, you can skip this section of the Terms of Service. You may host a server publicly for full or part time and have any compatible plugin you'd like to add to your server. You may not host your server publicly or promote it in any way which includes announcements and video showcases if one of these conditions are met. - -### A. Stolen Content - -As of April 24th, 2024, any content taken from other servers is now dependent on where the content came from. Server owners and or boss/map creators which port assets from other games or similar medias do not have the right to request a content removal for other servers or platforms like Garry's Mod. This doesn't apply to assets which were created by the map/boss creators as the creators do get to decide on how their content is handled. The latter of which has specific rules. Attempting to take self-made assets without the creator's intentions falls under content stealing. Removing the stolen content will no longer violate this rule. Consider this your warning. - -### B. Explicit Content - -Any content found in your server that contains Not Safe For Work (NSFW) elements or is prone to violate server rules specifically against NSFW content is prohibited to add and keep. Slender Fortress' main audience consists of users under the age of 18. Showing off the content in video form will result in you having to take that video down, not privating the video or unlisting it, but deleting it. Very few exceptions can be made where you can put age restrictions on your videos. Any other content that is prone to violate any server rules outside of your server such as explicit slurs, enforcing political and religious beliefs, fetishes, signs of sexual elements, nudity or revealing elements, discrimination, and propaganda are prohibited. We allow bosses like The Twins if any of these elements are censored out enough. - -### C. Server Handling - -Hosing a server and promoting it with the sole purpose of luring children in to show any explicit content, trying to enforce beliefs on users with specific content, or traumetizing children is prohibited. This includes failing to enforce your server rules properly, exposing the alformentioned factors publicly, creating content to specifically enforce beliefs, or not creating any server rule that stops any of the alformentioned factors from happening. We do not take this part lightly, consider fixing your server if this rule is violated. - -## 2. Content Creation - -Creating any form of content follows the same rules as hosting a server with extra considerations to take into account. If you do not plan to show the content off to anyone which includes servers even after the content is fully finished, you may skip this part of the Terms of Service. You may release any content publicly or privately. By allowing any server to take your boss, you agree to let server owners or developers edit your boss without your permission or consent. Unless the content is self-made in which you do have the right to enforce takedowns, but if you've done only ports, you cannot enforce takedowns. So these rules will only apply to self-made content. - -### A. Content Submissions + Remakes - -You may skip this part of the Terms of Service if you don't plan to submit any content to be displayed on the Github page for everyone to easily find and download or to any officially supported servers. Submitting any content to Mentrillum where it can be added on the Github wiki means you agree to follow the content creating guidelines before submitting your content. Any small detail that may hint at any of these violations will result in a request for removing that small detail. Failing to do so will not get your content added on the Github. If these details are noticed months after the content was added, it will be taken down immediately after the discovery and will only be readded if you remove those details. Content will also be reviewed based on the quality of the boss, we do not want carbon copy bosses sitting on the Github page. This does not mean your boss or map has to be balanced out perfectly. The same logic will apply to submitting content to servers that are officially supported (Disc-FF, Demon Hamster's server, etc). Fully remaking every asset a creation uses is allowed and doesn't need permission from the original self-made asset creator(s), which also includes restorations, sub plugin recreations, etc. - -## 3. Consequences - -After you are caught violating any of these rules, you will be approached by one of the higher ups at the SF2M team and will be requested to take action to fix any of these violations which may or may not include deleting parts or all of a certain piece of content, adjusting your server rules, deleting parts or a whole video, etc. Going forth with these requests and proving you put in effort to fix these violations without question will not make anything major happen depending on if these violations were intentional or unintentional. Intentionally violating these terms and fixing the violations will result in a warning meaning you can still create content and host a server. Depending on if you just create content for servers or host a server and make videos about it, the second warning will be different. For just creating and submitting content boss or map related, you will be given a second warning if these violations intentionally happened which most of the time is the case. The third warning for creating and submitting content boss or map related or the second warning for hosting a server and making videos on that server will depend on which of the following actions you do. Creating and submitting content will result in a blacklist that every server owner can see which they can quickly make the choice to either avoid your content or carelessly add it either way, the blacklist will have an associated reason as to why this blacklist happened. Getting a second warning for hosting a server or making videos about a server showing content that violates these terms will also result in a blacklist but for average players. Neglecting to fix these actions in any way which includes ignoring any team member's request, being defiant about these requests, never responding to these team members, or continuing to violate these already violated terms to a greater degree will result in a immediate blacklist. - -## 4. Appeals - -If you are already blacklisted in any way but you believe you have changed enough to where you don't violate these terms anymore, you can negociate with Mentrillum proving these terms are no longer violated and you show you will not violate these terms any longer. These appeals can only be given after a one month grace period since the blacklist addition. If these appeals are accepted, your blacklist will be removed and you'll be back to square one in terms of the warning system. Rejected appeals typically mean either you haven't changed or you aren't trusted enough to create content, host a server, or make videos about both that may violate the terms, if this is the case you'll have to wait for an undetermined long period of time. Checking back in on if an appeal can be accepted in an annoying way will lessen the likelihood of the current and following appeals to be accepted. diff --git a/addons/sourcemod/configs/sf2/class_stats.cfg b/addons/sourcemod/configs/sf2/class_stats.cfg index 9d1f9f5c..a2acf517 100644 --- a/addons/sourcemod/configs/sf2/class_stats.cfg +++ b/addons/sourcemod/configs/sf2/class_stats.cfg @@ -107,8 +107,8 @@ "flashlight_radius_multiplier" "1.0" "flashlight_length_multiplier" "1.0" "flashlight_damage_multiplier" "1.0" - "flashlight_drain_rate" "1.0" - "flashlight_recharge_rate" "1.0" + "flashlight_drain_rate" "0.12" + "flashlight_recharge_rate" "0.1" "flashlight_sound_radius" "0.5" "blocked_on_thanatophobia" "0" @@ -148,8 +148,8 @@ "flashlight_radius_multiplier" "1.0" "flashlight_length_multiplier" "1.0" "flashlight_damage_multiplier" "1.0" - "flashlight_drain_rate" "1.0" - "flashlight_recharge_rate" "1.0" + "flashlight_drain_rate" "0.12" + "flashlight_recharge_rate" "0.1" "flashlight_sound_radius" "0.5" "blocked_on_thanatophobia" "0" @@ -189,8 +189,8 @@ "flashlight_radius_multiplier" "1.0" "flashlight_length_multiplier" "1.0" "flashlight_damage_multiplier" "1.0" - "flashlight_drain_rate" "1.0" - "flashlight_recharge_rate" "1.0" + "flashlight_drain_rate" "0.12" + "flashlight_recharge_rate" "0.1" "flashlight_sound_radius" "0.5" "blocked_on_thanatophobia" "0" @@ -230,8 +230,8 @@ "flashlight_radius_multiplier" "1.0" "flashlight_length_multiplier" "1.0" "flashlight_damage_multiplier" "1.0" - "flashlight_drain_rate" "1.0" - "flashlight_recharge_rate" "1.0" + "flashlight_drain_rate" "0.12" + "flashlight_recharge_rate" "0.1" "flashlight_sound_radius" "0.5" "blocked_on_thanatophobia" "0" @@ -271,8 +271,8 @@ "flashlight_radius_multiplier" "1.0" "flashlight_length_multiplier" "1.0" "flashlight_damage_multiplier" "1.0" - "flashlight_drain_rate" "1.0" - "flashlight_recharge_rate" "1.0" + "flashlight_drain_rate" "0.12" + "flashlight_recharge_rate" "0.1" "flashlight_sound_radius" "0.5" "blocked_on_thanatophobia" "0" @@ -312,8 +312,8 @@ "flashlight_radius_multiplier" "2.0" "flashlight_length_multiplier" "3.0" "flashlight_damage_multiplier" "1.5" - "flashlight_drain_rate" "1.5" - "flashlight_recharge_rate" "0.8" + "flashlight_drain_rate" "0.06" + "flashlight_recharge_rate" "0.12" "flashlight_sound_radius" "1.25" "blocked_on_thanatophobia" "0" @@ -353,8 +353,8 @@ "flashlight_radius_multiplier" "1.0" "flashlight_length_multiplier" "1.0" "flashlight_damage_multiplier" "1.0" - "flashlight_drain_rate" "1.0" - "flashlight_recharge_rate" "1.0" + "flashlight_drain_rate" "0.12" + "flashlight_recharge_rate" "0.1" "flashlight_sound_radius" "0.5" "blocked_on_thanatophobia" "1" @@ -394,8 +394,8 @@ "flashlight_radius_multiplier" "1.0" "flashlight_length_multiplier" "1.0" "flashlight_damage_multiplier" "1.0" - "flashlight_drain_rate" "1.0" - "flashlight_recharge_rate" "1.0" + "flashlight_drain_rate" "0.12" + "flashlight_recharge_rate" "0.1" "flashlight_sound_radius" "0.5" "blocked_on_thanatophobia" "0" @@ -435,8 +435,8 @@ "flashlight_radius_multiplier" "1.0" "flashlight_length_multiplier" "1.0" "flashlight_damage_multiplier" "1.0" - "flashlight_drain_rate" "1.0" - "flashlight_recharge_rate" "1.0" + "flashlight_drain_rate" "0.12" + "flashlight_recharge_rate" "0.1" "flashlight_sound_radius" "0.5" "blocked_on_thanatophobia" "0" diff --git a/addons/sourcemod/gamedata/sf2.txt b/addons/sourcemod/gamedata/sf2.txt index 756c37f5..6babec27 100644 --- a/addons/sourcemod/gamedata/sf2.txt +++ b/addons/sourcemod/gamedata/sf2.txt @@ -6,103 +6,159 @@ { "CTFPlayer::WantsLagCompensationOnEntity" { - "linux" "335" - "windows" "334" + "linux" "336" + "linux64" "336" + "windows" "335" + "windows64" "335" } "CTFWeaponBase::GetCustomDamageType" { - "linux" "387" - "windows" "381" + "linux" "388" + "linux64" "388" + "windows" "382" + "windows64" "382" } "CBaseEntity::ShouldTransmit" { "linux" "21" + "linux64" "21" "windows" "20" + "windows64" "20" } "CBaseEntity::UpdateTransmitState" { "linux" "22" + "linux64" "22" "windows" "21" + "windows64" "21" } "CBaseProjectile::CanCollideWithTeammates" { - "linux" "230" - "windows" "229" + "linux" "231" + "linux64" "231" + "windows" "230" + "windows64" "230" } "CTFPlayer::EquipWearable" { - "linux" "439" - "windows" "438" + "linux" "440" + "linux64" "440" + "windows" "439" + "windows64" "439" } "CBaseTrigger::PassesTriggerFilters" { - "linux" "208" - "windows" "207" + "linux" "209" + "linux64" "209" + "windows" "208" + "windows64" "208" } "CTFWeaponBase::GetWeaponID" { "windows" "380" "linux" "386" + "linux64" "386" + "windows64" "380" } "CBaseEntity::IsBaseCombatWeapon" { "windows" "91" "linux" "92" + "linux64" "92" + "windows64" "91" } "CBaseEntity::ChangeTeam" { "library" "server" "linux" "97" + "linux64" "97" "windows" "96" + "windows64" "96" } "CTeam::AddPlayer" { "linux" "209" + "linux64" "209" "windows" "208" + "windows64" "208" } "CTeam::RemovePlayer" { "linux" "210" + "linux64" "210" "windows" "209" + "windows64" "209" } "CBaseGrenade::Explode" { "library" "server" "linux" "236" + "linux64" "236" "windows" "235" + "windows64" "235" } "CTFBaseRocket::Explode" { "library" "server" "linux" "240" + "linux64" "240" "windows" "239" + "windows64" "239" } "CBaseEntity::VPhysicsUpdate" { "library" "server" "linux" "164" + "linux64" "164" "windows" "163" + "windows64" "163" } "CTFSniperRifle::GetPenetrateType" { "library" "server" "linux" "407" + "linux64" "407" "windows" "400" + "windows64" "400" + } + + "CBaseEntity::Deflected" + { + "linux" "160" + "linux64" "160" + "windows" "159" + "windows64" "159" + } + + "CBasePlayer::Event_Killed" + { + "linux" "69" + "linux64" "69" + "windows" "68" + "windows64" "68" + } + + "CBaseObject::OnTakeDamage" + { + "linux" "65" + "linux64" "65" + "windows" "65" + "windows64" "65" } } @@ -113,7 +169,7 @@ "library" "server" "linux" "@_ZN9CTFPlayer20PlaySpecificSequenceEPKc" "linux64" "@_ZN9CTFPlayer20PlaySpecificSequenceEPKc" - "windows" "\x55\x8B\xEC\x53\x56\x8B\x75\x08\x57\x56\x8B\xF9\xE8\x2A\x2A\x2A\x2A\x8B\xD8" + "windows" "\x55\x8B\xEC\x56\xFF\x75\x08\x8B\xF1\xE8\x2A\x2A\x2A\x2A\x8B\xCE\x83\xF8\xFF" "windows64" "\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x20\x48\x8B\xFA\x48\x8B\xD9\xE8\x2A\x2A\x2A\x2A\x48\x8B\xCB\x83\xF8\xFF\x74\x2A" } @@ -216,6 +272,14 @@ "windows" "\x55\x8B\xEC\x53\x8B\xD9\x56\x57\x8B\x93\x10\x02\x00\x00\x85\xD2\x0F\x84\x2A\x2A\x2A\x2A\xB9\xFF\x1F\x00\x00\x83\xFA\xFF\x74\x2A\x0F\xB7\xCA\xA1\x2A\x2A\x2A\x2A\xC1\xE1\x04\x8D\x78\x04\x03\xF9\x0F\x84\x2A\x2A\x2A\x2A\xC1\xEA\x10\x39\x57\x04\x0F\x85\x2A\x2A\x2A\x2A\x8B\x3F\x85\xFF\x0F\x84\x2A\x2A\x2A\x2A\x8B\x07\x8B\xCF\x8B\x80\x48\x01\x00\x00\xFF\xD0\x84\xC0\x0F\x84\x2A\x2A\x2A\x2A\x8B\x75\x08\x85\xF6\x74\x2A" "windows64" "\x48\x89\x5C\x24\x10\x48\x89\x6C\x24\x18\x48\x89\x74\x24\x20\x57\x48\x83\xEC\x30\x48\x8B\xDA" } + + "CTFPlayer::TeamFortress_SetSpeed" + { + "linux" "@_ZN9CTFPlayer21TeamFortress_SetSpeedEv" + "windows" "\x55\x8B\xEC\x83\xEC\x24\xA1\x2A\x2A\x2A\x2A\x56\x8B\xF1\x89\x75\xF0" + "linux64" "@_ZN9CTFPlayer21TeamFortress_SetSpeedEv" + "windows64" "\x40\x55\x48\x83\xEC\x60\x48\x8B\x05\x03\x3D\x7A\x00" + } } "Functions" @@ -326,6 +390,55 @@ } } } + + "CBaseEntity::Deflected" + { + "offset" "CBaseEntity::Deflected" + "hooktype" "entity" + "return" "void" + "this" "entity" + "arguments" + { + "pDeflectedBy" + { + "type" "cbaseentity" + } + "vecDir" + { + "type" "vectorptr" + } + } + } + + "CBasePlayer::Event_Killed" + { + "offset" "CBasePlayer::Event_Killed" + "hooktype" "entity" + "return" "void" + "this" "entity" + "arguments" + { + "info" + { + "type" "objectptr" + } + } + } + + "CBaseObject::OnTakeDamage" + { + "offset" "CBaseObject::OnTakeDamage" + "hooktype" "entity" + "return" "int" + "this" "entity" + "arguments" + { + "info" + { + "type" "objectptr" + } + } + } } } } \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/sf2.inc b/addons/sourcemod/scripting/include/sf2.inc index 64621c15..91a68ea4 100644 --- a/addons/sourcemod/scripting/include/sf2.inc +++ b/addons/sourcemod/scripting/include/sf2.inc @@ -7,8 +7,8 @@ #include #include -#define PLUGIN_VERSION "1.8.0 M Alpha 2.7" -#define PLUGIN_VERSION_DISPLAY "1.8.0 M Alpha 2.7" +#define PLUGIN_VERSION "1.8.0 M Alpha 3 Indev 4-9-2025" +#define PLUGIN_VERSION_DISPLAY "1.8.0 M Alpha 3 Indev 4-9-2025" // Some defines. #define SF2_MAX_PROFILE_NAME_LENGTH 64 @@ -16,7 +16,7 @@ #define MAX_BOSSES 64 #define MAX_NPCTELEPORTER 5 -#define MAX_TRAPS 64 +#define MAX_TRAPS 32 #define MAX_CLASSES 9 // Difficulty modifiers. @@ -155,6 +155,8 @@ enum SF2BossSound_RageTwo = 15, SF2BossSound_RageThree, SF2BossSound_Despawn, + SF2BossSound_Hurt, + SF2BossSound_Jump, SF2BossSound_MaxSounds }; @@ -178,6 +180,9 @@ enum SF2BossAnimation_AttackEnd = 15, SF2BossAnimation_ProjectileShoot, SF2BossAnimation_Despawn, + SF2BossAnimation_Jump, + SF2BossAnimation_Air, + SF2BossAnimation_Land, SF2BossAnimation_MaxAnimations } @@ -223,7 +228,7 @@ enum SF2Attribute_Max }; -char g_SlenderVoiceList[SF2BossSound_MaxSounds][] = +stock char g_SlenderVoiceList[SF2BossSound_MaxSounds][] = { "sound_none", // Placeholder "sound_idle", @@ -242,10 +247,12 @@ char g_SlenderVoiceList[SF2BossSound_MaxSounds][] = "sound_rage", "sound_rage_2", "sound_rage_3", - "sound_despawn" + "sound_despawn", + "sound_hurt", + "sound_jump" }; -char g_SlenderAnimationsList[SF2BossAnimation_MaxAnimations][] = +stock char g_SlenderAnimationsList[SF2BossAnimation_MaxAnimations][] = { "idle", "walk", @@ -264,10 +271,13 @@ char g_SlenderAnimationsList[SF2BossAnimation_MaxAnimations][] = "attack_begin", "attack_end", "shoot", - "despawn" + "despawn", + "jump", + "air", + "land" }; -char g_AttributesList[SF2Attribute_Max][] = +stock char g_AttributesList[SF2Attribute_Max][] = { "reduced speed on look", "reduced walk speed on look", @@ -374,6 +384,7 @@ enum RenevantWave #define COND_ENEMYRECHASE (1 << 13) #define COND_DEBUG (1 << 14) #define COND_ALERT_TRIGGER_POS (1 << 15) +#define COND_ENEMYVISIBLE_NOGLASS (1 << 16) /** * Called after a boss profile is successfully loaded. @@ -381,14 +392,14 @@ enum RenevantWave * @param profile Profile name * @param kv Key Values */ -forward void SF2_OnBossProfileLoaded(const char[] profile, KeyValues kv); +forward void SF2_OnBossProfileLoaded(const char[] profile, SF2_BaseBossProfile data); /** * Called before a boss profile is unloaded. * * @param profile Profile name */ -forward void SF2_OnBossProfileUnloaded(const char[] profile); +forward void SF2_OnBossProfileUnloaded(const char[] profile, SF2_BaseBossProfile data); /** * Called when a boss is added into the game. @@ -432,8 +443,8 @@ forward Action SF2_OnBossAnimationUpdate(int bossIndex, const char[] animation); /** * Called to determine the boss's run speed. * - * @param bossIndex Boss index. - * @param speed The boss's run speed. + * @param bossIndex Boss index. + * @param speed The boss's run speed. * @return If Plugin_Changed, speed is used as the boss's run speed. */ forward Action SF2_OnBossGetSpeed(int bossIndex, float &speed); @@ -441,7 +452,7 @@ forward Action SF2_OnBossGetSpeed(int bossIndex, float &speed); /** * Called to determine the boss's walk speed. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param walkSpeed The boss's walk speed. * @return If Plugin_Changed, speed is used as the boss's walk speed. */ @@ -450,9 +461,9 @@ forward Action SF2_OnBossGetWalkSpeed(int bossIndex, float &walkSpeed); /** * Called after the boss has determined it can hear the entity. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param entity Entity index. - * @param soundType The type of sound to check. + * @param soundType The type of sound to check. * @return Returning a value other than Plugin_Continue will block hearing to the entity. */ forward Action SF2_OnBossHearEntity(int bossIndex, int entity, SoundType soundType); @@ -460,7 +471,7 @@ forward Action SF2_OnBossHearEntity(int bossIndex, int entity, SoundType soundTy /** * Called after the boss has determined it can see the entity. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param entity Entity index. * @return Returning a value other than Plugin_Continue will block vision to the entity. */ @@ -516,8 +527,8 @@ forward void SF2_OnBossAttacked(int bossIndex, int attackIndex); /** * Called when the boss finishes validating all attack indexes. * - * @param bossIndex Boss index. - * @param attackArray ArrayList containing all valid attacks + * @param bossIndex Boss index. + * @param attackArray ArrayList containing all valid attack indexes. */ forward void SF2_OnBossPreAttack(int bossIndex, ArrayList attackArray); @@ -541,7 +552,6 @@ forward Action SF2_OnBossPreTakeDamage(SF2_ChaserBossEntity chaser, CBaseEntity */ forward Action SF2_OnBossPreFlashlightDamage(SF2_ChaserBossEntity chaser, SF2_Player player); - /** * Called whenever a chaser boss gets a command string "suspend for action" using INextBotEventResponder.OnCommandString() * @@ -583,7 +593,7 @@ forward void SF2_OnClientBlink(int client); * Called when a player gets scared from a boss. * * @param client Client index. - * @param bossIndex Boss index. + * @param bossIndex Boss index. */ forward void SF2_OnClientScare(int client, int bossIndex); @@ -592,7 +602,7 @@ forward void SF2_OnClientScare(int client, int bossIndex); * a boss's kill radius, or dies from too much camping. This is also called for fake bosses. * * @param client Client index. - * @param bossIndex Boss index. + * @param bossIndex Boss index. */ forward void SF2_OnClientCaughtByBoss(int client, int bossIndex); @@ -600,7 +610,7 @@ forward void SF2_OnClientCaughtByBoss(int client, int bossIndex); * Called when giving queue points to the player at the end of the round. * * @param client Client index. - * @param addAmount The amount of queue points to give. + * @param addAmount The amount of queue points to give. * @return If Plugin_Changed, addAmount will be used as the amount to give. */ forward Action SF2_OnClientGiveQueuePoints(int client, int &addAmount); @@ -651,7 +661,7 @@ forward void SF2_OnClientEscape(int client); * Called when the player looks at the boss. * * @param client Client index. - * @param bossIndex Boss the client is looking at. + * @param bossIndex Boss the client is looking at. */ forward void SF2_OnClientLooksAtBoss(int client, int bossIndex); @@ -659,7 +669,7 @@ forward void SF2_OnClientLooksAtBoss(int client, int bossIndex); * Called when the player looks away from the boss. * * @param client Client index. - * @param bossIndex Boss the client is no longer looking at. + * @param bossIndex Boss the client is no longer looking at. */ forward void SF2_OnClientLooksAwayFromBoss(int client, int bossIndex); @@ -667,7 +677,7 @@ forward void SF2_OnClientLooksAwayFromBoss(int client, int bossIndex); * Called when the player gets caught in a boss's deathcam. * * @param client Client index. - * @param bossIndex Boss index. + * @param bossIndex Boss index. */ forward void SF2_OnClientStartDeathCam(int client, int bossIndex); @@ -691,18 +701,18 @@ forward void SF2_OnClientEndDeathCam(int client, int bossIndex, bool antiCamp); /** * Called to determine a player's default walk speed. * - * @param client Client index. + * @param client Client index. * @param defaultFloat The default walk speed. - * @return If Plugin_Changed, defaultFloat is used as player's default walk speed. + * @return If Plugin_Changed, defaultFloat is used as player's default walk speed. */ forward Action SF2_OnClientGetDefaultWalkSpeed(int client, float &defaultFloat); /** * Called to determine a player's default sprint speed. * - * @param client Client index. + * @param client Client index. * @param defaultFloat The default sprint speed. - * @return If Plugin_Changed, defaultFloat is used as player's default sprint speed. + * @return If Plugin_Changed, defaultFloat is used as player's default sprint speed. */ forward Action SF2_OnClientGetDefaultSprintSpeed(int client, float &defaultFloat); @@ -721,7 +731,7 @@ forward Action SF2_OnClientTakeDamage(int victim, int &attacker, int &inflictor, * Called when giving queue points to the group at the end of the round. * * @param groupIndex Group index. - * @param addAmount The amount of queue points to give. + * @param addAmount The amount of queue points to give. * @return If Plugin_Changed, addAmount will be used as the amount to give. */ forward Action SF2_OnGroupGiveQueuePoints(int groupIndex, int &addAmount); @@ -730,7 +740,7 @@ forward Action SF2_OnGroupGiveQueuePoints(int groupIndex, int &addAmount); * Called when a player gets damaged by the boss. * * @param client The player who got damaged. - * @param bossIndex Boss that damaged the player. + * @param bossIndex Boss that damaged the player. * @param inflictor The inflictor of the damage. * @param damage The amount of damage taken. * @param damageType The damage type. @@ -797,7 +807,7 @@ forward void SF2_OnDifficultyChanged(int difficulty, int oldDifficulty); * Called when a player is choosen to play * * @param client Client index. - * @return If Plugin_Handled, player is blocked from playing. + * @return If Plugin_Handled, player is blocked from playing. */ forward Action SF2_OnClientEnterGame(int client); @@ -805,7 +815,7 @@ forward Action SF2_OnClientEnterGame(int client); * Called when a player is choosen to play * * @param groupIndex Group index. - * @return If Plugin_Handled, group is blocked from playing. + * @return If Plugin_Handled, group is blocked from playing. */ forward Action SF2_OnGroupEnterGame(int groupIndex); @@ -831,7 +841,7 @@ forward void SF2_OnDifficultyVoteFinished(ArrayList list, bool isRunOff); * @return Returning a value other than Plugin_Continue will block the boss from using * the attack. */ -forward Action SF2_OnIsBossCustomAttackPossible(SF2_ChaserBossEntity chaser, const char[] attackName, SF2ChaserBossProfileAttackData data, CBaseEntity target); +forward Action SF2_OnIsBossCustomAttackPossible(SF2_ChaserBossEntity chaser, const char[] attackName, SF2_ChaserBossProfileBaseAttack data, CBaseEntity target); /** * Called when the Chaser boss has selected the custom attack and is requesting a @@ -844,7 +854,7 @@ forward Action SF2_OnIsBossCustomAttackPossible(SF2_ChaserBossEntity chaser, con * @param action Parameter to fill with the NextBotAction * @return Return a value other than Plugin_Continue to use `action` */ -forward Action SF2_OnBossGetCustomAttackAction(SF2_ChaserBossEntity chaser, const char[] attackName, SF2ChaserBossProfileAttackData data, CBaseEntity target, NextBotAction &action); +forward Action SF2_OnBossGetCustomAttackAction(SF2_ChaserBossEntity chaser, const char[] attackName, SF2_ChaserBossProfileBaseAttack data, CBaseEntity target, NextBotAction &action); /** * Called when a projectile from SF2 has touched another entity successfully and has either exploded or dealt damage. @@ -879,13 +889,6 @@ forward void SF2_OnChaserBossEndAttack(SF2_ChaserBossEntity chaser, const char[] */ forward void SF2_OnSelectedPvEBoss(const char[] boss, const float pos[3], const float ang[3]); -/** - * Returns the global variable g_Config. - * - * @return g_Config. - */ -native KeyValues SF2_GetConfig(); - /** * Returns a bool about the gamemode's state. * @@ -966,7 +969,7 @@ native int SF2_GetCurrentBossPack(char[] buffer, int bufferLen); * Returns the current group a player is in. * * @param client Client index. - * @return Group index the player is in, -1 otherwise. + * @return Group index the player is in, -1 otherwise. */ native int SF2_GetClientGroup(int client); @@ -974,7 +977,7 @@ native int SF2_GetClientGroup(int client); * Returns the amount of a queue points a player has. * * @param client Client index. - * @return Amount of queue points. + * @return Amount of queue points. */ native int SF2_GetClientQueuePoints(int client); @@ -1026,6 +1029,7 @@ native bool SF2_IsClientUbercharged(int client); */ native bool SF2_IsClientInKart(int client); +#if defined _tf2_included /** * Returns whether or not the client is in a specific condition for SF2. * @@ -1034,6 +1038,7 @@ native bool SF2_IsClientInKart(int client); * @return True if the player is in a condition, false if not. */ native bool SF2_IsClientInCondition(int client, TFCond condition); +#endif /** * Returns a bool about the client's elimination state. @@ -1079,7 +1084,7 @@ native bool SF2_IsClientProxy(int client); * Tells whether or not the client is looking at the boss. * * @param client Client index. - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return True if the player is looking at a boss, false if not. */ native bool SF2_IsClientLookingAtBoss(int client, int bossIndex); @@ -1126,7 +1131,7 @@ native int SF2_GetClientProxyMaster(int client); * If the client is a Proxy, then this sets the boss index that the client is associated with. * * @param client Client index. - * @param bossIndex Boss index. + * @param bossIndex Boss index. */ native void SF2_SetClientProxyMaster(int client, int bossIndex); @@ -1158,7 +1163,7 @@ native float SF2_GetClientProxyControlRate(int client); * If the client is a Proxy, then this sets the interval which each Control point will drain for the client. * * @param client Client index. - * @param interval Interval to subtract one Control point from the client. + * @param interval Interval to subtract one Control point from the client. */ native float SF2_SetClientProxyControlRate(int client, float interval); @@ -1271,7 +1276,7 @@ native void SF2_ClientStartDeathCam(int client, int bossIndex, const float lookP * Returns whether or not a proxy can be spawned, returns a teleport position or spawn point if defined and the result is true, works well with SF2_ClientForceProxy. * * @param client Client index. - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param teleportPos Position to return * @param spawnPoint Spawn point to return, defaults to -1. */ @@ -1281,7 +1286,7 @@ native bool SF2_ClientSpawnProxy(int client, int bossIndex, float teleportPos[3] * Spawns a player as a proxy at a given position or spawn point. * * @param client Client index. - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param teleportPos Position to return * @param spawnPoint Spawn point to return, defaults to -1. */ @@ -1318,7 +1323,7 @@ native int SF2_EntIndexToBossIndex(int entIndex); /** * Returns the entity index of a boss using EntRefToEntIndex(). * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return Entity index, or -1 if the boss has not spawned. */ native int SF2_BossIndexToEntIndex(int bossIndex); @@ -1326,7 +1331,7 @@ native int SF2_BossIndexToEntIndex(int bossIndex); /** * Returns the entity index of a boss using NPCGetEntIndex() rather than EntRefToEntIndex(). * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return Entity index, or -1 if the boss has not spawned. */ native int SF2_BossIndexToEntIndexEx(int bossIndex); @@ -1343,7 +1348,7 @@ native int SF2_BossIDToBossIndex(int bossID); * Returns the unique ID of a boss. A unique ID to a boss index is the equivalent of an entity reference to an entity index. * * @param bossIndex Boss index. - * @return Unique id. + * @return Unique ID. */ native int SF2_BossIndexToBossID(int bossIndex); @@ -1365,7 +1370,7 @@ native void SF2_ForceBossJump(NextBotGroundLocomotion nextbotLocomotion, float s * @param playSpawnSound Play spawn sound. * @return Boss index, or -1 if failed. */ -native int SF2_AddBoss(const char[] profile, int flags=0, bool spawnCompanions=true, bool playSpawnSound=true); +native int SF2_AddBoss(const char[] profile, int flags = 0, bool spawnCompanions = true, bool playSpawnSound = true); /** * Removes a boss from the game. @@ -1384,16 +1389,16 @@ native void SF2_DespawnBoss(int bossIndex); /** * Retrieves the profile name of the boss. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param buffer Buffer to store the profile name. - * @param bufferLen Max length of buffer. + * @param bufferLen Max length of buffer. */ native void SF2_GetBossName(int bossIndex, char[] buffer, int bufferLen); /** * Returns the type of the boss. See the SF2BossType enumeration for possible values. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return Entity index. */ native int SF2_GetBossType(int bossIndex); @@ -1401,7 +1406,7 @@ native int SF2_GetBossType(int bossIndex); /** * Returns the flags of the boss. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return Flags. */ native int SF2_GetBossFlags(int bossIndex); @@ -1409,7 +1414,7 @@ native int SF2_GetBossFlags(int bossIndex); /** * Sets the flags of the boss. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param flags Flags. */ native void SF2_SetBossFlags(int bossIndex, int flags); @@ -1432,7 +1437,7 @@ native bool SF2_IsBossSpawning(int bossIndex); /** * Returns the entity index of the boss's model. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return Entity index. */ native int SF2_GetBossModelEntity(int bossIndex); @@ -1440,7 +1445,7 @@ native int SF2_GetBossModelEntity(int bossIndex); /** * Returns the boss's current target. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return Entity index. */ native int SF2_GetBossTarget(int bossIndex); @@ -1450,23 +1455,25 @@ native int SF2_GetBossTarget(int bossIndex); * * Only used with Chaser bosses. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param client The new target. */ native void SF2_SetBossTarget(int bossIndex, int client); +#if defined _CBASENPC_EXTENSION_INC_ /** * Returns the boss's default path follower. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return Path Follower. */ native PathFollower SF2_GetBossPathFollower(int bossIndex); +#endif /** * If the boss is a copy, this returns the boss that this copy is associated with. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return Boss index of master boss. */ native int SF2_GetBossMaster(int bossIndex); @@ -1474,8 +1481,8 @@ native int SF2_GetBossMaster(int bossIndex); /** * Returns the boss's idle lifetime. * - * @param bossIndex Boss index. - * @param difficulty The difficulty index. + * @param bossIndex Boss index. + * @param difficulty The difficulty index. * @return The boss's default idle lifetime */ native float SF2_GetBossIdleLifetime(int bossIndex, int difficulty); @@ -1485,7 +1492,7 @@ native float SF2_GetBossIdleLifetime(int bossIndex, int difficulty); * * Only used with Chaser bosses. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return The boss's state. */ native int SF2_GetBossState(int bossIndex); @@ -1502,7 +1509,7 @@ native void SF2_SetBossState(int bossIndex, int state); /** * Returns the boss's eye position in the world. Used for visibility checks. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param eyePos Buffer to store result. */ native void SF2_GetBossEyePosition(int bossIndex, float eyePos[3]); @@ -1510,15 +1517,15 @@ native void SF2_GetBossEyePosition(int bossIndex, float eyePos[3]); /** * Returns the boss's eye position offset. This is a local offset vector. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param eyePosOffset Buffer to store result. */ native void SF2_GetBossEyePositionOffset(int bossIndex, float eyePosOffset[3]); -#pragma deprecated Use SF2ChaserBossProfileAttackData.StunEnabled[Difficulty_Max] instead. +#pragma deprecated Use SF2_ChaserBossProfile instead. native bool SF2_IsBossStunnable(int bossIndex); -#pragma deprecated Use SF2ChaserBossProfileAttackData.StunFlashlight[Difficulty_Max] instead. +#pragma deprecated Use SF2_ChaserBossProfile instead. native bool SF2_IsBossStunnableByFlashlight(int bossIndex); /** @@ -1526,7 +1533,7 @@ native bool SF2_IsBossStunnableByFlashlight(int bossIndex); * * Only used with Chaser bosses. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return True if cloaked, false otherwise. */ native bool SF2_IsBossCloaked(int bossIndex); @@ -1536,7 +1543,7 @@ native bool SF2_IsBossCloaked(int bossIndex); * * Only used with Chaser bosses. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @return The health amount. */ native float SF2_GetBossStunHealth(int bossIndex); @@ -1546,7 +1553,7 @@ native float SF2_GetBossStunHealth(int bossIndex); * * Only used with Chaser bosses. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param health The health amount. */ native void SF2_SetBossStunHealth(int bossIndex, float health); @@ -1556,7 +1563,7 @@ native void SF2_SetBossStunHealth(int bossIndex, float health); * * Only used with Chaser bosses. * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param position Buffer to store position. */ native void SF2_GetBossGoalPosition(int bossIndex, float position[3]); @@ -1569,43 +1576,43 @@ native void SF2_GetBossGoalPosition(int bossIndex, float position[3]); */ native bool SF2_IsBossProfileValid(const char[] profile); -#pragma deprecated Use SF2BossProfileData instead. -native int SF2_GetBossProfileNum(const char[] profile, const char[] key, int defaultValue=0); +#pragma deprecated Use SF2_BaseBossProfile instead. +native int SF2_GetBossProfileNum(const char[] profile, const char[] key, int defaultValue = 0); -#pragma deprecated Use SF2BossProfileData instead. -native float SF2_GetBossProfileFloat(const char[] profile, const char[] key, float defaultValue=0.0); +#pragma deprecated Use SF2_BaseBossProfile instead. +native float SF2_GetBossProfileFloat(const char[] profile, const char[] key, float defaultValue = 0.0); -#pragma deprecated Use SF2BossProfileData instead. -native bool SF2_GetBossProfileString(const char[] profile, const char[] key, char[] buffer, int bufferLen, const char[] defaultValue=""); +#pragma deprecated Use SF2_BaseBossProfile instead. +native bool SF2_GetBossProfileString(const char[] profile, const char[] key, char[] buffer, int bufferLen, const char[] defaultValue = ""); -#pragma deprecated Use SF2BossProfileData instead. -native bool SF2_GetBossProfileVector(const char[] profile, const char[] key, float buffer[3], const float defaultValue[3]=NULL_VECTOR); +#pragma deprecated Use SF2_BaseBossProfile instead. +native bool SF2_GetBossProfileVector(const char[] profile, const char[] key, float buffer[3], const float defaultValue[3] = NULL_VECTOR); -#pragma deprecated Use SF2ChaserBossProfileData.Attacks instead. -native int SF2_GetBossAttackProfileNum(const char[] profile, const char[] key, int defaultValue=0, const int attackIndex); +#pragma deprecated Use SF2_ChaserBossProfileBaseAttack instead. +native int SF2_GetBossAttackProfileNum(const char[] profile, const char[] key, int defaultValue = 0, const int attackIndex); -#pragma deprecated Use SF2ChaserBossProfileData.Attacks instead. -native float SF2_GetBossAttackProfileFloat(const char[] profile, const char[] key, float defaultValue=0.0, const int attackIndex); +#pragma deprecated Use SF2_ChaserBossProfileBaseAttack instead. +native float SF2_GetBossAttackProfileFloat(const char[] profile, const char[] key, float defaultValue = 0.0, const int attackIndex); -#pragma deprecated Use SF2ChaserBossProfileData.Attacks instead. -native bool SF2_GetBossAttackProfileString(const char[] profile, const char[] key, char[] buffer, int bufferLen, const char[] defaultValue="", const int attackIndex); +#pragma deprecated Use SF2_ChaserBossProfileBaseAttack instead. +native bool SF2_GetBossAttackProfileString(const char[] profile, const char[] key, char[] buffer, int bufferLen, const char[] defaultValue = "", const int attackIndex); -#pragma deprecated Use SF2ChaserBossProfileData.Attacks instead. -native bool SF2_GetBossAttackProfileVector(const char[] profile, const char[] key, float buffer[3], const float defaultValue[3]=NULL_VECTOR, const int attackIndex); +#pragma deprecated Use SF2_ChaserBossProfileBaseAttack instead. +native bool SF2_GetBossAttackProfileVector(const char[] profile, const char[] key, float buffer[3], const float defaultValue[3] = NULL_VECTOR, const int attackIndex); -#pragma deprecated Use SF2BossProfileData instead. -native bool SF2_GetRandomStringFromBossProfile(const char[] profile, const char[] key, char[] buffer, int bufferLen, int index=-1); +#pragma deprecated Use SF2_BaseBossProfile instead. +native bool SF2_GetRandomStringFromBossProfile(const char[] profile, const char[] key, char[] buffer, int bufferLen, int index = -1); -#pragma deprecated Use SF2BossProfileData instead. +#pragma deprecated Use SF2_BaseBossProfile instead. native bool SF2_GetBossAttributeName(int bossIndex, int attribute); -#pragma deprecated Use SF2BossProfileData instead. +#pragma deprecated Use SF2_BaseBossProfile instead. native float SF2_GetBossAttributeValue(int bossIndex, int attribute); #pragma deprecated Use SF2_ChaserBossEntity.AttackIndex instead. native int SF2_GetBossCurrentAttackIndex(int bossIndex); -#pragma deprecated Use SF2ChaserBossProfileAttackData.Type instead. +#pragma deprecated Use SF2_ChaserBossProfileBaseAttack instead. native int SF2_GetBossAttackIndexType(int bossIndex, int attackIndex); #pragma deprecated This no longer does anything. @@ -1626,10 +1633,10 @@ native void SF2_PerformBossVoice(int bossIndex, const int attackIndex = -1, int #pragma deprecated Use SF2_ChaserBossEntity.CreateSoundHint instead. native void SF2_CreateBossSoundHint(int bossIndex, SoundType soundType, const float position[3], int difficulty); -#pragma deprecated Use SF2ChaserBossProfileAttackData.Damage[Difficulty_Max] instead. +#pragma deprecated Use SF2_ChaserBossProfileBaseAttack instead. native float SF2_GetBossAttackIndexDamage(int bossIndex, int attackIndex, int difficulty); -#pragma deprecated Use SF2ChaserBossProfileAttackData.DamageType[Difficulty_Max] instead. +#pragma deprecated Use SF2_ChaserBossProfileBaseAttack instead. native int SF2_GetBossAttackIndexDamageType(int bossIndex, int attackIndex); #pragma deprecated Use SF2_BaseBossEntity.ResetProfileAnimation instead. @@ -1655,47 +1662,25 @@ native void SF2_SetBossTeleportThinkTimer(int bossIndex, Handle timer); /** * Returns the boss' teleport target * - * @param bossIndex Boss index. + * @param bossIndex Boss index. * @param difficulty The difficulty index. - * @return Teleport entity index. + * @return Teleport entity index. */ native int SF2_GetBossTeleportTarget(int bossIndex); -/** - * Returns the StringMap variable containing every precached base boss profile - * - * @return StringMap containing all base boss profile datas -*/ -native StringMap SF2_GetBossProfileData(); - -/** - * Returns the StringMap variable containing every precached chaser boss profile - * - * @return StringMap containing all chaser boss profile datas -*/ -native StringMap SF2_GetChaserBossProfileData(); - -/** - * Returns the StringMap variable containing every precached statue boss profile - * - * @return StringMap containing all statue boss profile datas -*/ -native StringMap SF2_GetStatueBossProfileData(); - /** * Spawns boss effects based on the list of effects used. * Note this will not spawn the extra effects like the festive lights and the disco ball * Only use this best for spawning in temporary effects. - * Note the array of effects must be an ArrayList that contains SF2BossProfileBaseEffectInfo. * - * @param effects The array of effects + * @param effects The effects section to use * @param bossIndex Boss to use * @param overridePos Override the spawn position of the effects * @param overrideAng Override the spawn angles of the effects * @param output The ArrayList that will contain all of the outputted entities as entity references * @param entityOverride If not INVALID_ENT_REFERENCE will attach all particles to the desired entity */ -native void SF2_SpawnBossEffects(ArrayList effects, int bossIndex, float overridePos[3] = NULL_VECTOR, float overrideAng[3] = NULL_VECTOR, ArrayList &output = null, int entityOverride = INVALID_ENT_REFERENCE); +native void SF2_SpawnBossEffects(SF2_ProfileEffectMaster effects, int bossIndex, float overridePos[3] = NULL_VECTOR, float overrideAng[3] = NULL_VECTOR, ArrayList &output = null, int entityOverride = INVALID_ENT_REFERENCE); /** * Returns if a boss can be seen by any player @@ -1710,7 +1695,7 @@ native bool SF2_CanBossBeSeen(int bossIndex, bool checkFOV = true, bool checkBli /** * Translates an activity name string into an Activity. * - * @param sActivity + * @param activity * @return Activity, or ACT_INVALID if not found. */ native Activity SF2_TranslateProfileActivityFromName(const char[] activity); @@ -1718,59 +1703,59 @@ native Activity SF2_TranslateProfileActivityFromName(const char[] activity); /** * Given an activity or sequence, translates into a sequence index. * - * @param iEntity Entity index - * @param sAnimation Activity/sequence string + * @param entity Entity index + * @param animation Activity/sequence string * @return Sequence index, or -1 if not found. */ native int SF2_LookupProfileAnimation(int entity, const char[] animation); /** - * Returns the profile data found on a boss index, note this is the basic profile data, I.E. what all bosses use + * Returns the profile data found on a boss index * * @param bossIndex Boss index. - * @param data Data to be stored in. Note this takes in SF2BossProfileData + * @return Stored profile data, or null if it doesn't exist */ -native void SF2_GetProfileFromBossIndex(int bossIndex, any data[sizeof(SF2BossProfileData)]); +native SF2_BaseBossProfile SF2_GetProfileFromBossIndex(int bossIndex); /** * Returns the chaser profile data found on a boss index. * * @param bossIndex Boss index. - * @param data Data to be stored in. Note this takes in SF2ChaserBossProfileData + * @return Stored profile data, or null if it doesn't exist */ -native void SF2_GetChaserProfileFromBossIndex(int bossIndex, any data[sizeof(SF2ChaserBossProfileData)]); +native SF2_ChaserBossProfile SF2_GetChaserProfileFromBossIndex(int bossIndex); /** * Returns the statue profile data found on a boss index. * * @param bossIndex Boss index. - * @param data Data to be stored in. Note this takes in SF2StatueBossProfileData + * @return Stored profile data, or null if it doesn't exist */ -native void SF2_GetStatueProfileFromBossIndex(int bossIndex, any data[sizeof(SF2StatueBossProfileData)]); +native SF2_StatueBossProfile SF2_GetStatueProfileFromBossIndex(int bossIndex); /** - * Returns the profile data found from a profile name, note this is the basic profile data, I.E. what all bosses use + * Returns the profile data found from a profile name * * @param profile Profile name. - * @param data Data to be stored in. Note this takes in SF2BossProfileData + * @return Stored profile data, or null if it doesn't exist */ -native bool SF2_GetProfileFromName(const char[] profile, any data[sizeof(SF2BossProfileData)]); +native SF2_BaseBossProfile SF2_GetProfileFromName(const char[] profile); /** * Returns the chaser profile data found from a profile name. * * @param profile Profile name. - * @param data Data to be stored in. Note this takes in SF2ChaserBossProfileData + * @return Stored profile data, or null if it doesn't exist */ -native bool SF2_GetChaserProfileFromName(const char[] profile, any data[sizeof(SF2ChaserBossProfileData)]); +native SF2_ChaserBossProfile SF2_GetChaserProfileFromName(const char[] profile); /** * Returns the statue profile data found from a profile name. * * @param profile Profile name. - * @param data Data to be stored in. Note this takes in SF2StatueBossProfileData + * @return Stored profile data, or null if it doesn't exist */ -native bool SF2_GetStatueProfileFromName(const char[] profile, any data[sizeof(SF2ChaserBossProfileData)]); +native SF2_StatueBossProfile SF2_GetStatueProfileFromName(const char[] profile); /** * Forces a boss to enter in the chase state and immediately go after the target. @@ -1803,6 +1788,14 @@ methodmap SF2_BaseBossEntity < CBaseCombatCharacter public native get(); } + /** + * Gets the controller methodmap that owns this entity. + */ + property SF2_BaseBossController ControllerEx + { + public native get(); + } + /** * Gets the target entity index. */ @@ -1871,375 +1864,1669 @@ methodmap SF2_BaseBossEntity < CBaseCombatCharacter public native void GetName(char[] buffer, int bufferLen); /** - * Attempts to cast an entity to be this entity. + * Prevents any animation from being played + */ + property bool LockAnimations + { + public native get(); + public native set(bool value); + } + + /** + * Attempts to cast an entity to be this entity. + * + * @param entity Entity to use + */ + public SF2_BaseBossEntity(int entity) + { + return view_as(entity); + } + + /** + * Gets the boss's profile data. + * + * @return Stored profile data, or null if it doesn't exist + */ + public native SF2_BaseBossProfile ProfileData(); + + /** + * Plays an animation on the boss from a specific animation section, even custom ones. Note this is limited to the main "animations" section. + * + * @param animType Animation section to search in. + * @param preDefinedIndex Optional animation index to use. + * @param preDefinedName Optional animation name to use. + * @param duration Optional animation duration return. + */ + public native void ResetProfileAnimation(const char[] animType, int preDefinedIndex = -1, const char[] preDefinedName = "", float &duration = 0.0) +} + +/** + * The methodmap designed for statue bosses. + */ +methodmap SF2_StatueBossEntity < SF2_BaseBossEntity +{ + /** + * Whether or not the entity is actually a statue boss + */ + property bool IsValid + { + public native get(); + } + + /** + * Gets the controller methodmap that owns this entity. + */ + property SF2_StatueBossController Controller + { + public native get(); + } + + /** + * Returns whether or not the statue is moving. + */ + property bool IsMoving + { + public native get(); + } + + /** + * Gets the time since the statue killed someone. + */ + property float LastKillTime + { + public native get(); + } + + /** + * Gets the boss's profile data. + * + * @return Stored profile data, or null if it doesn't exist + */ + public native SF2_StatueBossProfile ProfileData(); + + /** + * Attempts to cast an entity to be this entity. + * + * @param entity Entity to use + */ + public SF2_StatueBossEntity(int entity) + { + return view_as(entity); + } +} + +methodmap SF2_ChaserBossEntity < SF2_BaseBossEntity +{ + /** + * Whether or not the entity is actually a chaser boss + */ + property bool IsValid + { + public native get(); + } + + /** + * Gets the controller methodmap that owns this entity. + */ + property SF2_ChaserBossController Controller + { + public native get(); + } + + /** + * Attempts to cast an entity to be this entity. + * + * @param entity Entity to use + */ + public SF2_ChaserBossEntity(int entity) + { + return view_as(entity); + } + + /** + * Whether or not the boss is attempting to move somewhere else. + */ + property bool IsAttemptingToMove + { + public native get(); + } + + /** + * Whether or not the boss is attacking. + */ + property bool IsAttacking + { + public native get(); + } + + /** + * Whether or not the boss is stunned or not. + */ + property bool IsStunned + { + public native get(); + } + + /** + * The current stun health. + */ + property int StunHealth + { + public native get(); + } + + /** + * The maximum stun health the boss spawns with. + */ + property int MaxStunHealth + { + public native get(); + } + + /** + * Whether or not stun health can be subtracted from the boss. This is + * affected by several factors including: if stunning is enabled or + * stun cooldown has elapsed. + */ + property bool CanBeStunned + { + public native get(); + } + + /** + * Whether or not the boss can take damage. This is mainly useful if the + * boss is performing an attack that makes them invulnerable or are currently + * in a rage state. + */ + property bool CanTakeDamage + { + public native get(); + } + + /** + * Whether or not the boss is currently raging. + */ + property bool IsRaging + { + public native get(); + } + + /** + * Whether or not the boss is currently running away to go heal. + */ + property bool IsRunningAway + { + public native get(); + } + + /** + * Whether or not the boss is currently trying to self heal. + */ + property bool IsSelfHealing + { + public native get(); + } + + /** + * Gets the boss's profile data. + * + * @return Stored profile data, or null if it doesn't exist + */ + public native SF2_ChaserBossProfile ProfileData(); + + /** + * Forces the boss to perform a voice. Note custom sound sections will not work. + * + * @param soundType Sound type. + * @param attackName Optional attack name to search for. + * @return Whether or not the boss performed a voice. + */ + public native bool PerformVoice(int soundType = -1, const char[] attackName = ""); + + /** + * Forces the boss to perform a voice using a custom sound section. + * + * @param soundInfo Sound data to use. + * @return Whether or not the boss performed a voice. + */ + public native bool PerformCustomVoice(SF2_ProfileSound soundInfo); + + /** + * Gets the default posture of the boss. The default posture of a boss + * is named "default". + * + * @param buffer Buffer to store string + * @param bufferSize Size of buffer + * @return Number of characters written to buffer + */ + public native int GetDefaultPosture(char[] buffer, int bufferSize); + + /** + * Sets the default posture of the boss. + * + * @param buffer Posture to set to + * @param updateAnimations Optional bool to determine if the animations should update upon setting the posture. + * @return True if successful, false otherwise + */ + public native bool SetDefaultPosture(const char[] buffer, bool updateAnimations = true); + + /** + * Gets the current attack name of the boss. + * + * @param buffer Buffer to store string + * @param bufferSize Size of buffer + * @return Number of characters written to buffer + */ + public native int GetAttackName(char[] buffer, int bufferSize); + + /** + * Gets the current attack index of the boss. + */ + property int AttackIndex + { + public native get(); + } + + /** + * Gets the time which the attack can be used again + * + * @param attackName Attack name + * @return Timestamp + */ + public native float GetNextAttackTime(const char[] attackName); + + /** + * Sets the time which the attack can be used again + * + * @param attackName Attack name + * @param time Timestamp + */ + public native void SetNextAttackTime(const char[] attackName, float time); + + /** + * Drops a medkit/ammo pack depending on how the boss is configured + * + * @param death Whether or not to use the death state's item drop state + */ + public native void DropItem(bool death = false); + + /** + * Forces a boss to be alert at a specific position. + * + * @param position Position the boss should be alerted to. + */ + public native void CreateSoundHint(float position[3]); + + /** + * Enables/Disables the ability for bosses to utilize their ground movement as their speed. + * Only use this for situations where the boss does not constantly path towards someone. + */ + property bool GroundSpeedOverride + { + public native get(); + public native set(bool value); + } + + /** + * Sets/returns the boss's current movement type + */ + property SF2NPCMoveTypes MovementType + { + public native get(); + public native set(SF2NPCMoveTypes value); + } + + /** + * Prevents the plugin from setting the MovementType + * Note that any attempts to set MovementType via a sub plugin will work as normal + */ + property bool LockMovementType + { + public native get(); + public native set(bool value); + } + + /** + * Checks if an attack is possible the default way + * This only includes FOV and range checks + * Difficulty checks and health checks are not included by default + * Note that cooldown checks are already handled in SF2_ChaserEntity.GetAttackAction + * And visibility checks are already handled in SF2_ChaserAttackLayerAction + * You would use this primarily in SF2_OnIsBossCustomAttackPossible + * + * @param attack Attack data to use + * @param includeDifficulties Whether or not to include the difficulty checks + * @param includeHealth Whether or not to include target health checks + * @return Whether or not the attack is possible with the distance and FOV checks + */ + public bool IsAttackPossibleDefault(SF2_ChaserBossProfileBaseAttack attackData, bool includeDifficulties = false, bool includeHealth = false) + { + if (!this.Target.IsValid()) + { + return false; + } + + SF2_ChaserBossController controller = this.Controller; + int difficulty = controller.Difficulty; + + INextBot bot = this.MyNextBotPointer(); + float eyePos[3], targetPos[3], direction[3], eyeAng[3]; + this.GetAbsAngles(eyeAng); + SF2_GetBossEyePosition(controller.Index, eyePos); + this.Target.GetAbsOrigin(targetPos); + SubtractVectors(targetPos, eyePos, direction); + GetVectorAngles(direction, direction); + direction[2] = 180.0; + + float distance = bot.GetRangeSquaredTo(this.Target.index); + float fov = FloatAbs(AngleDiff(direction[1], eyeAng[1])); + + if (distance > Pow(attackData.GetBeginRange(difficulty), 2.0)) + { + return false; + } + + if (fov > attackData.GetBeginFOV(difficulty)) + { + return false; + } + + if (includeDifficulties) + { + if (difficulty < attackData.UseOnDifficulty) + { + return false; + } + + if (difficulty >= attackData.BlockOnDifficulty) + { + return false; + } + } + + if (includeHealth) + { + float health = float(this.Target.GetProp(Prop_Send, "m_iHealth")); + if (attackData.CanUseOnHealth(difficulty) != -1.0 && health < attackData.CanUseOnHealth(difficulty)) + { + return false; + } + + if (attackData.CanBlockOnHealth(difficulty) != -1.0 && health >= attackData.CanBlockOnHealth(difficulty)) + { + return false; + } + } + return true; + } +} + +/** + * An object that contains data from a profile. + */ +methodmap SF2_ProfileObject < Handle +{ + /** + * Retrieve the parent section of this object. + */ + property SF2_ProfileObject Parent + { + public native get(); + } + + /** + * Retrieve the number of keys (not sections) that exist in this object. + */ + property int KeyLength + { + public native get(); + } + + /** + * Retrieve the number of sections (not keys) that exist in this object. + */ + property int SectionLength + { + public native get(); + } + + /** + * Retrieves the object's name + * + * @param buffer String buffer + * @param bufferSize Size of string buffer + */ + public native void GetSectionName(char[] buffer, int bufferSize); + + /** + * Retrieves a key's name from a specified index. + * + * @param index Index to use + * @param buffer String buffer + * @param bufferSize Size of string buffer + * @return True on success, false otherwise + */ + public native bool GetKeyNameFromIndex(int index, char[] buffer, int bufferSize); + + /** + * Retrieves a section's name from a specified index. + * + * @param index Index to use + * @param buffer String buffer + * @param bufferSize Size of string buffer + * @return True on success, false otherwise + */ + public native bool GetSectionNameFromIndex(int index, char[] buffer, int bufferSize); + + /** + * Retrieves an integer value from the given key. + * + * @param key Key name + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native int GetInt(const char[] key, int defaultValue = 0); + + /** + * Sets an integer value with the given key. + * + * @param key Key name + * @param value Value to set + */ + public native void SetInt(const char[] key, int value); + + /** + * Retrieves a boolean value from the given key. + * + * @param key Key name + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native bool GetBool(const char[] key, bool defaultValue = false); + + /** + * Sets an boolean value with the given key. + * + * @param key Key name + * @param value Value to set + */ + public native void SetBool(const char[] key, bool value); + + /** + * Retrieves a float value from the given key. + * + * @param key Key name + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native float GetFloat(const char[] key, float defaultValue = 0.0); + + /** + * Sets an bool value with the given key. + * + * @param key Key name + * @param value Value to set + */ + public native void SetFloat(const char[] key, float value); + + /** + * Retrieves a section from the given key. + * + * @param key Key name + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native SF2_ProfileObject GetSection(const char[] key, SF2_ProfileObject defaultValue = view_as(INVALID_HANDLE)); + + /** + * Retrieves an array from the given key. + * + * @param key Key name + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native SF2_ProfileArray GetArray(const char[] key, SF2_ProfileArray defaultValue = view_as(INVALID_HANDLE)); + + /** + * Retrieves a string value from the given key. + * + * @param key Key name + * @param buffer String buffer + * @param bufferSize Size of string buffer + * @param defaultValue Value to return as a fallback + * @return Number of chars copied to buffer, excluding null terminator + */ + public native void GetString(const char[] key, char[] buffer, int bufferSize, const char[] defaultValue = ""); + + /** + * Sets an string value with the given key. + * + * @param key Key name + * @param value Value to set + */ + public native void SetString(const char[] key, const char[] value); + + /** + * Retrieves a Vector value from the given key. + * + * @param key Key name + * @param buffer Vector buffer + * @param defaultValue Value to return as a fallback + */ + public native void GetVector(const char[] key, float buffer[3], const float defaultValue[3] = { 0.0, 0.0, 0.0 }); + + /** + * Sets an Vector value with the given key. + * + * @param key Key name + * @param value Value to set + */ + public native void SetVector(const char[] key, float value[3]); + + /** + * Retrieves a Color value from the given key. + * + * @param key Key name + * @param buffer Color buffer + * @param defaultValue Value to return as a fallback + */ + public native void GetColor(const char[] key, int buffer[4], const int defaultValue[4] = { 0, 0, 0, 0 }); + + /** + * Sets an Color value with the given key. + * + * @param key Key name + * @param value Value to set + */ + public native void SetColor(const char[] key, int value[4]); + + /** + * Retrieves an integer value from the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native int GetDifficultyInt(const char[] key, int difficulty, int defaultValue = 0); + + /** + * Sets an integer value with the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param value Value to set + */ + public native void SetDifficultyInt(const char[] key, int difficulty, int value); + + /** + * Retrieves a boolean value from the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native bool GetDifficultyBool(const char[] key, int difficulty, bool defaultValue = false); + + /** + * Sets an boolean value with the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param value Value to set + */ + public native void SetDifficultyBool(const char[] key, int difficulty, bool value); + + /** + * Retrieves a float value from the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native float GetDifficultyFloat(const char[] key, int difficulty, float defaultValue = 0.0); + + /** + * Sets an float value with the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param value Value to set + */ + public native void SetDifficultyFloat(const char[] key, int difficulty, int value); + + /** + * Retrieves a section from the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native SF2_ProfileObject GetDifficultySection(const char[] key, int difficulty, SF2_ProfileObject defaultValue = view_as(INVALID_HANDLE)); + + /** + * Retrieves a string value from the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param buffer String buffer + * @param bufferSize Size of string buffer + * @param defaultValue Value to return as a fallback + */ + public native void GetDifficultyString(const char[] key, int difficulty, char[] buffer, int bufferSize, const char[] defaultValue = ""); + + /** + * Sets an string value with the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param value Value to set + */ + public native void SetDifficultyString(const char[] key, int difficulty, const char[] value); + + /** + * Retrieves a Vector value from the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param buffer Float buffer + * @param defaultValue Value to return as a fallback + */ + public native void GetDifficultyVector(const char[] key, int difficulty, float buffer[3], const float defaultValue[3] = { 0.0, 0.0, 0.0 }); + + /** + * Sets an Vector value with the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param value Value to set + */ + public native void SetDifficultyVector(const char[] key, int difficulty, float value[3]); + + /** + * Retrieves a Color value from the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param buffer Float buffer + * @param defaultValue Value to return as a fallback + */ + public native void GetDifficultyColor(const char[] key, int difficulty, int buffer[4], const int defaultValue[4] = { 0, 0, 0, 0 }); + + /** + * Sets an Color value with the given difficulty key. + * + * @param key Key name + * @param difficulty Difficulty value + * @param value Value to set + */ + public native void SetDifficultyColor(const char[] key, int difficulty, int value[4]); + + /** + * Helper function that converts a string index map to a string array. + * + * @param key Key name + */ + public native void ConvertValuesSectionToArray(const char[] key); + + /** + * Helper function that converts an object index map to a string array. + * + * @param key Key name + */ + public native void ConvertSectionsSectionToArray(const char[] key); +} + +methodmap SF2_ProfileArray < Handle +{ + /** + * Retrieve the size of the array. + */ + property int Length + { + public native get(); + } + + /** + * Retrieves an integer value from the given key. + * + * @param index Index number + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native int GetInt(int index, int defaultValue = 0); + + /** + * Retrieves a boolean value from the given key. + * + * @param index Index number + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native bool GetBool(int index, bool defaultValue = false); + + /** + * Retrieves a float value from the given key. + * + * @param index Index number + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native float GetFloat(int index, float defaultValue = 0.0); + + /** + * Retrieves a section from the given key. + * + * @param index Index number + * @param defaultValue Value to return as a fallback + * @return Value stored at key + */ + public native SF2_ProfileObject GetSection(int index, SF2_ProfileObject defaultValue = view_as(INVALID_HANDLE)); + + /** + * Retrieves a string value from the given key. + * + * @param index Index number + * @param buffer String buffer + * @param bufferSize Size of string buffer + * @param defaultValue Value to return as a fallback + */ + public native void GetString(int index, char[] buffer, int bufferSize, const char[] defaultValue = ""); +} + +/** + * Profile object that stores sound data of an animation. + */ +methodmap SF2_ProfileSound < SF2_ProfileObject +{ + /** + * Returns the sound paths this object uses. + */ + property SF2_ProfileArray Paths + { + public native get(); + } + + /** + * Sets the default channel value of this sound object. + * + * @param channel Sound channel + */ + public native void SetDefaultChannel(int channel); + + /** + * Sets the default level value of this sound object. + * + * @param level Sound level + */ + public native void SetDefaultLevel(int level); + + /** + * Sets the default flags value of this sound object. + * + * @param flags Sound flags + */ + public native void SetDefaultFlags(int flags); + + /** + * Sets the default minimum cooldown value of this sound object. + * + * @param cooldown Sound cooldown + */ + public native void SetDefaultCooldownMin(float cooldown); + + /** + * Sets the default maximum cooldown value of this sound object. + * + * @param cooldown Sound cooldown + */ + public native void SetDefaultCooldownMax(float cooldown); + + /** + * Gets the channel. + * + * @param difficulty Difficulty value + * @return Sound channel + */ + public native int GetChannel(int difficulty); + + /** + * Gets the volume. + * + * @param difficulty Difficulty value + * @return Sound volume + */ + public native float GetVolume(int difficulty); + + /** + * Gets the flags. + * + * @param difficulty Difficulty value + * @return Sound flags + */ + public native int GetFlags(int difficulty); + + /** + * Gets the level. + * + * @param difficulty Difficulty value + * @return Sound level + */ + public native int GetLevel(int difficulty); + + /** + * Gets the pitch. + * + * @param difficulty Difficulty value + * @return Sound pitch + */ + public native int GetPitch(int difficulty); + + /** + * Gets the minimum cooldown. + * + * @param difficulty Difficulty value + * @return Sound min cooldown + */ + public native float GetCooldownMin(int difficulty); + + /** + * Gets the maximum cooldown. + * + * @param difficulty Difficulty value + * @return Sound max cooldown + */ + public native float GetCooldownMax(int difficulty); + + /** + * Precaches all sounds and adds to downloads. + */ + public native void Precache(); + + /** + * Helper function that calls EmitSoundToAll with the parameters given by this + * object. + * + * @param entIndex Entity index to emit sound from + * @param difficulty Difficulty value + */ + public native void EmitToAll(int entIndex = SOUND_FROM_PLAYER, int difficulty = Difficulty_Normal); + + /** + * Helper function that calls EmitSoundToClient with the parameters given by this + * object. + * + * @param client Client index + * @param entIndex Entity index to emit sound from + * @param difficulty Difficulty value + */ + public native void EmitToClient(int client, int entIndex = SOUND_FROM_PLAYER, int difficulty = Difficulty_Normal); + + /** + * Stops all sounds in this section from playing. + * + * @param entity Entity index to stop the sounds from + */ + public native void StopAllSounds(int entity); +} + +/** + * Profile object that stores data of an animation. + * Note this covers 1 animation index, not the whole animations section or an animation named section like "idle". + * Use this over SF2_ProfileMasterAnimation if you're for sure you're gonna only use 1 animation only. + */ +methodmap SF2_ProfileAnimation < SF2_ProfileObject +{ + /** + * Retrieves a sequence or activity name. + * + * @param difficulty Difficulty value + * @param buffer String buffer to store the animation name + * @param bufferSize Size of buffer + */ + public native void GetAnimationName(int difficulty, char[] buffer, int bufferSize); + + /** + * Retrieves a gesture sequence or activity name. + * + * @param difficulty Difficulty value + * @param buffer String buffer to store the gesture name + * @param bufferSize Size of buffer + */ + public native void GetGestureName(int difficulty, char[] buffer, int bufferSize); + + /** + * Gets the playback rate of the animation. + * + * @param difficulty Difficulty value + * @return Playback rate + */ + public native float GetAnimationPlaybackRate(int difficulty); + + /** + * Gets the playback rate of the gesture. + * + * @param difficulty Difficulty value + * @return Playback rate + */ + public native float GetGesturePlaybackRate(int difficulty); + + /** + * Gets the duration of the animation. + * + * @param difficulty Difficulty value + * @return Duration + */ + public native float GetDuration(int difficulty); + + /** + * Gets the starting cycle value of the animation. + * + * @param difficulty Difficulty value + * @return Cycle + */ + public native float GetAnimationCycle(int difficulty); + + /** + * Gets the starting cycle value of the gesture. + * + * @param difficulty Difficulty value + * @return Cycle + */ + public native float GetGestureCycle(int difficulty); + + /** + * Plays an animation on the entity using the parameters provided by the object. + * + * @param entIndex Entity index to play on + * @param difficulty Difficulty value + * @param loops Whether the animation should loop or not + * @return True if successful, false otherwise. + */ + public native bool PlayAnimation(int entIndex, int difficulty, bool loops = false); + + /** + * Plays an gesture/layered animation on the entity using the parameters provided by the object. + * + * @note Layered animations can only be used on entities derived from CBaseAnimatingOverlay. + * @param entIndex Entity index to play on + * @param difficulty Difficulty value + * @param loops Whether the animation should loop or not + * @param layer Value to store the layer ID of the animation. + * @return True if successful, false otherwise. + */ + public native bool PlayGesture(int entIndex, int difficulty, bool loops = false, int& layer = -1); +} + +/** + * Profile object that stores all animation sections + * Note this covers the "animations" section. + */ +methodmap SF2_ProfileMasterAnimation < SF2_ProfileObject +{ + /** + * Returns if an animation section exists + * + * @param animType Animation section to search for + */ + public native bool HasAnimationSection(const char[] animType); + + /** + * Returns an animation index section based off of the given animation section name + * + * @param animType Animation section to search for + * @param preDefinedIndex If greater than -1 will pick a specified animation in the animation section based off the order in the config + * @param preDefinedName If not an empty string will pick a specified animation in the animation section based off the animation section name + * @param index Value to store the animation index in. + * @return Animation indexed section or null if the section does not exist + */ + public native SF2_ProfileAnimation GetAnimation(const char[] animType, int preDefinedIndex = -1, const char[] preDefinedName = "", int& index = -1); +} + +/** + * A section that defines the properties of an entity or other special effect. + */ +methodmap SF2_ProfileEffect < SF2_ProfileObject +{ + /** + * The effect type. + */ + property EffectType Type + { + public native get(); + } + + /** + * Precaches all assets and adds them to the downloads. + */ + public native void Precache(); +} + +/** + * A section that contains `SF2_ProfileEffect` objects meant to be spawned + * together. + */ +methodmap SF2_ProfileEffectMaster < SF2_ProfileObject +{ + /** + * Precaches all assets and adds them to the downloads. + */ + public native void Precache(); + + /** + * Spawns boss effects based on the list of effects used. + * Note this will not spawn the extra effects like the festive lights and the disco ball + * Only use this best for spawning in temporary effects. + * + * @param bossIndex Boss to use + * @param overridePos Override the spawn position of the effects + * @param overrideAng Override the spawn angles of the effects + * @param output The ArrayList that will contain all of the outputted entities as entity references + * @param entityOverride If not INVALID_ENT_REFERENCE will attach all particles to the desired entity + */ + public native void Spawn(int bossIndex, float overridePos[3] = NULL_VECTOR, float overrideAng[3] = NULL_VECTOR, ArrayList &output = null, int entityOverride = INVALID_ENT_REFERENCE); +} + +methodmap SF2_BaseBossProfile < SF2_ProfileObject +{ + /** + * The boss type. + */ + property int Type + { + public native get(); + } + + /** + * Determines if the boss is for the PvE arenas. + */ + property bool IsPvEBoss + { + public native get(); + } +} + +methodmap SF2_StatueBossProfile < SF2_BaseBossProfile +{ + +} + +methodmap SF2_ChaserBossProfile < SF2_BaseBossProfile +{ + /** + * Gets the amount of attacks stored in the Chaser boss profile. + * + * @return Amount of attacks + * @error Profile data is null or is not a Chaser boss profile + */ + public native int GetAttackCount(); + + /** + * Gets a Chaser boss profile's attack data with the given name. + * + * @param name Attack name + * @return The attack data, or null if it doesn't exist + * @error Profile data is null or is not a Chaser boss profile + */ + public native SF2_ChaserBossProfileBaseAttack GetAttack(const char[] name); + + /** + * Gets a Chaser boss profile's attack data with the given index. + * + * @param index Attack index + * @return The attack data, or null if it doesn't exist + * @error Profile data is null or is not a Chaser boss profile + */ + public native SF2_ChaserBossProfileBaseAttack GetAttackFromIndex(int index); +} + +methodmap SF2_ChaserBossProfileBaseAttack < SF2_ProfileObject +{ + /** + * The type of the attack. + */ + property int Type + { + public native get(); + } + + /** + * How long to wait after starting the attack to damage targets. + * + * @param difficulty Difficulty value + * @return Time to delay + */ + public native float GetDamageDelay(int difficulty); + + /** + * The range of the attack, in Hammer units. + * + * @param difficulty Difficulty value + * @return Range of attack + */ + public native float GetRange(int difficulty); + + /** + * How much damage to inflict to players. + * + * @param difficulty Difficulty value + * @return Damage of attack + */ + public native float GetDamage(int difficulty); + + /** + * The bitflag type value of the damage. + * + * @param difficulty Difficulty value + * @return Bitflags + */ + public native int GetDamageType(int difficulty); + + /** + * How much damage force to inflict to players. + * + * @param difficulty Difficulty value + * @return Damage force of attack + */ + public native float GetDamageForce(int difficulty); + + /** + * How much viewpunch to inflict on players that get hit by the attack. + * + * @param difficulty Difficulty value + * @param viewPunch Vector buffer to store punch angles + */ + public native void GetViewPunchAngles(int difficulty, float viewPunch[3]); + + /** + * How long the attack should last. + * + * @param difficulty Difficulty value + * @return Time + */ + public native float GetDuration(int difficulty); + + /** + * How wide the attack is, in degrees. + * + * @param difficulty Difficulty value + * @return FOV of attack + */ + public native float GetFOV(int difficulty); + + /** + * The maximum distance where the boss should use the attack. + * + * @param difficulty Difficulty value + * @return Range of atack + */ + public native float GetBeginRange(int difficulty); + + /** + * How much the player must be within this FOV should the boss use the attack. + * + * @param difficulty Difficulty value + * @return FOV of attack + */ + public native float GetBeginFOV(int difficulty); + + /** + * How long to wait until the boss can use this attack again. + * + * @param difficulty Difficulty value + * @return Cooldown of attack + */ + public native float GetCooldown(int difficulty); + + /** + * Determines what the minimum difficulty must be for this attack to be used on + * Anything lower than this threshold blocks the attack. + */ + property int UseOnDifficulty + { + public native get(); + } + + /** + * Determines what the maximum difficulty must not be for this attack to be used + * Anything higher than this threshold blocks the attack. + */ + property int BlockOnDifficulty + { + public native get(); + } + + /** + * Determines how much health the target must have at maximum for this attack to be used. + * Anything higher than this threshold blocks the attack. + * + * @param difficulty Difficulty value + * @return Maximum health threshold + */ + public native float CanUseOnHealth(int difficulty); + + /** + * Determines how much health the target must have at minimum for this attack to be used. + * Anything lower than this threshold blocks the attack. + * + * @param difficulty Difficulty value + * @return Minimum health threshold + */ + public native float CanBlockOnHealth(int difficulty); + + /** + * The animation's event index that'll be used for an attack to fire. + * + * @param difficulty Difficulty value + * @return Animation event index + */ + public native int GetEventNumber(int difficulty); +} + +/** + * The data of an attack with type `SF2BossAttackType_Custom`. + */ +methodmap SF2_ChaserBossProfileCustomAttack < SF2_ChaserBossProfileBaseAttack +{ + /** + * An identifier string of the custom attack. + * + * @param buffer Buffer to store the string + * @param bufferSize Size of buffer + */ + public native void GetSubType(char[] buffer, int bufferSize); + + /** + * Gets if the attack matches the given subtype string. + * + * @param subType String to check + * @return True if attack's subtype matches, false if not. + */ + public native bool IsSubType(const char[] subType); +} + +/** + * An action that plays a given sequence on the actor. This action exits when the + * duration has elapsed. + */ +methodmap SF2_PlaySequenceAndWaitAction < NextBotAction +{ + /** + * @param sequence Sequence index + * @param duration How long the action should last. If 0.0, then this will be the sequence's + * duration. + * @param rate Playback rate + * @param cycle Start cycle value, from 0.0 to 1.0 + */ + public native SF2_PlaySequenceAndWaitAction(int sequence, float duration = 0.0, float rate = 1.0, float cycle = 0.0); +} + +/** + * Interface to SF2NPC_BaseNPC + */ +const SF2_BaseBossController SF2_INVALID_BASE_CONTROLLER = view_as(-1); +methodmap SF2_BaseBossController +{ + /** + * Casts an boss index to this methodmap * - * @param entity Entity to use + * @param index Boss index to use + * @return Casted controller */ - public SF2_BaseBossEntity(int entity) + public SF2_BaseBossController(int index) { - return view_as(entity); + return view_as(index); } /** - * Gets the boss's profile data. - * - * @param data Data to be stored in. Note this takes in SF2BossProfileData - */ - public native void ProfileData(any data[sizeof(SF2BossProfileData)]); - - /** - * Plays an animation on the boss from a specific animation section, even custom ones. Note this is limited to the main "animations" section. - * - * @param animType Animation section to search in. - * @param preDefinedIndex Optional animation index to use. - * @param preDefinedName Optional animation name to use. - * @param duration Optional animation duration return. + * Returns if the boss controller is valid */ - public native void ResetProfileAnimation(const char[] animType, int preDefinedIndex = -1, const char[] preDefinedName = "", float &duration = 0.0) -} + public native bool IsValid(); -/** - * The methodmap designed for statue bosses. - */ -methodmap SF2_StatueBossEntity < SF2_BaseBossEntity -{ /** - * Whether or not the entity is actually a statue boss + * Returns the boss index this controller is associated with */ - property bool IsValid + property int Index { public native get(); } /** - * Returns whether or not the statue is moving. + * Returns the boss's PathFollower variable + * Requires CBaseNPC to be included in the sub plugin */ - property bool IsMoving + #if defined _CBASENPC_EXTENSION_INC_ + property PathFollower Path { public native get(); } + #endif /** - * Gets the time since the statue killed someone. + * Returns the boss's unique ID */ - property float LastKillTime + property int UniqueID { public native get(); } /** - * Gets the boss's profile data. + * Returns a controller when given an unique ID * - * @param data Data to be stored in. Note this takes in SF2StatueBossProfileData + * @param uniqueID Unique ID, cannot be negative */ - public native void ProfileData(any data[sizeof(SF2StatueBossProfileData)]); + public static native SF2_BaseBossController FromUniqueID(int uniqueID); /** - * Attempts to cast an entity to be this entity. + * Returns a controller when given an entity index * - * @param entity Entity to use + * @param entity Entity index */ - public SF2_StatueBossEntity(int entity) - { - return view_as(entity); - } -} + public static native SF2_BaseBossController FromEntity(int entity); -methodmap SF2_ChaserBossEntity < SF2_BaseBossEntity -{ /** - * Whether or not the entity is actually a chaser boss + * Returns the boss's entity reference or INVALID_ENT_REFERENCE if the boss is not on the map */ - property bool IsValid + property int EntRef { public native get(); } /** - * Attempts to cast an entity to be this entity. - * - * @param entity Entity to use + * Returns the boss's entity index */ - public SF2_ChaserBossEntity(int entity) + property int EntIndex { - return view_as(entity); + public native get(); } /** - * Whether or not the boss is attempting to move somewhere else. + * Returns the boss's profile data */ - property bool IsAttemptingToMove - { - public native get(); - } + public native SF2_BaseBossProfile GetProfileData(); /** - * Whether or not the boss is attacking. + * Gets/Sets the current difficulty by default + * If the map is a boxing map or the boss is a PvE boss, this Gets/Sets the boss's local difficulty */ - property bool IsAttacking + property int Difficulty { public native get(); + public native set(int value); } /** - * Whether or not the boss is stunned or not. + * Gets/Sets the boss's flags */ - property bool IsStunned + property int Flags { public native get(); + public native set(int value); } /** - * The current stun health. + * Gets/Sets the boss's copy master */ - property int StunHealth + property SF2_BaseBossController CopyMaster { public native get(); + public native set(SF2_BaseBossController value); } /** - * The maximum stun health the boss spawns with. + * Returns if the boss is a copy or not */ - property int MaxStunHealth + property bool IsCopy { public native get(); } /** - * Whether or not stun health can be subtracted from the boss. This is - * affected by several factors including: if stunning is enabled or - * stun cooldown has elapsed. + * Gets/Sets the boss's companion master */ - property bool CanBeStunned + property SF2_BaseBossController CompanionMaster { public native get(); + public native set(SF2_BaseBossController value); } /** - * Whether or not the boss can take damage. This is mainly useful if the - * boss is performing an attack that makes them invulnerable or are currently - * in a rage state. + * Returns the boss's queued teleporter it should go to as an entity reference + * The max teleporters the boss can use is 5 defined by MAX_NPCTELEPORTER + * + * @param teleporterNumber Index to search */ - property bool CanTakeDamage - { - public native get(); - } + public native int GetTeleporter(int teleporterNumber); /** - * Whether or not the boss is currently raging. + * Sets the boss's queued teleporter via an entity reference + * Use INVALID_ENT_REFERENCE to clear a boss's queued teleporter + * The max teleporters the boss can use is 5 defined by MAX_NPCTELEPORTER + * + * @param teleporterNumber Index to use + * @param ref Teleporter entity reference + * @error Entity is not a trigger_teleporter or INVALID_ENT_REFERENCE */ - property bool IsRaging - { - public native get(); - } + public native void SetTeleporter(int teleporterNumber, int ref); /** - * Whether or not the boss is currently running away to go heal. + * Spawns the boss on a set position + * + * @param pos Position */ - property bool IsRunningAway - { - public native get(); - } + public native void Spawn(float pos[3]); /** - * Whether or not the boss is currently trying to self heal. + * Despawns the boss + * + * @param instant Some bosses have despawn animation, this determines if the boss shouldn't use the despawn animations */ - property bool IsSelfHealing + public native void UnSpawn(bool instant = false); + + /** + * Despawns the boss and removes it from the game + */ + public native void Remove(); + + /** + * Returns if the boss is able to despawn + */ + property bool CanDespawn { public native get(); } /** - * Gets the boss's profile data. - * - * @param data Data to be stored in. Note this takes in SF2ChaserBossProfileData + * Marks the boss as a fake boss even if the boss is not a fake boss */ - public native void ProfileData(any data[sizeof(SF2ChaserBossProfileData)]); + public native void MarkAsFake(); /** - * Forces the boss to perform a voice. Note custom sound sections will not work. + * Gets the boss's profile name * - * @param soundType Sound type. - * @param attackName Optional attack name to search for. - * @return Whether or not the boss performed a voice. + * @param buffer Buffer to store the boss's profile name + * @param bufferLen Maximum length of string buffer */ - public native bool PerformVoice(int soundType = -1, const char[] attackName = "") + public native void GetProfile(char[] buffer, int bufferLen); /** - * Forces the boss to perform a voice using a custom sound section. + * Sets the boss's profile name * - * @param soundInfo Sound section to use. - * @return Whether or not the boss performed a voice. + * @param profileName New profile name */ - public native bool PerformCustomVoice(any soundInfo[sizeof(SF2BossProfileSoundInfo)]) + public native void SetProfile(const char[] profileName); /** - * Gets the default posture of the boss. The default posture of a boss - * is named "default". + * Returns the boss's current added run speed + */ + public native float GetAddSpeed(); + + /** + * Returns the boss's current added run speed that doesn't reset when the boss despawn + */ + public native float GetPersistentAddSpeed(); + + /** + * Adds onto the boss's current added run speed * - * @param buffer Buffer to store string - * @param bufferSize Size of buffer - * @return Number of characters written to buffer + * @param value Value */ - public native int GetDefaultPosture(char[] buffer, int bufferSize); + public native void SetAddSpeed(float value); /** - * Sets the default posture of the boss. + * Adds onto the boss's current added run speed that doesn't reset when the boss despawn * - * @param buffer Posture to set to - * @return True if successful, false otherwise + * @param value Value */ - public native void SetDefaultPosture(const char[] buffer); + public native void SetPersistentAddSpeed(float value); /** - * Gets the current attack name of the boss. + * Returns the boss's current added walk speed that doesn't reset when the boss despawn + */ + public native float GetPersistentAddWalkSpeed(); + + /** + * Adds onto the boss's current added walk speed that doesn't reset when the boss despawn * - * @param buffer Buffer to store string - * @param bufferSize Size of buffer - * @return Number of characters written to buffer + * @param value Value */ - public native int GetAttackName(char[] buffer, int bufferSize); + public native void SetPersistentAddWalkSpeed(float value); /** - * Gets the current attack index of the boss. + * Returns the boss's current added acceleration */ - property int AttackIndex - { - public native get(); - } + public native float GetAddAcceleration(); /** - * Gets the time which the attack can be used again + * Returns the boss's current added acceleration that doesn't reset when the boss despawn + */ + public native float GetPersistentAddAcceleration(); + + /** + * Adds onto the boss's current added acceleration * - * @param attackName Attack name - * @return Timestamp + * @param value Value */ - public native float GetNextAttackTime(const char[] attackName); + public native void SetAddAcceleration(float value); /** - * Sets the time which the attack can be used again + * Adds onto the boss's current added acceleration that doesn't reset when the boss despawn * - * @param attackName Attack name - * @param time Timestamp + * @param value Value */ - public native void SetNextAttackTime(const char[] attackName, float time); + public native void SetPersistentAddAcceleration(float value); /** - * Drops a medkit/ammo pack depending on how the boss is configured + * Determines if any player can see the boss * - * @param death Whether or not to use the death state's item drop state + * @param fov Determines if player FOV should be considered + * @param blink Determines if player blink states should be considered + * @param checkEliminated Determines if eliminated players should be considered */ - public native void DropItem(bool death = false); + public native bool CanBeSeen(bool fov = true, bool blink = false, bool checkEliminated = true); /** - * Forces a boss to be alert at a specific position. + * Determines if a specified player can see the boss * - * @param position Position the boss should be alerted to. + * @param client Client to check + * @param fov Determines if player FOV should be considered + * @param blink Determines if player blink states should be considered + * @param checkEliminated Determines if eliminated players should be considered */ - public native void CreateSoundHint(float position[3]); + public native bool PlayerCanSee(int client, bool fov = true, bool blink = false, bool checkEliminated = false); /** - * Enables/Disables the ability for bosses to utilize their ground movement as their speed. - * Only use this for situations where the boss does not constantly path towards someone. + * Returns if the boss should force a player to start blinking when they look at this boss */ - property bool GroundSpeedOverride - { - public native get(); - public native set(bool value); - } + public native bool IsAffectedBySight(); /** - * Checks if an attack is possible the default way - * This only includes FOV and range checks - * Difficulty checks and health checks are not included by default - * Note that cooldown checks are already handled in SF2_ChaserEntity.GetAttackAction - * And visibility checks are already handled in SF2_ChaserAttackLayerAction - * You would use this primarily in SF2_OnIsBossCustomAttackPossible + * Sets if a player should start blinking when looking at this boss * - * @param attackName Name of the attack - * @param includeDifficulties Whether or not to include the difficulty checks - * @param includeHealth Whether or not to include target health checks - * @return Whether or not the attack is possible with the distance and FOV checks + * @param state State */ - public bool IsAttackPossibleDefault(const char[] attackName, bool includeDifficulties = false, bool includeHealth = false) - { - if (!this.Target.IsValid()) - { - return false; - } - - int controller = this.Controller; - int difficulty = SF2_GetBossDifficulty(controller); + public native void SetAffectedBySight(bool state); +} - INextBot bot = this.MyNextBotPointer(); - SF2ChaserBossProfileData data; - this.ProfileData(data); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); - float eyePos[3], targetPos[3], direction[3], eyeAng[3]; - this.GetAbsAngles(eyeAng); - SF2_GetBossEyePosition(controller, eyePos); - this.Target.GetAbsOrigin(targetPos); - SubtractVectors(targetPos, eyePos, direction); - GetVectorAngles(direction, direction); - direction[2] = 180.0; +/** + * Interface to SF2NPC_Chaser + */ +const SF2_ChaserBossController SF2_INVALID_CHASER_CONTROLLER = view_as(-1); +methodmap SF2_ChaserBossController < SF2_BaseBossController +{ + /** + * Casts an boss index to this methodmap + * + * @param index Boss index to use + */ + public SF2_ChaserBossController(int index) + { + return view_as(SF2_BaseBossController(index)); + } - float distance = bot.GetRangeSquaredTo(this.Target.index); - float fov = FloatAbs(AngleDiff(direction[1], eyeAng[1])); + /** + * Returns the boss's profile data as an SF2_ChaserBossProfile + */ + public native SF2_ChaserBossProfile ProfileData(); - if (distance > Pow(attackData.BeginRange[difficulty], 2.0)) - { - return false; - } + /** + * Returns the boss's current on spawned death health + * + * @param difficulty Difficulty to check + */ + public native float GetDeathHealth(int difficulty); - if (fov > attackData.BeginFOV[difficulty]) - { - return false; - } + /** + * Sets the boss's current on spawned death health + * + * @param difficulty Difficulty to check + */ + public native void SetDeathHealth(int difficulty, float amount); - if (includeDifficulties) - { - if (difficulty < attackData.UseOnDifficulty) - { - return false; - } + /** + * Returns the boss's current added stun health + */ + public native float GetAddStunHealth(); - if (difficulty >= attackData.BlockOnDifficulty) - { - return false; - } - } + /** + * Adds onto the boss's current added stun health + * + * @param value Value + */ + public native void SetAddStunHealth(float value); - if (includeHealth) - { - float health = float(this.Target.GetProp(Prop_Send, "m_iHealth")); - if (attackData.UseOnHealth != -1.0 && health < attackData.UseOnHealth) - { - return false; - } - if (attackData.BlockOnHealth != -1.0 && health >= attackData.BlockOnHealth) - { - return false; - } - } - return true; + /** + * Returns an ArrayList of targets that looked at the boss if it is meant to auto chase targets upon looking + */ + property ArrayList ChaseOnLookTargets + { + public native get(); } } /** - * An action that plays a given sequence on the actor. This action exits when the - * duration has elapsed. + * Interface to SF2NPC_Statue */ -methodmap SF2_PlaySequenceAndWaitAction < NextBotAction +const SF2_StatueBossController SF2_INVALID_STATUE_CONTROLLER = view_as(-1); +methodmap SF2_StatueBossController < SF2_BaseBossController { /** - * @param sequence Sequence index - * @param duration How long the action should last. If 0.0, then this will be the sequence's - * duration. - * @param rate Playback rate - * @param cycle Start cycle value, from 0.0 to 1.0 + * Casts an boss index to this methodmap + * + * @param index Boss index to use */ - public native SF2_PlaySequenceAndWaitAction(int sequence, float duration = 0.0, float rate = 1.0, float cycle = 0.0); + public SF2_StatueBossController(int index) + { + return view_as(SF2_BaseBossController(index)); + } + + /** + * Returns the boss's profile data as an SF2_StatueBossProfile + */ + public native SF2_StatueBossProfile ProfileData(); } /** @@ -2284,7 +3571,7 @@ methodmap SF2_Player < CBaseCombatCharacter /** * Alias of IsClientInGame() - * Use this over IsClientInGame() though as it has extra optimizations + * Use this over IsClientInGame() as it has extra optimizations */ property bool IsInGame { @@ -2375,6 +3662,7 @@ methodmap SF2_Player < CBaseCombatCharacter */ public native int GetDataEnt(int offset); + #if defined _tf2_included /** * Alias of TF2_GetPlayerClass() */ @@ -2382,6 +3670,7 @@ methodmap SF2_Player < CBaseCombatCharacter { public native get(); } + #endif /** * Alias of GetClientTeam() @@ -2401,6 +3690,7 @@ methodmap SF2_Player < CBaseCombatCharacter public native set(bool state); } + #if defined _tf2_included /** * Alias of TF2_IsPlayerInCondition() * Use this over TF2_IsPlayerInCondition() though as it has extra optimizations @@ -2418,6 +3708,7 @@ methodmap SF2_Player < CBaseCombatCharacter * @param inflictor Inflictor of the condition */ public native bool ChangeCondition(TFCond condition, bool remove = false, float duration = -1.0, int inflictor = 0); + #endif /** * Whether or not the client is crit boosted @@ -2462,6 +3753,7 @@ methodmap SF2_Player < CBaseCombatCharacter */ public native void Regenerate(); + #if defined _tf2_included /** * Alias of TF2_SetPlayerClass() * @@ -2470,6 +3762,7 @@ methodmap SF2_Player < CBaseCombatCharacter * @param persistent Whether or not the class change should remain after death */ public native void SetClass(TFClassType classType, bool weapons = true, bool persistent = true); + #endif /** * Alias of GetClientEyePosition() @@ -2773,7 +4066,7 @@ methodmap SF2_Player < CBaseCombatCharacter public native void HandleSprint(bool sprint); /** - * Gets/sets the player's stamina + * Gets/Sets the player's stamina */ property float Stamina { @@ -2919,13 +4212,6 @@ methodmap SF2_Player < CBaseCombatCharacter public native set(int value); } - /** - * Updates the player's music state - * - * @param initialize Unknown????? Nobody knows what this actually does. - */ - public native void UpdateMusicSystem(bool initialize = false); - /** * Whether or not the player has a glow active */ @@ -3558,6 +4844,48 @@ public SharedPlugin __pl_sf2 = public void __pl_sf2_SetNTVOptional() { // sf2/methodmaps.sp + MarkNativeAsOptional("SF2_BaseBossController.IsValid"); + MarkNativeAsOptional("SF2_BaseBossController.Index.get"); + MarkNativeAsOptional("SF2_BaseBossController.Path.get"); + MarkNativeAsOptional("SF2_BaseBossController.UniqueID.get"); + MarkNativeAsOptional("SF2_BaseBossController.FromUniqueID"); + MarkNativeAsOptional("SF2_BaseBossController.FromEntity"); + MarkNativeAsOptional("SF2_BaseBossController.EntRef.get"); + MarkNativeAsOptional("SF2_BaseBossController.EntIndex.get"); + MarkNativeAsOptional("SF2_BaseBossController.ProfileData"); + MarkNativeAsOptional("SF2_BaseBossController.Difficulty.get"); + MarkNativeAsOptional("SF2_BaseBossController.Difficulty.set"); + MarkNativeAsOptional("SF2_BaseBossController.Flags.get"); + MarkNativeAsOptional("SF2_BaseBossController.Flags.set"); + MarkNativeAsOptional("SF2_BaseBossController.CopyMaster.get"); + MarkNativeAsOptional("SF2_BaseBossController.CopyMaster.set"); + MarkNativeAsOptional("SF2_BaseBossController.IsCopy.get"); + MarkNativeAsOptional("SF2_BaseBossController.CompanionMaster.get"); + MarkNativeAsOptional("SF2_BaseBossController.CompanionMaster.set"); + MarkNativeAsOptional("SF2_BaseBossController.GetTeleporter"); + MarkNativeAsOptional("SF2_BaseBossController.SetTeleporter"); + MarkNativeAsOptional("SF2_BaseBossController.Spawn"); + MarkNativeAsOptional("SF2_BaseBossController.UnSpawn"); + MarkNativeAsOptional("SF2_BaseBossController.Remove"); + MarkNativeAsOptional("SF2_BaseBossController.CanDespawn.get"); + MarkNativeAsOptional("SF2_BaseBossController.MarkAsFake"); + MarkNativeAsOptional("SF2_BaseBossController.GetProfile"); + MarkNativeAsOptional("SF2_BaseBossController.SetProfile"); + MarkNativeAsOptional("SF2_BaseBossController.GetAddSpeed"); + MarkNativeAsOptional("SF2_BaseBossController.GetPersistentAddSpeed"); + MarkNativeAsOptional("SF2_BaseBossController.SetAddSpeed"); + MarkNativeAsOptional("SF2_BaseBossController.SetPersistentAddSpeed"); + MarkNativeAsOptional("SF2_BaseBossController.GetPersistentAddWalkSpeed"); + MarkNativeAsOptional("SF2_BaseBossController.SetPersistentAddWalkSpeed"); + MarkNativeAsOptional("SF2_BaseBossController.GetAddAcceleration"); + MarkNativeAsOptional("SF2_BaseBossController.GetPersistentAddAcceleration"); + MarkNativeAsOptional("SF2_BaseBossController.SetAddAcceleration"); + MarkNativeAsOptional("SF2_BaseBossController.SetPersistentAddAcceleration"); + MarkNativeAsOptional("SF2_BaseBossController.CanBeSeen"); + MarkNativeAsOptional("SF2_BaseBossController.PlayerCanSee"); + MarkNativeAsOptional("SF2_BaseBossController.IsAffectedBySight"); + MarkNativeAsOptional("SF2_BaseBossController.SetAffectedBySight"); + MarkNativeAsOptional("SF2_Player.UserID.get"); MarkNativeAsOptional("SF2_Player.IsValid.get"); MarkNativeAsOptional("SF2_Player.IsAlive.get"); @@ -3657,7 +4985,6 @@ public void __pl_sf2_SetNTVOptional() MarkNativeAsOptional("SF2_Player.LatchCount.set"); MarkNativeAsOptional("SF2_Player.Latcher.get"); MarkNativeAsOptional("SF2_Player.Latcher.set"); - MarkNativeAsOptional("SF2_Player.UpdateMusicSystem"); MarkNativeAsOptional("SF2_Player.HasConstantGlow.get"); MarkNativeAsOptional("SF2_Player.SetPlayState"); MarkNativeAsOptional("SF2_Player.CanSeeSlender"); @@ -3668,6 +4995,15 @@ public void __pl_sf2_SetNTVOptional() MarkNativeAsOptional("SF2_Player.SetForceChaseState"); MarkNativeAsOptional("SF2_Player.IsLookingAtBoss"); + MarkNativeAsOptional("SF2_ChaserBossController.ProfileData"); + MarkNativeAsOptional("SF2_ChaserBossController.GetDeathHealth"); + MarkNativeAsOptional("SF2_ChaserBossController.SetDeathHealth"); + MarkNativeAsOptional("SF2_ChaserBossController.GetAddStunHealth"); + MarkNativeAsOptional("SF2_ChaserBossController.SetAddStunHealth"); + MarkNativeAsOptional("SF2_ChaserBossController.ChaseOnLookTargets"); + + MarkNativeAsOptional("SF2_StatueBossController.ProfileData"); + // sf2/npc.sp MarkNativeAsOptional("SF2_GetMaxBossCount"); MarkNativeAsOptional("SF2_EntIndexToBossIndex"); @@ -3730,9 +5066,6 @@ public void __pl_sf2_SetNTVOptional() MarkNativeAsOptional("SF2_GetBossAttributeName"); MarkNativeAsOptional("SF2_GetBossAttributeValue"); - MarkNativeAsOptional("SF2_GetBossProfileData"); - MarkNativeAsOptional("SF2_GetChaserBossProfileData"); - MarkNativeAsOptional("SF2_GetStatueBossProfileData"); MarkNativeAsOptional("SF2_TranslateProfileActivityFromName"); MarkNativeAsOptional("SF2_LookupProfileAnimation"); @@ -3797,7 +5130,6 @@ public void __pl_sf2_SetNTVOptional() MarkNativeAsOptional("SF2_Projectile_SentryRocket.IsValid.get"); // extras/natives.sp - MarkNativeAsOptional("SF2_GetConfig"); MarkNativeAsOptional("SF2_IsRunning"); MarkNativeAsOptional("SF2_GetRoundState"); MarkNativeAsOptional("SF2_IsRoundInGracePeriod"); @@ -3896,6 +5228,7 @@ public void __pl_sf2_SetNTVOptional() // npc/entities/base/entity.sp MarkNativeAsOptional("SF2_BaseBossEntity.IsValid.get"); MarkNativeAsOptional("SF2_BaseBossEntity.Controller.get"); + MarkNativeAsOptional("SF2_BaseBossEntity.ControllerEx.get"); MarkNativeAsOptional("SF2_BaseBossEntity.Target.get"); MarkNativeAsOptional("SF2_BaseBossEntity.State.get"); MarkNativeAsOptional("SF2_BaseBossEntity.CurrentChaseDuration.get"); @@ -3906,6 +5239,8 @@ public void __pl_sf2_SetNTVOptional() MarkNativeAsOptional("SF2_BaseBossEntity.EyePosition"); MarkNativeAsOptional("SF2_BaseBossEntity.GetProfileName"); MarkNativeAsOptional("SF2_BaseBossEntity.GetName"); + MarkNativeAsOptional("SF2_BaseBossEntity.LockAnimations.get"); + MarkNativeAsOptional("SF2_BaseBossEntity.LockAnimations.set"); MarkNativeAsOptional("SF2_BaseBossEntity.ProfileData"); MarkNativeAsOptional("SF2_BaseBossEntity.ResetProfileAnimation"); @@ -3914,6 +5249,7 @@ public void __pl_sf2_SetNTVOptional() // npc/entities/chaser/entity.sp MarkNativeAsOptional("SF2_ChaserBossEntity.IsValid.get"); + MarkNativeAsOptional("SF2_ChaserBossEntity.Controller.get"); MarkNativeAsOptional("SF2_ChaserBossEntity.IsAttemptingToMove.get"); MarkNativeAsOptional("SF2_ChaserBossEntity.IsAttacking.get"); MarkNativeAsOptional("SF2_ChaserBossEntity.IsStunned.get"); @@ -3937,11 +5273,124 @@ public void __pl_sf2_SetNTVOptional() MarkNativeAsOptional("SF2_ChaserBossEntity.CreateSoundHint"); MarkNativeAsOptional("SF2_ChaserBossEntity.GroundSpeedOverride.get"); MarkNativeAsOptional("SF2_ChaserBossEntity.GroundSpeedOverride.set"); + MarkNativeAsOptional("SF2_ChaserBossEntity.MovementType.get"); + MarkNativeAsOptional("SF2_ChaserBossEntity.MovementType.set"); + MarkNativeAsOptional("SF2_ChaserBossEntity.LockMovementType.get"); + MarkNativeAsOptional("SF2_ChaserBossEntity.LockMovementType.set"); // npc/entities/statue/entity.sp MarkNativeAsOptional("SF2_StatueBossEntity.IsValid.get"); + MarkNativeAsOptional("SF2_StatueBossEntity.Controller.get"); MarkNativeAsOptional("SF2_StatueBossEntity.IsMoving.get"); MarkNativeAsOptional("SF2_StatueBossEntity.LastKillTime.get"); MarkNativeAsOptional("SF2_StatueBossEntity.ProfileData"); + + MarkNativeAsOptional("SF2_ProfileObject.Parent.get"); + MarkNativeAsOptional("SF2_ProfileObject.KeyLength.get"); + MarkNativeAsOptional("SF2_ProfileObject.SectionLength.get"); + MarkNativeAsOptional("SF2_ProfileObject.GetSectionName"); + MarkNativeAsOptional("SF2_ProfileObject.GetKeyNameFromIndex"); + MarkNativeAsOptional("SF2_ProfileObject.GetSectionNameFromIndex"); + MarkNativeAsOptional("SF2_ProfileObject.GetInt"); + MarkNativeAsOptional("SF2_ProfileObject.SetInt"); + MarkNativeAsOptional("SF2_ProfileObject.GetBool"); + MarkNativeAsOptional("SF2_ProfileObject.SetBool"); + MarkNativeAsOptional("SF2_ProfileObject.GetFloat"); + MarkNativeAsOptional("SF2_ProfileObject.SetFloat"); + MarkNativeAsOptional("SF2_ProfileObject.GetSection"); + MarkNativeAsOptional("SF2_ProfileObject.GetArray"); + MarkNativeAsOptional("SF2_ProfileObject.GetString"); + MarkNativeAsOptional("SF2_ProfileObject.SetString"); + MarkNativeAsOptional("SF2_ProfileObject.GetVector"); + MarkNativeAsOptional("SF2_ProfileObject.SetVector"); + MarkNativeAsOptional("SF2_ProfileObject.GetColor"); + MarkNativeAsOptional("SF2_ProfileObject.SetColor"); + MarkNativeAsOptional("SF2_ProfileObject.GetDifficultyInt"); + MarkNativeAsOptional("SF2_ProfileObject.SetDifficultyInt"); + MarkNativeAsOptional("SF2_ProfileObject.GetDifficultyBool"); + MarkNativeAsOptional("SF2_ProfileObject.SetDifficultyBool"); + MarkNativeAsOptional("SF2_ProfileObject.GetDifficultyFloat"); + MarkNativeAsOptional("SF2_ProfileObject.SetDifficultyFloat"); + MarkNativeAsOptional("SF2_ProfileObject.GetDifficultySection"); + MarkNativeAsOptional("SF2_ProfileObject.GetDifficultyString"); + MarkNativeAsOptional("SF2_ProfileObject.SetDifficultyString"); + MarkNativeAsOptional("SF2_ProfileObject.GetDifficultyVector"); + MarkNativeAsOptional("SF2_ProfileObject.SetDifficultyVector"); + MarkNativeAsOptional("SF2_ProfileObject.GetDifficultyColor"); + MarkNativeAsOptional("SF2_ProfileObject.SetDifficultyColor"); + MarkNativeAsOptional("SF2_ProfileObject.ConvertValuesSectionToArray"); + MarkNativeAsOptional("SF2_ProfileObject.ConvertSectionsSectionToArray"); + + MarkNativeAsOptional("SF2_ProfileArray.Length.get"); + MarkNativeAsOptional("SF2_ProfileArray.GetInt"); + MarkNativeAsOptional("SF2_ProfileArray.GetBool"); + MarkNativeAsOptional("SF2_ProfileArray.GetFloat"); + MarkNativeAsOptional("SF2_ProfileArray.GetSection"); + MarkNativeAsOptional("SF2_ProfileArray.GetString"); + + MarkNativeAsOptional("SF2_ProfileSound.Paths.get"); + MarkNativeAsOptional("SF2_ProfileSound.SetDefaultChannel"); + MarkNativeAsOptional("SF2_ProfileSound.SetDefaultLevel"); + MarkNativeAsOptional("SF2_ProfileSound.SetDefaultFlags"); + MarkNativeAsOptional("SF2_ProfileSound.SetDefaultCooldownMin"); + MarkNativeAsOptional("SF2_ProfileSound.SetDefaultCooldownMax"); + MarkNativeAsOptional("SF2_ProfileSound.GetChannel"); + MarkNativeAsOptional("SF2_ProfileSound.GetVolume"); + MarkNativeAsOptional("SF2_ProfileSound.GetFlags"); + MarkNativeAsOptional("SF2_ProfileSound.GetLevel"); + MarkNativeAsOptional("SF2_ProfileSound.GetPitch"); + MarkNativeAsOptional("SF2_ProfileSound.GetCooldownMin"); + MarkNativeAsOptional("SF2_ProfileSound.GetCooldownMax"); + MarkNativeAsOptional("SF2_ProfileSound.Precache"); + MarkNativeAsOptional("SF2_ProfileSound.EmitToAll"); + MarkNativeAsOptional("SF2_ProfileSound.EmitToClient"); + MarkNativeAsOptional("SF2_ProfileSound.StopAllSounds"); + + MarkNativeAsOptional("SF2_ProfileAnimation.GetAnimationName"); + MarkNativeAsOptional("SF2_ProfileAnimation.GetGestureName"); + MarkNativeAsOptional("SF2_ProfileAnimation.GetAnimationPlaybackRate"); + MarkNativeAsOptional("SF2_ProfileAnimation.GetGesturePlaybackRate"); + MarkNativeAsOptional("SF2_ProfileAnimation.GetDuration"); + MarkNativeAsOptional("SF2_ProfileAnimation.GetAnimationCycle"); + MarkNativeAsOptional("SF2_ProfileAnimation.GetGestureCycle"); + MarkNativeAsOptional("SF2_ProfileAnimation.PlayAnimation"); + MarkNativeAsOptional("SF2_ProfileAnimation.PlayGesture"); + + MarkNativeAsOptional("SF2_ProfileMasterAnimation.HasAnimationSection"); + MarkNativeAsOptional("SF2_ProfileMasterAnimation.GetAnimation"); + + MarkNativeAsOptional("SF2_ProfileEffect.Type.get"); + MarkNativeAsOptional("SF2_ProfileEffect.Precache"); + + MarkNativeAsOptional("SF2_ProfileEffectMaster.Precache"); + MarkNativeAsOptional("SF2_ProfileEffectMaster.Spawn"); + + MarkNativeAsOptional("SF2_BaseBossProfile.Type.get"); + MarkNativeAsOptional("SF2_BaseBossProfile.IsPvEBoss.get"); + + MarkNativeAsOptional("SF2_ChaserBossProfile.GetAttackCount"); + MarkNativeAsOptional("SF2_ChaserBossProfile.GetAttack"); + MarkNativeAsOptional("SF2_ChaserBossProfile.GetAttackFromIndex"); + + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.Type.get"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetDamageDelay"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetRange"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetDamage"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetDamageType"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetDamageForce"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetViewPunchAngles"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetDuration"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetFOV"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetBeginRange"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetBeginFOV"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetCooldown"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.UseOnDifficulty.get"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.BlockOnDifficulty.get"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.CanUseOnHealth"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.CanBlockOnHealth"); + MarkNativeAsOptional("SF2_ChaserBossProfileBaseAttack.GetEventNumber"); + + MarkNativeAsOptional("SF2_ChaserBossProfileCustomAttack.GetSubType"); + MarkNativeAsOptional("SF2_ChaserBossProfileCustomAttack.IsSubType"); } #endif \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/sf2/defines.inc b/addons/sourcemod/scripting/include/sf2/defines.inc index 801baafa..f0d011f0 100644 --- a/addons/sourcemod/scripting/include/sf2/defines.inc +++ b/addons/sourcemod/scripting/include/sf2/defines.inc @@ -17,6 +17,7 @@ #define FIREWORKSRED_PARTICLENAME "utaunt_firework_teamcolor_red" #define TELEPORTEDINBLU_PARTICLENAME "teleported_red" #define SOUND_THUNDER ")ambient/explosions/explode_9.wav" +#define DEBUG_PAGEREVEALSOUND "#ui/cyoa_node_activate.wav" #define SPECIAL1UPSOUND "mvm/mvm_revive.wav" diff --git a/addons/sourcemod/scripting/include/sf2/profiles/profiles.inc b/addons/sourcemod/scripting/include/sf2/profiles/profiles.inc index 7a09cc3a..f04b243f 100644 --- a/addons/sourcemod/scripting/include/sf2/profiles/profiles.inc +++ b/addons/sourcemod/scripting/include/sf2/profiles/profiles.inc @@ -100,7 +100,7 @@ stock void TryPrecacheBossProfileSoundPath(const char[] soundPath, bool checkFil * @param xbox Determines if the .xbox file extension should be checked. * @param checkFile Determines if missing files should be checked. */ -stock int PrecacheModel2(const char[] path, bool phy = true, bool xbox = false, bool checkFile) +stock int PrecacheModel2(const char[] path, bool phy = true, bool xbox = false, bool checkFile = false) { if (path[0] == '\0') { @@ -117,6 +117,7 @@ stock int PrecacheModel2(const char[] path, bool phy = true, bool xbox = false, { strcopy(fixedPath, sizeof(fixedPath), path); } + ReplaceString(fixedPath, sizeof(fixedPath), "\\", "/", false); ReplaceString(fixedPath, sizeof(fixedPath), ".mdl", "", false); @@ -191,23 +192,6 @@ stock int PrecacheModel2(const char[] path, bool phy = true, bool xbox = false, } } - FormatEx(buffer, sizeof(buffer), "models/%s.sw.vtx", fixedPath); - if (checkFile) - { - if (FileExists(buffer, true)) - { - AddFileToDownloadsTable(buffer); - } - else - { - PrintToServer("[SF2] File %s is missing from the server files!", buffer); - } - } - else - { - AddFileToDownloadsTable(buffer); - } - FormatEx(buffer, sizeof(buffer), "models/%s.vvd", fixedPath); if (checkFile) { @@ -259,8 +243,19 @@ stock int PrecacheModel2(const char[] path, bool phy = true, bool xbox = false, */ stock void PrecacheMaterial2(const char[] path, bool checkFile) { - char buffer[PLATFORM_MAX_PATH]; - FormatEx(buffer, sizeof(buffer), "materials/%s.vmt", path); + char buffer[PLATFORM_MAX_PATH], fixedPath[PLATFORM_MAX_PATH]; + if (strncmp(path, "materials/", 10, false) == 0 || strncmp(path, "materials\\", 10, false) == 0) + { + strcopy(fixedPath, sizeof(fixedPath), path[10]); + } + else + { + strcopy(fixedPath, sizeof(fixedPath), path); + } + ReplaceString(fixedPath, sizeof(fixedPath), "\\", "/", false); + ReplaceString(fixedPath, sizeof(fixedPath), ".vmt", "", false); + ReplaceString(fixedPath, sizeof(fixedPath), ".vtf", "", false); + FormatEx(buffer, sizeof(buffer), "materials/%s.vmt", fixedPath); if (checkFile) { if (FileExists(buffer, true)) @@ -276,7 +271,7 @@ stock void PrecacheMaterial2(const char[] path, bool checkFile) { AddFileToDownloadsTable(buffer); } - FormatEx(buffer, sizeof(buffer), "materials/%s.vtf", path); + FormatEx(buffer, sizeof(buffer), "materials/%s.vtf", fixedPath); if (checkFile) { if (FileExists(buffer, true)) @@ -294,6 +289,22 @@ stock void PrecacheMaterial2(const char[] path, bool checkFile) } } +/** + * Removes the materials/ characters from a directory. Some materials like overlay + * or rope materials break if you try to use materials/. + * This also removes the .vmt or .vtf extensions. + * + * @param path Path to remove the characters from + * @param bufferSize Size of path + */ +stock void StripMaterialsFolder(char[] path, int bufferSize) +{ + ReplaceString(path, bufferSize, "\\", "/", false); + ReplaceString(path, bufferSize, ".vmt", "", false); + ReplaceString(path, bufferSize, ".vtf", "", false); + ReplaceString(path, bufferSize, "materials/", "", false); +} + /** * Precaches a particle system found in the string table ParticleEffectNames * @@ -327,7 +338,7 @@ stock int PrecacheParticleSystem(const char[] particleSystem) return index; } -int FindStringIndex2(int tableidx, const char[] str) +stock int FindStringIndex2(int tableidx, const char[] str) { char buf[1024]; @@ -634,7 +645,7 @@ static const char g_DifficultyDefaultStringValues[Difficulty_Max][] = * @param maxStringLen Maximum length of strings in buffer array * @param defaultValueStrings Array of default values if no key with the base key name is found */ -void GetProfileDifficultyStringValues(KeyValues kv, const char[] baseKeyName, char[][] bufferArray, int maxStringLen, const char[][] defaultValueStrings = g_DifficultyDefaultStringValues) +stock void GetProfileDifficultyStringValues(KeyValues kv, const char[] baseKeyName, char[][] bufferArray, int maxStringLen, const char[][] defaultValueStrings = g_DifficultyDefaultStringValues) { for (int i = 0; i < Difficulty_Max; i++) { @@ -704,7 +715,7 @@ stock void SetProfileDifficultyStringArrayValues(KeyValues kv, const char[] base } } -bool GetProfileColorNoBacks(KeyValues kv, +stock bool GetProfileColorNoBacks(KeyValues kv, const char[] keyValue, int &r, int &g, @@ -749,7 +760,7 @@ static const int g_DifficultyDefaultColorValues[Difficulty_Max][4] = * @param buffer Array to store evaluated values * @param defaultValueStrings Array of default values if no key with the base key name is found */ -void GetProfileDifficultyColorValues(KeyValues kv, const char[] baseKeyName, int buffer[Difficulty_Max][4], const int defaultColorValues[Difficulty_Max][4] = g_DifficultyDefaultColorValues) +stock void GetProfileDifficultyColorValues(KeyValues kv, const char[] baseKeyName, int buffer[Difficulty_Max][4], const int defaultColorValues[Difficulty_Max][4] = g_DifficultyDefaultColorValues) { for (int i = 0; i < Difficulty_Max; i++) { @@ -791,11 +802,83 @@ void GetProfileDifficultyColorValues(KeyValues kv, const char[] baseKeyName, int } } +enum EffectEvent +{ + EffectEvent_Invalid = -1, + EffectEvent_Constant = 0, + EffectEvent_HitPlayer, + EffectEvent_PlayerSeesBoss +}; + +enum EffectType +{ + EffectType_Invalid = -1, + EffectType_Steam = 0, + EffectType_DynamicLight, + EffectType_Particle, + EffectType_Trail, + EffectType_PropDynamic, + EffectType_PointSpotlight, + EffectType_Sprite, + EffectType_TempEntBeamRing, + EffectType_TempEntParticle, + EffectType_Sound, + EffectType_ScreenShake +}; + +enum +{ + SF2BossAttackType_Invalid = -1, + SF2BossAttackType_Melee = 0, + SF2BossAttackType_Ranged = 1, + SF2BossAttackType_Projectile = 2, + SF2BossAttackType_ExplosiveDance = 3, + SF2BossAttackType_LaserBeam = 4, + SF2BossAttackType_Custom = 5, + SF2BossAttackType_Tongue = 6, + SF2BossAttackType_Combo = 7 +}; + +enum +{ + SF2BossProjectileType_Invalid = -1, + SF2BossProjectileType_Fireball = 0, + SF2BossProjectileType_Iceball = 1, + SF2BossProjectileType_Rocket = 2, + SF2BossProjectileType_Grenade = 3, + SF2BossProjectileType_SentryRocket = 4, + SF2BossProjectileType_Arrow = 5, + SF2BossProjectileType_Mangler = 6, + SF2BossProjectileType_Baseball = 7, + SF2BossProjectileType_Custom +}; + +enum +{ + SF2BossTrapType_Invalid = -1, + SF2BossTrapType_BearTrap = 0, + SF2BossTrapType_Custom +}; + +enum +{ + SF2DamageType_Invalid = -1, + SF2DamageType_Jarate = 0, + SF2DamageType_Milk, + SF2DamageType_Gas, + SF2DamageType_Mark, + SF2DamageType_Ignite, + SF2DamageType_Stun, + SF2DamageType_Bleed, + SF2DamageType_Smite, + SF2DamageType_Random +} + // ====================================== // ENUM STRUCTS // ====================================== -enum struct SF2BossProfileSoundInfo +/*enum struct SF2BossProfileSoundInfo { char SectionName[64]; int Channel; @@ -1143,32 +1226,9 @@ enum struct SF2ParticleData } } } -} - -enum EffectEvent -{ - EffectEvent_Invalid = -1, - EffectEvent_Constant = 0, - EffectEvent_HitPlayer, - EffectEvent_PlayerSeesBoss -}; - -enum EffectType -{ - EffectType_Invalid = -1, - EffectType_Steam = 0, - EffectType_DynamicLight, - EffectType_Particle, - EffectType_Trail, - EffectType_PropDynamic, - EffectType_PointSpotlight, - EffectType_Sprite, - EffectType_TempEntBeamRing, - EffectType_TempEntParticle, - EffectType_Sound -}; +}*/ -enum struct SF2BossProfileBaseEffectInfo +/*enum struct SF2BossProfileBaseEffectInfo { char SectionName[64]; EffectEvent Event; @@ -3144,4 +3204,4 @@ enum struct SF2BossProfileData } #include -#include +#include */ diff --git a/addons/sourcemod/scripting/include/sf2/profiles/profiles_chaser.inc b/addons/sourcemod/scripting/include/sf2/profiles/profiles_chaser.inc index 8c301a9b..9de910cc 100644 --- a/addons/sourcemod/scripting/include/sf2/profiles/profiles_chaser.inc +++ b/addons/sourcemod/scripting/include/sf2/profiles/profiles_chaser.inc @@ -1,52 +1,5 @@ #include -enum -{ - SF2BossAttackType_Invalid = -1, - SF2BossAttackType_Melee = 0, - SF2BossAttackType_Ranged = 1, - SF2BossAttackType_Projectile = 2, - SF2BossAttackType_ExplosiveDance = 3, - SF2BossAttackType_LaserBeam = 4, - SF2BossAttackType_Custom = 5, - SF2BossAttackType_Tongue = 6 -}; - -enum -{ - SF2BossProjectileType_Invalid = -1, - SF2BossProjectileType_Fireball = 0, - SF2BossProjectileType_Iceball = 1, - SF2BossProjectileType_Rocket = 2, - SF2BossProjectileType_Grenade = 3, - SF2BossProjectileType_SentryRocket = 4, - SF2BossProjectileType_Arrow = 5, - SF2BossProjectileType_Mangler = 6, - SF2BossProjectileType_Baseball = 7, - SF2BossProjectileType_Custom -}; - -enum -{ - SF2BossTrapType_Invalid = -1, - SF2BossTrapType_BearTrap = 0, - SF2BossTrapType_Custom -}; - -enum -{ - SF2DamageType_Invalid = -1, - SF2DamageType_Jarate = 0, - SF2DamageType_Milk, - SF2DamageType_Gas, - SF2DamageType_Mark, - SF2DamageType_Ignite, - SF2DamageType_Stun, - SF2DamageType_Bleed, - SF2DamageType_Smite, - SF2DamageType_Random -} - enum struct SF2BossProfileAttackGestureData // This one covers the gestures { ArrayList Names; @@ -374,7 +327,7 @@ enum struct SF2ChaserBossProfileDamageEffectData // Can be any data this.Sounds.Destroy(); } - void Apply(CBaseCombatCharacter player, int difficulty, SF2_ChaserBossEntity chaser = SF2_ChaserBossEntity(-1)) + void Apply(CBaseCombatCharacter player, int difficulty, SF2_ChaserBossEntity chaser = view_as(-1)) { if (!this.Enabled[difficulty]) { @@ -600,7 +553,7 @@ enum struct SF2ChaserBossProfileShockwaveData } } - void ApplyDamageEffects(CBaseCombatCharacter player, int difficulty, SF2_ChaserBossEntity chaser = SF2_ChaserBossEntity(-1)) + void ApplyDamageEffects(CBaseCombatCharacter player, int difficulty, SF2_ChaserBossEntity chaser = view_as(-1)) { if (this.DamageEffects == null) { @@ -1061,7 +1014,7 @@ enum struct SF2ChaserBossProfileAttackData return strcmp(subType, this.SubType) == 0; } - void ApplyDamageEffects(CBaseCombatCharacter player, int difficulty, SF2_ChaserBossEntity chaser = SF2_ChaserBossEntity(-1)) + void ApplyDamageEffects(CBaseCombatCharacter player, int difficulty, SF2_ChaserBossEntity chaser = view_as(-1)) { if (this.DamageEffects == null) { diff --git a/addons/sourcemod/scripting/include/sf2/stocks.inc b/addons/sourcemod/scripting/include/sf2/stocks.inc index ffe87262..784f1ebf 100644 --- a/addons/sourcemod/scripting/include/sf2/stocks.inc +++ b/addons/sourcemod/scripting/include/sf2/stocks.inc @@ -1,6 +1,6 @@ #pragma semicolon 1 -bool DispatchParticleEffect(int entity, const char[] particle, float startPos[3], float angles[3], float endPos[3], +stock bool DispatchParticleEffect(int entity, const char[] particle, float startPos[3], float angles[3], float endPos[3], int attachmentPointIndex = 0, ParticleAttachment attachType = PATTACH_CUSTOMORIGIN, bool resetAllParticlesOnEntity = false) { char particleReal[PLATFORM_MAX_PATH]; @@ -55,7 +55,7 @@ bool DispatchParticleEffect(int entity, const char[] particle, float startPos[3] return true; } -bool DispatchParticleEffectBeam(int entity, const char[] particle, float startPos[3], float angles[3], float endPos[3], +stock bool DispatchParticleEffectBeam(int entity, const char[] particle, float startPos[3], float angles[3], float endPos[3], int attachmentPointIndex = 0, ParticleAttachment attachType = PATTACH_CUSTOMORIGIN, bool resetAllParticlesOnEntity = false) { char particleReal[PLATFORM_MAX_PATH]; diff --git a/addons/sourcemod/scripting/sf2.sp b/addons/sourcemod/scripting/sf2.sp index c16ff837..0beda656 100644 --- a/addons/sourcemod/scripting/sf2.sp +++ b/addons/sourcemod/scripting/sf2.sp @@ -1,3 +1,6 @@ +#pragma semicolon 1 +#pragma newdecls required + #include #include #include @@ -13,8 +16,6 @@ #include #include -#pragma semicolon 1 - #include #include #include @@ -36,7 +37,6 @@ bool steamworks; #define SF2 #include -#pragma newdecls required #pragma dynamic 131072 @@ -122,54 +122,20 @@ float g_LastCommandTime[MAXTF2PLAYERS]; bool g_Enabled; -KeyValues g_Config; -KeyValues g_RestrictedWeaponsConfig; KeyValues g_SpecialRoundsConfig; KeyValues g_ClassStatsConfig; -ArrayList g_PageMusicRanges; -int g_PageMusicActiveIndex[MAXTF2PLAYERS] = { -1, ... }; - int g_SlenderModel[MAX_BOSSES] = { INVALID_ENT_REFERENCE, ... }; int g_SlenderCopyMaster[MAX_BOSSES] = { -1, ... }; -int g_SlenderMaxCopies[MAX_BOSSES][Difficulty_Max]; int g_SlenderCompanionMaster[MAX_BOSSES] = { -1, ... }; -float g_SlenderEyePosOffset[MAX_BOSSES][3]; -float g_SlenderEyeAngOffset[MAX_BOSSES][3]; -float g_SlenderDetectMins[MAX_BOSSES][3]; -float g_SlenderDetectMaxs[MAX_BOSSES][3]; -int g_SlenderRenderColor[MAX_BOSSES][4]; -int g_SlenderRenderFX[MAX_BOSSES]; -int g_SlenderRenderMode[MAX_BOSSES]; Handle g_SlenderThink[MAX_BOSSES]; Handle g_SlenderEntityThink[MAX_BOSSES]; Handle g_SlenderFakeTimer[MAX_BOSSES]; Handle g_SlenderDeathCamTimer[MAX_BOSSES]; int g_SlenderDeathCamTarget[MAX_BOSSES]; -float g_SlenderStaticRadius[MAX_BOSSES][Difficulty_Max]; -float g_SlenderStaticRate[MAX_BOSSES][Difficulty_Max]; -float g_SlenderStaticRateDecay[MAX_BOSSES][Difficulty_Max]; -float g_SlenderStaticGraceTime[MAX_BOSSES][Difficulty_Max]; bool g_SlenderAddCompanionsOnDifficulty[MAX_BOSSES] = { false, ... }; float g_SlenderStatueIdleLifeTime[MAX_BOSSES]; -bool g_SlenderDeathCamScareSound[MAX_BOSSES]; -bool g_SlenderPublicDeathCam[MAX_BOSSES]; -float g_SlenderPublicDeathCamSpeed[MAX_BOSSES]; -float g_SlenderPublicDeathCamAcceleration[MAX_BOSSES]; -float g_SlenderPublicDeathCamDeceleration[MAX_BOSSES]; -float g_SlenderPublicDeathCamBackwardOffset[MAX_BOSSES]; -float g_SlenderPublicDeathCamDownwardOffset[MAX_BOSSES]; -bool g_SlenderDeathCamOverlay[MAX_BOSSES]; -float g_SlenderDeathCamOverlayTimeStart[MAX_BOSSES]; -float g_SlenderDeathCamTime[MAX_BOSSES]; - -//The Gaben's stuff -bool g_SlenderCustomOutroSong[MAX_BOSSES]; - -bool g_SlenderUseCustomOutlines[MAX_BOSSES]; -bool g_SlenderUseRainbowOutline[MAX_BOSSES]; - int g_TrapEntityCount; float g_RoundTimeMessage = 0.0; @@ -179,15 +145,9 @@ bool g_SlenderTeleportTargetIsCamping[MAX_BOSSES] = { false, ... }; float g_SlenderNextTeleportTime[MAX_BOSSES] = { -1.0, ... }; float g_SlenderTeleportTargetTime[MAX_BOSSES] = { -1.0, ... }; -float g_SlenderTeleportMinRange[MAX_BOSSES][Difficulty_Max]; -float g_SlenderTeleportMaxRange[MAX_BOSSES][Difficulty_Max]; float g_SlenderTeleportMaxTargetTime[MAX_BOSSES] = { -1.0, ... }; float g_SlenderTeleportMaxTargetStress[MAX_BOSSES] = { 0.0, ... }; float g_SlenderTeleportPlayersRestTime[MAX_BOSSES][MAXTF2PLAYERS]; -bool g_SlenderTeleportIgnoreChases[MAX_BOSSES]; -bool g_SlenderTeleportIgnoreVis[MAX_BOSSES]; - -bool g_SlenderProxiesAllowNormalVoices[MAX_BOSSES]; int g_SlenderBoxingBossCount = 0; int g_SlenderBoxingBossKilled = 0; @@ -208,14 +168,6 @@ float g_SlenderNextJumpScare[MAX_BOSSES] = { -1.0, ... }; float g_SlenderTimeUntilKill[MAX_BOSSES] = { -1.0, ... }; float g_SlenderTimeUntilNextProxy[MAX_BOSSES] = { -1.0, ... }; -float g_SlenderProxyDamageVsEnemy[MAX_BOSSES][Difficulty_Max]; -float g_SlenderProxyDamageVsBackstab[MAX_BOSSES][Difficulty_Max]; -float g_SlenderProxyDamageVsSelf[MAX_BOSSES][Difficulty_Max]; -int g_SlenderProxyControlGainHitEnemy[MAX_BOSSES][Difficulty_Max]; -int g_SlenderProxyControlGainHitByEnemy[MAX_BOSSES][Difficulty_Max]; -float g_SlenderProxyControlDrainRate[MAX_BOSSES][Difficulty_Max]; -int g_SlenderMaxProxies[MAX_BOSSES][Difficulty_Max]; - int g_NightVisionType = 0; //Healthbar @@ -227,6 +179,8 @@ enum struct SF2PageEntityData int EntRef; char CollectSound[PLATFORM_MAX_PATH]; int CollectSoundPitch; + float Pos[3]; + float Ang[3]; } ArrayList g_Pages; @@ -358,42 +312,6 @@ Handle g_PlayerFireworkTimer[MAXTF2PLAYERS] = { null, ... }; bool g_PlayerGettingPageReward[MAXTF2PLAYERS] = { false, ... }; -// Music system. -int g_PlayerMusicFlags[MAXTF2PLAYERS]; -char g_PlayerMusicString[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; -float g_PlayerMusicVolume[MAXTF2PLAYERS]; -float g_PlayerMusicTargetVolume[MAXTF2PLAYERS]; -Handle g_PlayerMusicTimer[MAXTF2PLAYERS]; -int g_PlayerPageMusicMaster[MAXTF2PLAYERS]; - -// Chase music system, which apparently also uses the alert song system. And the idle sound system. -char g_PlayerChaseMusicString[MAXTF2PLAYERS][MAX_BOSSES][PLATFORM_MAX_PATH]; -char g_PlayerChaseMusicSeeString[MAXTF2PLAYERS][MAX_BOSSES][PLATFORM_MAX_PATH]; -float g_PlayerChaseMusicVolumes[MAXTF2PLAYERS][MAX_BOSSES]; -float g_PlayerChaseMusicSeeVolumes[MAXTF2PLAYERS][MAX_BOSSES]; -Handle g_PlayerChaseMusicTimer[MAXTF2PLAYERS][MAX_BOSSES]; -Handle g_PlayerChaseMusicSeeTimer[MAXTF2PLAYERS][MAX_BOSSES]; -int g_PlayerChaseMusicMaster[MAXTF2PLAYERS] = { -1, ... }; -int g_PlayerChaseMusicSeeMaster[MAXTF2PLAYERS] = { -1, ... }; -int g_PlayerChaseMusicOldMaster[MAXTF2PLAYERS] = { -1, ... }; -int g_PlayerChaseMusicSeeOldMaster[MAXTF2PLAYERS] = { -1, ... }; - -char g_PlayerAlertMusicString[MAXTF2PLAYERS][MAX_BOSSES][PLATFORM_MAX_PATH]; -float g_PlayerAlertMusicVolumes[MAXTF2PLAYERS][MAX_BOSSES]; -Handle g_PlayerAlertMusicTimer[MAXTF2PLAYERS][MAX_BOSSES]; -int g_PlayerAlertMusicMaster[MAXTF2PLAYERS] = { -1, ... }; -int g_PlayerAlertMusicOldMaster[MAXTF2PLAYERS] = { -1, ... }; - -char g_PlayerIdleMusicString[MAXTF2PLAYERS][MAX_BOSSES][PLATFORM_MAX_PATH]; -float g_PlayerIdleMusicVolumes[MAXTF2PLAYERS][MAX_BOSSES]; -Handle g_PlayerIdleMusicTimer[MAXTF2PLAYERS][MAX_BOSSES]; -int g_PlayerIdleMusicMaster[MAXTF2PLAYERS] = { -1, ... }; -int g_PlayerIdleMusicOldMaster[MAXTF2PLAYERS] = { -1, ... }; - -char g_Player90sMusicString[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; -float g_Player90sMusicVolumes[MAXTF2PLAYERS]; -Handle g_Player90sMusicTimer[MAXTF2PLAYERS]; - SF2RoundState g_RoundState = SF2RoundState_Invalid; float g_RoundDifficultyModifier = DIFFICULTYMODIFIER_NORMAL; bool g_RoundInfiniteFlashlight = false; @@ -437,7 +355,6 @@ static int g_RoundIntroText; char g_RoundIntroMusic[PLATFORM_MAX_PATH] = ""; static char g_PageCollectSound[PLATFORM_MAX_PATH] = ""; static int g_PageSoundPitch = 100; -char currentMusicTrack[PLATFORM_MAX_PATH], currentMusicTrackNormal[PLATFORM_MAX_PATH], currentMusicTrackHard[PLATFORM_MAX_PATH], currentMusicTrackInsane[PLATFORM_MAX_PATH], currentMusicTrackNightmare[PLATFORM_MAX_PATH], currentMusicTrackApollyon[PLATFORM_MAX_PATH]; int g_RoundWarmupRoundCount = 0; @@ -564,6 +481,7 @@ ConVar g_WeaponCriticalsConVar; ConVar g_PhysicsPushScaleConVar; ConVar g_DragonsFuryBurningBonusConVar; ConVar g_DragonsFuryBurnDurationConVar; +ConVar g_TFBotForceClassConVar; bool g_IsPlayerShakeEnabled; bool g_PlayerViewbobHurtEnabled; @@ -648,13 +566,19 @@ PrivateForward g_OnMapEndPFwd; PrivateForward g_OnGameFramePFwd; PrivateForward g_OnRoundStartPFwd; PrivateForward g_OnRoundEndPFwd; +PrivateForward g_OnConfigsExecutedPFwd; PrivateForward g_OnEntityCreatedPFwd; PrivateForward g_OnEntityDestroyedPFwd; PrivateForward g_OnEntityTeleportedPFwd; +PrivateForward g_OnBuildingDestroyedPFwd; +PrivateForward g_OnPostInitMapEntitiesPFwd; +PrivateForward g_OnPostInitNewGamePFwd; +PrivateForward g_OnRoundStateChangePFwd; PrivateForward g_OnAdminMenuCreateOptionsPFwd; PrivateForward g_OnPlayerJumpPFwd; PrivateForward g_OnPlayerSpawnPFwd; PrivateForward g_OnPlayerTakeDamagePFwd; +PrivateForward g_OnPlayerTakeDamagePostPFwd; PrivateForward g_OnPlayerDeathPrePFwd; PrivateForward g_OnPlayerDeathPFwd; PrivateForward g_OnPlayerPutInServerPFwd; @@ -662,6 +586,8 @@ PrivateForward g_OnPlayerDisconnectedPFwd; PrivateForward g_OnPlayerEscapePFwd; PrivateForward g_OnPlayerTeamPFwd; PrivateForward g_OnPlayerClassPFwd; +PrivateForward g_OnPlayerPressButtonPFwd; +PrivateForward g_OnPlayerReleaseButtonPFwd; PrivateForward g_OnPlayerLookAtBossPFwd; PrivateForward g_OnPlayerChangePlayStatePFwd; PrivateForward g_OnPlayerChangeGhostStatePFwd; @@ -672,7 +598,10 @@ PrivateForward g_OnPlayerTurnOnFlashlightPFwd; PrivateForward g_OnPlayerTurnOffFlashlightPFwd; PrivateForward g_OnPlayerFlashlightBreakPFwd; PrivateForward g_OnPlayerAverageUpdatePFwd; +PrivateForward g_OnPlayerPostWeaponsPFwd; +PrivateForward g_OnPageCountChangedPFwd; PrivateForward g_OnSpecialRoundStartPFwd; +PrivateForward g_OnBossAddedPFwd; PrivateForward g_OnBossSpawnPFwd; PrivateForward g_OnBossRemovedPFwd; PrivateForward g_OnChaserGetAttackActionPFwd; @@ -694,6 +623,7 @@ Handle g_SDKSequenceVelocity; Handle g_SDKStartTouch; Handle g_SDKEndTouch; Handle g_SDKWeaponSwitch; +Handle g_SDKUpdateSpeed; DynamicHook g_DHookWantsLagCompensationOnEntity; DynamicHook g_DHookShouldTransmit; @@ -731,6 +661,10 @@ stock ArrayList g_FuncNavPrefer; int g_FlashlightHaloModel = -1; +bool g_PagesRevealed = false; +ArrayList g_PageLocations; +ArrayList g_PageLocationsGlow; + #if defined DEBUG #include "sf2/debug.sp" #endif @@ -800,8 +734,12 @@ public void OnLibraryRemoved(const char[] name) public void OnMapStart() { g_TimerFail = null; + g_PagesRevealed = false; + g_PageLocations.Clear(); + g_PageLocationsGlow.Clear(); FindHealthBar(); PrecacheSound(SOUND_THUNDER, true); + PrecacheSound(DEBUG_PAGEREVEALSOUND); PrecacheSound("weapons/teleporter_send.wav"); g_ShockwaveBeam = PrecacheModel("sprites/laser.vmt"); g_ShockwaveHalo = PrecacheModel("sprites/halo01.vmt"); @@ -850,23 +788,29 @@ public void OnConfigsExecuted() PrecacheStuff(); ReloadBossProfiles(); NPCOnConfigsExecuted(); + Call_StartForward(g_OnConfigsExecutedPFwd); + Call_Finish(); + g_BossCountUpdateTimer = CreateTimer(2.0, Timer_BossCountUpdate, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); int ent = -1; while ((ent = FindEntityByClassname(ent, "obj_sentrygun")) != -1) { g_Buildings.Push(EntIndexToEntRef(ent)); + HookSingleEntityOutput(ent, "OnDestroyed", Output_OnBuildingDestroyed, true); } ent = -1; while ((ent = FindEntityByClassname(ent, "obj_teleporter")) != -1) { g_Buildings.Push(EntIndexToEntRef(ent)); + HookSingleEntityOutput(ent, "OnDestroyed", Output_OnBuildingDestroyed, true); } ent = -1; while ((ent = FindEntityByClassname(ent, "obj_dispenser")) != -1) { g_Buildings.Push(EntIndexToEntRef(ent)); + HookSingleEntityOutput(ent, "OnDestroyed", Output_OnBuildingDestroyed, true); } ent = -1; @@ -909,7 +853,6 @@ public void OnConfigsExecuted() continue; } g_ClientInGame[i] = true; - SDKHook(i, SDKHook_OnTakeDamage, Hook_ClientOnTakeDamage); Call_StartForward(g_OnPlayerPutInServerPFwd); Call_PushCell(SF2_BasePlayer(i)); Call_Finish(); @@ -1065,6 +1008,9 @@ static void StartPlugin() NPCOnConfigsExecuted(); + Call_StartForward(g_OnConfigsExecutedPFwd); + Call_Finish(); + InitializeBossPackVotes(); SetupTimeLimitTimerForBossPackVote(); @@ -1082,18 +1028,21 @@ static void StartPlugin() while ((ent = FindEntityByClassname(ent, "obj_sentrygun")) != -1) { g_Buildings.Push(EntIndexToEntRef(ent)); + HookSingleEntityOutput(ent, "OnDestroyed", Output_OnBuildingDestroyed, true); } ent = -1; while ((ent = FindEntityByClassname(ent, "obj_teleporter")) != -1) { g_Buildings.Push(EntIndexToEntRef(ent)); + HookSingleEntityOutput(ent, "OnDestroyed", Output_OnBuildingDestroyed, true); } ent = -1; while ((ent = FindEntityByClassname(ent, "obj_dispenser")) != -1) { g_Buildings.Push(EntIndexToEntRef(ent)); + HookSingleEntityOutput(ent, "OnDestroyed", Output_OnBuildingDestroyed, true); } ent = -1; @@ -1335,11 +1284,6 @@ static void StopPlugin() cvar.SetBool(false); } - if (MusicActive()) - { - NPCStopMusic(); - } - // Cleanup clients. for (int i = 1; i <= MaxClients; i++) { @@ -1357,6 +1301,8 @@ static void StopPlugin() } } + NPCRemoveAll(); + Call_StartForward(g_OnGamemodeEndPFwd); Call_Finish(); @@ -1364,8 +1310,6 @@ static void StopPlugin() { delete g_FuncNavPrefer; } - - delete g_Config; } public void OnMapEnd() @@ -1388,7 +1332,6 @@ public void OnMapEnd() g_RenevantWallHax = false; BossProfilesOnMapEnd(); - NPCRemoveAll(); Call_StartForward(g_OnMapEndPFwd); Call_Finish(); @@ -1423,11 +1366,6 @@ public void TF2_OnConditionAdded(int client, TFCond cond) { player.ProxyControl -= 20; } - - if (player.UsingFlashlight) - { - player.HandleFlashlight(); - } } if (cond == TFCond_HalloweenKart) @@ -1511,7 +1449,7 @@ static Action Timer_GlobalGameFrame(Handle timer) { continue; } - NPCGetBossName(i, boxingBossName, sizeof(boxingBossName)); + baseNPC.GetProfileData().GetName(1, boxingBossName, sizeof(boxingBossName)); float health = float(chaser.GetProp(Prop_Data, "m_iHealth")); float maxHealth = chaser.MaxHealth; if (chaser.GetProp(Prop_Data, "m_takedamage") == DAMAGE_EVENTS_ONLY) @@ -1550,7 +1488,10 @@ static Action Timer_GlobalGameFrame(Handle timer) continue; } - if (!(NPCGetFlags(bossIndex) & SFF_PROXIES)) + int difficulty = GetLocalGlobalDifficulty(bossIndex); + BossProfileProxyData proxyData = SF2NPC_BaseNPC(bossIndex).GetProfileData().GetProxies(); + + if (!proxyData.IsEnabled(difficulty)) { continue; } @@ -1571,9 +1512,7 @@ static Action Timer_GlobalGameFrame(Handle timer) continue; // No teleport target. } - int difficulty = GetLocalGlobalDifficulty(bossIndex); - - int maxProxies = g_SlenderMaxProxies[bossIndex][difficulty]; + int maxProxies = proxyData.GetMaxProxies(difficulty); if (g_InProxySurvivalRageMode) { maxProxies += 5; @@ -1605,9 +1544,9 @@ static Action Timer_GlobalGameFrame(Handle timer) continue; } - float spawnChanceMin = NPCGetProxySpawnChanceMin(bossIndex, difficulty); - float spawnChanceMax = NPCGetProxySpawnChanceMax(bossIndex, difficulty); - float spawnChanceThreshold = NPCGetProxySpawnChanceThreshold(bossIndex, difficulty); + float spawnChanceMin = proxyData.GetMinSpawnChance(difficulty); + float spawnChanceMax = proxyData.GetMaxSpawnChance(difficulty); + float spawnChanceThreshold = proxyData.GetSpawnChanceThreshold(difficulty); float chance = GetRandomFloat(spawnChanceMin, spawnChanceMax); if (chance > spawnChanceThreshold && !g_InProxySurvivalRageMode) @@ -1620,8 +1559,8 @@ static Action Timer_GlobalGameFrame(Handle timer) int availableProxies = maxProxies - numActiveProxies; - int spawnNumMin = NPCGetProxySpawnNumMin(bossIndex, difficulty); - int spawnNumMax = NPCGetProxySpawnNumMax(bossIndex, difficulty); + int spawnNumMin = proxyData.GetMinSpawnedProxies(difficulty); + int spawnNumMax = proxyData.GetMaxSpawnedProxies(difficulty); int spawnNum = 0; @@ -1722,8 +1661,8 @@ static Action Timer_GlobalGameFrame(Handle timer) // Set the cooldown time! if (cooldown) { - float spawnCooldownMin = NPCGetProxySpawnCooldownMin(bossIndex, difficulty); - float spawnCooldownMax = NPCGetProxySpawnCooldownMax(bossIndex, difficulty); + float spawnCooldownMin = proxyData.GetMinSpawnCooldown(difficulty); + float spawnCooldownMax = proxyData.GetMaxSpawnCooldown(difficulty); g_SlenderTimeUntilNextProxy[bossIndex] = GetGameTime() + GetRandomFloat(spawnCooldownMin, spawnCooldownMax); } @@ -1821,11 +1760,6 @@ static Action Timer_BossCountUpdate(Handle timer) return Plugin_Stop; } - if (!g_Enabled) - { - return Plugin_Stop; - } - int bossCount = NPCGetCount(); int bossPreferredCount; @@ -1842,14 +1776,13 @@ static Action Timer_BossCountUpdate(Handle timer) bossPreferredCount++; int difficulty = GetLocalGlobalDifficulty(Npc.Index); - SF2BossProfileData data; - data = Npc.GetProfileData(); - if (!data.CopiesInfo.Enabled[GetLocalGlobalDifficulty(i)] || (Npc.Flags & SFF_NOCOPIES) != 0) + BaseBossProfile data = Npc.GetProfileData(); + if (!data.GetCopies().IsEnabled(GetLocalGlobalDifficulty(i)) || (Npc.Flags & SFF_NOCOPIES) != 0) { continue; } - int minCount = data.CopiesInfo.MinCopies[difficulty]; + int minCount = data.GetCopies().GetMinCopies(difficulty); if (minCount > 0) { for (int i2 = 0; i2 < MAX_BOSSES; i2++) @@ -2001,6 +1934,11 @@ static Action Timer_BossCountUpdate(Handle timer) } } + if (!g_Enabled) + { + bossPreferredCount = MAX_BOSSES; + } + int diff = bossCount - bossPreferredCount; if (diff != 0) { @@ -2036,13 +1974,12 @@ static Action Timer_BossCountUpdate(Handle timer) continue; } - if (Npc.CanRemove) + if (Npc.CanDespawn) { SF2NPC_BaseNPC master = Npc.CopyMaster; - SF2BossProfileData data; - data = master.GetProfileData(); + BaseBossProfile data = master.GetProfileData(); int difficulty = GetLocalGlobalDifficulty(master.Index); - if (data.CopiesInfo.MinCopies[difficulty] > 0) + if (data.GetCopies().GetMinCopies(difficulty) > 0) { int copyCount = 0; for (int i2 = 0; i2 < MAX_BOSSES; i2++) @@ -2068,7 +2005,7 @@ static Action Timer_BossCountUpdate(Handle timer) } copyCount++; } - if (copyCount > data.CopiesInfo.MinCopies[difficulty]) + if (copyCount > data.GetCopies().GetMinCopies(difficulty)) { Npc.Remove(); count--; @@ -2106,9 +2043,8 @@ static Action Timer_BossCountUpdate(Handle timer) continue; } - SF2BossProfileData data; - data = Npc.GetProfileData(); - if (!data.CopiesInfo.Enabled[GetLocalGlobalDifficulty(i)] || (Npc.Flags & SFF_NOCOPIES) != 0) + BaseBossProfile data = Npc.GetProfileData(); + if (!data.GetCopies().IsEnabled(GetLocalGlobalDifficulty(i)) || (Npc.Flags & SFF_NOCOPIES) != 0) { continue; } @@ -2142,7 +2078,7 @@ static Action Timer_BossCountUpdate(Handle timer) int difficulty = GetLocalGlobalDifficulty(Npc.Index); - int copyDifficulty = Npc.GetMaxCopies(difficulty); + int copyDifficulty = data.GetCopies().GetMaxCopies(difficulty); if (copyCount >= copyDifficulty) { continue; @@ -2157,36 +2093,6 @@ static Action Timer_BossCountUpdate(Handle timer) return Plugin_Continue; } -void ReloadRestrictedWeapons() -{ - if (g_RestrictedWeaponsConfig != null) - { - delete g_RestrictedWeaponsConfig; - g_RestrictedWeaponsConfig = null; - } - - char buffer[PLATFORM_MAX_PATH]; - if (!g_UseAlternateConfigDirectoryConVar.BoolValue) - { - BuildPath(Path_SM, buffer, sizeof(buffer), FILE_RESTRICTEDWEAPONS); - } - else - { - BuildPath(Path_SM, buffer, sizeof(buffer), FILE_RESTRICTEDWEAPONS_DATA); - } - KeyValues kv = new KeyValues("root"); - if (!FileToKeyValues(kv, buffer)) - { - delete kv; - LogError("Failed to load restricted weapons list! File not found!"); - } - else - { - g_RestrictedWeaponsConfig = kv; - LogSF2Message("Reloaded restricted weapons configuration file successfully"); - } -} - static Action Timer_RoundMessages(Handle timer) { if (!g_Enabled) @@ -2292,7 +2198,7 @@ void OnConVarChanged(Handle cvar, const char[] oldValue, const char[] intValue) SF2NPC_BaseNPC masterNpc = Npc.CompanionMaster; if (masterNpc.IsValid() && g_SlenderAddCompanionsOnDifficulty[masterNpc.Index]) { - Npc.RemoveFromGame(); + Npc.Remove(); } } for (int npcIndex = 0; npcIndex < MAX_BOSSES; npcIndex++) @@ -2349,8 +2255,8 @@ void OnConVarChanged(Handle cvar, const char[] oldValue, const char[] intValue) { if (g_RestartSessionConVar.BoolValue) { - ArrayList selectableBossesAdmin = GetSelectableAdminBossProfileList().Clone(); - ArrayList selectableBosses = GetSelectableBossProfileList().Clone(); + ArrayList selectableBossesAdmin = GetSelectableAdminBossProfileList(); + ArrayList selectableBosses = GetSelectableBossProfileList(); PlayNightmareSound(); SpecialRoundGameText("Its Restart Session time!", "leaderboard_streak"); CPrintToChatAll("{royalblue}%t {default}Your thirst for blood continues? Very well, let the blood spill. Let the demons feed off your unfortunate soul... Difficulty set to {mediumslateblue}%t!", "SF2 Prefix", "SF2 Calamity Difficulty"); @@ -2444,8 +2350,6 @@ void OnConVarChanged(Handle cvar, const char[] oldValue, const char[] intValue) } delete spawnPoint; } - delete selectableBosses; - delete selectableBossesAdmin; } else { @@ -2521,6 +2425,7 @@ public void OnEntityCreated(int ent, const char[] classname) strcmp(classname, "obj_teleporter", false) == 0) { g_Buildings.Push(EntIndexToEntRef(ent)); + HookSingleEntityOutput(ent, "OnDestroyed", Output_OnBuildingDestroyed, true); } if (strcmp(classname, "tank_boss", false) == 0) @@ -2861,9 +2766,10 @@ void SF_CollectTriggersMultiple() SDKHook(ent, SDKHook_EndTouch, Hook_FuncOnEndTouchEx); } } + static Action Hook_TriggerOnStartTouchEx(int trigger, int other) { - if (MaxClients >= other >= 1 && IsClientInGhostMode(other)) + if (IsValidClient(other) && IsClientInGhostMode(other)) { return Plugin_Handled; } @@ -2873,7 +2779,7 @@ static Action Hook_TriggerOnStartTouchEx(int trigger, int other) static Action Hook_TriggerOnTouchEx(int trigger, int other) { - if (MaxClients >= other >= 1 && IsClientInGhostMode(other)) + if (IsValidClient(other) && IsClientInGhostMode(other)) { return Plugin_Handled; } @@ -2882,7 +2788,7 @@ static Action Hook_TriggerOnTouchEx(int trigger, int other) static Action Hook_TriggerOnEndTouchEx(int trigger, int other) { - if (MaxClients >= other >= 1 && IsClientInGhostMode(other)) + if (IsValidClient(other) && IsClientInGhostMode(other)) { return Plugin_Handled; } @@ -2890,27 +2796,27 @@ static Action Hook_TriggerOnEndTouchEx(int trigger, int other) return Plugin_Continue; } -static Action Hook_FuncOnStartTouchEx(int iFunc, int other) +static Action Hook_FuncOnStartTouchEx(int func, int other) { - if (MaxClients >= other >= 1 && IsClientInGhostMode(other)) + if (IsValidClient(other) && IsClientInGhostMode(other)) { return Plugin_Handled; } return Plugin_Continue; } -static Action Hook_FuncOnTouchEx(int iFunc, int other) +static Action Hook_FuncOnTouchEx(int func, int other) { - if (MaxClients >= other >= 1 && IsClientInGhostMode(other)) + if (IsValidClient(other) && IsClientInGhostMode(other)) { return Plugin_Handled; } return Plugin_Continue; } -static Action Hook_FuncOnEndTouchEx(int iFunc, int other) +static Action Hook_FuncOnEndTouchEx(int func, int other) { - if (MaxClients >= other >= 1 && IsClientInGhostMode(other)) + if (IsValidClient(other) && IsClientInGhostMode(other)) { return Plugin_Handled; } @@ -3061,33 +2967,15 @@ void CollectPage(int page, int activator) char buffer[SF2_MAX_PROFILE_NAME_LENGTH], bossName[SF2_MAX_NAME_LENGTH]; if (NPCGetCount() < 63) { - if (g_DifficultyConVar.IntValue < 4 || GetSelectableAdminBossProfileList().Length <= 0) - { - ArrayList selectableBosses = GetSelectableBossProfileList().Clone(); - if (selectableBosses.Length > 0) - { - selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); - AddProfile(buffer); - NPCGetBossName(_, bossName, sizeof(bossName), buffer); - EmitSoundToAll(SR_SOUND_SELECT_BR, _, SNDCHAN_AUTO, _, _, 0.75); - SpecialRoundGameText(bossName, "d_purgatory"); - CPrintToChatAll("{royalblue}%t {default}Next on the roulette: {valve}%s", "SF2 Prefix", bossName); //Minimized HUD - } - delete selectableBosses; - } - else + ArrayList selectableBosses = GetSelectableBossProfileList(); + if (selectableBosses.Length > 0) { - ArrayList selectableBosses = GetSelectableAdminBossProfileList().Clone(); - if (selectableBosses.Length > 0) - { - selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); - AddProfile(buffer); - NPCGetBossName(_, bossName, sizeof(bossName), buffer); - EmitSoundToAll(SR_SOUND_SELECT_BR, _, SNDCHAN_AUTO, _, _, 0.75); - SpecialRoundGameText(bossName, "d_purgatory"); - CPrintToChatAll("{royalblue}%t {default}Next on the roulette: {valve}%s", "SF2 Prefix", bossName); - } - delete selectableBosses; + selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); + AddProfile(buffer); + GetBossProfile(buffer).GetName(1, bossName, sizeof(bossName)); + EmitSoundToAll(SR_SOUND_SELECT_BR, _, SNDCHAN_AUTO, _, _, 0.75); + SpecialRoundGameText(bossName, "d_purgatory"); + CPrintToChatAll("{royalblue}%t {default}Next on the roulette: {valve}%s", "SF2 Prefix", bossName); //Minimized HUD } } else @@ -3436,6 +3324,10 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 { player.SetAFKTime(); ClientOnButtonPress(player, button); + Call_StartForward(g_OnPlayerPressButtonPFwd); + Call_PushCell(player); + Call_PushCell(button); + Call_Finish(); if (button == IN_ATTACK2) { if (player.IsInPvP && !(buttons & IN_ATTACK)) @@ -3484,6 +3376,10 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 else if ((player.LastButtons & button)) { ClientOnButtonRelease(player, button); + Call_StartForward(g_OnPlayerReleaseButtonPFwd); + Call_PushCell(player); + Call_PushCell(button); + Call_Finish(); } } @@ -3599,7 +3495,6 @@ public void OnClientPutInServer(int client) { if (g_LoadOutsideMapsConVar.BoolValue) { - SDKHook(client, SDKHook_OnTakeDamage, Hook_ClientOnTakeDamage); Call_StartForward(g_OnPlayerPutInServerPFwd); Call_PushCell(SF2_BasePlayer(client)); Call_Finish(); @@ -3641,9 +3536,7 @@ public void OnClientPutInServer(int client) g_PlayerDesiredFOV[client] = 90; SDKHook(client, SDKHook_PreThink, Hook_ClientPreThink); - SDKHook(client, SDKHook_PreThinkPost, Hook_OnFlashlightThink); SDKHook(client, SDKHook_SetTransmit, Hook_ClientSetTransmit); - SDKHook(client, SDKHook_OnTakeDamage, Hook_ClientOnTakeDamage); g_DHookWantsLagCompensationOnEntity.HookEntity(Hook_Pre, client, Hook_ClientWantsLagCompensationOnEntity); @@ -4143,7 +4036,7 @@ void SetRoundState(SF2RoundState roundState) { continue; } - if (NPCChaserIsBoxingBoss(Npc.Index) && !Npc.GetProfileData().IsPvEBoss) + if (view_as(Npc).GetProfileData().BoxingBoss && !Npc.GetProfileData().IsPvEBoss) { g_SlenderBoxingBossCount++; } @@ -4208,6 +4101,11 @@ void SetRoundState(SF2RoundState roundState) Call_PushCell(oldRoundState); Call_PushCell(g_RoundState); Call_Finish(); + + Call_StartForward(g_OnRoundStateChangePFwd); + Call_PushCell(oldRoundState); + Call_PushCell(g_RoundState); + Call_Finish(); } bool IsRoundPlaying() @@ -4518,7 +4416,7 @@ void ForceInNextPlayersInQueue(int amount, bool showMessage = false) ClientSetGhostModeState(client, false); TF2_RespawnPlayer(client); TF2_RemoveCondition(client, TFCond_StealthedUserBuffFade); - g_LastCommandTime[client] = GetEngineTime()+0.5; + g_LastCommandTime[client] = GetEngineTime() + 0.5; CreateTimer(0.25, Timer_ForcePlayer, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); } else @@ -4667,6 +4565,7 @@ void SlenderOnClientStressUpdate(int client) } Npc.GetProfile(profile, sizeof(profile)); + BaseBossProfile profileData = Npc.GetProfileData(); int teleportTarget = EntRefToEntIndex(g_SlenderProxyTarget[Npc.Index]); if (teleportTarget && teleportTarget != INVALID_ENT_REFERENCE) @@ -4681,11 +4580,11 @@ void SlenderOnClientStressUpdate(int client) if (teleportTarget && teleportTarget != INVALID_ENT_REFERENCE && !g_PlayerIsExitCamping[teleportTarget]) { if (g_PlayerEliminated[teleportTarget] || DidClientEscape(teleportTarget) || - (!SF_BossesChaseEndlessly() && !SF_IsRenevantMap() && !SF_IsSurvivalMap() && !g_SlenderTeleportIgnoreChases[Npc.Index] && stress >= g_SlenderTeleportMaxTargetStress[bossIndex]) || + (!SF_BossesChaseEndlessly() && !SF_IsRenevantMap() && !SF_IsSurvivalMap() && !SF_IsRaidMap() && !profileData.TeleportIgnoreChases && stress >= g_SlenderTeleportMaxTargetStress[bossIndex]) || (g_SlenderTeleportMaxTargetTime[Npc.Index] > 0.0 && gameTime >= g_SlenderTeleportMaxTargetTime[Npc.Index])) { // Queue for a new target and mark the old target in the rest period. - float restPeriod = Npc.GetTeleportRestPeriod(difficulty); + float restPeriod = profileData.GetTeleportRestPeriod(difficulty); restPeriod = (restPeriod * GetRandomFloat(0.92, 1.08)) / (g_RoundDifficultyModifier); g_SlenderTeleportTarget[Npc.Index] = INVALID_ENT_REFERENCE; @@ -4703,8 +4602,8 @@ void SlenderOnClientStressUpdate(int client) { int preferredTeleportTarget = INVALID_ENT_REFERENCE; - float targetStressMin = Npc.GetTeleportStressMin(difficulty); - float targetStressMax = Npc.GetTeleportStressMax(difficulty); + float targetStressMin = profileData.GetMinTeleportStress(difficulty); + float targetStressMax = profileData.GetMaxTeleportStress(difficulty); float targetStress = targetStressMax - ((targetStressMax - targetStressMin) / (g_RoundDifficultyModifier)); @@ -4725,7 +4624,7 @@ void SlenderOnClientStressUpdate(int client) break; } } - if (g_PlayerStressAmount[i] < preferredTeleportTargetStress || g_RestartSessionEnabled || g_SlenderTeleportIgnoreChases[Npc.Index]) + if (g_PlayerStressAmount[i] < preferredTeleportTargetStress || g_RestartSessionEnabled || profileData.TeleportIgnoreChases || SF_BossesChaseEndlessly() || SF_IsRenevantMap() || SF_IsSurvivalMap() || SF_IsRaidMap()) { if (g_SlenderTeleportPlayersRestTime[Npc.Index][i] <= gameTime) { @@ -4781,83 +4680,6 @@ void GetPageEntities(ArrayList array) } } -static int GetPageMusicRanges() -{ - g_PageMusicRanges.Clear(); - - char name[64]; - - int ent = -1; - while ((ent = FindEntityByClassname(ent, "ambient_generic")) != -1) - { - GetEntPropString(ent, Prop_Data, "m_iName", name, sizeof(name)); - - if (name[0] != '\0' && !StrContains(name, "sf2_page_music_", false)) - { - ReplaceString(name, sizeof(name), "sf2_page_music_", "", false); - - char pageRanges[2][32]; - ExplodeString(name, "-", pageRanges, 2, 32); - - int index = g_PageMusicRanges.Push(EntIndexToEntRef(ent)); - if (index != -1) - { - int min = StringToInt(pageRanges[0]); - int max = StringToInt(pageRanges[1]); - - #if defined DEBUG - DebugMessage("Page range found: entity %d, min = %d, max = %d", ent, min, max); - #endif - g_PageMusicRanges.Set(index, min, 1); - g_PageMusicRanges.Set(index, max, 2); - } - } - } - - while ((ent = FindEntityByClassname(ent, "sf2_info_page_music")) != -1) - { - SF2PageMusicEntity pageMusic = SF2PageMusicEntity(ent); - if (!pageMusic.IsValid()) - { - continue; - } - - pageMusic.InsertRanges(g_PageMusicRanges); - } - - // precache - if (g_PageMusicRanges.Length > 0) - { - char path[PLATFORM_MAX_PATH]; - - for (int i = 0; i < g_PageMusicRanges.Length; i++) - { - ent = EntRefToEntIndex(g_PageMusicRanges.Get(i)); - if (!ent || ent == INVALID_ENT_REFERENCE) - { - continue; - } - - SF2PageMusicEntity pageMusic = SF2PageMusicEntity(ent); - if (pageMusic.IsValid()) - { - // Don't do anything; entity already precached its own music. - } - else - { - GetEntPropString(ent, Prop_Data, "m_iszSound", path, sizeof(path)); - if (path[0] != '\0') - { - PrecacheSound(path); - } - } - } - } - - LogSF2Message("Loaded page music ranges successfully!"); - return 0; -} - void SetPageCount(int num) { if (num > g_PageMax) @@ -5078,18 +4900,17 @@ void SetPageCount(int num) { continue; } - SF2BossProfileData data; - data = NPCGetProfileData(npcIndex); + BaseBossProfile data = SF2NPC_BaseNPC(npcIndex).GetProfileData(); - if (data.SlaughterRunData.SpawnTime[difficulty] > 0.0) + if (data.GetSlaughterRunData() != null && data.GetSlaughterRunData().GetCustomSpawnTime(difficulty) > 0.0) { - times[bosses] = data.SlaughterRunData.SpawnTime[difficulty]; + times[bosses] = data.GetSlaughterRunData().GetCustomSpawnTime(difficulty); bosses++; continue; } float originalSpeed, speed, timerCheck; - originalSpeed = data.RunSpeed[difficulty] + NPCGetAddSpeed(npcIndex); + originalSpeed = data.GetRunSpeed(difficulty) + NPCGetAddSpeed(npcIndex); float slaughterSpeed = g_SlaughterRunMinimumBossRunSpeedConVar.FloatValue; if (originalSpeed < slaughterSpeed) { @@ -5129,6 +4950,11 @@ void SetPageCount(int num) SF2MapEntity_OnPageCountChanged(g_PageCount, oldPageCount); + Call_StartForward(g_OnPageCountChangedPFwd); + Call_PushCell(g_PageCount); + Call_PushCell(oldPageCount); + Call_Finish(); + // Notify logic entities. char targetName[64]; char findTargetName[64]; @@ -5209,13 +5035,12 @@ void SetPageCount(int num) if (SF_SpecialRound(SPECIALROUND_LASTRESORT)) { char buffer[SF2_MAX_PROFILE_NAME_LENGTH]; - ArrayList selectableBosses = GetSelectableBossProfileList().Clone(); + ArrayList selectableBosses = GetSelectableBossProfileList(); if (selectableBosses.Length > 0) { selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); AddProfile(buffer); } - delete selectableBosses; } } else @@ -5635,43 +5460,32 @@ void HandlePlayerIntroState(int client) void HandlePlayerHUD(int client) { - if (SF_IsRaidMap() || SF_IsBoxingMap()) + SF2_BasePlayer player = SF2_BasePlayer(client); + if (SF_IsRaidMap() || SF_IsBoxingMap() || IsRoundInWarmup()) { + player.SetProp(Prop_Send, "m_iHideHUD", HIDEHUD_MATCH_STATUS); return; } - if (IsRoundInWarmup() || IsClientInGhostMode(client)) + int flags = HIDEHUD_MATCH_STATUS | HIDEHUD_CROSSHAIR | HIDEHUD_HEALTH | HIDEHUD_BUILDING_STATUS | HIDEHUD_CLOAK_AND_FEIGN | HIDEHUD_PIPES_AND_CHARGE | HIDEHUD_METAL | HIDEHUD_TARGET_ID | HIDEHUD_WEAPONSELECTION | HIDEHUD_PLAYERDEAD | HIDEHUD_NEEDSUIT | HIDEHUD_BONUS_PROGRESS; + if (!player.IsEliminated) { - SetEntProp(client, Prop_Send, "m_iHideHUD", 0); + if (player.HasEscaped) + { + flags = HIDEHUD_MATCH_STATUS; + } } else { - if (!g_PlayerEliminated[client]) + if (!player.IsProxy && !player.IsInGhostMode) { - if (!DidClientEscape(client)) - { - // Player is in the game; disable normal HUD. - SetEntProp(client, Prop_Send, "m_iHideHUD", HIDEHUD_CROSSHAIR | HIDEHUD_HEALTH); - } - else - { - // Player isn't in the game; enable normal HUD behavior. - SetEntProp(client, Prop_Send, "m_iHideHUD", 0); - } + flags = HIDEHUD_MATCH_STATUS; } - else + else if (player.IsProxy) { - if (g_PlayerProxy[client]) - { - // Player is in the game; disable normal HUD. - SetEntProp(client, Prop_Send, "m_iHideHUD", HIDEHUD_CROSSHAIR | HIDEHUD_HEALTH); - } - else - { - // Player isn't in the game; enable normal HUD behavior. - SetEntProp(client, Prop_Send, "m_iHideHUD", 0); - } + flags &= ~(HIDEHUD_HEALTH); } } + player.SetProp(Prop_Send, "m_iHideHUD", flags); } Action Timer_StopAirDash(Handle timer, any userid) @@ -5720,6 +5534,15 @@ Action Timer_SwitchBot(Handle timer, any userid) return Plugin_Stop; } + char value[32]; + g_TFBotForceClassConVar.GetString(value, sizeof(value)); + + TFClassType forced = TFClass_Unknown; + if (value[0] != '\0') + { + forced = TF2_GetClassFromName(value); + } + int random = GetRandomInt(1, 9); TFClassType newClass; switch (random) @@ -5761,6 +5584,10 @@ Action Timer_SwitchBot(Handle timer, any userid) newClass = TFClass_Spy; } } + if (forced != TFClass_Unknown) + { + newClass = forced; + } TF2_SetPlayerClass(client, newClass); TF2_RegeneratePlayer(client); @@ -6362,8 +6189,13 @@ Action Timer_ModifyRagdoll(Handle timer, any userid) { return Plugin_Stop; } - SF2BossProfileData data; - data = NPCGetProfileData(bossIndex); + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(bossIndex, profile, sizeof(profile)); + BaseBossProfile data = GetBossProfile(profile); + if (data == null) + { + return Plugin_Stop; + } int ragdoll = GetEntPropEnt(client, Prop_Send, "m_hRagdoll"); if (!IsValidEntity(ragdoll)) { @@ -6383,7 +6215,19 @@ Action Timer_ModifyRagdoll(Handle timer, any userid) SetEntPropVector(ent, Prop_Send, "m_vecRagdollOrigin", pos); if (data.PushRagdoll) { - force = data.PushRagdollForce; + data.GetPushRagdollForce(force); + int bossEnt = NPCGetEntIndex(bossIndex); + if (IsValidEntity(bossEnt)) + { + float myPos[3], direction[3]; + CBaseEntity(bossEnt).GetAbsOrigin(myPos); + SubtractVectors(pos, myPos, direction); + GetVectorAngles(direction, direction); + GetAngleVectors(direction, direction, NULL_VECTOR, NULL_VECTOR); + NormalizeVector(direction, direction); + force[0] *= direction[0]; + force[1] *= direction[1]; + } SetEntPropVector(ent, Prop_Send, "m_vecRagdollVelocity", force); SetEntPropVector(ent, Prop_Send, "m_vecForce", force); } @@ -6562,37 +6406,12 @@ Action Timer_PlayerSwitchToBlue(Handle timer, any userid) return Plugin_Stop; } -void CreateGeneralParticle(int entity, const char[] sectionName, float particleZPos = 0.0) +static void Output_OnBuildingDestroyed(const char[] output, int caller, int activator, float delay) { - if (entity == -1) - { - return; - } - - float slenderPosition[3], slenderAngles[3]; - GetEntPropVector(entity, Prop_Data, "m_vecAbsOrigin", slenderPosition); - GetEntPropVector(entity, Prop_Data, "m_angAbsRotation", slenderAngles); - slenderPosition[2] += particleZPos; - - if (!DispatchParticleEffect(entity, sectionName, slenderPosition, slenderAngles, slenderPosition)) - { - int particleEnt = CreateEntityByName("info_particle_system"); - - if (particleEnt != -1 && entity != -1) - { - TeleportEntity(particleEnt, slenderPosition, slenderAngles, NULL_VECTOR); - - DispatchKeyValue(particleEnt, "targetname", "tf2particle"); - DispatchKeyValue(particleEnt, "effect_name", sectionName); - - DispatchSpawn(particleEnt); - - ActivateEntity(particleEnt); - AcceptEntityInput(particleEnt, "start"); - - CreateTimer(0.1, Timer_KillEdict, particleEnt, TIMER_FLAG_NO_MAPCHANGE); - } - } + Call_StartForward(g_OnBuildingDestroyedPFwd); + Call_PushCell(CBaseEntity(caller)); + Call_PushCell(CBaseEntity(activator)); + Call_Finish(); } static Action Timer_RoundStart(Handle timer) @@ -7295,9 +7114,11 @@ static void InitializeMapEntities() GetRoundIntroParameters(); GetRoundEscapeParameters(); - GetPageMusicRanges(); SpawnPages(); + Call_StartForward(g_OnPostInitMapEntitiesPFwd); + Call_Finish(); + #if defined DEBUG if (g_DebugDetailConVar.IntValue > 0) { @@ -7565,6 +7386,8 @@ static void SpawnPages() SF2PageEntityData pageData; pageData.EntRef = EnsureEntRef(page); + pageData.Pos = pos; + pageData.Ang = angle; if (spawnPoint.IsValid()) { @@ -7801,14 +7624,12 @@ static void HandleNewBossRoundState() // Check if we have enough bosses. if (g_NewBossRound) { - ArrayList bossList = GetNewBossRoundProfileList().Clone(); + ArrayList bossList = GetNewBossRoundProfileList(); if (bossList.Length < 1) { g_NewBossRound = false; // Not enough bosses. } - - delete bossList; } if (g_NewBossRound) @@ -7876,7 +7697,7 @@ static void SelectStartingBossesForRound() } #endif - ArrayList selectableBossList = GetSelectableBossProfileQueueList().Clone(); + ArrayList selectableBossList = GetSelectableBossProfileQueueList(); // Select which boss profile to use. char profileOverride[SF2_MAX_PROFILE_NAME_LENGTH]; @@ -7986,7 +7807,6 @@ static void SelectStartingBossesForRound() } #endif } - delete selectableBossList; } static void GetRoundIntroParameters() @@ -8065,12 +7885,6 @@ void InitializeNewGame() } #endif - int ent = -1; - while ((ent = FindEntityByClassname(ent, "func_regenerate")) != -1) - { - AcceptEntityInput(ent, "Enable"); - } - InitializeMapEntities(); //Tutorial_EnableHooks(); @@ -8245,42 +8059,31 @@ void InitializeNewGame() else { // Spawn the boss! - if (!SF_SpecialRound(SPECIALROUND_MODBOSSES)) + if (!SF_SpecialRound(SPECIALROUND_MODBOSSES) && !SF_IsRenevantMap()) { - if (!SF_IsBoxingMap() && !SF_IsRenevantMap()) + if (SF_SpecialRound(SPECIALROUND_DOUBLETROUBLE) || SF_SpecialRound(SPECIALROUND_SILENTSLENDER) || SF_SpecialRound(SPECIALROUND_2DOUBLE)) { - if (SF_SpecialRound(SPECIALROUND_DOUBLETROUBLE) || SF_SpecialRound(SPECIALROUND_SILENTSLENDER) || SF_SpecialRound(SPECIALROUND_2DOUBLE)) - { - AddProfile(g_RoundBossProfile); - RemoveBossProfileFromQueueList(g_RoundBossProfile); - } - else if (SF_SpecialRound(SPECIALROUND_TRIPLEBOSSES)) - { - AddProfile(g_RoundBossProfile); - AddProfile(g_RoundBossProfile, _, _, _, false); - AddProfile(g_RoundBossProfile, _, _, _, false); - RemoveBossProfileFromQueueList(g_RoundBossProfile); - } - else if (!SF_SpecialRound(SPECIALROUND_DOUBLETROUBLE) && !SF_SpecialRound(SPECIALROUND_SILENTSLENDER) && !SF_SpecialRound(SPECIALROUND_2DOUBLE) && !SF_SpecialRound(SPECIALROUND_TRIPLEBOSSES)) - { - AddProfile(g_RoundBossProfile); - RemoveBossProfileFromQueueList(g_RoundBossProfile); - } + AddProfile(g_RoundBossProfile); + RemoveBossProfileFromQueueList(g_RoundBossProfile); } - else if (SF_IsBoxingMap()) + else if (SF_SpecialRound(SPECIALROUND_TRIPLEBOSSES)) { - char buffer[SF2_MAX_PROFILE_NAME_LENGTH]; - ArrayList selectableBosses = GetSelectableBoxingBossProfileList().Clone(); - if (selectableBosses.Length > 0) - { - selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); - AddProfile(buffer); - } - delete selectableBosses; + AddProfile(g_RoundBossProfile); + AddProfile(g_RoundBossProfile, _, _, _, false); + AddProfile(g_RoundBossProfile, _, _, _, false); + RemoveBossProfileFromQueueList(g_RoundBossProfile); + } + else if (!SF_SpecialRound(SPECIALROUND_DOUBLETROUBLE) &&!SF_SpecialRound(SPECIALROUND_SILENTSLENDER) && !SF_SpecialRound(SPECIALROUND_2DOUBLE) && !SF_SpecialRound(SPECIALROUND_TRIPLEBOSSES)) + { + AddProfile(g_RoundBossProfile); + RemoveBossProfileFromQueueList(g_RoundBossProfile); } } } + Call_StartForward(g_OnPostInitNewGamePFwd); + Call_Finish(); + #if defined DEBUG if (g_DebugDetailConVar.IntValue > 0) { @@ -8486,38 +8289,24 @@ static Action Timer_ActivateRoundFromIntro(Handle timer) SF2_RefreshRestrictions(); // Spawn the boss! - if (!SF_SpecialRound(SPECIALROUND_MODBOSSES)) + if (!SF_SpecialRound(SPECIALROUND_MODBOSSES) && !SF_IsRenevantMap()) { - if (!SF_IsBoxingMap() && !SF_IsRenevantMap()) + if (SF_SpecialRound(SPECIALROUND_DOUBLETROUBLE) || SF_SpecialRound(SPECIALROUND_SILENTSLENDER) || SF_SpecialRound(SPECIALROUND_2DOUBLE)) { - if (SF_SpecialRound(SPECIALROUND_DOUBLETROUBLE) || SF_SpecialRound(SPECIALROUND_SILENTSLENDER) || SF_SpecialRound(SPECIALROUND_2DOUBLE)) - { - AddProfile(g_RoundBossProfile); - RemoveBossProfileFromQueueList(g_RoundBossProfile); - } - else if (SF_SpecialRound(SPECIALROUND_TRIPLEBOSSES)) - { - AddProfile(g_RoundBossProfile); - AddProfile(g_RoundBossProfile, _, _, _, false); - AddProfile(g_RoundBossProfile, _, _, _, false); - RemoveBossProfileFromQueueList(g_RoundBossProfile); - } - else if (!SF_SpecialRound(SPECIALROUND_DOUBLETROUBLE) && !SF_SpecialRound(SPECIALROUND_SILENTSLENDER) && !SF_SpecialRound(SPECIALROUND_2DOUBLE) && !SF_SpecialRound(SPECIALROUND_TRIPLEBOSSES)) - { - AddProfile(g_RoundBossProfile); - RemoveBossProfileFromQueueList(g_RoundBossProfile); - } + AddProfile(g_RoundBossProfile); + RemoveBossProfileFromQueueList(g_RoundBossProfile); } - else if (SF_IsBoxingMap()) + else if (SF_SpecialRound(SPECIALROUND_TRIPLEBOSSES)) { - char buffer[SF2_MAX_PROFILE_NAME_LENGTH]; - ArrayList selectableBosses = GetSelectableBoxingBossProfileList().Clone(); - if (selectableBosses.Length > 0) - { - selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); - AddProfile(buffer); - } - delete selectableBosses; + AddProfile(g_RoundBossProfile); + AddProfile(g_RoundBossProfile, _, _, _, false); + AddProfile(g_RoundBossProfile, _, _, _, false); + RemoveBossProfileFromQueueList(g_RoundBossProfile); + } + else if (!SF_SpecialRound(SPECIALROUND_DOUBLETROUBLE) && !SF_SpecialRound(SPECIALROUND_SILENTSLENDER) && !SF_SpecialRound(SPECIALROUND_2DOUBLE) && !SF_SpecialRound(SPECIALROUND_TRIPLEBOSSES)) + { + AddProfile(g_RoundBossProfile); + RemoveBossProfileFromQueueList(g_RoundBossProfile); } } return Plugin_Stop; diff --git a/addons/sourcemod/scripting/sf2/adminmenu.sp b/addons/sourcemod/scripting/sf2/adminmenu.sp index 90211f03..add089a8 100644 --- a/addons/sourcemod/scripting/sf2/adminmenu.sp +++ b/addons/sourcemod/scripting/sf2/adminmenu.sp @@ -4,6 +4,7 @@ #define _sf2_adminmenu_included #pragma semicolon 1 +#pragma newdecls required static Handle g_TopMenu = null; static int g_PlayerAdminMenuTargetUserId[MAXTF2PLAYERS] = { -1, ... }; @@ -404,13 +405,12 @@ static void AddAllBossesToMenu(Menu menu) for (int i = 0; i < bossList.Length; i++) { bossList.GetString(i, profile, sizeof(profile)); - NPCGetBossName(_, displayName, sizeof(displayName), profile); + GetBossProfile(profile).GetName(1, displayName, sizeof(displayName)); if (displayName[0] == '\0') { strcopy(displayName, sizeof(displayName), profile); } - SF2BossProfileData data; - g_BossProfileData.GetArray(profile, data, sizeof(data)); + BaseBossProfile data = GetBossProfile(profile); if (data.IsPvEBoss) { continue; @@ -529,7 +529,7 @@ static int AddBossTargetsToMenu(Menu menuHandle) NPCGetProfile(i, profile, sizeof(profile)); - NPCGetBossName(_, buffer, sizeof(buffer), profile); + GetBossProfile(profile).GetName(1, buffer, sizeof(buffer)); if (buffer[0] == '\0') { strcopy(buffer, sizeof(buffer), profile); @@ -547,10 +547,7 @@ static int AddBossTargetsToMenu(Menu menuHandle) StrCat(display, sizeof(display), " (fake)"); } - SF2BossProfileData data; - data = NPCGetProfileData(i); - - if (data.IsPvEBoss) + if (GetBossProfile(profile).IsPvEBoss) { continue; } @@ -726,7 +723,7 @@ static int AdminMenu_BossAttackWaiters(Menu menu, MenuAction action, int param1, NPCGetProfile(index, profile, sizeof(profile)); char name[SF2_MAX_NAME_LENGTH]; - NPCGetBossName(_, name, sizeof(name), profile); + GetBossProfile(profile).GetName(1, name, sizeof(name)); if (name[0] == '\0') { strcopy(name, sizeof(name), profile); @@ -840,7 +837,7 @@ static int AdminMenu_BossTeleport(Menu menu, MenuAction action, int param1, int NPCGetProfile(index, profile, sizeof(profile)); char name[SF2_MAX_NAME_LENGTH]; - NPCGetBossName(_, name, sizeof(name), profile); + GetBossProfile(profile).GetName(1, name, sizeof(name)); if (name[0] == '\0') { strcopy(name, sizeof(name), profile); @@ -915,17 +912,11 @@ static bool DisplayOverrideBossAdminMenu(int client) for (int i = 0; i < bossList.Length; i++) { bossList.GetString(i, profile, sizeof(profile)); - NPCGetBossName(_, displayName, sizeof(displayName), profile); + GetBossProfile(profile).GetName(1, displayName, sizeof(displayName)); if (displayName[0] == '\0') { strcopy(displayName, sizeof(displayName), profile); } - SF2BossProfileData data; - g_BossProfileData.GetArray(profile, data, sizeof(data)); - if (data.IsPvEBoss) - { - continue; - } menuHandle.AddItem(profile, displayName); } @@ -936,7 +927,7 @@ static bool DisplayOverrideBossAdminMenu(int client) if (profileOverride[0] != '\0' && IsProfileValid(profileOverride)) { - NPCGetBossName(_, profileDisplayName, sizeof(profileDisplayName), profileOverride); + GetBossProfile(profileOverride).GetName(1, profileDisplayName, sizeof(profileDisplayName)); if (profileDisplayName[0] == '\0') { @@ -979,9 +970,8 @@ static int AdminMenu_OverrideBoss(Menu menu, MenuAction action, int param1, int g_BossProfileOverrideConVar.SetString(profile); - ArrayList arrayNames; - arrayNames = GetBossProfileNames(profile); - arrayNames.GetString(Difficulty_Normal, name, sizeof(name)); + BaseBossProfile profileData = GetBossProfile(profile); + profileData.GetName(Difficulty_Normal, name, sizeof(name)); CPrintToChatAll("{royalblue}%t {collectors}%N {default}set the next boss to {valve}%s{default}.", "SF2 Prefix", param1, name); @@ -1045,7 +1035,7 @@ static int AdminMenu_BossWanderToPos(Menu menu, MenuAction action, int param1, i else { SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(index); - switch (npc.Type) + switch (npc.GetProfileData().Type) { case SF2BossType_Chaser: { @@ -1145,7 +1135,7 @@ static int AdminMenu_BossAlertToPos(Menu menu, MenuAction action, int param1, in else { SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(index); - switch (npc.Type) + switch (npc.GetProfileData().Type) { case SF2BossType_Chaser: { @@ -1209,21 +1199,21 @@ static bool DisplayBossAttackAdminMenu(int client) continue; } - if (NPCGetType(i) != SF2BossType_Chaser) + if (SF2NPC_BaseNPC(i).GetProfileData().Type != SF2BossType_Chaser) { continue; } SF2NPC_Chaser controller = SF2NPC_Chaser(i); - SF2ChaserBossProfileData chaserData; - chaserData = controller.GetProfileData(); - if (chaserData.Attacks == null || chaserData.Attacks.Length == 0) + ChaserBossProfile chaserData = controller.GetProfileData(); + ProfileObject attacks = chaserData.GetSection("attacks"); + if (attacks == null || attacks.Size == 0) { continue; } controller.GetProfile(profile, sizeof(profile)); - controller.GetName(buffer, sizeof(buffer)); + chaserData.GetName(1, buffer, sizeof(buffer)); if (buffer[0] == '\0') { strcopy(buffer, sizeof(buffer), profile); @@ -1241,8 +1231,7 @@ static bool DisplayBossAttackAdminMenu(int client) StrCat(display, sizeof(display), " (fake)"); } - SF2BossProfileData data; - data = NPCGetProfileData(i); + BaseBossProfile data = GetBossProfile(profile); if (data.IsPvEBoss) { @@ -1332,14 +1321,17 @@ static bool DisplayBossAttackListAdminMenu(int client) return false; } - SF2ChaserBossProfileData data; - data = g_SelectedBoss[client].GetProfileData(); + ChaserBossProfile data = g_SelectedBoss[client].GetProfileData(); + ProfileObject attacks = data.GetSection("attacks"); Menu menuHandle = new Menu(AdminMenu_BossAttackList); - for (int i = 0; i < data.Attacks.Length; i++) + for (int i = 0; i < attacks.Size; i++) { - SF2ChaserBossProfileAttackData attackData; - data.GetAttackFromIndex(i, attackData); - menuHandle.AddItem(attackData.Name, attackData.Name); + char name[64]; + data.GetAttackName(i, name, sizeof(name)); + if (name[0] != '\0') + { + menuHandle.AddItem(name, name); + } } menuHandle.SetTitle("%t Make a boss use a attack\n \n", "SF2 Prefix"); diff --git a/addons/sourcemod/scripting/sf2/anticamping.sp b/addons/sourcemod/scripting/sf2/anticamping.sp index b7477de5..3cca34d9 100644 --- a/addons/sourcemod/scripting/sf2/anticamping.sp +++ b/addons/sourcemod/scripting/sf2/anticamping.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required // Anti-camping data. int g_PlayerCampingStrikes[MAXTF2PLAYERS] = { 0, ... }; diff --git a/addons/sourcemod/scripting/sf2/changelog.sp b/addons/sourcemod/scripting/sf2/changelog.sp index 9be812cc..1bd97c99 100644 --- a/addons/sourcemod/scripting/sf2/changelog.sp +++ b/addons/sourcemod/scripting/sf2/changelog.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static Menu g_ChangelogUpdate; static KeyValues g_ChangelogConfig; diff --git a/addons/sourcemod/scripting/sf2/classconfigs.sp b/addons/sourcemod/scripting/sf2/classconfigs.sp index b2a8b1dd..74396e23 100644 --- a/addons/sourcemod/scripting/sf2/classconfigs.sp +++ b/addons/sourcemod/scripting/sf2/classconfigs.sp @@ -4,6 +4,7 @@ #define _sf2_client_classconfigs_included #pragma semicolon 1 +#pragma newdecls required float g_ClassRunSpeed[MAX_CLASSES + 1]; float g_ClassWalkSpeed[MAX_CLASSES + 1]; diff --git a/addons/sourcemod/scripting/sf2/client.sp b/addons/sourcemod/scripting/sf2/client.sp index fd42a215..875d0fcf 100644 --- a/addons/sourcemod/scripting/sf2/client.sp +++ b/addons/sourcemod/scripting/sf2/client.sp @@ -4,16 +4,20 @@ #define _sf2_client_included #pragma semicolon 1 +#pragma newdecls required #define SF2_OVERLAY_DEFAULT "overlays/slender/newcamerahud_3" #define SF2_OVERLAY_DEFAULT_NO_FILMGRAIN "overlays/slender/nofilmgrain" +static int g_ClientModifiedMaxHealth[MAXTF2PLAYERS]; + //Client Special Round Timer static Handle g_ClientSpecialRoundTimer[MAXTF2PLAYERS]; // Jumpscare data. static int g_PlayerJumpScareBoss[MAXTF2PLAYERS] = { -1, ... }; static float g_PlayerJumpScareLifeTime[MAXTF2PLAYERS] = { -1.0, ... }; +static char g_PlayerJumpScareOverlay[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; static float g_PlayerScareBoostEndTime[MAXTF2PLAYERS] = { -1.0, ... }; @@ -37,14 +41,336 @@ int g_ClientFrame[MAXTF2PLAYERS]; #include "client/sprint.sp" #include "client/breathing.sp" #include "client/ghostmode.sp" -#include "client/music.sp" +#include "client/new_music.sp" +#include "client/weapons.sp" #include "client/proxy.sp" +void SetupClients() +{ + g_OnPlayerPutInServerPFwd.AddFunction(null, OnPutInServer); + g_OnPlayerSpawnPFwd.AddFunction(null, OnPlayerSpawn); +} + void Client_SetupAPI() { Sprint_SetupAPI(); } +static void OnPutInServer(SF2_BasePlayer client) +{ + SDKHook(client.index, SDKHook_OnTakeDamage, OnTakeDamage); + SDKHook(client.index, SDKHook_OnTakeDamageAlivePost, OnTakeDamageAlivePost); +} + +static void OnPlayerSpawn(SF2_BasePlayer client) +{ + CreateTimer(0.1, Timer_CheckHealth, client.UserID, TIMER_FLAG_NO_MAPCHANGE); +} + +static Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom) +{ + SF2_BasePlayer victimPlayer = SF2_BasePlayer(victim); + if (!g_Enabled) + { + if (NPCGetFromEntIndex(attacker) != -1 && GetEntProp(attacker, Prop_Data, "m_iTeamNum") == victimPlayer.Team) + { + damage = 0.0; + return Plugin_Changed; + } + return Plugin_Continue; + } + + Action action = Plugin_Continue; + + float damage2 = damage; + Call_StartForward(g_OnClientTakeDamageFwd); + Call_PushCell(victimPlayer.index); + Call_PushCellRef(attacker); + Call_PushCellRef(inflictor); + Call_PushFloatRef(damage2); + Call_Finish(action); + + if (action == Plugin_Changed) + { + damage = damage2; + return Plugin_Changed; + } + + Call_StartForward(g_OnPlayerTakeDamagePFwd); + Call_PushCell(victimPlayer); + Call_PushCellRef(attacker); + Call_PushCellRef(inflictor); + Call_PushFloatRef(damage2); + Call_PushCellRef(damagetype); + Call_PushCell(damagecustom); + Call_Finish(action); + + if (action == Plugin_Changed) + { + damage = damage2; + return Plugin_Changed; + } + + TFClassType class = victimPlayer.Class; + + if (IsClientInKart(victimPlayer.index) && (attacker == -1 || inflictor == -1)) + { + damage = 0.0; + return Plugin_Changed; + } + + char inflictorClass[32]; + if (inflictor >= 0) + { + GetEdictClassname(inflictor, inflictorClass, sizeof(inflictorClass)); + } + + if (IsValidClient(attacker) && victimPlayer.IsValid && g_PlayerProxy[attacker] && victimPlayer.Team == TFTeam_Red && victimPlayer.InCondition(TFCond_Gas)) + { + victimPlayer.Ignite(true); + victimPlayer.ChangeCondition(TFCond_Gas, true); + } + + if (victimPlayer.InCondition(TFCond_Gas) && SF2_ChaserEntity(attacker).IsValid()) + { + victimPlayer.Ignite(true); + victimPlayer.ChangeCondition(TFCond_Gas, true); + } + + if (IsValidClient(attacker) && victimPlayer.IsValid && (victimPlayer.IsInPvP || victimPlayer.IsInPvE) && victimPlayer.Team == TFTeam_Red && GetClientTeam(attacker) == TFTeam_Red && victim != attacker) + { + damage = 0.0; + return Plugin_Changed; + } + + if (IsValidClient(attacker) && victimPlayer.IsValid && victimPlayer.Team == TFTeam_Red && GetClientTeam(attacker) == TFTeam_Red && (victimPlayer.IsTrapped || victimPlayer.IsLatched)) + { + if (!g_PlayerEliminated[attacker] && !victimPlayer.IsEliminated && (damagetype & 0x80) != 0) + { + victimPlayer.IsTrapped = false; + if (victimPlayer.IsLatched) + { + victimPlayer.ChangeCondition(TFCond_Dazed, true); + for (int i = 0; i < MAX_BOSSES; i++) + { + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(i); + if (!npc.IsValid()) + { + continue; + } + + if (victimPlayer.Latcher != npc.Index) + { + continue; + } + + SF2_ChaserEntity chaser = SF2_ChaserEntity(npc.EntIndex); + if (!chaser.IsValid()) + { + continue; + } + + chaser.MyNextBotPointer().GetIntentionInterface().OnCommandString("break tongue"); + } + } + victimPlayer.IsLatched = false; + victimPlayer.LatchCount = 0; + TF2_AddCondition(attacker, TFCond_SpeedBuffAlly, 4.0); + TF2_AddCondition(victim, TFCond_SpeedBuffAlly, 4.0); + } + } + + if (IsValidClient(attacker) && !g_PlayerEliminated[attacker] && !DidClientEscape(attacker) && class == TFClass_Soldier && !(GetEntityFlags(attacker) & FL_ONGROUND)) + { + int weaponEnt = GetPlayerWeaponSlot(attacker, TFWeaponSlot_Melee); + if (IsValidEntity(weaponEnt)) + { + int itemDefInt = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); + float zVelocity[3]; + GetEntPropVector(attacker, Prop_Data, "m_vecVelocity", zVelocity); + if (itemDefInt == 416 && zVelocity[2] < 0.0 && weaponEnt == GetEntPropEnt(attacker, Prop_Send, "m_hActiveWeapon")) // A soldier has the market gardener and is currently falling down, like Minecraft with it's critical hits. + { + damagetype |= DMG_ACID; + } + } + } + + if (IsValidEntity(inflictor) && !SF2_ChaserEntity(inflictor).IsValid() && !SF2_StatueEntity(inflictor).IsValid() && !IsValidClient(inflictor)) + { + int npcIndex = NPCGetFromEntIndex(GetEntPropEnt(inflictor, Prop_Send, "m_hOwnerEntity")); + if (npcIndex != -1) + { + bool attackEliminated = (NPCGetFlags(npcIndex) & SFF_ATTACKWAITERS) != 0; + if (!attackEliminated && (GetClientTeam(victim) == TFTeam_Blue) && IsValidClient(victim) ) + { + damage = 0.0; + return Plugin_Changed; + } + } + } + + bool canDamage = false; + if (attacker != victim && IsValidClient(attacker)) + { + if (IsClientInPvP(victim) && IsClientInPvP(attacker)) + { + canDamage = true; + } + if (IsClientLeavingPvP(victim) && !IsClientInPvP(attacker)) + { + canDamage = true; + } + if (!IsRoundEnding()) + { + if (canDamage) + { + if (attacker == inflictor) + { + if (IsValidEdict(weapon)) + { + char weaponClass[64]; + GetEdictClassname(weapon, weaponClass, sizeof(weaponClass)); + + // Backstab check! + if ((strcmp(weaponClass, "tf_weapon_knife") == 0 || (TF2_GetPlayerClass(attacker) == TFClass_Spy && strcmp(weaponClass, "saxxy") == 0)) && + (damagecustom != TF_CUSTOM_TAUNT_FENCING)) + { + float myPos[3], hisPos[3], myDirection[3]; + GetClientAbsOrigin(victim, myPos); + GetClientAbsOrigin(attacker, hisPos); + GetClientEyeAngles(victim, myDirection); + GetAngleVectors(myDirection, myDirection, NULL_VECTOR, NULL_VECTOR); + NormalizeVector(myDirection, myDirection); + ScaleVector(myDirection, 32.0); + AddVectors(myDirection, myPos, myDirection); + + float p[3], s[3]; + MakeVectorFromPoints(myPos, hisPos, p); + MakeVectorFromPoints(myPos, myDirection, s); + if (GetVectorDotProduct(p, s) <= 0.0) // We can backstab him m8 + { + if (GetClientTeam(victim) == GetClientTeam(attacker) && class == TFClass_Sniper) + { + //look if the player has a razorback + int wearableEnt = INVALID_ENT_REFERENCE; + while ((wearableEnt = FindEntityByClassname(wearableEnt, "tf_wearable")) != -1) + { + if (GetEntPropEnt(wearableEnt, Prop_Send, "m_hOwnerEntity") == victim && GetEntProp(wearableEnt, Prop_Send, "m_iItemDefinitionIndex") == 57) + { + RemoveEntity(wearableEnt); + damage = 0.0; + EmitSoundToClient(victim, "player/spy_shield_break.wav", _, _, SNDLEVEL_TRAFFIC, SND_NOFLAGS, 0.7, 100); + EmitSoundToClient(attacker, "player/spy_shield_break.wav", _, _, SNDLEVEL_TRAFFIC, SND_NOFLAGS, 0.7, 100); + + SetEntPropFloat(weapon, Prop_Send, "m_flNextPrimaryAttack", GetGameTime() + 2.0); + SetEntPropFloat(attacker, Prop_Send, "m_flNextAttack", GetGameTime() + 2.0); + SetEntPropFloat(attacker, Prop_Send, "m_flStealthNextChangeTime", GetGameTime() + 2.0); + int vm = GetEntPropEnt(attacker, Prop_Send, "m_hViewModel"); + if (vm > MaxClients) + { + int meleeIndex = GetEntProp(weapon, Prop_Send, "m_iItemDefinitionIndex"); + int anim = 41; + switch (meleeIndex) + { + case 4, 194, 225, 356, 461, 574, 649, 665, 794, 803, 883, 892, 901, 910, 959, 968: + { + anim = 15; + } + case 638: + { + anim = 31; + } + } + SetEntProp(vm, Prop_Send, "m_nSequence", anim); + } + return Plugin_Changed; + } + } + } + if (damagecustom == TF_CUSTOM_BACKSTAB) // Modify backstab damage. + { + damage = 120.0; + if (damagetype & DMG_ACID) + { + damage = 120.0; + } + } + + if (g_WeaponCriticalsConVar != null && g_WeaponCriticalsConVar.BoolValue) + { + damagetype |= DMG_ACID; + } + + if (!IsClientCritUbercharged(victim)) + { + if (GetClientTeam(victim) == GetClientTeam(attacker)) + { + int pistol = GetPlayerWeaponSlot(attacker, TFWeaponSlot_Primary); + if (pistol > MaxClients && GetEntProp(pistol, Prop_Send, "m_iItemDefinitionIndex") == 525) //Give one crit fort the backstab + { + int crits = GetEntProp(attacker, Prop_Send, "m_iRevengeCrits"); + crits++; + SetEntProp(attacker, Prop_Send, "m_iRevengeCrits", crits); + } + } + if (GetEntProp(victim, Prop_Send, "m_iHealth") <= 120) + { + g_PlayerBackStabbed[victim] = true; + } + else + { + g_PlayerBackStabbed[victim] = false; + } + } + return Plugin_Changed; + } + } + } + } + } + else + { + damage = 0.0; + return Plugin_Changed; + } + } + else + { + if (g_PlayerEliminated[attacker] == g_PlayerEliminated[victim]) + { + damage = 0.0; + return Plugin_Changed; + } + } + } + + return Plugin_Continue; +} + +static void OnTakeDamageAlivePost(int victim, int attacker, int inflictor, float damage, int damageType, int weaponEntIndex, const float vecDamageForce[3], const float vecDamagePosition[3]) +{ + Call_StartForward(g_OnPlayerTakeDamagePostPFwd); + Call_PushCell(SF2_BasePlayer(victim)); + Call_PushCell(attacker); + Call_PushCell(inflictor); + Call_PushFloat(damage); + Call_PushCell(damageType); + Call_Finish(); +} + +static Action Timer_CheckHealth(Handle timer, any id) +{ + int client = GetClientOfUserId(id); + if (!IsValidClient(client)) + { + return Plugin_Stop; + } + + g_ClientModifiedMaxHealth[client] = SF2_BasePlayer(client).Health; + + return Plugin_Stop; +} + // ========================================================== // GENERAL CLIENT HOOK FUNCTIONS // ========================================================== @@ -102,8 +428,18 @@ void ClientSetScareBoostEndTime(int client, float time) g_PlayerScareBoostEndTime[client] = time; } +int ClientGetModifiedMaxHealth(int client) +{ + return g_ClientModifiedMaxHealth[client]; +} + Action Hook_HealthKitOnTouch(int healthKit, int client) { + if (!g_Enabled) + { + return Plugin_Continue; + } + if (IsValidClient(client)) { if (IsClientInPvE(client) || IsClientInPvP(client)) @@ -454,12 +790,13 @@ void ClientProcessVisibility(int client) NPCGetProfile(i, profile, sizeof(profile)); + BaseBossProfile profileData = GetBossProfile(profile); + int boss = NPCGetEntIndex(i); if (boss && boss != INVALID_ENT_REFERENCE) { CBaseEntity(boss).GetAbsOrigin(slenderPos); - NPCGetEyePosition(i, slenderEyePos); float slenderMins[3], slenderMaxs[3]; GetEntPropVector(boss, Prop_Send, "m_vecMins", slenderMins); @@ -479,6 +816,7 @@ void ClientProcessVisibility(int client) if (boss && boss != INVALID_ENT_REFERENCE) { int copyMaster = NPCGetFromUniqueID(g_SlenderCopyMaster[i]); + NPCGetEyePosition(i, slenderEyePos); if (!IsPointVisibleToPlayer(client, slenderEyePos, true, SlenderUsesBlink(i))) { @@ -489,7 +827,7 @@ void ClientProcessVisibility(int client) g_PlayerSeesSlender[client][i] = true; } - if ((GetGameTime() - g_PlayerSeesSlenderLastTime[client][i]) > g_SlenderStaticGraceTime[i][difficulty] || + if ((GetGameTime() - g_PlayerSeesSlenderLastTime[client][i]) > profileData.GetStaticOnLookGraceTime(difficulty) || (oldStaticMode[i] == Static_Increase && g_PlayerStaticAmount[client] > 0.1)) { if ((NPCGetFlags(i) & SFF_STATICONLOOK) && @@ -505,7 +843,7 @@ void ClientProcessVisibility(int client) } } else if ((NPCGetFlags(i) & SFF_STATICONRADIUS) && - GetVectorSquareMagnitude(myPos, slenderPos) <= SquareFloat(g_SlenderStaticRadius[i][difficulty])) + GetVectorSquareMagnitude(myPos, slenderPos) <= SquareFloat(profileData.GetStaticRadius(difficulty))) { bool noObstacles = IsPointVisibleToPlayer(client, slenderEyePos, false, false); if (!noObstacles) @@ -542,6 +880,7 @@ void ClientProcessVisibility(int client) } NPCGetProfile(master, masterProfile, sizeof(masterProfile)); + BaseBossProfile masterData = GetBossProfile(masterProfile); // Boss visiblity. if (g_PlayerSeesSlender[client][i] && !wasSeeingSlender[i]) @@ -550,39 +889,36 @@ void ClientProcessVisibility(int client) if (GetGameTime() >= g_PlayerScareNextTime[client][master]) { - if (GetVectorSquareMagnitude(myPos, slenderPos) <= SquareFloat(NPCGetScareRadius(i))) + if (GetVectorSquareMagnitude(myPos, slenderPos) <= SquareFloat(masterData.ScareRadius)) { ClientPerformScare(client, master); - if (NPCGetSpeedBoostOnScare(master)) + if (masterData.ScareSpeedBoostDuration > 0.0) { - TF2_AddCondition(client, TFCond_SpeedBuffAlly, NPCGetScareSpeedBoostDuration(master), client); + TF2_AddCondition(client, TFCond_SpeedBuffAlly, masterData.ScareSpeedBoostDuration, client); } - if (NPCGetScareReactionState(master)) + switch (masterData.ScareReactionType) { - switch (NPCGetScareReactionType(master)) + case 1: { - case 1: - { - SpeakResponseConcept(client, "TLK_PLAYER_SPELL_METEOR_SWARM"); - } - case 2: - { - SpeakResponseConcept(client, "HalloweenLongFall"); - } - case 3: - { - char scareReactionCustom[PLATFORM_MAX_PATH]; - GetBossProfileScareReactionCustom(masterProfile, scareReactionCustom, sizeof(scareReactionCustom)); - SpeakResponseConcept(client, scareReactionCustom); - } + SpeakResponseConcept(client, "TLK_PLAYER_SPELL_METEOR_SWARM"); + } + case 2: + { + SpeakResponseConcept(client, "HalloweenLongFall"); + } + case 3: + { + char scareReactionCustom[PLATFORM_MAX_PATH]; + masterData.GetCustomScareReaction(scareReactionCustom, sizeof(scareReactionCustom)); + SpeakResponseConcept(client, scareReactionCustom); } } - if (NPCGetScareReplenishSprintState(master)) + if (masterData.ScareReplenishSprintAmount > 0.0) { - SF2_BasePlayer(client).Stamina += NPCGetScareReplenishSprintAmount(master); + SF2_BasePlayer(client).Stamina += masterData.ScareReplenishSprintAmount; } float value = NPCGetAttributeValue(master, SF2Attribute_IgnitePlayerOnScare); @@ -613,19 +949,18 @@ void ClientProcessVisibility(int client) } } - if (NPCGetJumpscareOnScare(master)) + if (masterData.JumpscareOnScare) { - float jumpScareDuration = NPCGetJumpscareDuration(master, difficulty); - ClientDoJumpScare(client, master, jumpScareDuration); + ClientDoJumpScare(client, master, masterData.GetJumpscareDuration(difficulty)); } } else { - g_PlayerScareNextTime[client][master] = GetGameTime() + NPCGetScareCooldown(master); + g_PlayerScareNextTime[client][master] = GetGameTime() + masterData.ScareCooldown; } } - if (NPCGetType(i) == SF2BossType_Static) + if (profileData.Type == SF2BossType_Static) { if (NPCGetFlags(i) & SFF_FAKE) { @@ -668,11 +1003,11 @@ void ClientProcessVisibility(int client) if (NPCGetFlags(i) & SFF_HASSTATICLOOPLOCALSOUND) { char loopSound[PLATFORM_MAX_PATH]; - GetBossProfileStaticLocalSound(profile, loopSound, sizeof(loopSound)); + profileData.GetStaticLocalLoopSound(loopSound, sizeof(loopSound)); if (loopSound[0] != '\0') { - EmitSoundToClient(client, loopSound, boss, SNDCHAN_STATIC, GetBossProfileStaticShakeLocalLevel(profile), SND_CHANGEVOL, 1.0); + EmitSoundToClient(client, loopSound, boss, SNDCHAN_STATIC, profileData.StaticShakeLocalLevel, SND_CHANGEVOL, 1.0); ClientAddStress(client, 0.03); } } @@ -685,7 +1020,7 @@ void ClientProcessVisibility(int client) if (boss && boss != INVALID_ENT_REFERENCE) { char loopSound[PLATFORM_MAX_PATH]; - GetBossProfileStaticLocalSound(profile, loopSound, sizeof(loopSound)); + profileData.GetStaticLocalLoopSound(loopSound, sizeof(loopSound)); if (loopSound[0] != '\0') { @@ -779,10 +1114,12 @@ void ClientProcessVisibility(int client) { NPCGetProfile(bossNewStatic, profile, sizeof(profile)); + BaseBossProfile profileData = GetBossProfile(profile); + g_PlayerStaticSound[client][0] = '\0'; char staticSound[PLATFORM_MAX_PATH]; - GetBossProfileStaticSound(profile, staticSound, sizeof(staticSound)); + profileData.GetStaticSound(staticSound, sizeof(staticSound)); if (staticSound[0] != '\0') { @@ -801,8 +1138,8 @@ void ClientProcessVisibility(int client) TriggerTimer(g_PlayerLastStaticTimer[client], true); // Start up our own static timer. - float staticIncreaseRate = (g_SlenderStaticRate[bossNewStatic][difficulty] - (g_SlenderStaticRate[bossNewStatic][difficulty] * GetDifficultyModifier(difficulty)) / 10); - float staticDecreaseRate = (g_SlenderStaticRateDecay[bossNewStatic][difficulty] + (g_SlenderStaticRateDecay[bossNewStatic][difficulty] * GetDifficultyModifier(difficulty)) / 10); + float staticIncreaseRate = (profileData.GetStaticRate(difficulty) - (profileData.GetStaticRate(difficulty) * GetDifficultyModifier(difficulty)) / 10); + float staticDecreaseRate = (profileData.GetStaticRateDecay(difficulty) + (profileData.GetStaticRateDecay(difficulty) * GetDifficultyModifier(difficulty)) / 10); if (!IsClassConfigsValid()) { if (class == TFClass_Heavy) @@ -1100,13 +1437,12 @@ void ClientDoJumpScare(int client, int bossIndex, float lifeTime) char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); + BaseBossProfile profileData = GetBossProfile(profile); - char buffer[PLATFORM_MAX_PATH]; - GetBossProfileJumpscareSound(profile, buffer, sizeof(buffer)); - - if (buffer[0] != '\0') + profileData.GetJumpscareSounds().EmitSound(true, client); + if (profileData.GetJumpscareOverlays() != null) { - EmitSoundToClient(client, buffer, _, MUSIC_CHAN); + profileData.GetJumpscareOverlays().GetString(GetRandomInt(0, profileData.GetJumpscareOverlays().Length - 1), g_PlayerJumpScareOverlay[client], sizeof(g_PlayerJumpScareOverlay[])); } } @@ -1122,14 +1458,19 @@ void ClientPerformScare(int client, int bossIndex) return; } + float gameTime = GetGameTime(); + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); + BaseBossProfile profileData = GetBossProfile(profile); + + int difficulty = GetLocalGlobalDifficulty(bossIndex); - g_PlayerScareLastTime[client][bossIndex] = GetGameTime(); - g_PlayerScareNextTime[client][bossIndex] = GetGameTime() + NPCGetScareCooldown(bossIndex); + g_PlayerScareLastTime[client][bossIndex] = gameTime; + g_PlayerScareNextTime[client][bossIndex] = gameTime + profileData.ScareCooldown; // See how much Sanity should be drained from a scare. - float staticAmount = GetBossProfileStaticScareAmount(profile); + float staticAmount = profileData.GetScareStaticAmount(difficulty); g_PlayerStaticAmount[client] += staticAmount; if (g_PlayerStaticAmount[client] > 1.0) { @@ -1137,21 +1478,17 @@ void ClientPerformScare(int client, int bossIndex) } char scareSound[PLATFORM_MAX_PATH]; - ArrayList soundList; - SF2BossProfileSoundInfo soundInfo; - GetBossProfileScareSounds(profile, soundInfo); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) + ProfileSound soundInfo = profileData.GetScareSounds(); + if (soundInfo != null && soundInfo.Paths != null && soundInfo.Paths.Length > 0) { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), scareSound, sizeof(scareSound)); + soundInfo.Paths.GetString(GetRandomInt(0, soundInfo.Paths.Length - 1), scareSound, sizeof(scareSound)); } - soundList = null; if (scareSound[0] != '\0') { soundInfo.EmitSound(true, client); - g_PlayerSightSoundNextTime[client][bossIndex] = GetGameTime() + GetRandomFloat(soundInfo.CooldownMin, soundInfo.CooldownMax); + g_PlayerSightSoundNextTime[client][bossIndex] = gameTime + profileData.ScareCooldown; if (g_PlayerStressAmount[client] > 0.4) { @@ -1196,15 +1533,15 @@ static void ClientPerformSightSound(int client, int bossIndex) char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); + int difficulty = GetLocalGlobalDifficulty(bossIndex); - SF2BossProfileSoundInfo soundInfo; - GetBossProfileSightSounds(profile, soundInfo); + ProfileSound soundInfo = GetBossProfile(profile).GetSightSounds(); - if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) + if (soundInfo != null && soundInfo.Paths != null && soundInfo.Paths.Length > 0) { soundInfo.EmitSound(true, client); - g_PlayerSightSoundNextTime[client][master] = GetGameTime() + GetRandomFloat(soundInfo.CooldownMin, soundInfo.CooldownMax); + g_PlayerSightSoundNextTime[client][master] = GetGameTime() + GetRandomFloat(soundInfo.GetCooldownMin(difficulty), soundInfo.GetCooldownMax(difficulty)); float bossPos[3], myPos[3]; int boss = NPCGetEntIndex(bossIndex); @@ -1300,26 +1637,18 @@ Action Timer_PlayerOverlayCheck(Handle timer, any userid) return Plugin_Stop; } - if (IsRoundInWarmup()) - { - return Plugin_Continue; - } - int deathCamBoss = NPCGetFromUniqueID(g_PlayerDeathCamBoss[client]); int jumpScareBoss = NPCGetFromUniqueID(g_PlayerJumpScareBoss[client]); - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; char material[PLATFORM_MAX_PATH], overlay[PLATFORM_MAX_PATH]; - if (IsClientInDeathCam(client) && deathCamBoss != -1 && g_PlayerDeathCamShowOverlay[client]) + if (IsClientInDeathCam(client) && deathCamBoss != -1 && g_PlayerDeathCamCurrentOverlay[client][0] != '\0' && g_PlayerDeathCamShowOverlay[client]) { - NPCGetProfile(deathCamBoss, profile, sizeof(profile)); - GetBossProfileOverlayPlayerDeath(profile, material, sizeof(material)); + strcopy(material, sizeof(material), g_PlayerDeathCamCurrentOverlay[client]); } - else if (jumpScareBoss != -1 && GetGameTime() <= g_PlayerJumpScareLifeTime[client]) + else if (jumpScareBoss != -1 && g_PlayerJumpScareOverlay[client] [0] != '\0' && GetGameTime() <= g_PlayerJumpScareLifeTime[client]) { - NPCGetProfile(jumpScareBoss, profile, sizeof(profile)); - GetBossProfileOverlayJumpscare(profile, material, sizeof(material)); + strcopy(material, sizeof(material), g_PlayerJumpScareOverlay[client]); } else if (IsClientInGhostMode(client) && !SF_IsBoxingMap()) { @@ -1332,15 +1661,35 @@ Action Timer_PlayerOverlayCheck(Handle timer, any userid) } else { - if (!g_PlayerPreferences[client].PlayerPreference_FilmGrain) + for (int i = 0; i < MAX_BOSSES; i++) { - g_OverlayNoGrainConVar.GetString(overlay, sizeof(overlay)); - strcopy(material, sizeof(material), overlay); + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(i); + if (!npc.IsValid()) + { + continue; + } + + BaseBossProfile profile = npc.GetProfileData(); + if (profile.GetRedCameraOverlays() == null) + { + continue; + } + + profile.GetActiveRedCameraOverlay(material, sizeof(material)); + break; } - else + if (material[0] == '\0') { - g_CameraOverlayConVar.GetString(overlay, sizeof(overlay)); - strcopy(material, sizeof(material), overlay); + if (!g_PlayerPreferences[client].PlayerPreference_FilmGrain) + { + g_OverlayNoGrainConVar.GetString(overlay, sizeof(overlay)); + strcopy(material, sizeof(material), overlay); + } + else + { + g_CameraOverlayConVar.GetString(overlay, sizeof(overlay)); + strcopy(material, sizeof(material), overlay); + } } } @@ -1670,6 +2019,47 @@ void TF2_GetClassName(TFClassType class, char[] buffer, int bufferLen, bool alt } } +TFClassType TF2_GetClassFromName(const char[] class) +{ + if (strcmp(class, "scout", false) == 0) + { + return TFClass_Scout; + } + else if (strcmp(class, "soldier", false) == 0) + { + return TFClass_Soldier; + } + else if (strcmp(class, "pyro", false) == 0) + { + return TFClass_Pyro; + } + else if (strcmp(class, "demoman", false) == 0) + { + return TFClass_DemoMan; + } + else if (strcmp(class, "heavyweapons", false) == 0 || strcmp(class, "heavy", false) == 0) + { + return TFClass_Heavy; + } + else if (strcmp(class, "engineer", false) == 0) + { + return TFClass_Engineer; + } + else if (strcmp(class, "medic", false) == 0) + { + return TFClass_Medic; + } + else if (strcmp(class, "sniper", false) == 0) + { + return TFClass_Sniper; + } + else if (strcmp(class, "spy", false) == 0) + { + return TFClass_Spy; + } + return TFClass_Unknown; +} + bool IsPointVisibleToAPlayer(const float pos[3], bool checkFOV = true, bool checkBlink = false, bool checkEliminated = true, bool ignoreFog = false) { for (int i = 1; i <= MaxClients; i++) @@ -1721,8 +2111,9 @@ bool IsPointVisibleToPlayer(int client, const float pos[3], bool checkFOV = true } } - TR_TraceRayFilter(eyePos, pos, MASK_PLAYERSOLID_BRUSHONLY | CONTENTS_WINDOW, RayType_EndPoint, TraceRayDontHitAnything, client); - bool hit = TR_DidHit(); + Handle trace = TR_TraceRayFilterEx(eyePos, pos, MASK_PLAYERSOLID_BRUSHONLY | CONTENTS_WINDOW, RayType_EndPoint, TraceRayDontHitAnything, client); + bool hit = TR_DidHit(trace); + delete trace; if (hit) { diff --git a/addons/sourcemod/scripting/sf2/client/blink.sp b/addons/sourcemod/scripting/sf2/client/blink.sp index e8e9935b..5c421845 100644 --- a/addons/sourcemod/scripting/sf2/client/blink.sp +++ b/addons/sourcemod/scripting/sf2/client/blink.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required // Blink data. static Handle g_PlayerBlinkTimer[MAXTF2PLAYERS] = { null, ... }; @@ -263,14 +264,16 @@ static float ClientGetBlinkRate(int client) continue; } + BaseBossProfile profileData = SF2NPC_BaseNPC(i).GetProfileData(); + if (g_PlayerSeesSlender[client][i]) { - value *= NPCGetBlinkLookRate(i); + value *= profileData.BlinkLookRate; } else if (g_PlayerStaticMode[client][i] == Static_Increase) { - value *= NPCGetBlinkStaticRate(i); + value *= profileData.BlinkStaticRate; } } diff --git a/addons/sourcemod/scripting/sf2/client/breathing.sp b/addons/sourcemod/scripting/sf2/client/breathing.sp index 7e841929..9e251123 100644 --- a/addons/sourcemod/scripting/sf2/client/breathing.sp +++ b/addons/sourcemod/scripting/sf2/client/breathing.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required #define SF2_PLAYER_BREATH_COOLDOWN_MIN 0.8 #define SF2_PLAYER_BREATH_COOLDOWN_MAX 2.0 diff --git a/addons/sourcemod/scripting/sf2/client/deathcam.sp b/addons/sourcemod/scripting/sf2/client/deathcam.sp index c169291c..fe2c6cd5 100644 --- a/addons/sourcemod/scripting/sf2/client/deathcam.sp +++ b/addons/sourcemod/scripting/sf2/client/deathcam.sp @@ -1,14 +1,234 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap BossProfileDeathCamData < ProfileObject +{ + property bool IsEnabled + { + public get() + { + return this != null; + } + } + + property bool PlayScareSound + { + public get() + { + return this.GetBool("scare_sound", false); + } + } + + property KeyMap_Array Overlays + { + public get() + { + return this.GetArray("overlays"); + } + } + + property float LegacyOverlayStartTime + { + public get() + { + return this.GetFloat("__legacy_overlay_start", 0.0); + } + } + + property float LegacyDuration + { + public get() + { + return this.GetFloat("__legacy_duration", 0.0); + } + } + + public void GetLookPosition(float buffer[3]) + { + this.GetVector("look_position", buffer); + } + + property bool IsPublicDeathCamEnabled + { + public get() + { + return this.GetSection("public") != null; + } + } + + property float Speed + { + public get() + { + float def = 1000.0; + ProfileObject obj = this.GetSection("public"); + if (obj != null) + { + def = obj.GetFloat("speed", def); + } + return def; + } + } + + property float Acceleration + { + public get() + { + float def = 1000.0; + ProfileObject obj = this.GetSection("public"); + if (obj != null) + { + def = obj.GetFloat("acceleration", def); + } + return def; + } + } + + property float Deceleration + { + public get() + { + float def = 1000.0; + ProfileObject obj = this.GetSection("public"); + if (obj != null) + { + def = obj.GetFloat("deceleration", def); + } + return def; + } + } + + property float BackwardOffset + { + public get() + { + float def = 0.0; + ProfileObject obj = this.GetSection("public"); + obj = obj != null ? obj.GetSection("offset") : null; + if (obj != null) + { + def = obj.GetFloat("backward", def); + } + return def; + } + } + + property float DownwardOffset + { + public get() + { + float def = 0.0; + ProfileObject obj = this.GetSection("public"); + obj = obj != null ? obj.GetSection("offset") : null; + if (obj != null) + { + def = obj.GetFloat("downward", def); + } + return def; + } + } + + public void GetAttachment(char[] buffer, int bufferSize) + { + ProfileObject obj = this.GetSection("public"); + if (obj != null) + { + obj.GetString("attachment", buffer, bufferSize); + } + } + + public void GetTargetAttachment(char[] buffer, int bufferSize) + { + ProfileObject obj = this.GetSection("public"); + if (obj != null) + { + obj.GetString("target_attachment", buffer, bufferSize); + } + } + + property bool Blackout + { + public get() + { + return this.GetBool("blackout", false); + } + } + + property bool StopSounds + { + public get() + { + return this.GetBool("stop_sounds", false); + } + } + + property ProfileSound ExecutionSounds + { + public get() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + return view_as(obj.GetSection("execution")); + } + return null; + } + } + + public ProfileMasterAnimations GetAnimations() + { + return view_as(this.GetSection("animations")); + } + + public void Precache() + { + this.ConvertValuesSectionToArray("overlays"); + if (this.Overlays != null) + { + for (int i = 0; i < this.Overlays.Length; i++) + { + char value[PLATFORM_MAX_PATH]; + this.Overlays.GetString(i, value, sizeof(value)); + if (value[0] != '\0') + { + PrecacheMaterial2(value, g_FileCheckConVar.BoolValue); + StripMaterialsFolder(value, sizeof(value)); + this.Overlays.SetString(i, value); + } + } + } + if (this.ExecutionSounds != null) + { + this.ExecutionSounds.Precache(); + } + } +} + +methodmap DeathCamAnimation < ProfileAnimation +{ + public float GetPlayerDuration(int difficulty) + { + return this.GetDifficultyFloat("player_duration", difficulty, -1.0); + } + + public float GetOverlayStartTime(int difficulty) + { + return this.GetDifficultyFloat("overlay_start_time", difficulty, -1.0); + } +} // Deathcam data. int g_PlayerDeathCamBoss[MAXTF2PLAYERS] = { -1, ... }; static bool g_PlayerDeathCam[MAXTF2PLAYERS] = { false, ... }; static float g_PlayerDeathCamTimer[MAXTF2PLAYERS]; static bool g_PlayerDeathCamMustDoOverlay[MAXTF2PLAYERS]; +static float g_PlayerDeathCamOverlayTimer[MAXTF2PLAYERS]; bool g_PlayerDeathCamShowOverlay[MAXTF2PLAYERS] = { false, ... }; +char g_PlayerDeathCamCurrentOverlay[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; int g_PlayerDeathCamEnt[MAXTF2PLAYERS] = { INVALID_ENT_REFERENCE, ... }; static int g_PlayerDeathCamEnt2[MAXTF2PLAYERS] = { INVALID_ENT_REFERENCE, ... }; static int g_PlayerDeathCamTarget[MAXTF2PLAYERS] = { INVALID_ENT_REFERENCE, ... }; +static ProfileSound g_PlayerDeathCamSound[MAXTF2PLAYERS] = { null, ... }; bool g_CameraInDeathCamAdvanced[2049] = { false, ... }; float g_CameraPlayerOffsetBackward[2049] = { 0.0, ... }; float g_CameraPlayerOffsetDownward[2049] = { 0.0, ... }; @@ -119,27 +339,35 @@ static void ClientResetDeathCam(int client) if (deathCamBoss != -1) { + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(deathCamBoss, profile, sizeof(profile)); + BossProfileDeathCamData data = GetBossProfile(profile).GetDeathCamData(); if ((NPCGetFlags(deathCamBoss) & SFF_FAKE) == 0) { - SF2BossProfileData data; - data = NPCGetProfileData(deathCamBoss); - if (data.DeathCamData.Enabled && data.DeathCamData.Blackout) + if (data.IsEnabled) { - UTIL_ScreenFade(client, 10, 0, 0x0002 | 0x0010 | 0x0008, 0, 0, 0, 255); - CreateTimer(0.1, Timer_DeleteRagdoll, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); - TeleportEntity(client, {16000.0, 16000.0, 16000.0}); + if (data.Blackout) + { + UTIL_ScreenFade(client, 10, 0, 0x0002 | 0x0010 | 0x0008, 0, 0, 0, 255); + CreateTimer(0.1, Timer_DeleteRagdoll, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + TeleportEntity(client, {16000.0, 16000.0, 16000.0}); + + UTIL_ScreenFade(client, 10, 0, 0x0002 | 0x0010 | 0x0008, 0, 0, 0, 255); + } - SF2BossProfileSoundInfo soundInfo; - soundInfo = data.DeathCamData.ExecutionSounds; - if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) + if (data.ExecutionSounds != null) { - for (int i = 0; i < soundInfo.Paths.Length; i++) + for (int i = 0; i < data.ExecutionSounds.Paths.Length; i++) { - soundInfo.EmitSound(true, client, SOUND_FROM_PLAYER, _, _, _, i); + data.ExecutionSounds.EmitSound(true, client, SOUND_FROM_PLAYER, _, _, _, i); } } - UTIL_ScreenFade(client, 10, 0, 0x0002 | 0x0010 | 0x0008, 0, 0, 0, 255); + if (data.StopSounds && g_PlayerDeathCamSound[client] != null) + { + g_PlayerDeathCamSound[client].StopAllSounds(client); + g_PlayerDeathCamSound[client] = null; + } } } @@ -216,29 +444,26 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); - SF2BossProfileData data; - data = NPCGetProfileData(bossIndex); + BaseBossProfile data = GetBossProfile(profile); + BossProfileDeathCamData deathCamData = data.GetDeathCamData(); - SF2BossProfileSoundInfo soundInfo; - if (g_SlenderDeathCamScareSound[bossIndex]) + if (deathCamData.PlayScareSound) { - soundInfo = data.ScareSounds; - soundInfo.EmitSound(true, client, SOUND_FROM_PLAYER); + data.GetScareSounds().EmitSound(true, client, SOUND_FROM_PLAYER); } - soundInfo = data.ClientDeathCamSounds; - soundInfo.EmitSound(true, client, SOUND_FROM_PLAYER); + g_PlayerDeathCamSound[client] = data.GetClientDeathCamSounds(); + g_PlayerDeathCamSound[client].EmitSound(true, client, SOUND_FROM_PLAYER); if ((NPCGetFlags(bossIndex) & SFF_FAKE) == 0) { - soundInfo = data.GlobalDeathCamSounds; - for (int i = 0; i <= MaxClients; i++) + for (int i = 1; i <= MaxClients; i++) { if (!IsValidClient(i)) { continue; } - soundInfo.EmitSound(true, i); + data.GetGlobalDeathCamSounds().EmitSound(true, i); } } @@ -250,7 +475,7 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool if ((NPCGetFlags(bossIndex) & SFF_FAKE) == 0) { - if ((!NPCHasDeathCamEnabled(bossIndex) || antiCamp)) + if ((!deathCamData.IsEnabled || antiCamp)) { SetEntProp(client, Prop_Data, "m_takedamage", 2); // We do this because the point_viewcontrol changes our lifestate. @@ -261,7 +486,7 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool CBaseEntity boss = CBaseEntity(NPCGetEntIndex(bossIndex)); float damage = float(GetEntProp(client, Prop_Send, "m_iHealth")) * 4.0; - SDKHooks_TakeDamage(client, boss.IsValid() ? boss.index : 0, boss.IsValid() ? boss.index : 0, damage, 0x80 | DMG_PREVENT_PHYSICS_FORCE, _, { 0.0, 0.0, 0.0 }); + SDKHooks_TakeDamage(client, boss.IsValid() ? boss.index : 0, boss.IsValid() ? boss.index : 0, damage, 0x80 | DMG_PREVENT_PHYSICS_FORCE, _, { 0.0, 0.0, 0.0 }, .bypassHooks = false); ForcePlayerSuicide(client); // Sometimes SDKHooks_TakeDamage doesn't work (probably because of point_viewcontrol), the player is still alive and result in a endless round. KillClient(client); return; @@ -269,7 +494,7 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool } else { - if (!NPCHasDeathCamEnabled(bossIndex)) + if (!deathCamData.IsEnabled) { SlenderMarkAsFake(bossIndex); return; @@ -289,7 +514,7 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool // Create fake model. int slender = -1; - bool publicDeathcam = data.PublicDeathCam || data.DeathCamData.Enabled; + bool publicDeathcam = deathCamData.IsPublicDeathCamEnabled; if (!publicDeathcam) { slender = SpawnSlenderModel(bossIndex, lookPos, true); @@ -304,6 +529,7 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool { SF2_BaseBoss(slender).IsKillingSomeone = true; SF2_BaseBoss(slender).KillTarget = CBaseEntity(client); + SF2_BaseBoss(slender).FullDeathCamDuration = deathCamData.LegacyDuration <= 0.0; } } g_PlayerDeathCamEnt2[client] = EntIndexToEntRef(slender); @@ -315,7 +541,7 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool int target = CreateEntityByName("info_target"); if (!publicDeathcam) { - offsetPos = data.DeathCamPos; + deathCamData.GetLookPosition(offsetPos); AddVectors(lookPos, offsetPos, offsetPos); TeleportEntity(target, offsetPos, NULL_VECTOR, NULL_VECTOR); DispatchKeyValue(target, "targetname", name); @@ -330,11 +556,7 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool DispatchKeyValue(target, "targetname", name); SetVariantString("!activator"); AcceptEntityInput(target, "SetParent", slender); - strcopy(boneName, sizeof(boneName), data.PublicDeathCamAttachment); - if (data.DeathCamData.Attachment[0] != '\0') - { - strcopy(boneName, sizeof(boneName), data.DeathCamData.Attachment); - } + deathCamData.GetAttachment(boneName, sizeof(boneName)); if (boneName[0] != '\0') { SetVariantString(boneName); @@ -364,9 +586,9 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool { float camSpeed, camAcceleration, camDeceleration; - camSpeed = g_SlenderPublicDeathCamSpeed[bossIndex]; - camAcceleration = g_SlenderPublicDeathCamAcceleration[bossIndex]; - camDeceleration = g_SlenderPublicDeathCamDeceleration[bossIndex]; + camSpeed = deathCamData.Speed; + camAcceleration = deathCamData.Acceleration; + camDeceleration = deathCamData.Deceleration; FloatToString(camSpeed, buffer, sizeof(buffer)); DispatchKeyValue(camera, "acceleration", buffer); FloatToString(camAcceleration, buffer, sizeof(buffer)); @@ -377,53 +599,60 @@ void ClientStartDeathCam(int client, int bossIndex, const float lookPos[3], bool SetVariantString("!activator"); AcceptEntityInput(camera, "SetParent", slender); char attachmentName[PLATFORM_MAX_PATH]; - strcopy(attachmentName, sizeof(attachmentName), data.PublicDeathCamAttachment); - if (data.DeathCamData.Attachment[0] != '\0') - { - strcopy(attachmentName, sizeof(attachmentName), data.DeathCamData.Attachment); - } + deathCamData.GetAttachment(attachmentName, sizeof(attachmentName)); if (attachmentName[0] != '\0') { SetVariantString(attachmentName); AcceptEntityInput(camera, "SetParentAttachment"); } - strcopy(attachmentName, sizeof(attachmentName), data.PublicDeathCamAttachmentTarget); - if (data.DeathCamData.Attachment[0] != '\0') - { - strcopy(attachmentName, sizeof(attachmentName), data.DeathCamData.TargetAttachment); - } + deathCamData.GetTargetAttachment(attachmentName, sizeof(attachmentName)); DispatchKeyValue(camera, "targetname", attachmentName); g_CameraInDeathCamAdvanced[camera] = true; - g_CameraPlayerOffsetBackward[camera] = g_SlenderPublicDeathCamBackwardOffset[bossIndex]; - g_CameraPlayerOffsetDownward[camera] = g_SlenderPublicDeathCamDownwardOffset[bossIndex]; + g_CameraPlayerOffsetBackward[camera] = deathCamData.BackwardOffset; + g_CameraPlayerOffsetDownward[camera] = deathCamData.DownwardOffset; RequestFrame(Frame_PublicDeathCam, camera); //Resend taunt sound to eliminated players only } - float duration = g_SlenderDeathCamTime[bossIndex]; - if (g_SlenderDeathCamOverlay[bossIndex] && g_SlenderDeathCamOverlayTimeStart[bossIndex] >= 0.0) - { - duration = g_SlenderDeathCamOverlayTimeStart[bossIndex]; - } - if (duration <= 0.0 && publicDeathcam) + float duration = deathCamData.LegacyDuration; + float overlayDuration = deathCamData.LegacyOverlayStartTime; + g_PlayerDeathCamMustDoOverlay[client] = deathCamData.Overlays != null; + if (duration <= 0.0) { char animation[64]; - SF2BossProfileMasterAnimationsData animData; - animData = data.AnimationData; float rate = 1.0, cycle = 0.0; - animData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_DeathCam], difficulty, animation, sizeof(animation), rate, duration, cycle); - CBaseAnimating animator = CBaseAnimating(slender); - int sequence = animator.LookupSequence(animation); - if (duration <= 0.0 && sequence != -1) + DeathCamAnimation section = view_as(deathCamData.GetAnimations().GetAnimation("start")); + if (section != null) { - duration = animator.SequenceDuration(sequence) / rate; - duration *= (1.0 - cycle); + CBaseAnimating animator = CBaseAnimating(slender); + section.GetAnimationName(difficulty, animation, sizeof(animation)); + rate = section.GetAnimationPlaybackRate(difficulty); + cycle = section.GetAnimationCycle(difficulty); + duration = section.GetDuration(difficulty); + int sequence = animator.LookupSequence(animation); + if (duration <= 0.0 && sequence != -1) + { + duration = animator.SequenceDuration(sequence) / rate; + duration *= (1.0 - cycle); + } + if (section.GetPlayerDuration(difficulty) > 0.0) + { + duration = section.GetPlayerDuration(difficulty); + } + if (section.GetOverlayStartTime(difficulty) > 0.0) + { + overlayDuration = section.GetOverlayStartTime(difficulty); + } } } g_PlayerDeathCamTimer[client] = duration; - g_PlayerDeathCamMustDoOverlay[client] = g_SlenderDeathCamOverlay[bossIndex]; + g_PlayerDeathCamOverlayTimer[client] = overlayDuration; + if (g_PlayerDeathCamMustDoOverlay[client]) + { + deathCamData.Overlays.GetString(GetRandomInt(0, deathCamData.Overlays.Length - 1), g_PlayerDeathCamCurrentOverlay[client], sizeof(g_PlayerDeathCamCurrentOverlay[])); + } g_PlayerDeathCam[client] = true; TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, { 0.0, 0.0, 0.0 }); @@ -445,14 +674,14 @@ static void Frame_PublicDeathCam(int cameraRef) int client = GetEntPropEnt(camera, Prop_Data, "m_hPlayer"); if (IsValidEntity(slender) && IsValidClient(client)) { - float camPos[3], camAngs[3]; + float camPos[3], camAngs[3], newPos[3]; + newPos[0] -= g_CameraPlayerOffsetBackward[camera]; + newPos[2] -= g_CameraPlayerOffsetDownward[camera]; GetEntPropVector(camera, Prop_Data, "m_angAbsRotation", camAngs); GetEntPropVector(camera, Prop_Data, "m_vecAbsOrigin", camPos); + VectorTransform(newPos, camPos, camAngs, newPos); - camPos[0] -= g_CameraPlayerOffsetBackward[camera]; - camPos[2] -= g_CameraPlayerOffsetDownward[camera]; - - TeleportEntity(client, camPos, camAngs, NULL_VECTOR); + TeleportEntity(client, newPos, camAngs, NULL_VECTOR); RequestFrame(Frame_PublicDeathCam, EntIndexToEntRef(cameraRef)); } @@ -470,6 +699,7 @@ static void StopDeathCam(int client) SF2NPC_BaseNPC deathCamBoss = SF2NPC_BaseNPC(NPCGetFromUniqueID(g_PlayerDeathCamBoss[client])); if (deathCamBoss != SF2_INVALID_NPC) { + BossProfileDeathCamData deathCamData = deathCamBoss.GetProfileData().GetDeathCamData(); if ((deathCamBoss.Flags & SFF_FAKE) == 0) { if (deathCamBoss.HasAttribute(SF2Attribute_IgnitePlayerOnDeath)) @@ -477,9 +707,7 @@ static void StopDeathCam(int client) TF2_IgnitePlayer(client, client); } - SF2BossProfileData data; - data = deathCamBoss.GetProfileData(); - if (data.DeathCamData.Enabled && data.DeathCamData.Blackout && !g_Hooked) + if (deathCamData.IsEnabled && deathCamData.Blackout && !g_Hooked) { AddNormalSoundHook(NoPlayerVoiceHook); g_Hooked = true; @@ -492,7 +720,7 @@ static void StopDeathCam(int client) CBaseEntity boss = CBaseEntity(deathCamBoss.EntIndex); float damage = float(GetEntProp(client, Prop_Send, "m_iHealth")) * 4.0; - SDKHooks_TakeDamage(client, boss.IsValid() ? boss.index : 0, boss.IsValid() ? boss.index : 0, damage, 0x80 | DMG_PREVENT_PHYSICS_FORCE, _, { 0.0, 0.0, 0.0 }); + SDKHooks_TakeDamage(client, boss.IsValid() ? boss.index : 0, boss.IsValid() ? boss.index : 0, damage, 0x80 | DMG_PREVENT_PHYSICS_FORCE, _, { 0.0, 0.0, 0.0 }, .bypassHooks = false); ForcePlayerSuicide(client); // Sometimes SDKHooks_TakeDamage doesn't work (probably because of point_viewcontrol), the player is still alive and result in a endless round. KillClient(client); } @@ -535,41 +763,32 @@ static void Hook_DeathCamThink(int client) CBaseEntity ent = CBaseEntity(EntRefToEntIndex(g_PlayerDeathCamEnt[client])); if (ent.IsValid() && g_CameraInDeathCamAdvanced[ent.index]) { - float camPos[3], camAngs[3]; + float camPos[3], camAngs[3], newPos[3]; ent.GetAbsAngles(camAngs); ent.GetAbsOrigin(camPos); - camPos[0] -= g_CameraPlayerOffsetBackward[ent.index]; - camPos[2] -= g_CameraPlayerOffsetDownward[ent.index]; + newPos[0] -= g_CameraPlayerOffsetBackward[ent.index]; + newPos[2] -= g_CameraPlayerOffsetDownward[ent.index]; + VectorTransform(newPos, camPos, camAngs, newPos); - player.SetLocalOrigin(camPos); + player.SetLocalOrigin(newPos); player.SetLocalAngles(camAngs); } g_PlayerDeathCamTimer[player.index] -= GetGameFrameTime(); - if (g_PlayerDeathCamTimer[player.index] <= 0.0) + g_PlayerDeathCamOverlayTimer[player.index] -= GetGameFrameTime(); + if (g_PlayerDeathCamMustDoOverlay[player.index] && g_PlayerDeathCamOverlayTimer[player.index] <= 0.0) { - if (g_PlayerDeathCamMustDoOverlay[player.index]) + SF2NPC_BaseNPC Npc = SF2NPC_BaseNPC(NPCGetFromUniqueID(g_PlayerDeathCamBoss[player.index])); + if (Npc.IsValid()) { - SF2NPC_BaseNPC Npc = SF2NPC_BaseNPC(NPCGetFromUniqueID(g_PlayerDeathCamBoss[player.index])); - float duration = 0.0; - if (Npc.IsValid()) - { - g_PlayerDeathCamShowOverlay[player.index] = true; - SF2BossProfileData data; - data = Npc.GetProfileData(); - SF2BossProfileSoundInfo soundInfo; - soundInfo = data.PlayerDeathCamOverlaySounds; - soundInfo.EmitSound(true, player.index); - duration = g_SlenderDeathCamTime[Npc.Index]; - } - g_PlayerDeathCamTimer[player.index] = duration; - g_PlayerDeathCamMustDoOverlay[player.index] = false; - return; - } - else - { - StopDeathCam(player.index); + Npc.GetProfileData().GetOverlayDeathCamSounds().EmitSound(true, player.index); } + g_PlayerDeathCamShowOverlay[player.index] = true; + g_PlayerDeathCamMustDoOverlay[player.index] = false; + } + if (g_PlayerDeathCamTimer[player.index] <= 0.0) + { + StopDeathCam(player.index); } } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/client/flashlight.sp b/addons/sourcemod/scripting/sf2/client/flashlight.sp index 23b78234..2a147449 100644 --- a/addons/sourcemod/scripting/sf2/client/flashlight.sp +++ b/addons/sourcemod/scripting/sf2/client/flashlight.sp @@ -3,25 +3,27 @@ #endif #define _sf2_client_flashlight_included +#pragma semicolon 1 +#pragma newdecls required + #define SF2_FLASHLIGHT_WIDTH 512.0 // How wide the player's Flashlight should be in world units. #define SF2_FLASHLIGHT_BRIGHTNESS 0 // Intensity of the players' Flashlight. -#define SF2_FLASHLIGHT_DRAIN_RATE 0.65 // How long (in seconds) each bar on the player's Flashlight meter lasts. -#define SF2_FLASHLIGHT_RECHARGE_RATE 0.68 // How long (in seconds) it takes each bar on the player's Flashlight meter to recharge. +#define SF2_FLASHLIGHT_DRAIN_RATE 0.12 // How long (in seconds) each bar on the player's Flashlight meter lasts. +#define SF2_FLASHLIGHT_RECHARGE_RATE 0.1 // How long (in seconds) it takes each bar on the player's Flashlight meter to recharge. #define SF2_FLASHLIGHT_FLICKERAT 0.25 // The percentage of the Flashlight battery where the Flashlight will start to blink. #define SF2_FLASHLIGHT_ENABLEAT 0.3 // The percentage of the Flashlight battery where the Flashlight will be able to be used again (if the player shortens out the Flashlight from excessive use). #define SF2_FLASHLIGHT_COOLDOWN 0.4 // How much time players have to wait before being able to switch their flashlight on again after turning it off. -#pragma semicolon 1 - static bool g_PlayerHasFlashlight[MAXTF2PLAYERS] = { false, ... }; static bool g_PlayerFlashlightBroken[MAXTF2PLAYERS] = { false, ... }; static float g_PlayerFlashlightBatteryLife[MAXTF2PLAYERS] = { 1.0, ... }; static Handle g_PlayerFlashlightBatteryTimer[MAXTF2PLAYERS] = { null, ... }; static float g_PlayerFlashlightNextInputTime[MAXTF2PLAYERS] = { -1.0, ... }; +static bool g_PlayerFlashlightIsFlickering[MAXTF2PLAYERS] = { false, ... }; +static bool g_PlayerFlashlightFlickerState[MAXTF2PLAYERS] = { false, ... }; +static float g_PlayerFlashlightNextFlickerTime[MAXTF2PLAYERS] = { -1.0, ... }; static int g_PlayerFlashlightEnt[MAXTF2PLAYERS] = { INVALID_ENT_REFERENCE, ... }; -static int g_PlayerFlashlightEntAng[MAXTF2PLAYERS] = { INVALID_ENT_REFERENCE, ... }; -static int g_ClientFlashlightStartEntity[MAXTF2PLAYERS] = { INVALID_ENT_REFERENCE, ... }; -static int g_ClientFlashlightEndEntity[MAXTF2PLAYERS] = { INVALID_ENT_REFERENCE, ... }; +static int g_PlayerFlashlightLightEnt[MAXTF2PLAYERS] = { INVALID_ENT_REFERENCE, ... }; void SetupFlashlight() { @@ -29,7 +31,11 @@ void SetupFlashlight() g_OnPlayerDisconnectedPFwd.AddFunction(null, OnDisconnected); g_OnPlayerSpawnPFwd.AddFunction(null, OnPlayerSpawn); g_OnPlayerDeathPFwd.AddFunction(null, OnPlayerDeath); + g_OnPlayerPressButtonPFwd.AddFunction(null, OnPlayerPressButton); g_OnPlayerEscapePFwd.AddFunction(null, OnPlayerEscape); + g_OnPlayerConditionAddedPFwd.AddFunction(null, OnPlayerConditionAdded); + + RegConsoleCmd("sm_flashlight", Command_ToggleFlashlight); } static void OnPutInServer(SF2_BasePlayer client) @@ -39,6 +45,8 @@ static void OnPutInServer(SF2_BasePlayer client) return; } ClientResetFlashlight(client.index); + + SDKHook(client.index, SDKHook_PreThink, Hook_FlashlightThink); } static void OnDisconnected(SF2_BasePlayer client) @@ -64,76 +72,232 @@ static void OnPlayerDeath(SF2_BasePlayer client, int attacker, int inflictor, bo } } +static void OnPlayerPressButton(SF2_BasePlayer client, int button) +{ + if (!client.IsAlive || client.IsInDeathCam || button != IN_ATTACK2 || IsRoundInWarmup() || IsRoundInIntro() || IsRoundEnding() || client.IsEliminated || client.HasEscaped) + { + return; + } + + if (GetGameTime() >= client.GetFlashlightNextInputTime()) + { + client.ToggleFlashlight(); + } +} + static void OnPlayerEscape(SF2_BasePlayer client) { ClientResetFlashlight(client.index); } -static Action Timer_DrainFlashlight(Handle timer, any userid) +static void OnPlayerConditionAdded(SF2_BasePlayer client, TFCond condition) { - int client = GetClientOfUserId(userid); - if (client <= 0) + if (!g_Enabled) { - return Plugin_Stop; + return; } - if (timer != g_PlayerFlashlightBatteryTimer[client]) + if (condition == TFCond_Taunting && client.UsingFlashlight) { - return Plugin_Stop; + client.ToggleFlashlight(); } +} - if (!IsInfiniteFlashlightEnabled()) +static Action Command_ToggleFlashlight(int client, int args) +{ + if (!g_Enabled) { - ClientSetFlashlightBatteryLife(client, ClientGetFlashlightBatteryLife(client) - 0.01); + return Plugin_Continue; } - if (ClientGetFlashlightBatteryLife(client) <= 0.0) + SF2_BasePlayer player = SF2_BasePlayer(client); + if (!player.IsValid || !player.IsAlive || player.IsInDeathCam || IsRoundInWarmup() || IsRoundInIntro() || IsRoundEnding() || player.IsEliminated || player.HasEscaped) { - // Break the player's flashlight, but also start recharging. - ClientBreakFlashlight(client); - ClientStartRechargingFlashlightBattery(client); - ClientActivateUltravision(client); - return Plugin_Stop; + return Plugin_Handled; } - else + + if (GetGameTime() >= player.GetFlashlightNextInputTime()) { - ClientHandleFlashlightFlickerState(client); + player.ToggleFlashlight(); } - return Plugin_Continue; + return Plugin_Handled; } -static Action Timer_RechargeFlashlight(Handle timer, any userid) +static void Hook_FlashlightThink(int client) { - int client = GetClientOfUserId(userid); - if (client <= 0) + SF2_BasePlayer player = SF2_BasePlayer(client); + if (!player.IsAlive || player.IsEliminated || player.HasEscaped || player.IsInGhostMode || player.IsInDeathCam) { - return Plugin_Stop; + return; } - if (timer != g_PlayerFlashlightBatteryTimer[client]) + if (player.InCondition(TFCond_Taunting) && player.UsingFlashlight) { - return Plugin_Stop; + player.ToggleFlashlight(); + return; } - ClientSetFlashlightBatteryLife(client, ClientGetFlashlightBatteryLife(client) + 0.01); - - if (IsClientFlashlightBroken(client) && ClientGetFlashlightBatteryLife(client) >= SF2_FLASHLIGHT_ENABLEAT) + if (player.UsingFlashlight) + { + ClientProcessFlashlightAngles(player.index); + if (!IsInfiniteFlashlightEnabled()) + { + player.FlashlightBatteryLife -= GetFlashlightDecreaseRate(player) * GetGameFrameTime(); + if (player.FlashlightBatteryLife <= 0.0) + { + ClientBreakFlashlight(player.index); + player.FlashlightBatteryLife = 0.0; + } + } + } + else { - // Repair the flashlight. - g_PlayerFlashlightBroken[client] = false; + player.FlashlightBatteryLife += GetFlashlightRechargeRate(player) * GetGameFrameTime(); + if (player.FlashlightBatteryLife >= 1.0) + { + player.FlashlightBatteryLife = 1.0; + } + if (IsClientFlashlightBroken(player.index) && player.FlashlightBatteryLife >= SF2_FLASHLIGHT_ENABLEAT) + { + g_PlayerFlashlightBroken[player.index] = false; + } } + ClientHandleFlashlightFlickerState(player.index); +} + +void ClientToggleFlashlight(SF2_BasePlayer client) +{ + g_PlayerFlashlightNextInputTime[client.index] = GetGameTime() + SF2_FLASHLIGHT_COOLDOWN; + + bool nightVision = IsNightVisionEnabled(); + + if (client.UsingFlashlight) + { + ClientTurnOffFlashlight(client.index); + ClientDeactivateUltravision(client.index); + ClientActivateUltravision(client.index); + + if (!nightVision) + { + if (!SF_SpecialRound(SPECIALROUND_SINGLEPLAYER)) + { + EmitSoundToAll(FLASHLIGHT_CLICKSOUND, client.index, SNDCHAN_ITEM, SNDLEVEL_DRYER); + } + else + { + EmitSoundToClient(client.index, FLASHLIGHT_CLICKSOUND, client.index, SNDCHAN_ITEM, SNDLEVEL_DRYER); + } + } - if (ClientGetFlashlightBatteryLife(client) >= 1.0) + Call_StartForward(g_OnPlayerTurnOffFlashlightPFwd); + Call_PushCell(client); + Call_Finish(); + } + else { - // I am fully charged! - ClientSetFlashlightBatteryLife(client, 1.0); - g_PlayerFlashlightBatteryTimer[client] = null; + if (client.IsEliminated || SF_SpecialRound(SPECIALROUND_LIGHTSOUT) || SF_IsRaidMap() || SF_IsBoxingMap()) + { + return; + } + + if (IsClientFlashlightBroken(client.index)) + { + EmitSoundToClient(client.index, FLASHLIGHT_NOSOUND, _, SNDCHAN_ITEM, SNDLEVEL_NONE); + return; + } + + ClientDeactivateUltravision(client.index); + if (nightVision) + { + g_PlayerHasFlashlight[client.index] = true; + ClientActivateUltravision(client.index, nightVision); + } + else + { + ClientTurnOnFlashlight(client.index); + } + + if (!SF_SpecialRound(SPECIALROUND_SINGLEPLAYER)) + { + EmitSoundToAll((nightVision) ? FLASHLIGHT_CLICKSOUND_NIGHTVISION : FLASHLIGHT_CLICKSOUND, client.index, SNDCHAN_ITEM, SNDLEVEL_DRYER); + } + else + { + EmitSoundToClient(client.index, (nightVision) ? FLASHLIGHT_CLICKSOUND_NIGHTVISION : FLASHLIGHT_CLICKSOUND, client.index, SNDCHAN_ITEM, SNDLEVEL_DRYER); + } - return Plugin_Stop; + Call_StartForward(g_OnPlayerTurnOnFlashlightPFwd); + Call_PushCell(client); + Call_Finish(); } +} - return Plugin_Continue; +static float GetFlashlightDecreaseRate(SF2_BasePlayer client) +{ + float drainRate = SF2_FLASHLIGHT_DRAIN_RATE; + + TFClassType class = client.Class; + int classToInt = view_as(class); + + if (!IsClassConfigsValid()) + { + if (class == TFClass_Engineer) + { + // Engineers have a 50% longer battery life and 20% decreased recharge rate, basically. + drainRate *= 1.5; + } + } + else + { + drainRate *= g_ClassFlashlightDrainRate[classToInt]; + } + if (IsNightVisionEnabled() && g_NightVisionType == 2) //Blue nightvision + { + switch (g_DifficultyConVar.IntValue) + { + case Difficulty_Normal: + { + drainRate *= 0.3; + } + case Difficulty_Hard: + { + drainRate *= 0.25; + } + case Difficulty_Insane: + { + drainRate *= 0.2; + } + case Difficulty_Nightmare: + { + drainRate *= 0.15; + } + case Difficulty_Apollyon: + { + drainRate *= 0.1; + } + } + } + return drainRate; +} + +static float GetFlashlightRechargeRate(SF2_BasePlayer client) +{ + TFClassType class = client.Class; + int classToInt = view_as(class); + float rechargeRate = SF2_FLASHLIGHT_RECHARGE_RATE; + if (!IsClassConfigsValid()) + { + if (class == TFClass_Engineer) + { + rechargeRate *= 1.2; + } + } + else + { + rechargeRate *= g_ClassFlashlightRechargeRate[classToInt]; + } + return rechargeRate; } bool IsClientUsingFlashlight(int client) @@ -163,20 +327,15 @@ void ClientProcessFlashlightAngles(int client) int fl; - if (IsClientUsingFlashlight(client)) + fl = EntRefToEntIndex(g_PlayerFlashlightEnt[client]); + if (fl && fl != INVALID_ENT_REFERENCE) { - fl = EntRefToEntIndex(g_PlayerFlashlightEnt[client]); - if (fl && fl != INVALID_ENT_REFERENCE) - { - TeleportEntity(fl, NULL_VECTOR, { 0.0, 0.0, 0.0 }, NULL_VECTOR); - } - fl = EntRefToEntIndex(g_PlayerFlashlightEntAng[client]); - if (fl && fl != INVALID_ENT_REFERENCE) - { - float ang[3]; - GetClientEyeAngles(client, ang); - TeleportEntity(fl, NULL_VECTOR, ang, NULL_VECTOR); - } + float pos[3], ang[3]; + GetClientEyePosition(client, pos); + GetClientEyeAngles(client, ang); + SF2PointSpotlightEntity(fl).SetLocalOrigin(pos); + SF2PointSpotlightEntity(fl).Start.SetLocalOrigin(pos); + SF2PointSpotlightEntity(fl).SetLocalAngles(ang); } } @@ -193,30 +352,37 @@ void ClientHandleFlashlightFlickerState(int client) if (IsClientUsingFlashlight(client)) { bool flicker = ClientGetFlashlightBatteryLife(client) <= SF2_FLASHLIGHT_FLICKERAT; + float gameTime = GetGameTime(); - int fl = EntRefToEntIndex(g_PlayerFlashlightEnt[client]); - if (fl && fl != INVALID_ENT_REFERENCE) + if (flicker && g_PlayerFlashlightNextFlickerTime[client] <= gameTime) { - if (flicker) - { - SetEntProp(fl, Prop_Data, "m_LightStyle", 10); - } - else + g_PlayerFlashlightFlickerState[client] = !g_PlayerFlashlightFlickerState[client]; + g_PlayerFlashlightNextFlickerTime[client] = gameTime + GetRandomFloat(0.1, 0.2); + int effects = GetEntProp(client, Prop_Send, "m_fEffects"); + int fl = EntRefToEntIndex(g_PlayerFlashlightEnt[client]); + if (g_PlayerFlashlightFlickerState[client]) { - SetEntProp(fl, Prop_Data, "m_LightStyle", 0); - } - } + if (g_PlayerPreferences[client].PlayerPreference_ProjectedFlashlight) + { + SetEntProp(client, Prop_Send, "m_fEffects", effects | (1 << 2)); + } - fl = EntRefToEntIndex(g_PlayerFlashlightEntAng[client]); - if (fl && fl != INVALID_ENT_REFERENCE) - { - if (flicker) - { - SetEntityRenderFx(fl, view_as(13)); + if (fl && fl != INVALID_ENT_REFERENCE) + { + SF2PointSpotlightEntity(fl).TurnOn(); + } } else { - SetEntityRenderFx(fl, view_as(0)); + if (g_PlayerPreferences[client].PlayerPreference_ProjectedFlashlight) + { + SetEntProp(client, Prop_Send, "m_fEffects", effects &= ~(1 << 2)); + } + + if (fl && fl != INVALID_ENT_REFERENCE) + { + SF2PointSpotlightEntity(fl).TurnOff(); + } } } } @@ -253,6 +419,8 @@ void ClientBreakFlashlight(int client) ClientSetFlashlightBatteryLife(client, 0.0); ClientTurnOffFlashlight(client); + ClientDeactivateUltravision(client); + ClientActivateUltravision(client); ClientAddStress(client, 0.2); @@ -284,14 +452,9 @@ void ClientResetFlashlight(int client) g_PlayerFlashlightBroken[client] = false; g_PlayerFlashlightBatteryTimer[client] = null; g_PlayerFlashlightNextInputTime[client] = -1.0; - - SF2PointSpotlightEntity spotlight = SF2PointSpotlightEntity(EntRefToEntIndex(g_PlayerFlashlightEntAng[client])); - if (spotlight.IsValid()) - { - spotlight.TurnOff(); - RemoveEntity(spotlight.index); - } - g_PlayerFlashlightEntAng[client] = INVALID_ENT_REFERENCE; + g_PlayerFlashlightIsFlickering[client] = false; + g_PlayerFlashlightFlickerState[client] = false; + g_PlayerFlashlightNextFlickerTime[client] = -1.0; #if defined DEBUG if (g_DebugDetailConVar.IntValue > 2) @@ -301,20 +464,29 @@ void ClientResetFlashlight(int client) #endif } -static Action Hook_FlashlightSetTransmit(int ent,int other) +static Action FlashlightBeamSetTransmit(int ent, int other) { if (!g_Enabled) { return Plugin_Continue; } - if (EntRefToEntIndex(g_PlayerFlashlightEnt[other]) != ent) + if (IsValidClient(other) && g_PlayerFlashlightEnt[other] == EntIndexToEntRef(ent)) { return Plugin_Handled; } - // We've already checked for flashlight ownership in the last statement. So we can do just this. - if (g_PlayerPreferences[other].PlayerPreference_ProjectedFlashlight) + return Plugin_Continue; +} + +static Action FlashlightLightSetTransmit(int ent, int other) +{ + if (!g_Enabled) + { + return Plugin_Continue; + } + + if (IsValidClient(other) && g_PlayerFlashlightLightEnt[other] == EntIndexToEntRef(ent)) { return Plugin_Handled; } @@ -408,275 +580,114 @@ void ClientTurnOnFlashlight(int client) else { // Spawn the light which only the user will see. - int ent = CreateEntityByName("light_dynamic"); - if (ent != -1) + SF2PointSpotlightEntity ent = SF2PointSpotlightEntity(CreateEntityByName("sf2_point_spotlight")); + if (ent.IsValid()) { - TeleportEntity(ent, eyePos, eyeAng, NULL_VECTOR); - DispatchKeyValue(ent, "targetname", "WUBADUBDUBMOTHERBUCKERS"); + ent.Teleport(eyePos, eyeAng, NULL_VECTOR); switch (g_PlayerPreferences[client].PlayerPreference_FlashlightTemperature) { case 1: { - DispatchKeyValue(ent, "rendercolor", "255 150 50"); + ent.SetColor({ 255, 150, 50, 255 }); } case 2: { - DispatchKeyValue(ent, "rendercolor", "255 210 100"); + ent.SetColor({ 255, 210, 100, 255 }); } case 3: { - DispatchKeyValue(ent, "rendercolor", "255 255 120"); + ent.SetColor({ 255, 255, 120, 255 }); } case 4: { - DispatchKeyValue(ent, "rendercolor", "255 255 185"); + ent.SetColor({ 255, 255, 185, 255 }); } case 5: { - DispatchKeyValue(ent, "rendercolor", "255 255 210"); + ent.SetColor({ 255, 255, 210, 255 }); } case 6: { - DispatchKeyValue(ent, "rendercolor", "255 255 255"); + ent.SetColor({ 255, 255, 255, 255 }); } case 7: { - DispatchKeyValue(ent, "rendercolor", "210 255 255"); + ent.SetColor({ 210, 255, 255, 255 }); } case 8: { - DispatchKeyValue(ent, "rendercolor", "185 255 255"); + ent.SetColor({ 185, 255, 255, 255 }); } case 9: { - DispatchKeyValue(ent, "rendercolor", "150 255 255"); + ent.SetColor({ 150, 255, 255, 255 }); } case 10: { - DispatchKeyValue(ent, "rendercolor", "125 255 255"); + ent.SetColor({ 125, 255, 255, 255 }); } } + float value = radius; if (!IsClassConfigsValid()) { - if (class != TFClass_Engineer) - { - SetVariantFloat(radius); - } - else + if (class == TFClass_Engineer) { - SetVariantFloat(doubleRadius); + value = doubleRadius; } } else { - float customRadius = radius * g_ClassFlashlightRadius[classToInt]; - SetVariantFloat(customRadius); + value = radius * g_ClassFlashlightRadius[classToInt]; } - AcceptEntityInput(ent, "spotlight_radius"); + ent.Distance = value; + ent.SpotlightRadius = value; + value = length; if (!IsClassConfigsValid()) { - if (class != TFClass_Engineer) - { - SetVariantFloat(length); - } - else + if (class == TFClass_Engineer) { - SetVariantFloat(doubleLength); + value = doubleLength; } } else { - float customLength = length * g_ClassFlashlightLength[classToInt]; - SetVariantFloat(customLength); - } - AcceptEntityInput(ent, "distance"); - if (!IsClassConfigsValid()) - { - SetVariantInt(SF2_FLASHLIGHT_BRIGHTNESS); + value = length * g_ClassFlashlightLength[classToInt]; } - else + ent.Length = value; + int intValue = SF2_FLASHLIGHT_BRIGHTNESS; + if (IsClassConfigsValid()) { - int customBrightness = SF2_FLASHLIGHT_BRIGHTNESS + g_ClassFlashlightBrightness[classToInt]; - SetVariantInt(customBrightness); + intValue = SF2_FLASHLIGHT_BRIGHTNESS + g_ClassFlashlightBrightness[classToInt]; } - AcceptEntityInput(ent, "brightness"); - - SetVariantInt(RoundToFloor(cone)); - AcceptEntityInput(ent, "_inner_cone"); - SetVariantInt(RoundToFloor(cone)); - AcceptEntityInput(ent, "_cone"); - DispatchSpawn(ent); - ActivateEntity(ent); + ent.Brightness = intValue; + ent.Cone = RoundToFloor(cone); + ent.Width = 40.0; + ent.EndWidth = ent.Width * 2.0; + ent.HaloScale = 40.0; + ent.Spawn(); + ent.Activate(); SetVariantString("!activator"); - AcceptEntityInput(ent, "SetParent", client); - AcceptEntityInput(ent, "TurnOn"); - - g_PlayerFlashlightEnt[client] = EntIndexToEntRef(ent); + ent.Start.AcceptInput("ClearParent"); + SetVariantString("!activator"); + ent.End.AcceptInput("ClearParent"); + ent.TurnOn(); - SDKHook(ent, SDKHook_SetTransmit, Hook_FlashlightSetTransmit); - } - } + g_PlayerFlashlightEnt[client] = EntIndexToEntRef(ent.index); + g_PlayerFlashlightLightEnt[client] = EntIndexToEntRef(ent.End.index); + SDKHook(ent.index, SDKHook_SetTransmit, FlashlightBeamSetTransmit); - // Spawn the light that only everyone else will see. - /*SF2PointSpotlightEntity spotlight = SF2PointSpotlightEntity(CreateEntityByName("sf2_point_spotlight")); - if (spotlight.IsValid()) - { - TeleportEntity(spotlight.index, eyePos, eyeAng); - switch (g_PlayerPreferences[client].PlayerPreference_FlashlightTemperature) - { - case 1: - { - spotlight.SetRenderColor(255, 150, 50, 255); - } - case 2: - { - spotlight.SetRenderColor(255, 210, 100, 255); - } - case 3: - { - spotlight.SetRenderColor(255, 255, 120, 255); - } - case 4: - { - spotlight.SetRenderColor(255, 255, 185, 255); - } - case 5: - { - spotlight.SetRenderColor(255, 255, 210, 255); - } - case 6: - { - spotlight.SetRenderColor(255, 255, 255, 255); - } - case 7: - { - spotlight.SetRenderColor(210, 255, 255, 255); - } - case 8: - { - spotlight.SetRenderColor(185, 255, 255, 255); - } - case 9: - { - spotlight.SetRenderColor(150, 255, 255, 255); - } - case 10: - { - spotlight.SetRenderColor(125, 255, 255, 255); - } - } - if (!IsClassConfigsValid()) - { - if (class != TFClass_Engineer) - { - spotlight.Length = length; - } - else - { - spotlight.Length = doubleLength; - } - } - else - { - float customLength = length * g_ClassFlashlightLength[classToInt]; - spotlight.Length = customLength; - } - if (!IsClassConfigsValid()) - { - if (class != TFClass_Engineer) - { - spotlight.Width = radius; - } - else + if (g_PlayerPreferences[client].PlayerPreference_ProjectedFlashlight) { - spotlight.Width = doubleRadius; + SDKHook(ent.End.index, SDKHook_SetTransmit, FlashlightLightSetTransmit); } } - else - { - float customRadius = radius * g_ClassFlashlightRadius[classToInt]; - spotlight.Width = customRadius; - } - if (!IsClassConfigsValid()) - { - spotlight.Brightness = SF2_FLASHLIGHT_BRIGHTNESS; - } - else - { - int customBrightness = SF2_FLASHLIGHT_BRIGHTNESS + g_ClassFlashlightBrightness[classToInt]; - spotlight.Brightness = customBrightness; - } - spotlight.Cone = RoundToFloor(cone); - spotlight.EndWidth = spotlight.Width * 2.0; - spotlight.Distance = spotlight.EndWidth; - spotlight.SpotlightRadius = spotlight.Length; - spotlight.HaloScale = 20.0; - spotlight.Spawn(); - spotlight.Activate(); - SetVariantString("!activator"); - spotlight.AcceptInput("SetParent", client); - spotlight.TurnOn(); - g_PlayerFlashlightEntAng[client] = EntIndexToEntRef(spotlight.index); - }*/ + } Call_StartForward(g_OnClientActivateFlashlightFwd); Call_PushCell(client); Call_Finish(); } -void Hook_OnFlashlightThink(int client) -{ - TFClassType class = TF2_GetPlayerClass(client); - int classToInt = view_as(class); - float length; - if (!IsClassConfigsValid()) - { - if (class != TFClass_Engineer) - { - length = SF2_FLASHLIGHT_LENGTH; - } - else - { - length = SF2_FLASHLIGHT_LENGTH * 3.0; - } - } - else - { - length = SF2_FLASHLIGHT_LENGTH * g_ClassFlashlightLength[classToInt]; - } - if (IsClientUsingFlashlight(client)) - { - int endEnt = EntRefToEntIndex(g_ClientFlashlightEndEntity[client]); - if (IsValidEntity(endEnt)) - { - float entPos[3], entRot[3], tempRot[3], endPos[3]; - GetClientEyePosition(client, entPos); - GetClientEyeAngles(client, entRot); - GetEntPropVector(client, Prop_Data, "m_angAbsRotation", tempRot); - tempRot[0] = 0.0; - tempRot[2] = 0.0; - AddVectors(entRot, tempRot, entRot); - GetAngleVectors(entRot, entRot, NULL_VECTOR, NULL_VECTOR); - endPos = entRot; - ScaleVector(endPos, length); - AddVectors(endPos, entPos, endPos); - - CBaseEntity spotlightEnd = CBaseEntity(endEnt); - if (spotlightEnd.IsValid()) - { - TR_TraceRayFilter(entPos, endPos, MASK_SOLID_BRUSHONLY, RayType_EndPoint, TraceRayDontHitEntity, client); - - float hitPos[3]; - TR_GetEndPosition(hitPos); - - hitPos[2] += 20.0; - - spotlightEnd.SetAbsOrigin(hitPos); - } - } - } -} - /** * Turns off the player's flashlight. Nothing else. */ @@ -693,32 +704,12 @@ void ClientTurnOffFlashlight(int client) // Remove user-only light. int ent = EntRefToEntIndex(g_PlayerFlashlightEnt[client]); if (ent && ent != INVALID_ENT_REFERENCE) - { - AcceptEntityInput(ent, "TurnOff"); - RemoveEntity(ent); - } - - // Remove everyone-else-only light. - ent = EntRefToEntIndex(g_PlayerFlashlightEntAng[client]); - if (ent && ent != INVALID_ENT_REFERENCE) { RemoveEntity(ent); } - ent = EntRefToEntIndex(g_ClientFlashlightStartEntity[client]); - if (ent && ent != INVALID_ENT_REFERENCE) - { - CreateTimer(0.1, Timer_KillEntity, g_ClientFlashlightStartEntity[client], TIMER_FLAG_NO_MAPCHANGE); - } - ent = EntRefToEntIndex(g_ClientFlashlightEndEntity[client]); - if (ent && ent != INVALID_ENT_REFERENCE) - { - CreateTimer(0.1, Timer_KillEntity, g_ClientFlashlightEndEntity[client], TIMER_FLAG_NO_MAPCHANGE); - } g_PlayerFlashlightEnt[client] = INVALID_ENT_REFERENCE; - g_ClientFlashlightStartEntity[client] = INVALID_ENT_REFERENCE; - g_ClientFlashlightEndEntity[client] = INVALID_ENT_REFERENCE; - g_PlayerFlashlightEntAng[client] = INVALID_ENT_REFERENCE; + g_PlayerFlashlightLightEnt[client] = INVALID_ENT_REFERENCE; if (IsValidClient(client)) { @@ -737,162 +728,6 @@ void ClientTurnOffFlashlight(int client) Call_Finish(); } -void ClientStartRechargingFlashlightBattery(int client) -{ - TFClassType class = TF2_GetPlayerClass(client); - int classToInt = view_as(class); - float rechargeRate = SF2_FLASHLIGHT_RECHARGE_RATE; - if (!IsClassConfigsValid()) - { - if (class == TFClass_Engineer) - { - rechargeRate *= 0.8; - } - } - else - { - rechargeRate *= g_ClassFlashlightRechargeRate[classToInt]; - } - - g_PlayerFlashlightBatteryTimer[client] = CreateTimer(rechargeRate, Timer_RechargeFlashlight, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); -} - -void ClientStartDrainingFlashlightBattery(int client) -{ - float drainRate = SF2_FLASHLIGHT_DRAIN_RATE; - - TFClassType class = TF2_GetPlayerClass(client); - int classToInt = view_as(class); - - if (!IsClassConfigsValid()) - { - if (class == TFClass_Engineer) - { - // Engineers have a 50% longer battery life and 20% decreased recharge rate, basically. - drainRate *= 1.5; - } - } - else - { - drainRate *= g_ClassFlashlightDrainRate[classToInt]; - } - if (IsNightVisionEnabled() && g_NightVisionType == 2) //Blue nightvision - { - switch (g_DifficultyConVar.IntValue) - { - case Difficulty_Normal: - { - drainRate *= 0.3; - } - case Difficulty_Hard: - { - drainRate *= 0.25; - } - case Difficulty_Insane: - { - drainRate *= 0.2; - } - case Difficulty_Nightmare: - { - drainRate *= 0.15; - } - case Difficulty_Apollyon: - { - drainRate *= 0.1; - } - } - } - - g_PlayerFlashlightBatteryTimer[client] = CreateTimer(drainRate, Timer_DrainFlashlight, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); -} - -void ClientHandleFlashlight(int client) -{ - if (!IsValidClient(client) || !IsPlayerAlive(client) || (TF2_IsPlayerInCondition(client, TFCond_Taunting) && !IsClientUsingFlashlight(client))) - { - return; - } - - if (IsClientInDeathCam(client)) - { - return; - } - - bool nightVision = IsNightVisionEnabled(); - - if (IsClientUsingFlashlight(client) || TF2_IsPlayerInCondition(client, TFCond_Taunting)) - { - ClientTurnOffFlashlight(client); - ClientStartRechargingFlashlightBattery(client); - ClientDeactivateUltravision(client); - ClientActivateUltravision(client); - - g_PlayerFlashlightNextInputTime[client] = GetGameTime() + SF2_FLASHLIGHT_COOLDOWN; - - if (!nightVision) - { - if (!SF_SpecialRound(SPECIALROUND_SINGLEPLAYER)) - { - EmitSoundToAll(FLASHLIGHT_CLICKSOUND, client, SNDCHAN_ITEM, SNDLEVEL_DRYER); - } - else - { - EmitSoundToClient(client, FLASHLIGHT_CLICKSOUND, client, SNDCHAN_ITEM, SNDLEVEL_DRYER); - } - } - - Call_StartForward(g_OnPlayerTurnOffFlashlightPFwd); - Call_PushCell(SF2_BasePlayer(client)); - Call_Finish(); - } - else - { - // Only players in the "game" can use the flashlight. - if (!g_PlayerEliminated[client]) - { - bool canUseFlashlight = true; - if (SF_SpecialRound(SPECIALROUND_LIGHTSOUT) || SF_IsRaidMap() || SF_IsBoxingMap()) - { - // Unequip the flashlight please. - canUseFlashlight = false; - } - - if (!IsClientFlashlightBroken(client) && canUseFlashlight) - { - ClientDeactivateUltravision(client); - if (nightVision) - { - g_PlayerHasFlashlight[client] = true; - ClientActivateUltravision(client, nightVision); - } - else - { - ClientTurnOnFlashlight(client); - } - ClientStartDrainingFlashlightBattery(client); - - g_PlayerFlashlightNextInputTime[client] = GetGameTime(); - if (!SF_SpecialRound(SPECIALROUND_SINGLEPLAYER)) - { - EmitSoundToAll((nightVision) ? FLASHLIGHT_CLICKSOUND_NIGHTVISION : FLASHLIGHT_CLICKSOUND, client, SNDCHAN_ITEM, SNDLEVEL_DRYER); - } - else - { - EmitSoundToClient(client, (nightVision) ? FLASHLIGHT_CLICKSOUND_NIGHTVISION : FLASHLIGHT_CLICKSOUND, client, SNDCHAN_ITEM, SNDLEVEL_DRYER); - } - - Call_StartForward(g_OnPlayerTurnOnFlashlightPFwd); - Call_PushCell(SF2_BasePlayer(client)); - Call_Finish(); - } - else - { - EmitSoundToClient(client, FLASHLIGHT_NOSOUND, _, SNDCHAN_ITEM, SNDLEVEL_NONE); - } - } - } -} - /* void ClientSDKFlashlightTurnOn(int client) { diff --git a/addons/sourcemod/scripting/sf2/client/ghostmode.sp b/addons/sourcemod/scripting/sf2/client/ghostmode.sp index 6a387e9c..a3d523b6 100644 --- a/addons/sourcemod/scripting/sf2/client/ghostmode.sp +++ b/addons/sourcemod/scripting/sf2/client/ghostmode.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required #define SF2_OVERLAY_GHOST "overlays/slender/ghostcamera" @@ -16,6 +17,7 @@ void SetupGhost() g_OnPlayerDeathPFwd.AddFunction(null, OnPlayerDeath); g_OnPlayerEscapePFwd.AddFunction(null, OnPlayerEscape); g_OnPlayerTeamPFwd.AddFunction(null, OnPlayerTeam); + g_OnPlayerTakeDamagePFwd.AddFunction(null, OnPlayerTakeDamage); } static void OnPutInServer(SF2_BasePlayer client) @@ -25,6 +27,7 @@ static void OnPutInServer(SF2_BasePlayer client) return; } ClientSetGhostModeState(client.index, false); + SDKHook(client.index, SDKHook_PreThinkPost, GhostThink); } static void OnPlayerSpawn(SF2_BasePlayer client) @@ -67,6 +70,42 @@ static void OnPlayerTeam(SF2_BasePlayer client, int team) } } +static void GhostThink(int client) +{ + SF2_BasePlayer player = SF2_BasePlayer(client); + if (!player.IsInGhostMode) + { + return; + } + + player.SetPropFloat(Prop_Send, "m_flNextAttack", GetGameTime() + 2.0); + player.SetPropFloat(Prop_Send, "m_flMaxspeed", 520.0); + player.SetPropFloat(Prop_Send, "m_flModelScale", 1.0); + player.SetPropFloat(Prop_Send, "m_flHeadScale", 1.0); + player.SetPropFloat(Prop_Send, "m_flTorsoScale", 1.0); + player.SetPropFloat(Prop_Send, "m_flHandScale", 1.0); + if (IsClientInKart(player.index)) + { + player.ChangeCondition(TFCond_HalloweenKart, true); + player.ChangeCondition(TFCond_HalloweenKartDash, true); + player.ChangeCondition(TFCond_HalloweenKartNoTurn, true); + player.ChangeCondition(TFCond_HalloweenKartCage, true); + ClientHandleGhostMode(player.index, true); + } + player.ChangeCondition(TFCond_Taunting, true); +} + +static Action OnPlayerTakeDamage(SF2_BasePlayer client, int &attacker, int &inflictor, float &damage, int &damageType, int damageCustom) +{ + if (client.IsInGhostMode) + { + damage = 0.0; + return Plugin_Changed; + } + + return Plugin_Continue; +} + bool IsClientInGhostMode(int client) { return g_PlayerInGhostMode[client]; @@ -314,7 +353,7 @@ void ClientGhostModeNextTarget(int client, bool ignoreSetting = false) continue; } - if (NPCGetProfileData(bossIndex).IsPvEBoss) + if (SF2NPC_BaseNPC(bossIndex).GetProfileData().IsPvEBoss) { continue; } diff --git a/addons/sourcemod/scripting/sf2/client/hints.sp b/addons/sourcemod/scripting/sf2/client/hints.sp index 71202ee4..f5e9b31b 100644 --- a/addons/sourcemod/scripting/sf2/client/hints.sp +++ b/addons/sourcemod/scripting/sf2/client/hints.sp @@ -3,6 +3,9 @@ #endif #define _sf2_client_hints_included +#pragma semicolon 1 +#pragma newdecls required + bool g_PlayerHints[MAXTF2PLAYERS][PlayerHint_MaxNum]; static Handle g_ShowHintTimer[MAXPLAYERS + 1]; diff --git a/addons/sourcemod/scripting/sf2/client/interactables.sp b/addons/sourcemod/scripting/sf2/client/interactables.sp index e9bbd7fb..33b84b8e 100644 --- a/addons/sourcemod/scripting/sf2/client/interactables.sp +++ b/addons/sourcemod/scripting/sf2/client/interactables.sp @@ -1,3 +1,6 @@ +#pragma semicolon 1 +#pragma newdecls required + static int g_PlayerInteractiveGlowEntity[MAXPLAYERS + 1] = { INVALID_ENT_REFERENCE, ... }; static int g_PlayerInteractiveGlowTargetEntity[MAXPLAYERS + 1] = { INVALID_ENT_REFERENCE, ... }; static float g_NextInteractiveThink[MAXPLAYERS + 1] = { 0.0, ... }; @@ -189,9 +192,10 @@ static void UpdateInteractiveGlow(SF2_BasePlayer client) ScaleVector(endPos, maxRange); AddVectors(startPos, endPos, endPos); - TR_TraceRayFilter(startPos, endPos, MASK_VISIBLE, RayType_EndPoint, TraceRayDontHitPlayers, -1); + Handle trace = TR_TraceRayFilterEx(startPos, endPos, MASK_VISIBLE, RayType_EndPoint, TraceRayDontHitPlayers, -1); int oldTargetEnt = EntRefToEntIndex(g_PlayerInteractiveGlowTargetEntity[client.index]); - int targetEnt = TR_GetEntityIndex(); + int targetEnt = TR_GetEntityIndex(trace); + delete trace; if (!IsEntityInteractable(targetEnt)) { targetEnt = -1; diff --git a/addons/sourcemod/scripting/sf2/client/music.sp b/addons/sourcemod/scripting/sf2/client/music.sp index d0d3458f..638eee37 100644 --- a/addons/sourcemod/scripting/sf2/client/music.sp +++ b/addons/sourcemod/scripting/sf2/client/music.sp @@ -4,6 +4,7 @@ #define _sf2_client_music_included #pragma semicolon 1 +#pragma newdecls required void SetupMusic() { @@ -73,74 +74,22 @@ static void OnPlayerEscape(SF2_BasePlayer client) static void OnBossRemoved(SF2NPC_BaseNPC npc) { - if (!MusicActive()) - { - for (int i = 1; i <= MaxClients; i++) - { - if (!IsValidClient(i)) - { - continue; - } - - // Remove chase music. - if (g_PlayerChaseMusicMaster[i] == npc.Index) - { - ClientChaseMusicReset(i); - } - - // Don't forget search theme - if (g_PlayerAlertMusicMaster[i] == npc.Index) - { - ClientAlertMusicReset(i); - } - if (g_PlayerChaseMusicSeeMaster[i] == npc.Index) - { - ClientChaseMusicSeeReset(i); - } - - if (g_PlayerIdleMusicMaster[i] == npc.Index) - { - ClientIdleMusicReset(i); - } - - ClientUpdateMusicSystem(i); - } - } } static void OnDifficultyChange(int oldDifficulty, int newDifficulty) { - CheckIfMusicValid(); - if (MusicActive()) - { - for (int i = 1; i <= MaxClients; i++) - { - SF2_BasePlayer client = SF2_BasePlayer(i); - if (!client.IsValid || client.IsSourceTV) - { - continue; - } - char path[PLATFORM_MAX_PATH]; - GetBossMusic(path, sizeof(path)); - if (path[0] != '\0') - { - StopSound(i, MUSIC_CHAN, path); - } - client.UpdateMusicSystem(); - } - } } void ClientResetChannels(int client) { - ClientChaseMusicReset(client); + /*ClientChaseMusicReset(client); ClientChaseMusicSeeReset(client); ClientAlertMusicReset(client); ClientIdleMusicReset(client); Client90sMusicReset(client); - ClientMusicReset(client); + ClientMusicReset(client);*/ } void ClientUpdateMusicSystem(int client, bool initialize = false) @@ -150,7 +99,7 @@ void ClientUpdateMusicSystem(int client, bool initialize = false) return; } - int oldPageMusicMaster = EntRefToEntIndex(g_PlayerPageMusicMaster[client]); + /*int oldPageMusicMaster = EntRefToEntIndex(g_PlayerPageMusicMaster[client]); int oldPageMusicActiveIndex = g_PageMusicActiveIndex[client]; int oldMusicFlags = g_PlayerMusicFlags[client]; int chasingBoss = -1; @@ -288,8 +237,7 @@ void ClientUpdateMusicSystem(int client, bool initialize = false) continue; } - SF2BossProfileData data; - data = NPCGetProfileData(i); + BaseBossProfile data = SF2NPC_BaseNPC(i).GetProfileData(); if (data.IsPvEBoss) { continue; @@ -297,7 +245,7 @@ void ClientUpdateMusicSystem(int client, bool initialize = false) NPCGetProfile(i, profile, sizeof(profile)); - int bossType = NPCGetType(i); + int bossType = data.Type; switch (bossType) { @@ -310,8 +258,8 @@ void ClientUpdateMusicSystem(int client, bool initialize = false) continue; } - SF2BossProfileSoundInfo soundInfo; - ArrayList soundList; + ProfileSound soundInfo; + ChaserBossProfile chaserData = SF2NPC_Chaser(i).GetProfileData(); GetClientAbsOrigin(client, buffer); chaser.GetAbsOrigin(buffer3); float pos[3]; @@ -329,16 +277,15 @@ void ClientUpdateMusicSystem(int client, bool initialize = false) { target.GetAbsOrigin(buffer2); - if ((chaser.State == STATE_CHASE || chaser.State == STATE_ATTACK || (chaser.State == STATE_STUN && (chaser.PreviousState == STATE_CHASE || chaser.PreviousState == STATE_ATTACK))) && + if ((chaser.State == STATE_CHASE || chaser.State == STATE_ATTACK || chaser.IsKillingSomeone || (chaser.State == STATE_STUN && (chaser.PreviousState == STATE_CHASE || chaser.PreviousState == STATE_ATTACK))) && !(NPCGetFlags(i) & SFF_MARKEDASFAKE)) { - GetChaserProfileChaseMusics(profile, soundInfo); + soundInfo = chaserData.GetChaseMusics(); - if ((target.index == client || GetVectorSquareMagnitude(buffer, buffer2) <= SquareFloat(soundInfo.Radius) || - GetVectorSquareMagnitude(buffer, buffer3) <= SquareFloat(soundInfo.Radius) || GetVectorSquareMagnitude(buffer, pos) <= SquareFloat(soundInfo.Radius))) + if (soundInfo != null && (target.index == client || GetVectorSquareMagnitude(buffer, buffer2) <= SquareFloat(soundInfo.GetMusicRadius(1)) || + GetVectorSquareMagnitude(buffer, buffer3) <= SquareFloat(soundInfo.GetMusicRadius(1)) || GetVectorSquareMagnitude(buffer, pos) <= SquareFloat(soundInfo.GetMusicRadius(1)))) { - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) + if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) { chasingBoss = i; } @@ -346,9 +293,8 @@ void ClientUpdateMusicSystem(int client, bool initialize = false) if ((chaser.State == STATE_CHASE || chaser.State == STATE_ATTACK || chaser.State == STATE_STUN) && PlayerCanSeeSlender(client, i, false)) { - GetChaserProfileChaseVisibleMusics(profile, soundInfo); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) + soundInfo = chaserData.GetVisibleChaseMusics(); + if (soundInfo != null && soundInfo.Paths != null && soundInfo.Paths.Length > 0) { chasingSeeBoss = i; } @@ -359,16 +305,15 @@ void ClientUpdateMusicSystem(int client, bool initialize = false) if (chaser.State == STATE_ALERT || (chaser.State == STATE_STUN && chaser.PreviousState == STATE_ALERT)) { - GetChaserProfileAlertMusics(profile, soundInfo); - soundList = soundInfo.Paths; - if (soundList == null || soundList.Length <= 0) + soundInfo = chaserData.GetAlertMusics(); + if (soundInfo == null || soundInfo.Paths == null || soundInfo.Paths.Length <= 0) { continue; } if (!(NPCGetFlags(i) & SFF_MARKEDASFAKE)) { - if (GetVectorSquareMagnitude(buffer, pos) <= SquareFloat(soundInfo.Radius) || GetVectorSquareMagnitude(buffer, buffer3) <= SquareFloat(soundInfo.Radius)) + if (GetVectorSquareMagnitude(buffer, pos) <= SquareFloat(soundInfo.GetMusicRadius(1)) || GetVectorSquareMagnitude(buffer, buffer3) <= SquareFloat(soundInfo.GetMusicRadius(1))) { alertBoss = i; } @@ -377,22 +322,20 @@ void ClientUpdateMusicSystem(int client, bool initialize = false) if (chaser.State == STATE_IDLE || (chaser.State == STATE_STUN && chaser.PreviousState == STATE_IDLE)) { - GetChaserProfileIdleMusics(profile, soundInfo); - soundList = soundInfo.Paths; - if (soundList == null || soundList.Length <= 0) + soundInfo = chaserData.GetIdleMusics(); + if (soundInfo == null || soundInfo.Paths == null || soundInfo.Paths.Length <= 0) { continue; } if (!(NPCGetFlags(i) & SFF_MARKEDASFAKE)) { - if (GetVectorSquareMagnitude(buffer, buffer3) <= SquareFloat(soundInfo.Radius) || GetVectorSquareMagnitude(buffer, pos) <= SquareFloat(soundInfo.Radius)) + if (GetVectorSquareMagnitude(buffer, buffer3) <= SquareFloat(soundInfo.GetMusicRadius(1)) || GetVectorSquareMagnitude(buffer, pos) <= SquareFloat(soundInfo.GetMusicRadius(1))) { idleBoss = i; } } } - soundList = null; } } } @@ -709,7 +652,7 @@ void ClientUpdateMusicSystem(int client, bool initialize = false) ClientAddStress(client, stressAdd); } } - } + }*/ } void ClientMusicReset(int client) @@ -750,10 +693,7 @@ void ClientMusicStart(int client, const char[] newMusic, float volume = -1.0, fl StopSound(client, MUSIC_CHAN, oldMusic); } strcopy(g_PlayerMusicString[client], sizeof(g_PlayerMusicString[]), newMusic); - if (MusicActive()) // A boss is overriding the music. - { - GetBossMusic(g_PlayerMusicString[client], sizeof(g_PlayerMusicString[])); - } + if (volume >= 0.0) { g_PlayerMusicVolume[client] = volume; @@ -791,23 +731,22 @@ void ClientAlertMusicReset(int client) g_PlayerAlertMusicOldMaster[client] = -1; ClientRemoveMusicFlag(client, MUSICF_ALERT); - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - for (int i = 0; i < MAX_BOSSES; i++) { g_PlayerAlertMusicTimer[client][i] = null; g_PlayerAlertMusicVolumes[client][i] = 0.0; g_PlayerAlertMusicString[client][i][0] = '\0'; + if (!SF2NPC_BaseNPC(i).IsValid()) + { + continue; + } + BaseBossProfile data = SF2NPC_BaseNPC(i).GetProfileData(); - if (NPCGetUniqueID(i) != -1 && NPCGetType(i) == SF2BossType_Chaser) + if (data.Type != -1 && data.Type == SF2BossType_Chaser) { if (IsValidClient(client)) { - NPCGetProfile(i, profile, sizeof(profile)); - - SF2BossProfileSoundInfo soundInfo; - GetChaserProfileAlertMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); + SF2NPC_Chaser(i).GetProfileData().GetAlertMusics().StopAllSounds(1, client); } } } @@ -832,8 +771,7 @@ void ClientAlertMusicStart(int client,int bossIndex) NPCGetProfile(bossIndex, profile, sizeof(profile)); char buffer[PLATFORM_MAX_PATH]; - SF2BossProfileSoundInfo soundInfo; - GetChaserProfileAlertMusics(profile, soundInfo); + ProfileSound soundInfo = SF2NPC_Chaser(bossIndex).GetProfileData().GetAlertMusics(); if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) { soundInfo.Paths.GetString(GetRandomInt(0, soundInfo.Paths.Length - 1), buffer, sizeof(buffer)); @@ -895,23 +833,22 @@ void ClientIdleMusicReset(int client) g_PlayerIdleMusicOldMaster[client] = -1; ClientRemoveMusicFlag(client, MUSICF_IDLE); - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - for (int i = 0; i < MAX_BOSSES; i++) { g_PlayerIdleMusicTimer[client][i] = null; g_PlayerIdleMusicVolumes[client][i] = 0.0; g_PlayerIdleMusicString[client][i][0] = '\0'; + if (!SF2NPC_BaseNPC(i).IsValid()) + { + continue; + } + BaseBossProfile data = SF2NPC_BaseNPC(i).GetProfileData(); - if (NPCGetUniqueID(i) != -1 && NPCGetType(i) == SF2BossType_Chaser) + if (data.Type != -1 && data.Type == SF2BossType_Chaser) { if (IsValidClient(client)) { - NPCGetProfile(i, profile, sizeof(profile)); - - SF2BossProfileSoundInfo soundInfo; - GetChaserProfileIdleMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); + SF2NPC_Chaser(i).GetProfileData().GetIdleMusics().StopAllSounds(1, client); } } } @@ -936,8 +873,7 @@ void ClientIdleMusicStart(int client,int bossIndex) NPCGetProfile(bossIndex, profile, sizeof(profile)); char buffer[PLATFORM_MAX_PATH]; - SF2BossProfileSoundInfo soundInfo; - GetChaserProfileIdleMusics(profile, soundInfo); + ProfileSound soundInfo = SF2NPC_Chaser(bossIndex).GetProfileData().GetIdleMusics(); if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) { soundInfo.Paths.GetString(GetRandomInt(0, soundInfo.Paths.Length - 1), buffer, sizeof(buffer)); @@ -999,23 +935,22 @@ void ClientChaseMusicReset(int client) g_PlayerChaseMusicOldMaster[client] = -1; ClientRemoveMusicFlag(client, MUSICF_CHASE); - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - for (int i = 0; i < MAX_BOSSES; i++) { g_PlayerChaseMusicVolumes[client][i] = 0.0; g_PlayerChaseMusicTimer[client][i] = null; g_PlayerChaseMusicString[client][i][0] = '\0'; + if (!SF2NPC_BaseNPC(i).IsValid()) + { + continue; + } + BaseBossProfile data = SF2NPC_BaseNPC(i).GetProfileData(); - if (NPCGetUniqueID(i) != -1 && NPCGetType(i) == SF2BossType_Chaser) + if (data.Type != -1 && data.Type == SF2BossType_Chaser) { if (IsValidClient(client)) { - NPCGetProfile(i, profile, sizeof(profile)); - - SF2BossProfileSoundInfo soundInfo; - GetChaserProfileChaseMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); + SF2NPC_Chaser(i).GetProfileData().GetChaseMusics().StopAllSounds(1, client); } } } @@ -1040,8 +975,7 @@ void ClientMusicChaseStart(int client,int bossIndex) NPCGetProfile(bossIndex, profile, sizeof(profile)); char buffer[PLATFORM_MAX_PATH]; - SF2BossProfileSoundInfo soundInfo; - GetChaserProfileChaseMusics(profile, soundInfo); + ProfileSound soundInfo = SF2NPC_Chaser(bossIndex).GetProfileData().GetChaseMusics(); if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) { soundInfo.Paths.GetString(GetRandomInt(0, soundInfo.Paths.Length - 1), buffer, sizeof(buffer)); @@ -1068,10 +1002,7 @@ void ClientMusicChaseStart(int client,int bossIndex) } return; } - if (MusicActive()) // A boss is overriding the music. - { - GetBossMusic(g_PlayerChaseMusicString[client][bossIndex],sizeof(g_PlayerChaseMusicString[][])); - } + g_PlayerChaseMusicTimer[client][bossIndex] = CreateTimer(0.01, Timer_PlayerFadeInChaseMusic, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); TriggerTimer(g_PlayerChaseMusicTimer[client][bossIndex], true); @@ -1107,29 +1038,28 @@ void ClientChaseMusicSeeReset(int client) g_PlayerChaseMusicSeeOldMaster[client] = -1; ClientRemoveMusicFlag(client, MUSICF_CHASEVISIBLE); - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - for (int i = 0; i < MAX_BOSSES; i++) { g_PlayerChaseMusicSeeTimer[client][i] = null; g_PlayerChaseMusicSeeVolumes[client][i] = 0.0; g_PlayerChaseMusicSeeString[client][i][0] = '\0'; + if (!SF2NPC_BaseNPC(i).IsValid()) + { + continue; + } + BaseBossProfile data = SF2NPC_BaseNPC(i).GetProfileData(); - if (NPCGetUniqueID(i) != -1 && NPCGetType(i) == SF2BossType_Chaser) + if (data.Type != -1 && data.Type == SF2BossType_Chaser) { if (IsValidClient(client)) { - NPCGetProfile(i, profile, sizeof(profile)); - - SF2BossProfileSoundInfo soundInfo; - GetChaserProfileChaseVisibleMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); + SF2NPC_Chaser(i).GetProfileData().GetVisibleChaseMusics().StopAllSounds(1, client); } } } } -void ClientMusicChaseSeeStart(int client,int bossIndex) +void ClientMusicChaseSeeStart(int client, int bossIndex) { if (!IsValidClient(client) || !IsPlayerAlive(client)) { @@ -1148,8 +1078,7 @@ void ClientMusicChaseSeeStart(int client,int bossIndex) NPCGetProfile(bossIndex, profile, sizeof(profile)); char buffer[PLATFORM_MAX_PATH]; - SF2BossProfileSoundInfo soundInfo; - GetChaserProfileChaseVisibleMusics(profile, soundInfo); + ProfileSound soundInfo = SF2NPC_Chaser(bossIndex).GetProfileData().GetVisibleChaseMusics(); if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) { soundInfo.Paths.GetString(GetRandomInt(0, soundInfo.Paths.Length - 1), buffer, sizeof(buffer)); @@ -1205,65 +1134,6 @@ void ClientMusicChaseSeeStop(int client,int bossIndex) TriggerTimer(g_PlayerChaseMusicSeeTimer[client][bossIndex], true); } -void Client90sMusicReset(int client) -{ - char oldMusic[PLATFORM_MAX_PATH]; - strcopy(oldMusic, sizeof(oldMusic), g_Player90sMusicString[client]); - g_Player90sMusicString[client][0] = '\0'; - if (IsValidClient(client) && oldMusic[0] != '\0') - { - StopSound(client, MUSIC_CHAN, oldMusic); - } - - g_Player90sMusicTimer[client] = null; - g_Player90sMusicVolumes[client] = 0.0; - - if (IsValidClient(client)) - { - oldMusic = NINETYSMUSIC; - if (oldMusic[0] != '\0') - { - StopSound(client, MUSIC_CHAN, oldMusic); - } - } -} - -void Client90sMusicStart(int client) -{ - if (!IsValidClient(client) || !IsPlayerAlive(client)) - { - return; - } - - char buffer[PLATFORM_MAX_PATH]; - buffer = NINETYSMUSIC; - - if (buffer[0] == '\0') - { - return; - } - - strcopy(g_Player90sMusicString[client], sizeof(g_Player90sMusicString[]), buffer); - g_Player90sMusicTimer[client] = CreateTimer(0.01, Timer_PlayerFadeIn90sMusic, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); - TriggerTimer(g_Player90sMusicTimer[client], true); -} - -void Client90sMusicStop(int client) -{ - if (!IsValidClient(client)) - { - return; - } - - if (!IsClientSprinting(client)) - { - g_Player90sMusicString[client][0] = '\0'; - } - - g_Player90sMusicTimer[client]= CreateTimer(0.01, Timer_PlayerFadeOut90sMusic, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); - TriggerTimer(g_Player90sMusicTimer[client], true); -} - Action Timer_PlayerFadeInMusic(Handle timer, any userid) { int client = GetClientOfUserId(userid); @@ -1353,10 +1223,13 @@ Action Timer_PlayerFadeInAlertMusic(Handle timer, any userid) return Plugin_Stop; } - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(bossIndex); - SF2BossProfileSoundInfo info; - info = data.AlertMusics; + ChaserBossProfile data = SF2NPC_Chaser(bossIndex).GetProfileData(); + if (data == null) + { + StopSound(client, MUSIC_CHAN, g_PlayerAlertMusicString[client][bossIndex]); + return Plugin_Stop; + } + ProfileSound info = data.GetAlertMusics(); /*int oldBoss = g_PlayerAlertMusicOldMaster[client]; @@ -1375,7 +1248,7 @@ Action Timer_PlayerFadeInAlertMusic(Handle timer, any userid) if (g_PlayerAlertMusicString[client][bossIndex][0] != '\0') { - EmitSoundToClient(client, g_PlayerAlertMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerAlertMusicVolumes[client][bossIndex] * info.Volume * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.Pitch); + EmitSoundToClient(client, g_PlayerAlertMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerAlertMusicVolumes[client][bossIndex] * info.GetVolume(GetLocalGlobalDifficulty(bossIndex)) * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.GetPitch(GetLocalGlobalDifficulty(bossIndex))); } if (g_PlayerAlertMusicVolumes[client][bossIndex] >= 1.0) @@ -1410,10 +1283,13 @@ Action Timer_PlayerFadeOutAlertMusic(Handle timer, any userid) return Plugin_Stop; } - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(bossIndex); - SF2BossProfileSoundInfo info; - info = data.AlertMusics; + ChaserBossProfile data = SF2NPC_Chaser(bossIndex).GetProfileData(); + if (data == null) + { + StopSound(client, MUSIC_CHAN, g_PlayerAlertMusicString[client][bossIndex]); + return Plugin_Stop; + } + ProfileSound info = data.GetAlertMusics(); int oldBoss = g_PlayerAlertMusicOldMaster[client]; int newBoss = g_PlayerAlertMusicMaster[client]; @@ -1435,7 +1311,7 @@ Action Timer_PlayerFadeOutAlertMusic(Handle timer, any userid) if (g_PlayerAlertMusicString[client][bossIndex][0] != '\0') { - EmitSoundToClient(client, g_PlayerAlertMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerAlertMusicVolumes[client][bossIndex] * info.Volume * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.Pitch); + EmitSoundToClient(client, g_PlayerAlertMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerAlertMusicVolumes[client][bossIndex] * info.GetVolume(GetLocalGlobalDifficulty(bossIndex)) * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.GetPitch(GetLocalGlobalDifficulty(bossIndex))); } if (g_PlayerAlertMusicVolumes[client][bossIndex] <= 0.0) @@ -1472,10 +1348,13 @@ Action Timer_PlayerFadeInIdleMusic(Handle timer, any userid) return Plugin_Stop; } - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(bossIndex); - SF2BossProfileSoundInfo info; - info = data.IdleMusics; + ChaserBossProfile data = SF2NPC_Chaser(bossIndex).GetProfileData(); + if (data == null) + { + StopSound(client, MUSIC_CHAN, g_PlayerIdleMusicString[client][bossIndex]); + return Plugin_Stop; + } + ProfileSound info = data.GetIdleMusics(); /*int oldBoss = g_PlayerIdleMusicOldMaster[client]; @@ -1494,7 +1373,7 @@ Action Timer_PlayerFadeInIdleMusic(Handle timer, any userid) if (g_PlayerIdleMusicString[client][bossIndex][0] != '\0') { - EmitSoundToClient(client, g_PlayerIdleMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerIdleMusicVolumes[client][bossIndex] * info.Volume * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.Pitch); + EmitSoundToClient(client, g_PlayerIdleMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerIdleMusicVolumes[client][bossIndex] * info.GetVolume(GetLocalGlobalDifficulty(bossIndex)) * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.GetPitch(GetLocalGlobalDifficulty(bossIndex))); } if (g_PlayerIdleMusicVolumes[client][bossIndex] >= 1.0) @@ -1529,10 +1408,13 @@ Action Timer_PlayerFadeOutIdleMusic(Handle timer, any userid) return Plugin_Stop; } - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(bossIndex); - SF2BossProfileSoundInfo info; - info = data.IdleMusics; + ChaserBossProfile data = SF2NPC_Chaser(bossIndex).GetProfileData(); + if (data == null) + { + StopSound(client, MUSIC_CHAN, g_PlayerIdleMusicString[client][bossIndex]); + return Plugin_Stop; + } + ProfileSound info = data.GetIdleMusics(); int oldBoss = g_PlayerIdleMusicOldMaster[client]; int newBoss = g_PlayerIdleMusicMaster[client]; @@ -1554,7 +1436,7 @@ Action Timer_PlayerFadeOutIdleMusic(Handle timer, any userid) if (g_PlayerIdleMusicString[client][bossIndex][0] != '\0') { - EmitSoundToClient(client, g_PlayerIdleMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerIdleMusicVolumes[client][bossIndex] * info.Volume * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.Pitch); + EmitSoundToClient(client, g_PlayerIdleMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerIdleMusicVolumes[client][bossIndex] * info.GetVolume(GetLocalGlobalDifficulty(bossIndex)) * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.GetPitch(GetLocalGlobalDifficulty(bossIndex))); } if (g_PlayerIdleMusicVolumes[client][bossIndex] <= 0.0) @@ -1591,10 +1473,13 @@ Action Timer_PlayerFadeInChaseMusic(Handle timer, any userid) return Plugin_Stop; } - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(bossIndex); - SF2BossProfileSoundInfo info; - info = data.ChaseMusics; + ChaserBossProfile data = SF2NPC_Chaser(bossIndex).GetProfileData(); + if (data == null) + { + StopSound(client, MUSIC_CHAN, g_PlayerChaseMusicString[client][bossIndex]); + return Plugin_Stop; + } + ProfileSound info = data.GetChaseMusics(); /*int oldBoss = g_PlayerChaseMusicOldMaster[client]; @@ -1613,7 +1498,7 @@ Action Timer_PlayerFadeInChaseMusic(Handle timer, any userid) if (g_PlayerChaseMusicString[client][bossIndex][0] != '\0') { - EmitSoundToClient(client, g_PlayerChaseMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerChaseMusicVolumes[client][bossIndex] * info.Volume * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.Pitch); + EmitSoundToClient(client, g_PlayerChaseMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerChaseMusicVolumes[client][bossIndex] * info.GetVolume(GetLocalGlobalDifficulty(bossIndex)) * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.GetPitch(GetLocalGlobalDifficulty(bossIndex))); } if (g_PlayerChaseMusicVolumes[client][bossIndex] >= 1.0) @@ -1648,10 +1533,13 @@ Action Timer_PlayerFadeInChaseMusicSee(Handle timer, any userid) return Plugin_Stop; } - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(bossIndex); - SF2BossProfileSoundInfo info; - info = data.ChaseVisibleMusics; + ChaserBossProfile data = SF2NPC_Chaser(bossIndex).GetProfileData(); + if (data == null) + { + StopSound(client, MUSIC_CHAN, g_PlayerChaseMusicSeeString[client][bossIndex]); + return Plugin_Stop; + } + ProfileSound info = data.GetVisibleChaseMusics(); /*int oldBoss = g_PlayerChaseMusicSeeOldMaster[client]; @@ -1670,7 +1558,7 @@ Action Timer_PlayerFadeInChaseMusicSee(Handle timer, any userid) if (g_PlayerChaseMusicSeeString[client][bossIndex][0] != '\0') { - EmitSoundToClient(client, g_PlayerChaseMusicSeeString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerChaseMusicSeeVolumes[client][bossIndex] * info.Volume * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.Pitch); + EmitSoundToClient(client, g_PlayerChaseMusicSeeString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerChaseMusicSeeVolumes[client][bossIndex] * info.GetVolume(GetLocalGlobalDifficulty(bossIndex)) * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.GetPitch(GetLocalGlobalDifficulty(bossIndex))); } if (g_PlayerChaseMusicSeeVolumes[client][bossIndex] >= 1.0) @@ -1705,10 +1593,13 @@ Action Timer_PlayerFadeOutChaseMusic(Handle timer, any userid) return Plugin_Stop; } - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(bossIndex); - SF2BossProfileSoundInfo info; - info = data.ChaseMusics; + ChaserBossProfile data = SF2NPC_Chaser(bossIndex).GetProfileData(); + if (data == null) + { + StopSound(client, MUSIC_CHAN, g_PlayerChaseMusicString[client][bossIndex]); + return Plugin_Stop; + } + ProfileSound info = data.GetChaseMusics(); int oldBoss = g_PlayerChaseMusicOldMaster[client]; int newBoss = g_PlayerChaseMusicMaster[client]; @@ -1730,7 +1621,7 @@ Action Timer_PlayerFadeOutChaseMusic(Handle timer, any userid) if (g_PlayerChaseMusicString[client][bossIndex][0] != '\0') { - EmitSoundToClient(client, g_PlayerChaseMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerChaseMusicVolumes[client][bossIndex] * info.Volume * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.Pitch); + EmitSoundToClient(client, g_PlayerChaseMusicString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerChaseMusicVolumes[client][bossIndex] * info.GetVolume(GetLocalGlobalDifficulty(bossIndex)) * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.GetPitch(GetLocalGlobalDifficulty(bossIndex))); } if (g_PlayerChaseMusicVolumes[client][bossIndex] <= 0.0) @@ -1767,10 +1658,13 @@ Action Timer_PlayerFadeOutChaseMusicSee(Handle timer, any userid) return Plugin_Stop; } - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(bossIndex); - SF2BossProfileSoundInfo info; - info = data.ChaseVisibleMusics; + ChaserBossProfile data = SF2NPC_Chaser(bossIndex).GetProfileData(); + if (data == null) + { + StopSound(client, MUSIC_CHAN, g_PlayerChaseMusicSeeString[client][bossIndex]); + return Plugin_Stop; + } + ProfileSound info = data.GetVisibleChaseMusics(); int oldBoss = g_PlayerChaseMusicSeeOldMaster[client]; int newBoss = g_PlayerChaseMusicSeeMaster[client]; @@ -1792,7 +1686,7 @@ Action Timer_PlayerFadeOutChaseMusicSee(Handle timer, any userid) if (g_PlayerChaseMusicSeeString[client][bossIndex][0] != '\0') { - EmitSoundToClient(client, g_PlayerChaseMusicSeeString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerChaseMusicSeeVolumes[client][bossIndex] * info.Volume * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.Pitch); + EmitSoundToClient(client, g_PlayerChaseMusicSeeString[client][bossIndex], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_PlayerChaseMusicSeeVolumes[client][bossIndex] * info.GetVolume(GetLocalGlobalDifficulty(bossIndex)) * g_PlayerPreferences[client].PlayerPreference_MusicVolume, info.GetPitch(GetLocalGlobalDifficulty(bossIndex))); } if (g_PlayerChaseMusicSeeVolumes[client][bossIndex] <= 0.0) @@ -1806,81 +1700,6 @@ Action Timer_PlayerFadeOutChaseMusicSee(Handle timer, any userid) return Plugin_Continue; } -Action Timer_PlayerFadeIn90sMusic(Handle timer, any userid) -{ - int client = GetClientOfUserId(userid); - if (client <= 0) - { - return Plugin_Stop; - } - - if (g_Player90sMusicTimer[client] != timer) - { - return Plugin_Stop; - } - - g_Player90sMusicVolumes[client] += 0.28; - if (g_Player90sMusicVolumes[client] > 0.5) - { - g_Player90sMusicVolumes[client] = 0.5; - } - - if (g_Player90sMusicString[client][0] != '\0') - { - EmitSoundToClient(client, g_Player90sMusicString[client], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_Player90sMusicVolumes[client] * g_PlayerPreferences[client].PlayerPreference_MusicVolume, 100); - } - - if (g_Player90sMusicVolumes[client] >= 0.5) - { - g_Player90sMusicTimer[client] = null; - return Plugin_Stop; - } - - return Plugin_Continue; -} - -Action Timer_PlayerFadeOut90sMusic(Handle timer, any userid) -{ - int client = GetClientOfUserId(userid); - if (client <= 0) - { - return Plugin_Stop; - } - - if (g_Player90sMusicTimer[client] != timer) - { - return Plugin_Stop; - } - - char buffer[PLATFORM_MAX_PATH]; - buffer = NINETYSMUSIC; - - if (strcmp(buffer, g_Player90sMusicString[client], false) == 0) - { - g_Player90sMusicTimer[client] = null; - return Plugin_Stop; - } - - g_Player90sMusicVolumes[client] -= 0.28; - if (g_Player90sMusicVolumes[client] < 0.0) - { - g_Player90sMusicVolumes[client] = 0.0; - } - - if (buffer[0] != '\0') - { - EmitSoundToClient(client, buffer, _, MUSIC_CHAN, _, SND_CHANGEVOL, g_Player90sMusicVolumes[client] * g_PlayerPreferences[client].PlayerPreference_MusicVolume, 100); - } - - if (g_Player90sMusicVolumes[client] <= 0.0) - { - g_Player90sMusicTimer[client] = null; - return Plugin_Stop; - } - - return Plugin_Continue; -} - bool ClientHasMusicFlag(int client, int flag) { return (g_PlayerMusicFlags[client] & flag) != 0; diff --git a/addons/sourcemod/scripting/sf2/client/new_music.sp b/addons/sourcemod/scripting/sf2/client/new_music.sp index 1bc7e0a9..6ca08783 100644 --- a/addons/sourcemod/scripting/sf2/client/new_music.sp +++ b/addons/sourcemod/scripting/sf2/client/new_music.sp @@ -5,7 +5,7 @@ enum SF2MusicFadeState { SF2MusicFadeState_None = 0, SF2MusicFadeState_FadeIn = 1, - SF2MusicFadeState_FadeOut = 2 + SF2MusicFadeState_FadeOut = 2, } enum SF2BossMusicState @@ -66,24 +66,32 @@ methodmap ProfileGlobalTracks < ProfileObject return SF2BossGlobalMusicShuffleType_None; } - public KeyMap_Array GetTracks() + public float GetChance(int difficulty) { - return this.GetArray("tracks"); + return this.GetDifficultyFloat("chance", difficulty, 1.0); + } + + public KeyMap_Array GetTracks(int difficulty) + { + return this.GetDifficultyArray("tracks", difficulty); } public void Precache() { - this.ConvertSectionsSectionToArray("tracks"); - if (this.GetTracks() != null) + this.ConvertDifficultySectionsSectionToArray("tracks"); + for (int i = 0; i < Difficulty_Max; i++) { - for (int i = 0; i < this.GetTracks().Length; i++) + if (this.GetTracks(i) != null) { - ProfileGlobalTrack track = view_as(this.GetTracks().GetSection(i)); - if (track == null) + for (int i2 = 0; i2 < this.GetTracks(i).Length; i2++) { - continue; + ProfileGlobalTrack track = view_as(this.GetTracks(i).GetSection(i2)); + if (track == null) + { + continue; + } + track.Precache(); } - track.Precache(); } } } @@ -91,55 +99,57 @@ methodmap ProfileGlobalTracks < ProfileObject methodmap ProfileGlobalTrack < ProfileObject { - public bool IsEnabled(int difficulty) + public bool IsEnabled() { if (this == null) { return false; } - return this.GetDifficultyBool("enabled", difficulty, true); + return this.GetBool("enabled", true); } - public void GetPath(int difficulty, char[] buffer, int bufferSize) + public void GetPath(char[] buffer, int bufferSize) { - this.GetDifficultyString("path", difficulty, buffer, bufferSize); + this.GetString("path", buffer, bufferSize); } - public float GetVolume(int difficulty) + public float GetVolume() { - return this.GetDifficultyFloat("volume", difficulty, SNDVOL_NORMAL); + return this.GetFloat("volume", SNDVOL_NORMAL); } - public int GetPitch(int difficulty) + public int GetPitch() { - return this.GetDifficultyInt("pitch", difficulty, SNDPITCH_NORMAL); + return this.GetInt("pitch", SNDPITCH_NORMAL); } - public float GetLength(int difficulty) + public float GetLength() { - return this.GetDifficultyFloat("length", difficulty, -1.0); + return this.GetFloat("length", -1.0); } - public float GetMinPageRange(int difficulty) + public float GetMinPageRange() { - return this.GetDifficultyFloat("min_range", difficulty, 0.0); + return this.GetFloat("min_range", 0.0); } - public float GetMaxPageRange(int difficulty) + public float GetMaxPageRange() { - return this.GetDifficultyFloat("max_range", difficulty, 0.0); + return this.GetFloat("max_range", 0.0); + } + + public bool CanInstantFade(int difficulty = Difficulty_Normal) + { + return this.GetDifficultyBool("instant_fade", difficulty, false); } public void Precache() { char path[PLATFORM_MAX_PATH]; - for (int i = 0; i < Difficulty_Max; i++) + this.GetPath(path, sizeof(path)); + if (path[0] != '\0') { - this.GetPath(i, path, sizeof(path)); - if (path[0] != '\0') - { - PrecacheSound2(path, g_FileCheckConVar.BoolValue); - } + PrecacheSound2(path, g_FileCheckConVar.BoolValue); } } } @@ -176,8 +186,10 @@ enum struct MusicState int Pitch; float FadeSpeed; SF2MusicFadeState FadeState; + bool InstantFade; float Length; bool StopAfterFade; + bool ShouldConstantReplay; void Init() { @@ -188,11 +200,13 @@ enum struct MusicState this.Pitch = 100; this.FadeSpeed = 1.0; this.FadeState = SF2MusicFadeState_None; + this.InstantFade = false; this.Length = -1.0; this.StopAfterFade = false; + this.ShouldConstantReplay = false; } - void FadeIn(SF2_BasePlayer client, float delta, bool instant = false) + void FadeIn(SF2_BasePlayer client, float delta) { if (this.IsNull) { @@ -211,7 +225,7 @@ enum struct MusicState } this.CurrentVolume += this.FadeSpeed * delta; - if (instant) + if (this.InstantFade) { this.CurrentVolume = this.MaxVolume; } @@ -224,7 +238,7 @@ enum struct MusicState EmitSoundToClient(client.index, this.MusicPath, _, MUSIC_CHAN, SNDLEVEL_NONE, SND_CHANGEVOL, volume, this.Pitch); } - void FadeOut(SF2_BasePlayer client, float delta, bool instant = false) + void FadeOut(SF2_BasePlayer client, float delta) { if (this.IsNull) { @@ -237,7 +251,7 @@ enum struct MusicState } this.CurrentVolume -= this.FadeSpeed * delta; - if (instant) + if (this.InstantFade) { this.CurrentVolume = 0.0; } @@ -299,6 +313,27 @@ enum struct MusicState this.Init(); } + void Replay(SF2_BasePlayer client) + { + if (this.IsNull) + { + return; + } + + if (this.MusicPath[0] == '\0') + { + return; + } + + if (this.FadeState != SF2MusicFadeState_None) + { + return; + } + + float volume = this.MaxVolume * g_PlayerPreferences[client.index].PlayerPreference_MusicVolume; + EmitSoundToClient(client.index, this.MusicPath, _, MUSIC_CHAN, SNDLEVEL_NONE, SND_CHANGEVOL, volume, this.Pitch); + } + void SetFadeState(SF2MusicFadeState state) { if (this.IsNull) @@ -323,7 +358,9 @@ enum struct PageMusicInfo } static ArrayList g_PageMusicRanges = null; +static ArrayList g_BossPageMusicRanges = null; static ArrayList g_BossesWithGlobalMusic = null; +static ArrayList g_BossesWithPageMusic = null; static bool g_CanSwapBackToDefault[MAXTF2PLAYERS] = { true, ... }; static float g_CurrentTrackLength[MAXTF2PLAYERS]; static StringMap g_QueuedThemes[MAXTF2PLAYERS]; // Fade out queue @@ -349,6 +386,7 @@ void SetupNewMusic() { g_OnGamemodeEndPFwd.AddFunction(null, OnGamemodeEnd); g_OnMapStartPFwd.AddFunction(null, MapStart); + g_OnRoundEndPFwd.AddFunction(null, OnRoundEnd); g_OnConfigsExecutedPFwd.AddFunction(null, ConfigsExecuted); g_OnPlayerSpawnPFwd.AddFunction(null, OnPlayerSpawn); g_OnPlayerDeathPFwd.AddFunction(null, OnPlayerDeath); @@ -363,7 +401,9 @@ void SetupNewMusic() g_OnDifficultyChangePFwd.AddFunction(null, OnDifficultyChange); g_PageMusicRanges = new ArrayList(sizeof(PageMusicInfo)); + g_BossPageMusicRanges = new ArrayList(sizeof(PageMusicInfo)); g_BossesWithGlobalMusic = new ArrayList(); + g_BossesWithPageMusic = new ArrayList(); g_NullMusicState.Init(); g_NullMusicState.IsNull = true; } @@ -386,6 +426,15 @@ static void MapStart() g_LastMusicThink = -1.0; RequestFrame(MusicThink); g_BossesWithGlobalMusic.Clear(); + g_BossesWithPageMusic.Clear(); + g_BossPageMusicRanges.Clear(); +} + +static void OnRoundEnd() +{ + g_BossesWithGlobalMusic.Clear(); + g_BossesWithPageMusic.Clear(); + g_BossPageMusicRanges.Clear(); } static void ConfigsExecuted() @@ -463,7 +512,7 @@ static void OnBossAdded(SF2NPC_BaseNPC npc) return; } - if (npc.GetProfileDataEx().IsPvEBoss) + if (npc.GetProfileData().IsPvEBoss) { return; } @@ -475,11 +524,16 @@ static void OnBossAdded(SF2NPC_BaseNPC npc) int oldId = g_GlobalMusicTrackMasterID; char path[PLATFORM_MAX_PATH]; - GetBossMusicTrack(npc, SF2BossMusicTrackType_Global, path, sizeof(path)); + bool isPageMusic = false; + GetBossMusicTrack(npc, SF2BossMusicTrackType_Global, path, sizeof(path), .isPageMusic = isPageMusic, .checkRanges = false); - if (path[0] != '\0') + if (path[0] != '\0' || isPageMusic) { g_BossesWithGlobalMusic.Push(npc.UniqueID); + if (isPageMusic) + { + g_BossesWithPageMusic.Push(npc.UniqueID); + } if (NPCGetFromUniqueID(oldId) == -1) { UpdateBossGlobalTracks(); @@ -494,7 +548,7 @@ static void OnBossRemoved(SF2NPC_BaseNPC npc) return; } - if (npc.GetProfileDataEx().IsPvEBoss) + if (npc.GetProfileData().IsPvEBoss) { return; } @@ -504,12 +558,19 @@ static void OnBossRemoved(SF2NPC_BaseNPC npc) return; } - int index; + int index = -1; if ((index = g_BossesWithGlobalMusic.FindValue(npc.UniqueID)) != -1) { g_BossesWithGlobalMusic.Erase(index); } + index = -1; + if ((index = g_BossesWithPageMusic.FindValue(npc.UniqueID)) != -1) + { + g_BossesWithPageMusic.Erase(index); + g_BossPageMusicRanges.Clear(); + } + if (g_GlobalMusicTrackMasterID == npc.UniqueID) { UpdateBossGlobalTracks(); @@ -524,6 +585,8 @@ static void OnDifficultyChange(int oldDifficulty, int newDifficulty) } g_BossesWithGlobalMusic.Clear(); + g_BossesWithPageMusic.Clear(); + g_BossPageMusicRanges.Clear(); for (int i = 0; i < MAX_BOSSES; i++) { SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(i); @@ -532,17 +595,22 @@ static void OnDifficultyChange(int oldDifficulty, int newDifficulty) continue; } - if (npc.GetProfileDataEx().IsPvEBoss) + if (npc.GetProfileData().IsPvEBoss) { continue; } char path[PLATFORM_MAX_PATH]; - GetBossMusicTrack(npc, SF2BossMusicTrackType_Global, path, sizeof(path)); + bool isPageMusic = false; + GetBossMusicTrack(npc, SF2BossMusicTrackType_Global, path, sizeof(path), .isPageMusic = isPageMusic, .checkRanges = false); - if (path[0] != '\0') + if (path[0] != '\0' || isPageMusic) { g_BossesWithGlobalMusic.Push(npc.UniqueID); + if (isPageMusic) + { + g_BossesWithPageMusic.Push(npc.UniqueID); + } UpdateBossGlobalTracks(); } } @@ -574,6 +642,7 @@ static void GetPageMusicRanges() continue; } + musicInfo.State.Init(); if (path[0] != '#' && path[1] != '#') { Format(path, sizeof(path), "#%s", path); @@ -718,7 +787,7 @@ static void ClearMusic(SF2_BasePlayer client) static void UpdatePageMusic() { - if (g_BossesWithGlobalMusic.Length != 0) + if (g_BossesWithGlobalMusic.Length != 0 && g_BossesWithPageMusic.Length == 0) { return; } @@ -727,14 +796,19 @@ static void UpdatePageMusic() oldState = g_DefaultMusicState; g_OldDefaultMusicState = g_DefaultMusicState; g_DefaultMusicState = g_NullMusicState; + ArrayList ranges = g_PageMusicRanges; + if (g_BossesWithPageMusic.Length > 0) + { + ranges = g_BossPageMusicRanges; + } if (g_PageCount < g_PageMax || !g_RoundStopPageMusicOnEscape) { PageMusicInfo info; - for (int i = 0; i < g_PageMusicRanges.Length; i++) + for (int i = 0; i < ranges.Length; i++) { - g_PageMusicRanges.GetArray(i, info); + ranges.GetArray(i, info); if (g_PageCount >= info.MinRequiredPages && g_PageCount <= info.MaxRequiredPages) { g_DefaultMusicState = info.State; @@ -785,6 +859,22 @@ static void UpdateBossGlobalTrackForPlayer(SF2_BasePlayer client) { return; } + for (int i = 0; i < g_BossesWithPageMusic.Length; i++) + { + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(NPCGetFromUniqueID(g_BossesWithPageMusic.Get(i))); + if (!npc.IsValid()) + { + g_BossesWithPageMusic.Erase(i); + i--; + continue; + } + + GetBossMusicTrack(npc, SF2BossMusicTrackType_Global, .checkRanges = true); + if (g_BossPageMusicRanges.Length > 0) + { + return; + } + } g_OldGlobalMusicState[client.index] = g_GlobalMusicState[client.index]; g_OldGlobalMusicState[client.index].StopAfterFade = true; @@ -801,8 +891,13 @@ static void UpdateBossGlobalTrackForPlayer(SF2_BasePlayer client) char path[PLATFORM_MAX_PATH]; float volume = 1.0, length = -1.0; int pitch = 100; + bool isPageMusic = false; SF2BossMusicState priority = SF2BossMusicState_Max; - GetBossMusicTrack(npc, SF2BossMusicTrackType_Global, path, sizeof(path), volume, pitch, .priority = priority, .length = length, .client = client); + GetBossMusicTrack(npc, SF2BossMusicTrackType_Global, path, sizeof(path), volume, pitch, .priority = priority, .length = length, .client = client, .isPageMusic = isPageMusic, .checkRanges = false); + if (isPageMusic) + { + continue; + } if (path[0] != '\0') { @@ -812,6 +907,10 @@ static void UpdateBossGlobalTrackForPlayer(SF2_BasePlayer client) state.MaxVolume = volume; state.Pitch = pitch; state.Length = length; + if (state.Length <= 0.0) + { + state.ShouldConstantReplay = true; + } g_GlobalMusicState[client.index] = state; g_GlobalMusicUpdateLimit[client.index] = priority; g_CurrentTrackLength[client.index] = 0.0; @@ -869,8 +968,29 @@ static void AddMusicToQueue(SF2_BasePlayer client, MusicState state) { currentState.StopAfterFade = true; } + if (state.InstantFade) + { + currentState.InstantFade = true; + if (g_QueuedThemes[client.index] != null) + { + MusicState temp; + StringMapSnapshot snapshot = g_QueuedThemes[client.index].Snapshot(); + + for (int i = 0; i < snapshot.Length; i++) + { + char key[PLATFORM_MAX_PATH]; + snapshot.GetKey(i, key, sizeof(key)); + g_QueuedThemes[client.index].GetArray(key, temp, sizeof(temp)); + temp.InstantFade = true; + g_QueuedThemes[client.index].SetArray(key, temp, sizeof(temp)); + } + + delete snapshot; + } + } currentState.SetFadeState(SF2MusicFadeState_FadeOut); g_QueuedThemes[client.index].SetArray(currentState.MusicPath, currentState, sizeof(currentState)); + if (g_LayeredThemes[client.index] != null) { StringMapSnapshot snapshot = g_LayeredThemes[client.index].Snapshot(); @@ -885,6 +1005,7 @@ static void AddMusicToQueue(SF2_BasePlayer client, MusicState state) delete g_LayeredThemes[client.index]; delete snapshot; } + g_QueuedThemes[client.index].Remove(state.MusicPath); } state.SetFadeState(SF2MusicFadeState_FadeIn); @@ -1004,6 +1125,10 @@ static void MusicThink() float delta = g_LastMusicThink < 0.0 ? 0.0 : gameTime - g_LastMusicThink; g_LastMusicThink = gameTime; bool globalMusic = g_BossesWithGlobalMusic.Length > 0; + if (globalMusic && g_BossesWithPageMusic.Length > 0) + { + globalMusic = false; + } for (int i = 1; i <= MaxClients; i++) { @@ -1036,6 +1161,11 @@ static void MusicThink() { UpdateBossGlobalTrackForPlayer(client); } + + if (g_CurrentTheme[client.index].ShouldConstantReplay) + { + g_CurrentTheme[client.index].Replay(client); + } } if (g_CurrentTheme[client.index].FadeState == SF2MusicFadeState_FadeIn) @@ -1152,7 +1282,7 @@ static void MusicThink() continue; } - BaseBossProfile data = SF2NPC_BaseNPC(i).GetProfileDataEx(); + BaseBossProfile data = SF2NPC_BaseNPC(i).GetProfileData(); if (data.IsPvEBoss) { continue; @@ -1268,7 +1398,7 @@ static void MusicThink() continue; } - BaseBossProfile data = SF2NPC_BaseNPC(i).GetProfileDataEx(); + BaseBossProfile data = SF2NPC_BaseNPC(i).GetProfileData(); if (data.IsPvEBoss) { continue; @@ -1384,8 +1514,8 @@ static int ConvertMusicStateToBossState(SF2BossMusicState state) static void GetBossMusicTrack(SF2NPC_BaseNPC controller, SF2BossMusicTrackType trackType, - char[] buffer, - int bufferSize, + char[] buffer = "", + int bufferSize = 0, float& volume = 1.0, int& pitch = 100, float& radius = 850.0, @@ -1394,14 +1524,16 @@ static void GetBossMusicTrack(SF2NPC_BaseNPC controller, SF2BossMusicState& priority = SF2BossMusicState_Max, float& length = -1.0, SF2BossGlobalMusicShuffleType& shuffleType = SF2BossGlobalMusicShuffleType_None, - SF2_BasePlayer client = SF2_INVALID_PLAYER) + SF2_BasePlayer client = SF2_INVALID_PLAYER, + bool& isPageMusic = false, + bool checkRanges = false) { if (!controller.IsValid()) { return; } - BaseBossProfile data = controller.GetProfileDataEx(); + BaseBossProfile data = controller.GetProfileData(); if (data.IsPvEBoss) { return; @@ -1448,6 +1580,9 @@ static void GetBossMusicTrack(SF2NPC_BaseNPC controller, soundInfo = view_as(data).GetChaseMusics(); } } + pitch = soundInfo.GetPitch(); + volume = soundInfo.GetVolume(); + radius = soundInfo.GetRadius(); } case SF2BossMusicTrackType_Visible: @@ -1479,6 +1614,9 @@ static void GetBossMusicTrack(SF2NPC_BaseNPC controller, soundInfo = view_as(data).GetVisibleChaseMusics(); } } + pitch = soundInfo.GetPitch(); + volume = soundInfo.GetVolume(); + radius = soundInfo.GetRadius(); } case SF2BossMusicTrackType_Global: @@ -1488,11 +1626,15 @@ static void GetBossMusicTrack(SF2NPC_BaseNPC controller, { return; } - KeyMap_Array tracks = globalTrack.GetTracks(); + KeyMap_Array tracks = globalTrack.GetTracks(difficulty); if (tracks == null || tracks.Length == 0) { return; } + if (globalTrack.GetChance(difficulty) < GetRandomFloat()) + { + return; + } priority = globalTrack.GetPriority(difficulty); shuffleType = globalTrack.GetShuffleType(difficulty); bool changed = false; @@ -1517,7 +1659,7 @@ static void GetBossMusicTrack(SF2NPC_BaseNPC controller, { continue; } - if (!track.IsEnabled(difficulty)) + if (!track.IsEnabled()) { continue; } @@ -1561,7 +1703,7 @@ static void GetBossMusicTrack(SF2NPC_BaseNPC controller, continue; } char temp[PLATFORM_MAX_PATH]; - tempTrack.GetPath(difficulty, temp, sizeof(temp)); + tempTrack.GetPath(temp, sizeof(temp)); if (client.IsValid && !client.IsBot && strcmp(g_GlobalMusicState[client.index].MusicPath, temp, false) == 0) { tempTracks.Erase(i); @@ -1597,11 +1739,40 @@ static void GetBossMusicTrack(SF2NPC_BaseNPC controller, } track = g_CurrentTracks.Get(g_CurrentTrackIndex); } + + case SF2BossGlobalMusicShuffleType_Range: + { + if (checkRanges && g_BossPageMusicRanges.Length == 0) + { + ArrayList tempTracks = g_CurrentTracks.Clone(); + for (int i = 0; i < tempTracks.Length; i++) + { + ProfileGlobalTrack tempTrack = view_as(tempTracks.Get(i)); + if (tempTrack == null) + { + continue; + } + char temp[PLATFORM_MAX_PATH]; + tempTrack.GetPath(temp, sizeof(temp)); + + PageMusicInfo musicInfo; + musicInfo.State.Init(); + musicInfo.MinRequiredPages = RoundToFloor(float(g_PageMax) * tempTrack.GetMinPageRange()); + musicInfo.MaxRequiredPages = RoundToFloor(float(g_PageMax) * tempTrack.GetMaxPageRange()); + musicInfo.State.MusicPath = temp; + musicInfo.State.InstantFade = tempTrack.CanInstantFade(); + g_BossPageMusicRanges.PushArray(musicInfo); + } + delete tempTracks; + } + isPageMusic = true; + return; + } } - track.GetPath(difficulty, buffer, bufferSize); - volume = track.GetVolume(difficulty); - pitch = track.GetPitch(difficulty); - length = track.GetLength(difficulty); + track.GetPath(buffer, bufferSize); + volume = track.GetVolume(); + pitch = track.GetPitch(); + length = track.GetLength(); return; } } diff --git a/addons/sourcemod/scripting/sf2/client/peek.sp b/addons/sourcemod/scripting/sf2/client/peek.sp index b191ee64..39b6561a 100644 --- a/addons/sourcemod/scripting/sf2/client/peek.sp +++ b/addons/sourcemod/scripting/sf2/client/peek.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required //Peeking Data bool g_PlayerPeeking[MAXTF2PLAYERS] = { false, ... }; diff --git a/addons/sourcemod/scripting/sf2/client/proxy.sp b/addons/sourcemod/scripting/sf2/client/proxy.sp index 495ed120..1f422040 100644 --- a/addons/sourcemod/scripting/sf2/client/proxy.sp +++ b/addons/sourcemod/scripting/sf2/client/proxy.sp @@ -4,1931 +4,1933 @@ #define _sf2_client_proxy_functions_included #pragma semicolon 1 +#pragma newdecls required -static int g_ActionItemIndexes[] = { 57, 231 }; - -//Proxy model -static char g_ClientProxyModel[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; -static char g_ClientProxyModelHard[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; -static char g_ClientProxyModelInsane[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; -static char g_ClientProxyModelNightmare[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; -static char g_ClientProxyModelApollyon[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; - -void SetupProxy() -{ - g_OnMapStartPFwd.AddFunction(null, MapStart); - g_OnPlayerPutInServerPFwd.AddFunction(null, OnPutInServer); - g_OnPlayerDisconnectedPFwd.AddFunction(null, OnDisconnected); - g_OnPlayerSpawnPFwd.AddFunction(null, OnPlayerSpawn); - g_OnPlayerDeathPFwd.AddFunction(null, OnPlayerDeath); - g_OnPlayerEscapePFwd.AddFunction(null, OnPlayerEscape); - - AddNormalSoundHook(Hook_ProxySoundHook); -} - -static void MapStart() +methodmap BossProfileProxyData < ProfileObject { - for (int i = 1; i <= 18; i++) + public bool IsEnabled(int difficulty) { - char sound[PLATFORM_MAX_PATH]; - FormatEx(sound, sizeof(sound), "mvm/player/footsteps/robostep_%s%i.wav", (i < 10) ? "0" : "", i); - PrecacheSound(sound, true); + if (this == null) + { + return false; + } + return this.GetDifficultyBool("enabled", difficulty, true); } -} -static void OnPutInServer(SF2_BasePlayer client) -{ - if (!g_Enabled) + public int GetMaxProxies(int difficulty) { - return; + return this.GetDifficultyInt("__proxies_max", difficulty); } - ClientResetProxy(client.index); - ClientStartProxyAvailableTimer(client.index); -} - -static void OnDisconnected(SF2_BasePlayer client) -{ - ClientStopProxyForce(client.index); -} -static void OnPlayerSpawn(SF2_BasePlayer client) -{ - if (!g_Enabled) + public void SetMaxProxies(int difficulty, int value) { - return; + this.SetDifficultyInt("__proxies_max", difficulty, value); } - ClientResetProxy(client.index); -} - -static void OnPlayerDeath(SF2_BasePlayer client) -{ - if (!g_Enabled) + public float GetMinSpawnChance(int difficulty) { - return; + return this.GetDifficultyFloat("spawn_chance_min", difficulty, 0.0); } - if (!client.IsProxy) + public float GetMaxSpawnChance(int difficulty) { - return; + return this.GetDifficultyFloat("spawn_chance_max", difficulty, 0.0); } - // We're a proxy, so play some sounds. - - int proxyMaster = NPCGetFromUniqueID(g_PlayerProxyMaster[client.index]); - if (proxyMaster != -1) + public float GetSpawnChanceThreshold(int difficulty) { - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(proxyMaster, profile, sizeof(profile)); - - SF2BossProfileSoundInfo soundInfo; - GetBossProfileProxyDeathSounds(profile, soundInfo); - soundInfo.EmitSound(_, client.index); + return this.GetDifficultyFloat("spawn_chance_threshold", difficulty, 0.25); } - CreateTimer(0.1, Timer_ResetProxy, client.UserID, TIMER_FLAG_NO_MAPCHANGE); -} - -static Action Timer_ResetProxy(Handle timer, any userid) -{ - SF2_BasePlayer client = SF2_BasePlayer(GetClientOfUserId(userid)); - if (!client.IsValid) + public int GetMinSpawnedProxies(int difficulty) { - return Plugin_Stop; + return this.GetDifficultyInt("spawn_num_min", difficulty, 0); } - ClientResetProxy(client.index, true); - return Plugin_Stop; -} + public int GetMaxSpawnedProxies(int difficulty) + { + return this.GetDifficultyInt("spawn_num_max", difficulty, 0); + } -static void OnPlayerEscape(SF2_BasePlayer client) -{ - ClientResetProxy(client.index); -} + public float GetMinSpawnCooldown(int difficulty) + { + return this.GetDifficultyFloat("spawn_cooldown_min", difficulty, 4.0); + } -static Action Hook_ProxySoundHook(int clients[64], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags) -{ - if (!g_Enabled) + public float GetMaxSpawnCooldown(int difficulty) { - return Plugin_Continue; + return this.GetDifficultyFloat("spawn_cooldown_max", difficulty, 8.0); } - SF2_BasePlayer client = SF2_BasePlayer(entity); - if (!client.IsValid || !client.IsProxy) + public float GetMinTeleportRange(int difficulty) { - return Plugin_Continue; + return this.GetDifficultyFloat("spawn_range_min", difficulty, 500.0); } - int master = NPCGetFromUniqueID(client.ProxyMaster); - if (master == -1) + public float GetMaxTeleportRange(int difficulty) { - return Plugin_Continue; + return this.GetDifficultyFloat("spawn_range_max", difficulty, 3200.0); } - SF2BossProfileData data; - data = NPCGetProfileData(master); + public BossProfileProxyClass GetDefaultClassData() + { + ProfileObject obj = this.GetSection("classes"); + if (obj == null) + { + return null; + } + + return view_as(obj.GetSection("default")); + } - switch (channel) + public BossProfileProxyClass GetClassData(TFClassType class) { - case SNDCHAN_VOICE: + ProfileObject obj = this.GetSection("classes"); + if (obj == null) { - if (!data.ProxyAllowVoices) + return null; + } + + switch (class) + { + case TFClass_Scout: { - return Plugin_Handled; + return view_as(obj.GetSection("scout")); + } + case TFClass_Soldier: + { + return view_as(obj.GetSection("soldier")); + } + case TFClass_Pyro: + { + return view_as(obj.GetSection("pyro")); + } + case TFClass_DemoMan: + { + return view_as(obj.GetSection("demoman")); + } + case TFClass_Heavy: + { + return view_as(obj.GetSection("heavy")); + } + case TFClass_Engineer: + { + return view_as(obj.GetSection("engineer")); + } + case TFClass_Medic: + { + return view_as(obj.GetSection("medic")); + } + case TFClass_Sniper: + { + return view_as(obj.GetSection("sniper")); + } + case TFClass_Spy: + { + return view_as(obj.GetSection("spy")); } } + + return null; } - if (data.ProxyRobots) + public void Precache() { - switch (channel) + BossProfileProxyClass classData = this.GetDefaultClassData(); + if (classData != null) { - case SNDCHAN_VOICE: - { - if (StrContains(sample, "vo/", false) == -1) - { - return Plugin_Continue; - } + classData.Precache(); + } - ReplaceString(sample, sizeof(sample), "vo/", "vo/mvm/norm/", false); - ReplaceString(sample, sizeof(sample), ".wav", ".mp3", false); - char className[10], classMvM[15]; - TF2_GetClassName(client.Class, className, sizeof(className), true); - FormatEx(classMvM, sizeof(classMvM), "%s_mvm", className); - ReplaceString(sample, sizeof(sample), className, classMvM, false); - for (int i = 0; i < strlen(sample); i++) - { - sample[i] = CharToLower(sample[i]); - } - PrecacheSound(sample); - return Plugin_Changed; + for (int i = view_as(TFClass_Scout); i <= view_as(TFClass_Engineer); i++) + { + TFClassType class = view_as(i); + classData = this.GetClassData(class); + if (classData != null) + { + classData.Precache(); } - case SNDCHAN_BODY: + + for (int i2 = 0; i2 < Difficulty_Max; i2++) { - if (StrContains(sample, "player/footsteps/", false) == -1 || client.Class == TFClass_Medic) + int max = this.GetMaxProxies(i2); + if (classData.GetMax(i2) >= 0) { - return Plugin_Handled; + max += classData.GetMax(i2); + this.SetMaxProxies(i2, max); } - int random = GetRandomInt(1, 18); - pitch = GetRandomInt(95, 100); - FormatEx(sample, sizeof(sample), "mvm/player/footsteps/robostep_%s%i.wav", (random < 10) ? "0" : "", random); - EmitSoundToAll(sample, client.index, _, _, _, 0.25, pitch); - return Plugin_Changed; } } } - - return Plugin_Continue; } -void ClientResetProxy(int client, bool resetFull = true) +methodmap BossProfileProxyClass < ProfileObject { - #if defined DEBUG - if (g_DebugDetailConVar.IntValue > 2) + public BossProfileProxyClass GetDefaultClassData() { - DebugMessage("START ClientResetProxy(%d)", client); + BossProfileProxyData parent = view_as(this.Parent); + return parent.GetDefaultClassData(); } - #endif - bool oldProxy = g_PlayerProxy[client]; - if (IsValidClient(client) && oldProxy != g_PlayerProxy[client]) + public void GetModel(int difficulty, char[] buffer, int bufferSize, const char[] defaultValue = "") { - Call_StartForward(g_OnPlayerChangeProxyStatePFwd); - Call_PushCell(SF2_BasePlayer(client)); - Call_PushCell(false); - Call_Finish(); + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + def.GetModel(difficulty, buffer, bufferSize, defaultValue); + } + + this.GetDifficultyString("model", difficulty, buffer, bufferSize, buffer); + ReplaceString(buffer, bufferSize, "\\", "/", false); } - if (resetFull) + public int GetMax(int difficulty, int defValue = 8) { - g_PlayerProxy[client] = false; - g_PlayerProxyMaster[client] = -1; - } + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + defValue = def.GetDifficultyInt("max", difficulty, defValue); + } - g_PlayerProxyControl[client] = 0; - g_PlayerProxyControlTimer[client] = null; - g_PlayerProxyControlRate[client] = 0.0; - g_PlayerProxyVoiceTimer[client] = null; + return this.GetDifficultyInt("max", difficulty, defValue); + } - if (IsValidClient(client)) + public float GetWeight(int difficulty, float defValue = 1.0) { - if (oldProxy) + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) { - ClientStartProxyAvailableTimer(client); - - if (resetFull) - { - SetVariantString(""); - AcceptEntityInput(client, "SetCustomModel"); - } + defValue = def.GetDifficultyFloat("weight", difficulty, defValue); } + + return this.GetDifficultyFloat("weight", difficulty, defValue); } - #if defined DEBUG - if (g_DebugDetailConVar.IntValue > 2) + public int GetHealth(int difficulty, int defValue = 0) { - DebugMessage("END ClientResetProxy(%d)", client); + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + defValue = def.GetDifficultyInt("health", difficulty, defValue); + } + + return this.GetDifficultyInt("health", difficulty, defValue); } - #endif -} -void ClientStartProxyAvailableTimer(int client) -{ - g_PlayerProxyAvailable[client] = false; - float cooldown = g_PlayerProxyWaitTimeConVar.FloatValue; - if (g_InProxySurvivalRageMode) + public float GetWalkSpeed(int difficulty, float defValue = 150.0) { - cooldown -= 10.0; + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + defValue = def.GetDifficultyFloat("walkspeed", difficulty, defValue); + } + + return this.GetDifficultyFloat("walkspeed", difficulty, defValue); } - if (cooldown <= 0.0) + + public float GetRunSpeed(int difficulty, float defValue = 300.0) { - cooldown = 0.0; - } + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + defValue = def.GetDifficultyFloat("runspeed", difficulty, defValue); + } - g_PlayerProxyAvailableTimer[client] = CreateTimer(cooldown, Timer_ClientProxyAvailable, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); -} + return this.GetDifficultyFloat("runspeed", difficulty, defValue); + } -void ClientStartProxyForce(int client, int slenderID, const float pos[3], int spawnPoint) -{ - #if defined DEBUG - if (g_DebugDetailConVar.IntValue > 2) + public float GetDamageScale(int difficulty, float defValue = 1.0) { - DebugMessage("START ClientStartProxyForce(%d, %d, pos)", client, slenderID); + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + defValue = def.GetDifficultyFloat("damage_scale", difficulty, defValue); + } + + return this.GetDifficultyFloat("damage_scale", difficulty, defValue); } - #endif - g_PlayerProxyAskMaster[client] = slenderID; - for (int i = 0; i < 3; i++) + public float GetSelfDamageScale(int difficulty, float defValue = 1.0) { - g_PlayerProxyAskPosition[client][i] = pos[i]; - } - g_PlayerProxyAskSpawnPoint[client] = EnsureEntRef(spawnPoint); + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + defValue = def.GetDifficultyFloat("self_damage_scale", difficulty, defValue); + } - g_PlayerProxyAvailableCount[client] = 0; - g_PlayerProxyAvailableInForce[client] = true; - g_PlayerProxyAvailableTimer[client] = CreateTimer(1.0, Timer_ClientForceProxy, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); - TriggerTimer(g_PlayerProxyAvailableTimer[client], true); + return this.GetDifficultyFloat("self_damage_scale", difficulty, defValue); + } - #if defined DEBUG - if (g_DebugDetailConVar.IntValue > 2) + public float GetBackstabDamageScale(int difficulty, float defValue = 0.2) { - DebugMessage("END ClientStartProxyForce(%d, %d, pos)", client, slenderID); - } - #endif -} + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + defValue = def.GetDifficultyFloat("backstab_damage_scale", difficulty, defValue); + } -void ClientStopProxyForce(int client) -{ - g_PlayerProxyAvailableCount[client] = 0; - g_PlayerProxyAvailableInForce[client] = false; - g_PlayerProxyAvailableTimer[client] = null; -} + return this.GetDifficultyFloat("backstab_damage_scale", difficulty, defValue); + } -Action Timer_ClientForceProxy(Handle timer, any userid) -{ - int client = GetClientOfUserId(userid); - if (client <= 0) + public bool AllowVoices(bool defValue = false) { - return Plugin_Stop; + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + defValue = def.GetBool("allow_voices", defValue); + } + + return this.GetBool("allow_voices", defValue); } - if (timer != g_PlayerProxyAvailableTimer[client]) + public bool Zombies(bool defValue = false) { - return Plugin_Stop; + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + defValue = def.GetBool("zombie", defValue); + } + + return this.GetBool("zombie", defValue); } - if (!IsRoundEnding()) + public bool Robots(bool defValue = false) { - int bossIndex = NPCGetFromUniqueID(g_PlayerProxyAskMaster[client]); - if (bossIndex != -1) + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) { - int difficulty = GetLocalGlobalDifficulty(bossIndex); - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(bossIndex, profile, sizeof(profile)); + defValue = def.GetBool("robot", defValue); + } - int maxProxies = g_SlenderMaxProxies[bossIndex][difficulty]; - int numProxies = 0; + return this.GetBool("robot", defValue); + } - for (int i = 1; i <= MaxClients; i++) - { - if (!IsValidClient(i) || !g_PlayerEliminated[i]) - { - continue; - } - if (!g_PlayerProxy[i]) - { - continue; - } - if (NPCGetFromUniqueID(g_PlayerProxyMaster[i]) != bossIndex) - { - continue; - } + public float GetControlDrainRate(int difficulty, float defValue = 0.0) + { + float value = defValue; - numProxies++; - } + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + value = def.GetControlDrainRate(difficulty, value); + } - if (numProxies < maxProxies) - { - if (g_PlayerProxyAvailableCount[client] > 0) - { - g_PlayerProxyAvailableCount[client]--; + ProfileObject obj = this.GetSection("control"); + if (obj != null) + { + value = obj.GetDifficultyFloat("level", difficulty, value); + } - SetHudTextParams(-1.0, 0.25, - 1.0, - 255, 255, 255, 255, - _, - _, - 0.25, 1.25); + return value; + } - ShowSyncHudText(client, g_HudSync, "%T", "SF2 Proxy Force Message", client, g_PlayerProxyAvailableCount[client]); + public int GetControlGainHitEnemy(int difficulty, int defValue = 0) + { + int value = defValue; - return Plugin_Continue; - } - else - { - ClientEnableProxy(client, bossIndex, g_PlayerProxyAskPosition[client], g_PlayerProxyAskSpawnPoint[client]); - } - } - else - { - //PrintToChat(client, "%T", "SF2 Too Many Proxies", client); - } + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + value = def.GetControlGainHitEnemy(difficulty, value); } - } - ClientStopProxyForce(client); - return Plugin_Stop; -} + ProfileObject obj = this.GetSection("control"); + if (obj != null) + { + value = obj.GetDifficultyInt("hit_enemy", difficulty, value); + } -void DisplayProxyAskMenu(int client, int askMaster, const float pos[3], int spawnPoint) -{ - if (IsRoundEnding() || IsRoundInIntro() || IsRoundInWarmup()) - { - return; + return value; } - char buffer[512]; - Menu menu = new Menu(Menu_ProxyAsk); - menu.SetTitle("%T\n \n%T\n \n", "SF2 Proxy Ask Menu Title", client, "SF2 Proxy Ask Menu Description", client); - - FormatEx(buffer, sizeof(buffer), "%T", "Yes", client); - menu.AddItem("1", buffer); - FormatEx(buffer, sizeof(buffer), "%T", "No", client); - menu.AddItem("0", buffer); - g_PlayerProxyAskMaster[client] = askMaster; - for (int i = 0; i < 3; i++) + public int GetControlGainHitByEnemy(int difficulty, int defValue = 0) { - g_PlayerProxyAskPosition[client][i] = pos[i]; - } - g_PlayerProxyAskSpawnPoint[client] = EnsureEntRef(spawnPoint); + int value = defValue; - menu.Display(client, 15); -} - -int Menu_ProxyAsk(Menu menu, MenuAction action, int param1, int param2) -{ - switch (action) - { - case MenuAction_End: + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) { - delete menu; + value = def.GetControlGainHitByEnemy(difficulty, value); } - case MenuAction_Select: - { - if (!IsRoundEnding() && !IsRoundInIntro() && !IsRoundInWarmup()) - { - int bossIndex = NPCGetFromUniqueID(g_PlayerProxyAskMaster[param1]); - if (bossIndex != -1) - { - int difficulty = GetLocalGlobalDifficulty(bossIndex); - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(bossIndex, profile, sizeof(profile)); - - int maxProxies = g_SlenderMaxProxies[bossIndex][difficulty]; - int numProxies; - - for (int client = 1; client <= MaxClients; client++) - { - if (!IsValidClient(client) || !g_PlayerEliminated[client]) - { - continue; - } - if (!g_PlayerProxy[client]) - { - continue; - } - if (NPCGetFromUniqueID(g_PlayerProxyMaster[client]) != bossIndex) - { - continue; - } - numProxies++; - } + ProfileObject obj = this.GetSection("control"); + if (obj != null) + { + value = obj.GetDifficultyInt("hit_by_enemy", difficulty, value); + } - if (numProxies < maxProxies) - { - if (param2 == 0) - { - bool ignoreVisibility = false; - int spawnPointEnt = g_PlayerProxyAskSpawnPoint[param1]; - float spawnPos[3]; + return value; + } - if (IsValidEntity(spawnPointEnt)) - { - GetEntPropVector(spawnPointEnt, Prop_Data, "m_vecAbsOrigin", spawnPos); + public ProfileSound GetSpawnSounds() + { + ProfileSound value = null; - SF2PlayerProxySpawnEntity spawnPoint = SF2PlayerProxySpawnEntity(spawnPointEnt); - if (spawnPoint.IsValid()) - { - ignoreVisibility = spawnPoint.IgnoreVisibility; - } - } - else - { - for (int i = 0; i < 3; i++) - { - spawnPos[i] = g_PlayerProxyAskPosition[param1][i]; - } - } + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + value = def.GetSpawnSounds(); + } - if (ignoreVisibility || !IsPointVisibleToAPlayer(spawnPos, _, false)) - { - ClientEnableProxy(param1, bossIndex, spawnPos, spawnPointEnt); - } - else - { - CPrintToChat(param1, "%T", "SF2 Too Much Time", param1); - } - } - else - { - ClientStartProxyAvailableTimer(param1); - } - } - else - { - PrintToChat(param1, "%T", "SF2 Too Many Proxies", param1); - } - } - } + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + value = view_as(obj.GetSection("spawn")); } - } - return 0; -} -Action Timer_ClientProxyAvailable(Handle timer, any userid) -{ - int client = GetClientOfUserId(userid); - if (client <= 0) - { - return Plugin_Stop; + return value; } - if (timer != g_PlayerProxyAvailableTimer[client]) + public ProfileSound GetIdleSounds() { - return Plugin_Stop; - } + ProfileSound value = null; - g_PlayerProxyAvailable[client] = true; - g_PlayerProxyAvailableTimer[client] = null; + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + value = def.GetIdleSounds(); + } - return Plugin_Stop; -} + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + value = view_as(obj.GetSection("idle")); + } -/** - * Respawns a player as a proxy. - * - * @noreturn - */ -void ClientEnableProxy(int client, int bossIndex, const float pos[3], int spawnPointEnt=-1) -{ - if (NPCGetUniqueID(bossIndex) == -1) - { - return; + return value; } - if (!(NPCGetFlags(bossIndex) & SFF_PROXIES)) + public ProfileSound GetHurtSounds() { - return; - } + ProfileSound value = null; - if (GetClientTeam(client) != TFTeam_Blue) - { - return; + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + value = def.GetHurtSounds(); + } + + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + value = view_as(obj.GetSection("hurt")); + } + + return value; } - if (g_PlayerProxy[client]) + public ProfileSound GetDeathSounds() { - return; - } + ProfileSound value = null; - TF2_RemovePlayerDisguise(client); + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + value = def.GetDeathSounds(); + } - int difficulty = GetLocalGlobalDifficulty(bossIndex); + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + value = view_as(obj.GetSection("death")); + } - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(bossIndex, profile, sizeof(profile)); + return value; + } - ClientSetGhostModeState(client, false); + public KeyMap_Array GetDeathAnimations() + { + KeyMap_Array value = null; + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + value = def.GetArray("death_animations", value); + } - ClientStopProxyForce(client); + return this.GetArray("death_animations", value); + } - if (IsClientInKart(client)) + public BossProfileProxyWeapon GetWeapon(int weaponSlot) { - TF2_RemoveCondition(client, TFCond_HalloweenKart); - TF2_RemoveCondition(client, TFCond_HalloweenKartDash); - TF2_RemoveCondition(client, TFCond_HalloweenKartNoTurn); - TF2_RemoveCondition(client, TFCond_HalloweenKartCage); - } + ProfileObject value = null; - g_PlayerProxy[client] = true; + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) + { + value = def.GetWeapon(weaponSlot); + } - ChangeClientTeamNoSuicide(client, TFTeam_Blue); - PvP_SetPlayerPvPState(client, false, true, false); - PvE_SetPlayerPvEState(client, false, false); - TF2_RespawnPlayer(client); + ProfileObject obj = this.GetSection("weapons"); + if (obj != null) + { + value = obj.GetSection("death"); + switch (weaponSlot) + { + case TFWeaponSlot_Primary: + { + value = obj.GetSection("primary"); + } - // Speed recalculation. Props to the creators of FF2/VSH for this snippet. - TF2_AddCondition(client, TFCond_SpeedBuffAlly, 0.001); + case TFWeaponSlot_Secondary: + { + value = obj.GetSection("secondary"); + } - g_PlayerProxy[client] = true; + case TFWeaponSlot_Melee: + { + value = obj.GetSection("melee"); + } - g_PlayerProxyMaster[client] = NPCGetUniqueID(bossIndex); - g_PlayerProxyControl[client] = 100; - g_PlayerProxyControlRate[client] = g_SlenderProxyControlDrainRate[bossIndex][difficulty]; - g_PlayerProxyControlTimer[client] = CreateTimer(g_PlayerProxyControlRate[client], Timer_ClientProxyControl, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); - g_PlayerProxyAvailable[client] = false; - g_PlayerProxyAvailableTimer[client] = null; + case TFWeaponSlot_Grenade: // Yes, this is the fucking building PDA and the disguise kit + { + value = obj.GetSection("pda_build"); + value = obj.GetSection("disguise", value); + } + + case TFWeaponSlot_Building: // Yes, this is the fucking destruction PDA and the invis watch + { + value = obj.GetSection("pda_destroy"); + value = obj.GetSection("invis_watch", value); + } + } + } - char allowedClasses[512]; - GetBossProfileProxyClasses(profile, allowedClasses, sizeof(allowedClasses)); + return view_as(value); + } - char className[64]; - TF2_GetClassName(TF2_GetPlayerClass(client), className, sizeof(className)); - if (allowedClasses[0] != '\0' && className[0] != '\0' && StrContains(allowedClasses, className, false) == -1) + public KeyMap_Array GetSpawnEffects() { - // Pick the first class that's allowed. - char allowedClassesList[32][32]; - int classCount = ExplodeString(allowedClasses, " ", allowedClassesList, 32, 32); - if (classCount) + KeyMap_Array value = null; + BossProfileProxyClass def = this.GetDefaultClassData(); + if (def != null && def != this) { - TF2_SetPlayerClass(client, TF2_GetClass(allowedClassesList[0]), _, false); - - int maxHealth = GetEntProp(client, Prop_Send, "m_iHealth"); - TF2_RegeneratePlayer(client); - SetEntProp(client, Prop_Data, "m_iHealth", maxHealth); - SetEntProp(client, Prop_Send, "m_iHealth", maxHealth); + value = def.GetArray("spawn_effects", value); } + + return this.GetArray("spawn_effects", value); } - UTIL_ScreenFade(client, 200, 1, FFADE_IN, 255, 255, 255, 100); - EmitSoundToClient(client, "weapons/teleporter_send.wav", _, SNDCHAN_STATIC); + public void Precache() + { + char path[PLATFORM_MAX_PATH]; + for (int i = 0; i < Difficulty_Max; i++) + { + this.GetModel(i, path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheModel2(path, _, _, g_FileCheckConVar.BoolValue); + } + } - ClientActivateUltravision(client); + this.ConvertSectionsSectionToArray("spawn_effects"); + this.ConvertSectionsSectionToArray("death_animations"); - TF2Attrib_SetByDefIndex(client, 28, 1.0); + if (this.GetSpawnSounds() != null) + { + this.GetSpawnSounds().Precache(); + } - CreateTimer(0.33, Timer_ApplyCustomModel, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + if (this.GetIdleSounds() != null) + { + this.GetIdleSounds().Precache(); + } - if (NPCHasProxyWeapons(bossIndex)) - { - CreateTimer(1.0, Timer_GiveWeaponAll, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); - } + if (this.GetHurtSounds() != null) + { + this.GetHurtSounds().Precache(); + } - //SDKHook(client, SDKHook_ShouldCollide, Hook_ClientProxyShouldCollide); + if (this.GetDeathSounds() != null) + { + this.GetDeathSounds().Precache(); + } + } +} - SF2PlayerProxySpawnEntity spawnPoint = SF2PlayerProxySpawnEntity(spawnPointEnt); - if (spawnPoint.IsValid()) +methodmap BossProfileProxyWeapon < ProfileObject +{ + public void GetClassname(int difficulty, char[] buffer, int bufferSize) { - float spawnPos[3]; float ang[3]; - GetEntPropVector(spawnPointEnt, Prop_Data, "m_vecAbsOrigin", spawnPos); - GetEntPropVector(spawnPointEnt, Prop_Data, "m_angAbsRotation", ang); - TeleportEntity(client, spawnPos, ang, view_as({ 0.0, 0.0, 0.0 })); - spawnPoint.FireOutput("OnSpawn", client); + this.GetDifficultyString("classname", difficulty, buffer, bufferSize); } - else + + public int GetIndex(int difficulty) { - TeleportEntity(client, pos, NULL_VECTOR, view_as({ 0.0, 0.0, 0.0 })); + return this.GetDifficultyInt("index", difficulty, -1); } - if (NPCGetProxySpawnEffectState(bossIndex)) + public void GetAttributes(int difficulty, char[] buffer, int bufferSize) { - char spawnEffect[PLATFORM_MAX_PATH]; - GetBossProfileProxySpawnEffectName(profile, spawnEffect, sizeof(spawnEffect)); - CreateGeneralParticle(client, spawnEffect, NPCGetProxySpawnEffectZOffset(bossIndex)); + this.GetDifficultyString("stats", difficulty, buffer, bufferSize); } - Call_StartForward(g_OnClientSpawnedAsProxyFwd); - Call_PushCell(client); - Call_Finish(); - - Call_StartForward(g_OnPlayerChangeProxyStatePFwd); - Call_PushCell(SF2_BasePlayer(client)); - Call_PushCell(true); - Call_Finish(); -} - -static Action Timer_GiveWeaponAll(Handle timer, any userid) -{ - if (!g_Enabled) + public int GetLevel(int difficulty) { - return Plugin_Stop; + return this.GetDifficultyInt("level", difficulty, 0); } - int client = GetClientOfUserId(userid); - if (client <= 0) + public int GetQuality(int difficulty) { - return Plugin_Stop; + return this.GetDifficultyInt("quality", difficulty, 0); } - int bossIndex = NPCGetFromUniqueID(g_PlayerProxyMaster[client]); - - if (g_PlayerProxy[client] && bossIndex != -1) + public bool ShouldPreserveAttributes(int difficulty) { - if (!NPCHasProxyWeapons(bossIndex)) - { - return Plugin_Stop; - } - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(bossIndex, profile, sizeof(profile)); + return this.GetDifficultyBool("preserve_default_attributes", difficulty, true); + } +} - int weaponIndex, weaponSlot; - char weaponName[PLATFORM_MAX_PATH], weaponStats[PLATFORM_MAX_PATH]; - int classIndex = view_as(TF2_GetPlayerClass(client)); - classIndex--; - ArrayList weaponArray = GetBossProfileProxyWeaponClassNames(profile); - if (weaponArray == null) - { - return Plugin_Stop; - } - weaponArray.GetString(classIndex, weaponName, sizeof(weaponName)); - weaponArray = GetBossProfileProxyWeaponStats(profile); - if (weaponArray == null) - { - return Plugin_Stop; - } - weaponArray.GetString(classIndex, weaponStats, sizeof(weaponStats)); - weaponIndex = GetBossProfileProxyWeaponIndexes(profile, classIndex + 1); - weaponSlot = GetBossProfileProxyWeaponSlots(profile, classIndex + 1); +//Proxy model +static char g_ClientProxyModel[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; +static char g_ClientProxyModelHard[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; +static char g_ClientProxyModelInsane[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; +static char g_ClientProxyModelNightmare[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; +static char g_ClientProxyModelApollyon[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; - switch (weaponSlot) - { - case 0: - { - TF2_RemoveWeaponSlot(client, TFWeaponSlot_Primary); - } - case 1: - { - TF2_RemoveWeaponSlot(client, TFWeaponSlot_Secondary); - } - case 2: - { - TF2_RemoveWeaponSlot(client, TFWeaponSlot_Melee); - } - } - Handle weaponHandle = PrepareItemHandle(weaponName, weaponIndex, 0, 0, weaponStats); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - EquipPlayerWeapon(client, entity); - SetEntProp(entity, Prop_Send, "m_bValidatedAttachedEntity", 1); - } - return Plugin_Stop; +void SetupProxy() +{ + g_OnMapStartPFwd.AddFunction(null, MapStart); + g_OnPlayerPutInServerPFwd.AddFunction(null, OnPutInServer); + g_OnPlayerDisconnectedPFwd.AddFunction(null, OnDisconnected); + g_OnPlayerSpawnPFwd.AddFunction(null, OnPlayerSpawn); + g_OnPlayerDeathPFwd.AddFunction(null, OnPlayerDeath); + g_OnPlayerEscapePFwd.AddFunction(null, OnPlayerEscape); + g_OnPlayerTakeDamagePFwd.AddFunction(null, OnPlayerTakeDamage); + + AddNormalSoundHook(Hook_ProxySoundHook); } -bool Hook_ClientProxyShouldCollide(int ent, int collisiongroup, int contentsmask, bool originalResult) +static void MapStart() { - if (!g_Enabled || !g_PlayerProxy[ent] || IsClientInPvP(ent) || IsClientInPvE(ent)) - { - SDKUnhook(ent, SDKHook_ShouldCollide, Hook_ClientProxyShouldCollide); - return originalResult; - } - if ((contentsmask & MASK_RED)) + for (int i = 1; i <= 18; i++) { - return true; + char sound[PLATFORM_MAX_PATH]; + FormatEx(sound, sizeof(sound), "mvm/player/footsteps/robostep_%s%i.wav", (i < 10) ? "0" : "", i); + PrecacheSound(sound, true); } - //To-do add no collision proxy-boss here, the collision boss-proxy is done, see npc_chaser.sp - return originalResult; } -//RequestFrame// -void ProxyDeathAnimation(any client) + +static void OnPutInServer(SF2_BasePlayer client) { - if (client != -1) + if (!g_Enabled) { - if (g_ClientFrame[client] >= g_ClientMaxFrameDeathAnim[client]) - { - g_ClientFrame[client]--; - KillClient(client); - } - else - { - g_ClientFrame[client]++; - RequestFrame(ProxyDeathAnimation,client); - } + return; } + ClientResetProxy(client.index); + ClientStartProxyAvailableTimer(client.index); + SDKHook(client.index, SDKHook_PreThinkPost, ProxyThink); } -Action Timer_ClientProxyControl(Handle timer, any userid) +static void OnDisconnected(SF2_BasePlayer client) { - int client = GetClientOfUserId(userid); - if (client <= 0) + ClientStopProxyForce(client.index); +} + +static void OnPlayerSpawn(SF2_BasePlayer client) +{ + if (!g_Enabled) { - return Plugin_Stop; + return; } - if (timer != g_PlayerProxyControlTimer[client]) + ClientResetProxy(client.index); +} + +static void OnPlayerDeath(SF2_BasePlayer client) +{ + if (!g_Enabled) { - return Plugin_Stop; + return; } - g_PlayerProxyControl[client]--; - if (TF2_IsPlayerInCondition(client, TFCond_Taunting)) + if (!client.IsProxy) { - g_PlayerProxyControl[client] -= 5; + return; } - if (g_PlayerProxyControl[client] <= 0) + + // We're a proxy, so play some sounds. + + int proxyMaster = NPCGetFromUniqueID(g_PlayerProxyMaster[client.index]); + if (proxyMaster != -1) { - // ForcePlayerSuicide isn't really dependable, since the player doesn't suicide until several seconds after spawning has passed. - KillClient(client); - return Plugin_Stop; - } + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(proxyMaster, profile, sizeof(profile)); - g_PlayerProxyControlTimer[client] = CreateTimer(g_PlayerProxyControlRate[client], Timer_ClientProxyControl, userid, TIMER_FLAG_NO_MAPCHANGE); + BossProfileProxyData proxyData = GetBossProfile(profile).GetProxies(); + BossProfileProxyClass classData = proxyData.GetClassData(client.Class); + classData.GetDeathSounds().EmitSound(_, client.index); + } - return Plugin_Stop; + CreateTimer(0.1, Timer_ResetProxy, client.UserID, TIMER_FLAG_NO_MAPCHANGE); } -Action Timer_ApplyCustomModel(Handle timer, any userid) +static Action Timer_ResetProxy(Handle timer, any userid) { - int client = GetClientOfUserId(userid); - if (client <= 0) + SF2_BasePlayer client = SF2_BasePlayer(GetClientOfUserId(userid)); + if (!client.IsValid) { return Plugin_Stop; } - SetEntProp(client, Prop_Send, "m_iAirDash", 99999); + ClientResetProxy(client.index, true); + return Plugin_Stop; +} - int master = NPCGetFromUniqueID(g_PlayerProxyMaster[client]); +static void OnPlayerEscape(SF2_BasePlayer client) +{ + ClientResetProxy(client.index); +} - if (g_PlayerProxy[client] && master != -1) +static Action OnPlayerTakeDamage(SF2_BasePlayer client, int &attacker, int &inflictor, float &damage, int &damageType, int damageCustom) +{ + SF2_BasePlayer attackerPlayer = SF2_BasePlayer(attacker); + if (!attackerPlayer.IsValid) { - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(master, profile, sizeof(profile)); - - int difficulty = GetLocalGlobalDifficulty(master); - - // Set custom model, if any. - char buffer[PLATFORM_MAX_PATH]; - - TF2_RegeneratePlayer(client); + return Plugin_Continue; + } - char className[64]; - TFClassType playerClass = TF2_GetPlayerClass(client); - int classToInt = view_as(playerClass); - TF2_GetClassName(playerClass, className, sizeof(className)); + if (attackerPlayer.index == client.index) + { + return Plugin_Continue; + } - ArrayList modelsArray; + if (client.IsProxy || attackerPlayer.IsProxy) + { + if (client.IsEliminated && attackerPlayer.IsEliminated) + { + damage = 0.0; + return Plugin_Changed; + } - if (GetBossProfileProxyDifficultyModelsState(profile)) + BossProfileProxyData proxyData; + BossProfileProxyClass classData; + if (attackerPlayer.IsProxy) { - switch (difficulty) + int maxHealth = attackerPlayer.MaxHealth; + SF2NPC_BaseNPC master = SF2NPC_BaseNPC(attackerPlayer.ProxyMaster); + if (master.IsValid()) { - case Difficulty_Normal: + proxyData = master.GetProfileData().GetProxies(); + classData = proxyData.GetClassData(attackerPlayer.Class); + int difficulty = master.Difficulty; + + if (damageCustom == TF_CUSTOM_TAUNT_GRAND_SLAM || + damageCustom == TF_CUSTOM_TAUNT_FENCING || + damageCustom == TF_CUSTOM_TAUNT_ARROW_STAB || + damageCustom == TF_CUSTOM_TAUNT_GRENADE || + damageCustom == TF_CUSTOM_TAUNT_BARBARIAN_SWING || + damageCustom == TF_CUSTOM_TAUNT_ENGINEER_ARM || + damageCustom == TF_CUSTOM_TAUNT_ARMAGEDDON) { - modelsArray = GetBossProfileProxyModels(profile, classToInt, difficulty); - if (modelsArray == null) + if (damage >= float(maxHealth)) { - modelsArray = GetBossProfileProxyModels(profile, 0, difficulty); + damage = float(maxHealth) * 0.5; + } + else + { + damage = 0.0; } - modelsArray.GetString(GetRandomInt(0, modelsArray.Length - 1), buffer, sizeof(buffer)); - strcopy(g_ClientProxyModel[client],sizeof(g_ClientProxyModel[]),buffer); } - case Difficulty_Hard: + else if (damageCustom == TF_CUSTOM_BACKSTAB) // Modify backstab damage. { - modelsArray = GetBossProfileProxyModels(profile, classToInt, difficulty); - if (modelsArray == null) + damage = float(maxHealth) * classData.GetBackstabDamageScale(difficulty); + if (damageType & DMG_ACID) { - modelsArray = GetBossProfileProxyModels(profile, 0, difficulty); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 1); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, 1); - } - } + damage /= 2.0; } - modelsArray.GetString(GetRandomInt(0, modelsArray.Length - 1), buffer, sizeof(buffer)); - strcopy(g_ClientProxyModelHard[client],sizeof(g_ClientProxyModelHard[]),buffer); } - case Difficulty_Insane: + + attackerPlayer.ProxyControl += classData.GetControlGainHitEnemy(difficulty); + + float originalPercentage = classData.GetDamageScale(difficulty); + float additionPercentage = 0.15; + if (!IsClassConfigsValid()) { - modelsArray = GetBossProfileProxyModels(profile, classToInt, difficulty); - if (modelsArray == null) + if (client.Class == TFClass_Medic) { - modelsArray = GetBossProfileProxyModels(profile, 0, difficulty); - if (modelsArray == null) + damage *= (originalPercentage + additionPercentage); + } + else + { + damage *= originalPercentage; + } + } + else + { + damage *= originalPercentage + g_ClassProxyDamageVulnerability[view_as(client.Class)]; + } + } + return Plugin_Changed; + } + else if (client.IsProxy) + { + SF2NPC_BaseNPC master = SF2NPC_BaseNPC(client.ProxyMaster); + if (master.IsValid()) + { + proxyData = master.GetProfileData().GetProxies(); + classData = proxyData.GetClassData(client.Class); + int difficulty = master.Difficulty; + + client.ProxyControl += classData.GetControlGainHitByEnemy(difficulty); + + damage *= classData.GetSelfDamageScale(difficulty); + if (classData.GetHurtSounds() != null) + { + classData.GetHurtSounds().EmitSound(_, client.index); + } + + if (damage * (damageType & DMG_CRIT ? 3.0 : 1.0) >= float(client.Health) && !client.InCondition(TFCond_FreezeInput)) // The proxy is about to die + { + char buffer[PLATFORM_MAX_PATH]; + KeyMap_Array array = classData.GetDeathAnimations(); + ProfileAnimation animation = null; + if (array != null && array.Length > 0) + { + animation = view_as(array.GetSection(GetRandomInt(0, array.Length - 1))); + } + if (animation != null) + { + animation.GetAnimationName(difficulty, buffer, sizeof(buffer)); + if (buffer[0] != '\0') { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 2); - if (modelsArray == null) + g_ClientMaxFrameDeathAnim[client.index] = animation.GetDifficultyInt("frames", difficulty, 0); + if (g_ClientMaxFrameDeathAnim[client.index] > 0) { - modelsArray = GetBossProfileProxyModels(profile, 0, 2); - if (modelsArray == null) + // Cancel out any other taunts. + if (client.InCondition(TFCond_Taunting)) { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 1); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, 1); - } + client.ChangeCondition(TFCond_Taunting, true); } + //The model has a death anim play it. + SDK_PlaySpecificSequence(client.index, buffer); + g_ClientFrame[client.index] = 0; + RequestFrame(ProxyDeathAnimation, client.index); + client.ChangeCondition(TFCond_FreezeInput, _, 5.0); + //Prevent death, and show the damage to the attacker. + client.ChangeCondition(TFCond_PreventDeath, _, 0.5); + return Plugin_Changed; } } } - modelsArray.GetString(GetRandomInt(0, modelsArray.Length - 1), buffer, sizeof(buffer)); - strcopy(g_ClientProxyModelInsane[client],sizeof(g_ClientProxyModelInsane[]),buffer); + + // The player has no death anim leave him die. } - case Difficulty_Nightmare: - { - modelsArray = GetBossProfileProxyModels(profile, classToInt, difficulty); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, difficulty); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 3); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, 3); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 2); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, 2); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 1); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, 1); - } - } - } - } - } - } - } - modelsArray.GetString(GetRandomInt(0, modelsArray.Length - 1), buffer, sizeof(buffer)); - strcopy(g_ClientProxyModelNightmare[client],sizeof(g_ClientProxyModelNightmare[]),buffer); + } + return Plugin_Changed; + } + } + + return Plugin_Continue; +} + +static void ProxyThink(int client) +{ + SF2_BasePlayer player = SF2_BasePlayer(client); + if (!player.IsProxy) + { + return; + } + + SF2NPC_BaseNPC controller = SF2NPC_BaseNPC(player.ProxyMaster); + if (!controller.IsValid()) + { + return; + } + + int difficulty = controller.Difficulty; + TFClassType class = player.Class; + + BossProfileProxyData proxyData = controller.GetProfileData().GetProxies(); + BossProfileProxyClass classData = proxyData.GetClassData(class); + + float speed = classData.GetRunSpeed(difficulty); + if (player.InCondition(TFCond_SpeedBuffAlly) || g_InProxySurvivalRageMode) + { + speed += 30.0; + } + + player.SetPropFloat(Prop_Send, "m_flMaxspeed", speed); +} + +static Action Hook_ProxySoundHook(int clients[64], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags) +{ + if (!g_Enabled) + { + return Plugin_Continue; + } + + SF2_BasePlayer client = SF2_BasePlayer(entity); + if (!client.IsValid || !client.IsProxy) + { + return Plugin_Continue; + } + + int master = NPCGetFromUniqueID(client.ProxyMaster); + if (master == -1) + { + return Plugin_Continue; + } + + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(master, profile, sizeof(profile)); + BossProfileProxyData proxyData = GetBossProfile(profile).GetProxies(); + BossProfileProxyClass classData = proxyData.GetClassData(client.Class); + + switch (channel) + { + case SNDCHAN_VOICE: + { + if (!classData.AllowVoices()) + { + return Plugin_Handled; + } + } + } + + if (classData.Robots()) + { + switch (channel) + { + case SNDCHAN_VOICE: + { + if (StrContains(sample, "vo/", false) == -1) + { + return Plugin_Continue; } - case Difficulty_Apollyon: + + ReplaceString(sample, sizeof(sample), "vo/", "vo/mvm/norm/", false); + ReplaceString(sample, sizeof(sample), ".wav", ".mp3", false); + char className[10], classMvM[15]; + TF2_GetClassName(client.Class, className, sizeof(className), true); + FormatEx(classMvM, sizeof(classMvM), "%s_mvm", className); + ReplaceString(sample, sizeof(sample), className, classMvM, false); + for (int i = 0; i < strlen(sample); i++) { - modelsArray = GetBossProfileProxyModels(profile, classToInt, difficulty); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, difficulty); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 4); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, 4); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 3); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, 3); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 2); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, 2); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 1); - if (modelsArray == null) - { - modelsArray = GetBossProfileProxyModels(profile, 0, 1); - } - } - } - } - } - } - } - } - } - modelsArray.GetString(GetRandomInt(0, modelsArray.Length - 1), buffer, sizeof(buffer)); - strcopy(g_ClientProxyModelApollyon[client],sizeof(g_ClientProxyModelApollyon[]),buffer); + sample[i] = CharToLower(sample[i]); } + PrecacheSound(sample); + return Plugin_Changed; + } + case SNDCHAN_BODY: + { + if (StrContains(sample, "player/footsteps/", false) == -1 || client.Class == TFClass_Medic) + { + return Plugin_Handled; + } + int random = GetRandomInt(1, 18); + pitch = GetRandomInt(95, 100); + FormatEx(sample, sizeof(sample), "mvm/player/footsteps/robostep_%s%i.wav", (random < 10) ? "0" : "", random); + EmitSoundToAll(sample, client.index, _, _, _, 0.25, pitch); + return Plugin_Changed; } + } + } - if (buffer[0] != '\0') + return Plugin_Continue; +} + +void ClientResetProxy(int client, bool resetFull = true) +{ + #if defined DEBUG + if (g_DebugDetailConVar.IntValue > 2) + { + DebugMessage("START ClientResetProxy(%d)", client); + } + #endif + + bool oldProxy = g_PlayerProxy[client]; + if (IsValidClient(client) && oldProxy != g_PlayerProxy[client]) + { + Call_StartForward(g_OnPlayerChangeProxyStatePFwd); + Call_PushCell(SF2_BasePlayer(client)); + Call_PushCell(false); + Call_Finish(); + } + + if (resetFull) + { + g_PlayerProxy[client] = false; + g_PlayerProxyMaster[client] = -1; + } + + g_PlayerProxyControl[client] = 0; + g_PlayerProxyControlTimer[client] = null; + g_PlayerProxyControlRate[client] = 0.0; + g_PlayerProxyVoiceTimer[client] = null; + + if (IsValidClient(client)) + { + if (oldProxy) + { + ClientStartProxyAvailableTimer(client); + + if (resetFull) { - SetVariantString(buffer); + SetVariantString(""); AcceptEntityInput(client, "SetCustomModel"); - SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); - - CreateTimer(0.5, ClientCheckProxyModel, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); } } - else + } + + #if defined DEBUG + if (g_DebugDetailConVar.IntValue > 2) + { + DebugMessage("END ClientResetProxy(%d)", client); + } + #endif +} + +void ClientStartProxyAvailableTimer(int client) +{ + g_PlayerProxyAvailable[client] = false; + float cooldown = g_PlayerProxyWaitTimeConVar.FloatValue; + if (g_InProxySurvivalRageMode) + { + cooldown -= 10.0; + } + if (cooldown <= 0.0) + { + cooldown = 0.0; + } + + g_PlayerProxyAvailableTimer[client] = CreateTimer(cooldown, Timer_ClientProxyAvailable, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); +} + +void ClientStartProxyForce(int client, int slenderID, const float pos[3], int spawnPoint) +{ + #if defined DEBUG + if (g_DebugDetailConVar.IntValue > 2) + { + DebugMessage("START ClientStartProxyForce(%d, %d, pos)", client, slenderID); + } + #endif + + g_PlayerProxyAskMaster[client] = slenderID; + for (int i = 0; i < 3; i++) + { + g_PlayerProxyAskPosition[client][i] = pos[i]; + } + g_PlayerProxyAskSpawnPoint[client] = EnsureEntRef(spawnPoint); + + g_PlayerProxyAvailableCount[client] = 0; + g_PlayerProxyAvailableInForce[client] = true; + g_PlayerProxyAvailableTimer[client] = CreateTimer(1.0, Timer_ClientForceProxy, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + TriggerTimer(g_PlayerProxyAvailableTimer[client], true); + + #if defined DEBUG + if (g_DebugDetailConVar.IntValue > 2) + { + DebugMessage("END ClientStartProxyForce(%d, %d, pos)", client, slenderID); + } + #endif +} + +void ClientStopProxyForce(int client) +{ + g_PlayerProxyAvailableCount[client] = 0; + g_PlayerProxyAvailableInForce[client] = false; + g_PlayerProxyAvailableTimer[client] = null; +} + +Action Timer_ClientForceProxy(Handle timer, any userid) +{ + int client = GetClientOfUserId(userid); + if (client <= 0) + { + return Plugin_Stop; + } + + if (timer != g_PlayerProxyAvailableTimer[client]) + { + return Plugin_Stop; + } + + if (!IsRoundEnding()) + { + int bossIndex = NPCGetFromUniqueID(g_PlayerProxyAskMaster[client]); + if (bossIndex != -1) { - modelsArray = GetBossProfileProxyModels(profile, classToInt, 1); - if (modelsArray == null) + int difficulty = GetLocalGlobalDifficulty(bossIndex); + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(bossIndex, profile, sizeof(profile)); + + int maxProxies = GetBossProfile(profile).GetProxies().GetMaxProxies(difficulty); + int numProxies = 0; + + for (int i = 1; i <= MaxClients; i++) { - modelsArray = GetBossProfileProxyModels(profile, 0, 1); + if (!IsValidClient(i) || !g_PlayerEliminated[i]) + { + continue; + } + if (!g_PlayerProxy[i]) + { + continue; + } + if (NPCGetFromUniqueID(g_PlayerProxyMaster[i]) != bossIndex) + { + continue; + } + + numProxies++; } - if (modelsArray != null) + + if (numProxies < maxProxies) { - modelsArray.GetString(GetRandomInt(0, modelsArray.Length - 1), buffer, sizeof(buffer)); - if (buffer[0] != '\0') + if (g_PlayerProxyAvailableCount[client] > 0) { - SetVariantString(buffer); - AcceptEntityInput(client, "SetCustomModel"); - SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); - strcopy(g_ClientProxyModel[client],sizeof(g_ClientProxyModel[]),buffer); - strcopy(g_ClientProxyModelHard[client],sizeof(g_ClientProxyModelHard[]),buffer); - strcopy(g_ClientProxyModelInsane[client],sizeof(g_ClientProxyModelInsane[]),buffer); - strcopy(g_ClientProxyModelNightmare[client],sizeof(g_ClientProxyModelNightmare[]),buffer); - strcopy(g_ClientProxyModelApollyon[client],sizeof(g_ClientProxyModelApollyon[]),buffer); - //Prevent plugins like Model manager to override proxy model. - CreateTimer(0.5, ClientCheckProxyModel, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + g_PlayerProxyAvailableCount[client]--; + + SetHudTextParams(-1.0, 0.25, + 1.0, + 255, 255, 255, 255, + _, + _, + 0.25, 1.25); + + ShowSyncHudText(client, g_HudSync, "%T", "SF2 Proxy Force Message", client, g_PlayerProxyAvailableCount[client]); + + return Plugin_Continue; } + else + { + ClientEnableProxy(client, bossIndex, g_PlayerProxyAskPosition[client], g_PlayerProxyAskSpawnPoint[client]); + } + } + else + { + //PrintToChat(client, "%T", "SF2 Too Many Proxies", client); } } + } - if (IsPlayerAlive(client)) - { - g_PlayerProxyNextVoiceSound[client] = GetGameTime(); - SF2BossProfileSoundInfo soundInfo; - GetBossProfileProxySpawnSounds(profile, soundInfo); - // Play any sounds, if any. - soundInfo.EmitSound(_, client); + ClientStopProxyForce(client); + return Plugin_Stop; +} - bool zombie = GetBossProfileProxyZombiesState(profile); - if (zombie) +void DisplayProxyAskMenu(int client, int askMaster, const float pos[3], int spawnPoint) +{ + if (IsRoundEnding() || IsRoundInIntro() || IsRoundInWarmup()) + { + return; + } + char buffer[512]; + Menu menu = new Menu(Menu_ProxyAsk); + menu.SetTitle("%T\n \n%T\n \n", "SF2 Proxy Ask Menu Title", client, "SF2 Proxy Ask Menu Description", client); + + FormatEx(buffer, sizeof(buffer), "%T", "Yes", client); + menu.AddItem("1", buffer); + FormatEx(buffer, sizeof(buffer), "%T", "No", client); + menu.AddItem("0", buffer); + + g_PlayerProxyAskMaster[client] = askMaster; + for (int i = 0; i < 3; i++) + { + g_PlayerProxyAskPosition[client][i] = pos[i]; + } + g_PlayerProxyAskSpawnPoint[client] = EnsureEntRef(spawnPoint); + + menu.Display(client, 15); +} + +int Menu_ProxyAsk(Menu menu, MenuAction action, int param1, int param2) +{ + switch (action) + { + case MenuAction_End: + { + delete menu; + } + case MenuAction_Select: + { + if (!IsRoundEnding() && !IsRoundInIntro() && !IsRoundInWarmup()) { - int value = g_ForcedHolidayConVar.IntValue; - if (value != 9 && value != 2) - { - g_ForcedHolidayConVar.SetInt(9); //Full-Moon - } - int index; - TFClassType class = TF2_GetPlayerClass(client); - switch (class) + int bossIndex = NPCGetFromUniqueID(g_PlayerProxyAskMaster[param1]); + if (bossIndex != -1) { - case TFClass_Scout: - { - index = 5617; - } - case TFClass_Soldier: - { - index = 5618; - } - case TFClass_Pyro: - { - index = 5624; - } - case TFClass_DemoMan: - { - index = 5620; - } - case TFClass_Engineer: - { - index = 5621; - } - case TFClass_Heavy: - { - index = 5619; - } - case TFClass_Medic: + int difficulty = GetLocalGlobalDifficulty(bossIndex); + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(bossIndex, profile, sizeof(profile)); + + int maxProxies = GetBossProfile(profile).GetProxies().GetMaxProxies(difficulty); + int numProxies; + + for (int client = 1; client <= MaxClients; client++) { - index = 5622; + if (!IsValidClient(client) || !g_PlayerEliminated[client]) + { + continue; + } + if (!g_PlayerProxy[client]) + { + continue; + } + if (NPCGetFromUniqueID(g_PlayerProxyMaster[client]) != bossIndex) + { + continue; + } + + numProxies++; } - case TFClass_Sniper: + + if (numProxies < maxProxies) { - index = 5625; + if (param2 == 0) + { + bool ignoreVisibility = false; + int spawnPointEnt = g_PlayerProxyAskSpawnPoint[param1]; + float spawnPos[3]; + + if (IsValidEntity(spawnPointEnt)) + { + GetEntPropVector(spawnPointEnt, Prop_Data, "m_vecAbsOrigin", spawnPos); + + SF2PlayerProxySpawnEntity spawnPoint = SF2PlayerProxySpawnEntity(spawnPointEnt); + if (spawnPoint.IsValid()) + { + ignoreVisibility = spawnPoint.IgnoreVisibility; + } + } + else + { + for (int i = 0; i < 3; i++) + { + spawnPos[i] = g_PlayerProxyAskPosition[param1][i]; + } + } + + if (ignoreVisibility || !IsPointVisibleToAPlayer(spawnPos, _, false)) + { + ClientEnableProxy(param1, bossIndex, spawnPos, spawnPointEnt); + } + else + { + CPrintToChat(param1, "%T", "SF2 Too Much Time", param1); + } + } + else + { + ClientStartProxyAvailableTimer(param1); + } } - case TFClass_Spy: + else { - index = 5623; + PrintToChat(param1, "%T", "SF2 Too Many Proxies", param1); } } - Handle zombieSoul = PrepareItemHandle("tf_wearable", index, 100, 7,"448 ; 1.0 ; 450 ; 1"); - int entity = TF2Items_GiveNamedItem(client, zombieSoul); - delete zombieSoul; - zombieSoul = null; - if (IsValidEdict(entity)) - { - SDK_EquipWearable(client, entity); - } - if (TF2_GetPlayerClass(client) == TFClass_Spy) - { - SetEntProp(client, Prop_Send, "m_nForcedSkin", 23); - } - else - { - SetEntProp(client, Prop_Send, "m_nForcedSkin", 5); - } } - - ClientSwitchToWeaponSlot(client, TFWeaponSlot_Melee); } } - return Plugin_Stop; + return 0; } -Action ClientCheckProxyModel(Handle timer, any userid) +Action Timer_ClientProxyAvailable(Handle timer, any userid) { int client = GetClientOfUserId(userid); if (client <= 0) { return Plugin_Stop; } - if (!IsValidClient(client)) - { - return Plugin_Stop; - } - if (!IsPlayerAlive(client)) - { - return Plugin_Stop; - } - if (!g_PlayerProxy[client]) + + if (timer != g_PlayerProxyAvailableTimer[client]) { return Plugin_Stop; } - int difficulty = g_DifficultyConVar.IntValue; - char model[PLATFORM_MAX_PATH]; - GetEntPropString(client, Prop_Data, "m_ModelName", model, sizeof(model)); - switch (difficulty) + g_PlayerProxyAvailable[client] = true; + g_PlayerProxyAvailableTimer[client] = null; + + return Plugin_Stop; +} + +static TFClassType SelectProxyClass(SF2_BasePlayer client) +{ + SF2NPC_BaseNPC controller = SF2NPC_BaseNPC(client.ProxyMaster); + int difficulty = controller.Difficulty; + + BossProfileProxyData proxyData = controller.GetProfileData().GetProxies(); + ArrayList allowed = new ArrayList(); + + int classCount[10] = { 0, ... }; + for (int otherIndex = 1; otherIndex <= MaxClients; otherIndex++) { - case Difficulty_Normal: - { - if (strcmp(model, g_ClientProxyModel[client]) != 0 && g_ClientProxyModel[client][0] != '\0') - { - SetVariantString(g_ClientProxyModel[client]); - AcceptEntityInput(client, "SetCustomModel"); - SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); - } - } - case Difficulty_Hard: + SF2_BasePlayer other = SF2_BasePlayer(otherIndex); + if (other == client || !other.IsValid) { - if (strcmp(model, g_ClientProxyModelHard[client]) != 0 && g_ClientProxyModelHard[client][0] != '\0') - { - SetVariantString(g_ClientProxyModelHard[client]); - AcceptEntityInput(client, "SetCustomModel"); - SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); - } + continue; } - case Difficulty_Insane: + + if (other.IsProxy && other.ProxyMaster == controller.Index) { - if (strcmp(model, g_ClientProxyModelInsane[client]) != 0 && g_ClientProxyModelInsane[client][0] != '\0') - { - SetVariantString(g_ClientProxyModelInsane[client]); - AcceptEntityInput(client, "SetCustomModel"); - SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); - } + classCount[view_as(other.Class)]++; } - case Difficulty_Nightmare: + } + + for (int classIndex = view_as(TFClass_Scout); classIndex <= view_as(TFClass_Engineer); classIndex++) + { + if (proxyData.GetClassData(view_as(classIndex)) == null) { - if (strcmp(model, g_ClientProxyModelNightmare[client]) != 0 && g_ClientProxyModelNightmare[client][0] != '\0') - { - SetVariantString(g_ClientProxyModelNightmare[client]); - AcceptEntityInput(client, "SetCustomModel"); - SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); - } + continue; } - case Difficulty_Apollyon: + + allowed.Push(view_as(classIndex)); + } + + if (allowed.Length == 0) + { + // If no classes specified then assume all classes. + // We really don't have a choice here anyway. + allowed.Push(TFClass_Scout); + allowed.Push(TFClass_Sniper); + allowed.Push(TFClass_Soldier); + allowed.Push(TFClass_DemoMan); + allowed.Push(TFClass_Medic); + allowed.Push(TFClass_Heavy); + allowed.Push(TFClass_Pyro); + allowed.Push(TFClass_Spy); + allowed.Push(TFClass_Engineer); + } + + float maxWeight = 0.0; + for (int i = 0; i < allowed.Length; i++) + { + TFClassType tfClass = allowed.Get(i); + BossProfileProxyClass classData = proxyData.GetClassData(tfClass); + + int classLimit = classData.GetMax(difficulty); + if (classCount[view_as(tfClass)] < classLimit) { - if (strcmp(model, g_ClientProxyModelApollyon[client]) != 0 && g_ClientProxyModelApollyon[client][0] != '\0') - { - SetVariantString(g_ClientProxyModelApollyon[client]); - AcceptEntityInput(client, "SetCustomModel"); - SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); - } + allowed.Erase(i); + i--; + continue; } + maxWeight += classData.GetWeight(difficulty); } - return Plugin_Continue; -} -void SF2_RefreshRestrictions() -{ - for(int client = 1; client <= MaxClients; client++) + float randomWeight = GetRandomFloat(0.0, maxWeight); + TFClassType newClass = client.Class; + + for (int i = 0; i < allowed.Length; i++) { - if (IsValidClient(client) && (!g_PlayerEliminated[client] || (g_PlayerEliminated[client] && !IsClientInPvP(client) && !IsClientInPvE(client)))) + TFClassType tfClass = allowed.Get(i); + BossProfileProxyClass classData = proxyData.GetClassData(tfClass); + + float weight = classData.GetWeight(difficulty); + if (weight <= 0.0) { - ClientSwitchToWeaponSlot(client, TFWeaponSlot_Melee); - g_PlayerPostWeaponsTimer[client] = CreateTimer(1.0, Timer_ClientPostWeapons, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + continue; + } + + randomWeight -= weight; + if (randomWeight >= 0.0) + { + continue; } + + newClass = tfClass; + break; } + + delete allowed; + return newClass; } -Action Timer_ClientPostWeapons(Handle timer, any userid) +/** + * Respawns a player as a proxy. + * + * @noreturn + */ +void ClientEnableProxy(int clientIndex, int bossIndex, const float pos[3], int spawnPointEnt=-1) { - int client = GetClientOfUserId(userid); - if (client <= 0) + SF2_BasePlayer client = SF2_BasePlayer(clientIndex); + if (NPCGetUniqueID(bossIndex) == -1) { - return Plugin_Stop; + return; } - if (IsValidClient(client) && !IsPlayerAlive(client)) + if (!(NPCGetFlags(bossIndex) & SFF_PROXIES)) { - return Plugin_Stop; + return; } - if (!IsValidClient(client)) + if (client.Team != TFTeam_Blue) { - return Plugin_Stop; + return; } - if (timer != g_PlayerPostWeaponsTimer[client]) + if (client.IsProxy) { - return Plugin_Stop; + return; } - g_PlayerHasRegenerationItem[client] = false; + TF2_RemovePlayerDisguise(client.index); - #if defined DEBUG - if (g_DebugDetailConVar.IntValue > 0) + int difficulty = GetLocalGlobalDifficulty(bossIndex); + + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(bossIndex, profile, sizeof(profile)); + BossProfileProxyData proxyData = GetBossProfile(profile).GetProxies(); + TFClassType class = SelectProxyClass(client); + BossProfileProxyClass classData = proxyData.GetClassData(class); + + client.SetGhostState(false); + + ClientStopProxyForce(client.index); + + if (IsClientInKart(client.index)) { - DebugMessage("START Timer_ClientPostWeapons(%d)", client); + client.ChangeCondition(TFCond_HalloweenKart, true); + client.ChangeCondition(TFCond_HalloweenKartDash, true); + client.ChangeCondition(TFCond_HalloweenKartNoTurn, true); + client.ChangeCondition(TFCond_HalloweenKartCage, true); } - int oldWeaponItemIndexes[6] = { -1, ... }; - int newWeaponItemIndexes[6] = { -1, ... }; + client.IsProxy = true; - for (int i = 0; i <= 5; i++) - { - if (IsValidClient(client)) - { - int weaponEnt = GetPlayerWeaponSlot(client, i); - if (!IsValidEdict(weaponEnt)) - { - continue; - } + ChangeClientTeamNoSuicide(client.index, TFTeam_Blue); + PvP_SetPlayerPvPState(client.index, false, true, false); + PvE_SetPlayerPvEState(client.index, false, false); + client.Respawn(); - oldWeaponItemIndexes[i] = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); - } + // Speed recalculation. Props to the creators of FF2/VSH for this snippet. + client.ChangeCondition(TFCond_SpeedBuffAlly, _, 0.001); + + client.ProxyMaster = NPCGetUniqueID(bossIndex); + client.ProxyControl = 100; + g_PlayerProxyControlRate[client.index] = classData.GetControlDrainRate(difficulty); + g_PlayerProxyControlTimer[client.index] = CreateTimer(g_PlayerProxyControlRate[client.index], Timer_ClientProxyControl, client.UserID, TIMER_FLAG_NO_MAPCHANGE); + g_PlayerProxyAvailable[client.index] = false; + g_PlayerProxyAvailableTimer[client.index] = null; + + int health = classData.GetHealth(difficulty); + if (health <= 0) + { + health = client.Health; } - #endif - bool removeWeapons = true; - bool keepUtilityItems = false; - bool restrictWeapons = true; - bool useStock = false; - bool removeWearables = false; - bool preventAttack = false; + client.Regenerate(); + client.Health = health; + + client.ScreenFade(200, 1, FFADE_IN, 255, 255, 255, 100); + EmitSoundToClient(client.index, "weapons/teleporter_send.wav", _, SNDCHAN_STATIC); + + ClientActivateUltravision(client.index); + + TF2Attrib_SetByDefIndex(client.index, 28, 1.0); - if (IsRoundEnding()) + char path[PLATFORM_MAX_PATH]; + classData.GetModel(difficulty, path, sizeof(path)); + SetVariantString(path); + client.AcceptInput("SetCustomModel"); + client.SetProp(Prop_Send, "m_bUseClassAnimations", true); + + /*if (proxyData.CustomWeapons) { - if (!g_PlayerEliminated[client]) - { - removeWeapons = false; - restrictWeapons = false; - } + CreateTimer(1.0, Timer_GiveWeaponAll, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + }*/ + + //SDKHook(client, SDKHook_ShouldCollide, Hook_ClientProxyShouldCollide); + + SF2PlayerProxySpawnEntity spawnPoint = SF2PlayerProxySpawnEntity(spawnPointEnt); + if (spawnPoint.IsValid()) + { + float spawnPos[3]; float ang[3]; + GetEntPropVector(spawnPointEnt, Prop_Data, "m_vecAbsOrigin", spawnPos); + GetEntPropVector(spawnPointEnt, Prop_Data, "m_angAbsRotation", ang); + TeleportEntity(client.index, spawnPos, ang, view_as({ 0.0, 0.0, 0.0 })); + spawnPoint.FireOutput("OnSpawn", client.index); + } + else + { + TeleportEntity(client.index, pos, NULL_VECTOR, view_as({ 0.0, 0.0, 0.0 })); } - if (g_PlayerEliminated[client] && g_PlayerKeepWeaponsConVar.BoolValue) + /*if (proxyData.SpawnEffect) + { + char spawnEffect[PLATFORM_MAX_PATH]; + proxyData.GetSpawnEffectName(spawnEffect, sizeof(spawnEffect)); + CreateGeneralParticle(client, spawnEffect, proxyData.SpawnEffectZOffset); + }*/ + + Call_StartForward(g_OnClientSpawnedAsProxyFwd); + Call_PushCell(client); + Call_Finish(); + + Call_StartForward(g_OnPlayerChangeProxyStatePFwd); + Call_PushCell(client); + Call_PushCell(true); + Call_Finish(); +} + +static Action Timer_GiveWeaponAll(Handle timer, any userid) +{ + if (!g_Enabled) { - removeWeapons = false; - restrictWeapons = false; - preventAttack = true; + return Plugin_Stop; } - // pvp - if (IsClientInPvP(client)) + int client = GetClientOfUserId(userid); + if (client <= 0) { - removeWeapons = false; - restrictWeapons = false; - keepUtilityItems = false; - preventAttack = false; + return Plugin_Stop; } - if (IsClientInPvE(client)) + /*int bossIndex = NPCGetFromUniqueID(g_PlayerProxyMaster[client]); + + if (g_PlayerProxy[client] && bossIndex != -1) { - if (IsPvEBoxing()) + BossProfileProxyData proxyData = SF2NPC_BaseNPC(bossIndex).GetProfileData().GetProxies(); + if (!proxyData.CustomWeapons) { - restrictWeapons = false; - keepUtilityItems = true; - preventAttack = false; + return Plugin_Stop; } - else + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(bossIndex, profile, sizeof(profile)); + BossProfileProxyClass classData = proxyData.GetClassData(); + + int weaponIndex, weaponSlot; + char weaponName[PLATFORM_MAX_PATH], weaponStats[PLATFORM_MAX_PATH]; + TFClassType class = TF2_GetPlayerClass(client); + classData.GetWeaponClassname(class, weaponName, sizeof(weaponName)); + if (weaponName[0] == '\0') { - removeWeapons = false; - restrictWeapons = false; - keepUtilityItems = false; - preventAttack = false; + return Plugin_Stop; } - } + classData.GetWeaponClassname(class, weaponStats, sizeof(weaponStats)); + weaponIndex = classData.GetWeaponIndex(class); + weaponSlot = classData.GetWeaponSlot(class); + + switch (weaponSlot) + { + case 0: + { + TF2_RemoveWeaponSlot(client, TFWeaponSlot_Primary); + } + case 1: + { + TF2_RemoveWeaponSlot(client, TFWeaponSlot_Secondary); + } + case 2: + { + TF2_RemoveWeaponSlot(client, TFWeaponSlot_Melee); + } + } + Handle weaponHandle = PrepareItemHandle(weaponName, weaponIndex, 0, 0, weaponStats); + int entity = TF2Items_GiveNamedItem(client, weaponHandle); + delete weaponHandle; + EquipPlayerWeapon(client, entity); + SetEntProp(entity, Prop_Send, "m_bValidatedAttachedEntity", 1); + }*/ + return Plugin_Stop; +} - if (g_PlayerProxy[client]) +//RequestFrame// +void ProxyDeathAnimation(any client) +{ + if (client != -1) { - restrictWeapons = true; - removeWeapons = true; - useStock = true; - removeWearables = true; - keepUtilityItems = false; + if (g_ClientFrame[client] >= g_ClientMaxFrameDeathAnim[client]) + { + g_ClientFrame[client]--; + KillClient(client); + } + else + { + g_ClientFrame[client]++; + RequestFrame(ProxyDeathAnimation,client); + } } +} - if (IsRoundInWarmup()) +Action Timer_ClientProxyControl(Handle timer, any userid) +{ + int client = GetClientOfUserId(userid); + if (client <= 0) { - removeWeapons = false; - restrictWeapons = false; - keepUtilityItems = false; - preventAttack = false; + return Plugin_Stop; } - if (IsClientInGhostMode(client)) + if (timer != g_PlayerProxyControlTimer[client]) { - removeWeapons = true; + return Plugin_Stop; } - if (SF_IsRaidMap() && !g_PlayerEliminated[client]) + g_PlayerProxyControl[client]--; + if (TF2_IsPlayerInCondition(client, TFCond_Taunting)) { - removeWeapons = false; - restrictWeapons = false; - keepUtilityItems = false; - preventAttack = false; + g_PlayerProxyControl[client] -= 5; } - - if (SF_IsBoxingMap() && !g_PlayerEliminated[client] && !IsRoundEnding()) + if (g_PlayerProxyControl[client] <= 0) { - restrictWeapons = false; - keepUtilityItems = true; - preventAttack = false; + // ForcePlayerSuicide isn't really dependable, since the player doesn't suicide until several seconds after spawning has passed. + KillClient(client); + return Plugin_Stop; } - if (removeWeapons && !keepUtilityItems) - { - for (int i = 0; i <= 5; i++) - { - if (i == TFWeaponSlot_Melee && !IsClientInGhostMode(client)) - { - continue; - } - TF2_RemoveWeaponSlotAndWearables(client, i); - } - - int ent = -1; - while ((ent = FindEntityByClassname(ent, "tf_weapon_builder")) != -1) - { - if (GetEntPropEnt(ent, Prop_Send, "m_hOwnerEntity") == client) - { - RemoveEntity(ent); - } - } - - ent = -1; - while ((ent = FindEntityByClassname(ent, "tf_wearable_demoshield")) != -1) - { - if (GetEntPropEnt(ent, Prop_Send, "m_hOwnerEntity") == client) - { - RemoveEntity(ent); - } - } + g_PlayerProxyControlTimer[client] = CreateTimer(g_PlayerProxyControlRate[client], Timer_ClientProxyControl, userid, TIMER_FLAG_NO_MAPCHANGE); - ClientSwitchToWeaponSlot(client, TFWeaponSlot_Melee); - } + return Plugin_Stop; +} - if (keepUtilityItems) +Action Timer_ApplyCustomModel(Handle timer, any userid) +{ + int client = GetClientOfUserId(userid); + if (client <= 0) { - for (int i = 0; i <= 5; i++) - { - if ((i == TFWeaponSlot_Melee || i == TFWeaponSlot_Secondary) && !IsClientInGhostMode(client)) - { - continue; - } - TF2_RemoveWeaponSlotAndWearables(client, i); - } - - int ent = -1; - while ((ent = FindEntityByClassname(ent, "tf_weapon_builder")) != -1) - { - if (GetEntPropEnt(ent, Prop_Send, "m_hOwnerEntity") == client) - { - RemoveEntity(ent); - } - } - - int weaponEnt = INVALID_ENT_REFERENCE; - weaponEnt = GetPlayerWeaponSlot(client, TFWeaponSlot_Secondary); + return Plugin_Stop; + } - if (IsValidEdict(weaponEnt)) - { - int itemIndex = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); - switch (itemIndex) - { - case 163, 129, 226, 354, 1001, 131, 406, 1099, 42, 159, 311, 433, 863, 1002, 1190: - { - //Do nothing - } - default: - { - TF2_RemoveWeaponSlotAndWearables(client, TFWeaponSlot_Secondary); - } - } - } + SetEntProp(client, Prop_Send, "m_iAirDash", 99999); - ClientSwitchToWeaponSlot(client, TFWeaponSlot_Melee); - } + /*int master = NPCGetFromUniqueID(g_PlayerProxyMaster[client]); - if (removeWearables) + if (g_PlayerProxy[client] && master != -1) { - TF2_StripWearables(client); - } + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(master, profile, sizeof(profile)); + BossProfileProxyData proxyData = GetBossProfile(profile).GetProxies(); + BossProfileProxyClass classData = proxyData.GetClassData(); - TFClassType playerClass = TF2_GetPlayerClass(client); - int classToInt = view_as(playerClass); + int difficulty = GetLocalGlobalDifficulty(master); - if (SF_SpecialRound(SPECIALROUND_THANATOPHOBIA)) - { - int ent = -1; - while ((ent = FindEntityByClassname(ent, "tf_wearable")) != -1) - { - int itemIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + // Set custom model, if any. + char buffer[PLATFORM_MAX_PATH]; - if (642 == itemIndex) - { - if (GetEntPropEnt(ent, Prop_Send, "m_hOwnerEntity") == client) - { - RemoveEntity(ent); - } - } - } - } + TF2_RegeneratePlayer(client); - if (restrictWeapons) - { - int health = GetEntProp(client, Prop_Send, "m_iHealth"); + char className[64], keyName[64]; + TFClassType playerClass = TF2_GetPlayerClass(client); + TF2_GetClassName(playerClass, className, sizeof(className)); - int ent = -1; - while ((ent = FindEntityByClassname(ent, "tf_wearable")) != -1) - { - int itemIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + ProfileObject modelsArray; - for (int i = 0; i < sizeof(g_ActionItemIndexes); i++) + if (proxyData.DifficultyModels) + { + switch (difficulty) { - if (g_ActionItemIndexes[i] == itemIndex) + case Difficulty_Normal: { - if (GetEntPropEnt(ent, Prop_Send, "m_hOwnerEntity") == client) + modelsArray = classData.GetModel(playerClass, difficulty); + if (modelsArray == null) { - RemoveEntity(ent); + modelsArray = proxyData.GetUniversalModel(difficulty); } + modelsArray.GetKeyNameFromIndex(GetRandomInt(0, modelsArray.Size - 1), keyName, sizeof(keyName)); + modelsArray.GetString(keyName, buffer, sizeof(buffer)); + strcopy(g_ClientProxyModel[client], sizeof(g_ClientProxyModel[]), buffer); } - } - } - - ent = -1; - while ((ent = FindEntityByClassname(ent, "tf_wearable_razorback")) != -1) - { - if (GetEntPropEnt(ent, Prop_Send, "m_hOwnerEntity") == client) - { - RemoveEntity(ent); - } - } - - if (g_RestrictedWeaponsConfig != null) - { - int weaponEnt = INVALID_ENT_REFERENCE; - for (int slot = 0; slot <= 5; slot++) - { - Handle itemHandle = null; - weaponEnt = GetPlayerWeaponSlot(client, slot); - - if (IsValidEdict(weaponEnt)) + case Difficulty_Hard: { - if (useStock || IsWeaponRestricted(playerClass, GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"))) + modelsArray = classData.GetModel(playerClass, difficulty); + if (modelsArray == null) { - TF2_RemoveWeaponSlotAndWearables(client, slot); - switch (slot) + modelsArray = proxyData.GetUniversalModel(difficulty); + if (modelsArray == null) { - case TFWeaponSlot_Primary: + modelsArray = classData.GetModel(playerClass, 1); + if (modelsArray == null) { - switch (playerClass) - { - case TFClass_Scout: - { - itemHandle = PrepareItemHandle("tf_weapon_scattergun", 13, 0, 0, ""); - } - case TFClass_Sniper: - { - itemHandle = PrepareItemHandle("tf_weapon_sniperrifle", 14, 0, 0, ""); - } - case TFClass_Soldier: - { - itemHandle = PrepareItemHandle("tf_weapon_rocketlauncher", 18, 0, 0, ""); - } - case TFClass_DemoMan: - { - itemHandle = PrepareItemHandle("tf_weapon_grenadelauncher", 19, 0, 0, ""); - } - case TFClass_Heavy: - { - itemHandle = PrepareItemHandle("tf_weapon_minigun", 15, 0, 0, ""); - } - case TFClass_Medic: - { - itemHandle = PrepareItemHandle("tf_weapon_syringegun_medic", 17, 0, 0, ""); - } - case TFClass_Pyro: - { - itemHandle = PrepareItemHandle("tf_weapon_flamethrower", 21, 0, 0, "254 ; 4.0"); - } - case TFClass_Spy: - { - itemHandle = PrepareItemHandle("tf_weapon_revolver", 24, 0, 0, ""); - } - case TFClass_Engineer: - { - itemHandle = PrepareItemHandle("tf_weapon_shotgun", 9, 0, 0, ""); - } - } + modelsArray = proxyData.GetUniversalModel(1); } - case TFWeaponSlot_Secondary: + } + } + modelsArray.GetKeyNameFromIndex(GetRandomInt(0, modelsArray.Size - 1), keyName, sizeof(keyName)); + modelsArray.GetString(keyName, buffer, sizeof(buffer)); + strcopy(g_ClientProxyModelHard[client], sizeof(g_ClientProxyModelHard[]), buffer); + } + case Difficulty_Insane: + { + modelsArray = classData.GetModel(playerClass, difficulty); + if (modelsArray == null) + { + modelsArray = proxyData.GetUniversalModel(difficulty); + if (modelsArray == null) + { + modelsArray = classData.GetModel(playerClass, 2); + if (modelsArray == null) { - switch (playerClass) + modelsArray = proxyData.GetUniversalModel(2); + if (modelsArray == null) { - case TFClass_Scout: - { - itemHandle = PrepareItemHandle("tf_weapon_pistol", 23, 0, 0, ""); - } - case TFClass_Sniper: - { - itemHandle = PrepareItemHandle("tf_weapon_smg", 16, 0, 0, ""); - } - case TFClass_Soldier: - { - itemHandle = PrepareItemHandle("tf_weapon_shotgun", 10, 0, 0, ""); - } - case TFClass_DemoMan: - { - itemHandle = PrepareItemHandle("tf_weapon_pipebomblauncher", 20, 0, 0, ""); - } - case TFClass_Heavy: - { - itemHandle = PrepareItemHandle("tf_weapon_shotgun", 11, 0, 0, ""); - } - case TFClass_Medic: - { - itemHandle = PrepareItemHandle("tf_weapon_medigun", 29, 0, 0, ""); - } - case TFClass_Pyro: - { - itemHandle = PrepareItemHandle("tf_weapon_shotgun", 12, 0, 0, ""); - } - case TFClass_Engineer: + modelsArray = classData.GetModel(playerClass, 1); + if (modelsArray == null) { - itemHandle = PrepareItemHandle("tf_weapon_pistol", 22, 0, 0, ""); + modelsArray = proxyData.GetUniversalModel(1); } } } - case TFWeaponSlot_Melee: + } + } + modelsArray.GetKeyNameFromIndex(GetRandomInt(0, modelsArray.Size - 1), keyName, sizeof(keyName)); + modelsArray.GetString(keyName, buffer, sizeof(buffer)); + strcopy(g_ClientProxyModelInsane[client], sizeof(g_ClientProxyModelInsane[]), buffer); + } + case Difficulty_Nightmare: + { + modelsArray = classData.GetModel(playerClass, difficulty); + if (modelsArray == null) + { + modelsArray = proxyData.GetUniversalModel(difficulty); + if (modelsArray == null) + { + modelsArray = classData.GetModel(playerClass, 3); + if (modelsArray == null) { - switch (playerClass) + modelsArray = proxyData.GetUniversalModel(3); + if (modelsArray == null) { - case TFClass_Scout: - { - itemHandle = PrepareItemHandle("tf_weapon_bat", 0, 0, 0, ""); - } - case TFClass_Sniper: - { - itemHandle = PrepareItemHandle("tf_weapon_club", 3, 0, 0, ""); - } - case TFClass_Soldier: - { - itemHandle = PrepareItemHandle("tf_weapon_shovel", 6, 0, 0, ""); - } - case TFClass_DemoMan: - { - itemHandle = PrepareItemHandle("tf_weapon_bottle", 1, 0, 0, ""); - } - case TFClass_Heavy: - { - itemHandle = PrepareItemHandle("tf_weapon_fists", 5, 0, 0, ""); - } - case TFClass_Medic: - { - itemHandle = PrepareItemHandle("tf_weapon_bonesaw", 8, 0, 0, ""); - } - case TFClass_Pyro: - { - itemHandle = PrepareItemHandle("tf_weapon_fireaxe", 2, 0, 0, ""); - } - case TFClass_Spy: - { - itemHandle = PrepareItemHandle("tf_weapon_knife", 4, 0, 0, ""); - } - case TFClass_Engineer: + modelsArray = classData.GetModel(playerClass, 2); + if (modelsArray == null) { - itemHandle = PrepareItemHandle("tf_weapon_wrench", 7, 0, 0, ""); + modelsArray = proxyData.GetUniversalModel(2); + if (modelsArray == null) + { + modelsArray = classData.GetModel(playerClass, 1); + if (modelsArray == null) + { + modelsArray = proxyData.GetUniversalModel(1); + } + } } } } - case 4: + } + } + modelsArray.GetKeyNameFromIndex(GetRandomInt(0, modelsArray.Size - 1), keyName, sizeof(keyName)); + modelsArray.GetString(keyName, buffer, sizeof(buffer)); + strcopy(g_ClientProxyModelNightmare[client], sizeof(g_ClientProxyModelNightmare[]), buffer); + } + case Difficulty_Apollyon: + { + modelsArray = classData.GetModel(playerClass, difficulty); + if (modelsArray == null) + { + modelsArray = proxyData.GetUniversalModel(difficulty); + if (modelsArray == null) + { + modelsArray = classData.GetModel(playerClass, 4); + if (modelsArray == null) { - switch (playerClass) + modelsArray = proxyData.GetUniversalModel(4); + if (modelsArray == null) { - case TFClass_Spy: + modelsArray = classData.GetModel(playerClass, 3); + if (modelsArray == null) { - itemHandle = PrepareItemHandle("tf_weapon_invis", 297, 0, 0, ""); + modelsArray = proxyData.GetUniversalModel(3); + if (modelsArray == null) + { + modelsArray = classData.GetModel(playerClass, 2); + if (modelsArray == null) + { + modelsArray = proxyData.GetUniversalModel(2); + if (modelsArray == null) + { + modelsArray = classData.GetModel(playerClass, 1); + if (modelsArray == null) + { + modelsArray = proxyData.GetUniversalModel(1); + } + } + } + } } } } } - - if (itemHandle != null) - { - int newWeapon = TF2Items_GiveNamedItem(client, itemHandle); - if (IsValidEntity(newWeapon)) - { - EquipPlayerWeapon(client, newWeapon); - } - } - } - else - { - if (!g_PlayerHasRegenerationItem[client]) - { - g_PlayerHasRegenerationItem[client] = IsRegenWeapon(weaponEnt); - } } + modelsArray.GetKeyNameFromIndex(GetRandomInt(0, modelsArray.Size - 1), keyName, sizeof(keyName)); + modelsArray.GetString(keyName, buffer, sizeof(buffer)); + strcopy(g_ClientProxyModelApollyon[client], sizeof(g_ClientProxyModelApollyon[]), buffer); } - delete itemHandle; - itemHandle = null; } - } - // Fixes the Pretty Boy's Pocket Pistol glitch. - int maxHealth = SDKCall(g_SDKGetMaxHealth, client); - if (health > maxHealth) - { - SetEntProp(client, Prop_Data, "m_iHealth", maxHealth); - SetEntProp(client, Prop_Send, "m_iHealth", maxHealth); - } - } + if (buffer[0] != '\0') + { + SetVariantString(buffer); + AcceptEntityInput(client, "SetCustomModel"); + SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); - // Change stats on some weapons. - if (!g_PlayerEliminated[client] || g_PlayerProxy[client]) - { - int weaponEnt = INVALID_ENT_REFERENCE; - Handle weaponHandle = null; - for (int slot = 0; slot <= 5; slot++) + CreateTimer(0.5, ClientCheckProxyModel, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + } + } + else { - weaponEnt = GetPlayerWeaponSlot(client, slot); - if (!weaponEnt || weaponEnt == INVALID_ENT_REFERENCE) + modelsArray = classData.GetModel(playerClass, 1); + if (modelsArray == null) { - continue; + modelsArray = proxyData.GetUniversalModel(1); } - - int itemDef = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); - switch (itemDef) + if (modelsArray != null) { - case 214: // Powerjack + modelsArray.GetKeyNameFromIndex(GetRandomInt(0, modelsArray.Size - 1), keyName, sizeof(keyName)); + modelsArray.GetString(keyName, buffer, sizeof(buffer)); + if (buffer[0] != '\0') { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_fireaxe", 214, 0, 0, "180 ; 12.0 ; 412 ; 1.2"); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); + SetVariantString(buffer); + AcceptEntityInput(client, "SetCustomModel"); + SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); + strcopy(g_ClientProxyModel[client], sizeof(g_ClientProxyModel[]), buffer); + strcopy(g_ClientProxyModelHard[client], sizeof(g_ClientProxyModelHard[]), buffer); + strcopy(g_ClientProxyModelInsane[client], sizeof(g_ClientProxyModelInsane[]), buffer); + strcopy(g_ClientProxyModelNightmare[client], sizeof(g_ClientProxyModelNightmare[]), buffer); + strcopy(g_ClientProxyModelApollyon[client], sizeof(g_ClientProxyModelApollyon[]), buffer); + //Prevent plugins like Model manager to override proxy model. + CreateTimer(0.5, ClientCheckProxyModel, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); } - case 310: //The Warrior's Spirit - { - TF2_RemoveWeaponSlot(client, slot); + } + } - weaponHandle = PrepareItemHandle("tf_weapon_fists", 310, 0, 0, "2 ; 1.3 ; 412 ; 1.3"); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - } - case 326: // The Back Scratcher - { - TF2_RemoveWeaponSlot(client, slot); + if (IsPlayerAlive(client)) + { + g_PlayerProxyNextVoiceSound[client] = GetGameTime(); + // Play any sounds, if any. + proxyData.GetSpawnSounds().EmitSound(_, client); - weaponHandle = PrepareItemHandle("tf_weapon_fireaxe", 326, 0, 0, "2 ; 1.25 ; 412 ; 1.25 ; 69 ; 0.25 ; 108 ; 1.25"); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); + bool zombie = proxyData.Zombies; + if (zombie) + { + int value = g_ForcedHolidayConVar.IntValue; + if (value != 9 && value != 2) + { + g_ForcedHolidayConVar.SetInt(9); //Full-Moon } - case 304: // Amputator + int index; + TFClassType class = TF2_GetPlayerClass(client); + switch (class) { - TF2_RemoveWeaponSlot(client, slot); - - if (!SF_SpecialRound(SPECIALROUND_THANATOPHOBIA)) + case TFClass_Scout: + { + index = 5617; + } + case TFClass_Soldier: { - weaponHandle = PrepareItemHandle("tf_weapon_bonesaw", 304, 0, 0, "200 ; 0.0 ; 57 ; 2 ; 1 ; 0.8"); + index = 5618; } - else + case TFClass_Pyro: { - weaponHandle = PrepareItemHandle("tf_weapon_bonesaw", 304, 0, 0, "1 ; 0.8"); + index = 5624; } - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - } - case 239: //GRU - { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_fists", 239, 0, 0, "107 ; 1.3 ; 772 ; 1.5 ; 129 ; 0.0 ; 414 ; 1.0 ; 1 ; 0.75"); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - if (!IsRoundPlaying()) + case TFClass_DemoMan: { - SetEntityHealth(client, 300); + index = 5620; } - } - case 1100: //Bread Bite - { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_fists", 1100, 0, 0, "107 ; 1.3 ; 772 ; 1.5 ; 129 ; 0.0 ; 414 ; 1.0 ; 1 ; 0.75"); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - } - case 426: //Eviction Notice - { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_fists", 426, 0, 0, "6 ; 0.6 ; 107 ; 1.15 ; 737 ; 4.0 ; 1 ; 0.4 ; 412 ; 1.2"); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - if (!IsRoundPlaying()) + case TFClass_Engineer: + { + index = 5621; + } + case TFClass_Heavy: { - SetEntityHealth(client, 300); + index = 5619; + } + case TFClass_Medic: + { + index = 5622; + } + case TFClass_Sniper: + { + index = 5625; + } + case TFClass_Spy: + { + index = 5623; } } - case 775: //The Escape Plan (Its like, real buggy on wearer) - { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_shovel", 775, 0, 0, "414 ; 1 ; 734 ; 0.1"); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - } - case 452: //Three Rune Blade - { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_bat", 452, 0, 0, ""); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - } - case 325: //Boston Basher - { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_bat", 325, 0, 0, ""); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - } - case 450: //Atomizer - { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_bat", 450, 0, 0, ""); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - } - case 225: //Your Eternal Reward + Handle zombieSoul = PrepareItemHandle("tf_wearable", index, 100, 7,"448 ; 1.0 ; 450 ; 1"); + int entity = TF2Items_GiveNamedItem(client, zombieSoul); + delete zombieSoul; + zombieSoul = null; + if (IsValidEdict(entity)) { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_knife", 225, 0, 0, ""); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); + SDK_EquipWearable(client, entity); } - case 649: //Spy-cicle + if (TF2_GetPlayerClass(client) == TFClass_Spy) { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_knife", 649, 0, 0, ""); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); + SetEntProp(client, Prop_Send, "m_nForcedSkin", 23); } - case 574: //Spy-cicle + else { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_knife", 574, 0, 0, ""); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); + SetEntProp(client, Prop_Send, "m_nForcedSkin", 5); } } - } - delete weaponHandle; - } - if (preventAttack) - { - int weaponEnt = INVALID_ENT_REFERENCE; - while ((weaponEnt = FindEntityByClassname(weaponEnt, "tf_wearable_demoshield")) != INVALID_ENT_REFERENCE) - { - if (GetEntPropEnt(weaponEnt, Prop_Send, "m_hOwnerEntity") == client) - { - RemoveEntity(weaponEnt); - } + ClientSwitchToWeaponSlot(client, TFWeaponSlot_Melee); } + }*/ + return Plugin_Stop; +} - for (int slot = 0; slot <= 5; slot++) - { - if (slot == TFWeaponSlot_Melee) - { - continue; - } - - weaponEnt = GetPlayerWeaponSlot(client, slot); - if (!IsValidEntity(weaponEnt)) - { - continue; - } - - int itemDef = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); - switch (itemDef) - { - case 30, 212, 59, 60, 297, 947, 1101: // Invis Watch, Base Jumper - { - TF2_RemoveWeaponSlotAndWearables(client, slot); - } - default: - { - SetEntPropFloat(weaponEnt, Prop_Send, "m_flNextPrimaryAttack", 99999999.9); - SetEntPropFloat(weaponEnt, Prop_Send, "m_flNextSecondaryAttack", 99999999.9); - } - } - } +Action ClientCheckProxyModel(Handle timer, any userid) +{ + int client = GetClientOfUserId(userid); + if (client <= 0) + { + return Plugin_Stop; } - - //Remove the teleport ability - if (IsClientInPvP(client) || IsClientInPvE(client) || ((SF_IsRaidMap() || SF_IsBoxingMap()) && !g_PlayerEliminated[client])) //DidClientEscape(client) + if (!IsValidClient(client)) { - int weaponEnt = INVALID_ENT_REFERENCE; - Handle weaponHandle = null; - for (int slot = 0; slot <= 5; slot++) - { - weaponEnt = GetPlayerWeaponSlot(client, slot); - if (!weaponEnt || weaponEnt == INVALID_ENT_REFERENCE) - { - continue; - } - - int itemDef = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); - switch (itemDef) - { - case 589: // Eureka Effect - { - TF2_RemoveWeaponSlot(client, slot); - - weaponHandle = PrepareItemHandle("tf_weapon_wrench", 589, 0, 0, "93 ; 0.5 ; 732 ; 0.5"); - int entity = TF2Items_GiveNamedItem(client, weaponHandle); - delete weaponHandle; - weaponHandle = null; - EquipPlayerWeapon(client, entity); - } - } - } - delete weaponHandle; + return Plugin_Stop; + } + if (!IsPlayerAlive(client)) + { + return Plugin_Stop; + } + if (!g_PlayerProxy[client]) + { + return Plugin_Stop; } - //Force them to take their melee wep, it prevents the civilian bug. - ClientSwitchToWeaponSlot(client, TFWeaponSlot_Melee); + int difficulty = g_DifficultyConVar.IntValue; - // Remove all hats. - if (IsClientInGhostMode(client)) + char model[PLATFORM_MAX_PATH]; + GetEntPropString(client, Prop_Data, "m_ModelName", model, sizeof(model)); + switch (difficulty) { - int ent = -1; - while ((ent = FindEntityByClassname(ent, "tf_wearable")) != -1) + case Difficulty_Normal: { - if (GetEntPropEnt(ent, Prop_Send, "m_hOwnerEntity") == client) + if (strcmp(model, g_ClientProxyModel[client]) != 0 && g_ClientProxyModel[client][0] != '\0') { - RemoveEntity(ent); + SetVariantString(g_ClientProxyModel[client]); + AcceptEntityInput(client, "SetCustomModel"); + SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); } } - ent = -1; - while ((ent = FindEntityByClassname(ent, "tf_wearable_campaign_item")) != -1) + case Difficulty_Hard: { - if (GetEntPropEnt(ent, Prop_Send, "m_hOwnerEntity") == client) + if (strcmp(model, g_ClientProxyModelHard[client]) != 0 && g_ClientProxyModelHard[client][0] != '\0') { - RemoveEntity(ent); + SetVariantString(g_ClientProxyModelHard[client]); + AcceptEntityInput(client, "SetCustomModel"); + SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); } } - } - - float healthFromPack = 1.0; - if (!IsClassConfigsValid()) - { - if (!g_PlayerEliminated[client] && !SF_IsBoxingMap()) + case Difficulty_Insane: { - if (g_PlayerHasRegenerationItem[client]) - { - healthFromPack = 0.40; - } - if (TF2_GetPlayerClass(client) == TFClass_Medic) + if (strcmp(model, g_ClientProxyModelInsane[client]) != 0 && g_ClientProxyModelInsane[client][0] != '\0') { - healthFromPack = 0.0; + SetVariantString(g_ClientProxyModelInsane[client]); + AcceptEntityInput(client, "SetCustomModel"); + SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); } } - } - else - { - if (!g_PlayerEliminated[client] && !SF_IsBoxingMap()) + case Difficulty_Nightmare: { - healthFromPack = g_ClassHealthPickupMultiplier[classToInt]; - if (g_PlayerHasRegenerationItem[client]) - { - healthFromPack -= 0.6; - } - if (healthFromPack <= 0.0) + if (strcmp(model, g_ClientProxyModelNightmare[client]) != 0 && g_ClientProxyModelNightmare[client][0] != '\0') { - healthFromPack = 0.0; + SetVariantString(g_ClientProxyModelNightmare[client]); + AcceptEntityInput(client, "SetCustomModel"); + SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); } } - } - - TF2Attrib_SetByDefIndex(client, 109, healthFromPack); - - #if defined DEBUG - int weaponEnt = INVALID_ENT_REFERENCE; - - for (int i = 0; i <= 5; i++) - { - weaponEnt = GetPlayerWeaponSlot(client, i); - if (!IsValidEdict(weaponEnt)) - { - continue; - } - - newWeaponItemIndexes[i] = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); - } - - if (g_DebugDetailConVar.IntValue > 0) - { - for (int i = 0; i <= 5; i++) - { - DebugMessage("-> slot %d: %d (old: %d)", i, newWeaponItemIndexes[i], oldWeaponItemIndexes[i]); - } - - DebugMessage("END Timer_ClientPostWeapons(%d) -> remove = %d, restrict = %d", client, removeWeapons, restrictWeapons); - } - #endif - return Plugin_Stop; -} - -public Action TF2Items_OnGiveNamedItem(int client, char[] classname, int itemDefinitionIndex, Handle &itemHandle) -{ - if (!g_Enabled) - { - return Plugin_Continue; - } - - /*if (itemDefinitionIndex == 649) - { - RequestFrame(Frame_ReplaceSpyCicle, client); - return Plugin_Handled; - }*/ - switch (itemDefinitionIndex) - { - case 642: + case Difficulty_Apollyon: { - Handle itemOverride = PrepareItemHandle("tf_wearable", 642, 0, 0, "376 ; 1.0 ; 377 ; 0.2 ; 57 ; 2 ; 412 ; 1.10"); - - if (itemOverride != null) + if (strcmp(model, g_ClientProxyModelApollyon[client]) != 0 && g_ClientProxyModelApollyon[client][0] != '\0') { - itemHandle = itemOverride; - - return Plugin_Changed; + SetVariantString(g_ClientProxyModelApollyon[client]); + AcceptEntityInput(client, "SetCustomModel"); + SetEntProp(client, Prop_Send, "m_bUseClassAnimations", true); } - delete itemOverride; - itemOverride = null; } } - return Plugin_Continue; } @@ -1953,152 +1955,3 @@ void Frame_ClientHealArrow(int client) } } } - -bool IsRegenWeapon(int weaponEnt) -{ - Address attribRegen = TF2Attrib_GetByDefIndex(weaponEnt, 1003); - if (attribRegen != Address_Null) - { - return true; - } - attribRegen = TF2Attrib_GetByDefIndex(weaponEnt, 490); - if (attribRegen != Address_Null) - { - return true; - } - attribRegen = TF2Attrib_GetByDefIndex(weaponEnt, 190); - if (attribRegen != Address_Null) - { - return true; - } - attribRegen = TF2Attrib_GetByDefIndex(weaponEnt, 130); - if (attribRegen != Address_Null) - { - return true; - } - attribRegen = TF2Attrib_GetByDefIndex(weaponEnt, 57); - if (attribRegen != Address_Null) - { - return true; - } - attribRegen = TF2Attrib_GetByDefIndex(weaponEnt, 220); - if (attribRegen != Address_Null) - { - return true; - } - return false; -} - -bool IsWeaponRestricted(TFClassType class,int itemDefInt) -{ - if (g_RestrictedWeaponsConfig == null) - { - return false; - } - - bool returnBool = false; - - char itemDef[32]; - FormatEx(itemDef, sizeof(itemDef), "%d", itemDefInt); - - g_RestrictedWeaponsConfig.Rewind(); - bool proxyBoss = false; - for (int i = 0; i < MAX_BOSSES; i++) - { - SF2NPC_BaseNPC Npc = SF2NPC_BaseNPC(i); - if (!Npc.IsValid()) - { - continue; - } - if (Npc.Flags & SFF_PROXIES) - { - proxyBoss = true; - break; - } - } - if (g_RestrictedWeaponsConfig.JumpToKey("all")) - { - //returnBool = (g_RestrictedWeaponsConfig.GetNum(itemDef)) != 0; - //view_as bool value turn to 2 into a true value. - if (g_RestrictedWeaponsConfig.GetNum(itemDef)==1) - { - returnBool=true; - } - if (proxyBoss && !returnBool) - { - int proxyRestricted = g_RestrictedWeaponsConfig.GetNum(itemDef, 0); - if (proxyRestricted==2) - { - returnBool = true; - } - } - } - - bool bFoundSection = false; - g_RestrictedWeaponsConfig.Rewind(); - - switch (class) - { - case TFClass_Scout: - { - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("scout"); - } - case TFClass_Soldier: - { - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("soldier"); - } - case TFClass_Sniper: - { - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("sniper"); - } - case TFClass_DemoMan: - { - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("demoman"); - } - case TFClass_Heavy: - { - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("heavy"); - - if (!bFoundSection) - { - g_RestrictedWeaponsConfig.Rewind(); - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("heavyweapons"); - } - } - case TFClass_Medic: - { - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("medic"); - } - case TFClass_Spy: - { - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("spy"); - } - case TFClass_Pyro: - { - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("pyro"); - } - case TFClass_Engineer: - { - bFoundSection = g_RestrictedWeaponsConfig.JumpToKey("engineer"); - } - } - - if (bFoundSection) - { - //returnBool = (g_RestrictedWeaponsConfig.GetNum(itemDef, returnBool)) != 0; - if (g_RestrictedWeaponsConfig.GetNum(itemDef)==1) - { - returnBool = true; - } - if (proxyBoss && !returnBool) - { - int proxyRestricted = g_RestrictedWeaponsConfig.GetNum(itemDef, 0); - if (proxyRestricted==2) - { - returnBool = true; - } - } - } - - return returnBool; -} diff --git a/addons/sourcemod/scripting/sf2/client/sprint.sp b/addons/sourcemod/scripting/sf2/client/sprint.sp index 4b327ce8..bebcdd94 100644 --- a/addons/sourcemod/scripting/sf2/client/sprint.sp +++ b/addons/sourcemod/scripting/sf2/client/sprint.sp @@ -1,10 +1,15 @@ #pragma semicolon 1 +#pragma newdecls required static bool g_PlayerSprint[MAXTF2PLAYERS] = { false, ... }; static bool g_WantsToSprint[MAXTF2PLAYERS] = { false, ... }; static float g_Stamina[MAXTF2PLAYERS] = { 1.0, ... }; static float g_StaminaRechargeTime[MAXTF2PLAYERS] = { 1.0, ... }; +static char g_Player90sMusicString[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; +static float g_Player90sMusicVolumes[MAXTF2PLAYERS]; +static Handle g_Player90sMusicTimer[MAXTF2PLAYERS]; + static ConVar g_PlayerScareSprintBoost; void SetupSprint() @@ -317,7 +322,7 @@ void SetPlayerStaminaRechargeTime(SF2_BasePlayer client, float time, bool checkT g_StaminaRechargeTime[client.index] = time; } -float GetStaminaDecreaseRate(SF2_BasePlayer client) +static float GetStaminaDecreaseRate(SF2_BasePlayer client) { float rate = 0.03; @@ -352,7 +357,7 @@ float GetStaminaDecreaseRate(SF2_BasePlayer client) return rate; } -float GetStaminaRechargeRate(SF2_BasePlayer client) +static float GetStaminaRechargeRate(SF2_BasePlayer client) { float rate = 0.02; @@ -414,6 +419,8 @@ static void ClientResetSprint(SF2_BasePlayer client) DebugMessage("END ClientResetSprint(%d)", client.index); } #endif + + Client90sMusicReset(client.index); } /** @@ -517,7 +524,7 @@ static void Hook_SpeedThink(int client) continue; } - if (NPCGetType(i) == SF2BossType_Chaser) + if (SF2NPC_BaseNPC(i).GetProfileData().Type == SF2BossType_Chaser) { SF2_ChaserEntity chaser = SF2_ChaserEntity(ent); bossTarget = chaser.Target; @@ -563,7 +570,7 @@ static void Hook_SpeedThink(int client) continue; } - if (NPCGetType(i) == SF2BossType_Chaser) + if (SF2NPC_BaseNPC(i).GetProfileData().Type == SF2BossType_Chaser) { if (state == STATE_ALERT) { @@ -608,10 +615,10 @@ static void Hook_SpeedThink(int client) } } - if (player.IsTrapped || g_PlayerPeeking[player.index]) + if (player.IsTrapped || player.IsLatched || g_PlayerPeeking[player.index]) { - player.SetPropFloat(Prop_Send, "m_flMaxspeed", 0.1); - player.SetPropFloat(Prop_Send, "m_flCurrentTauntMoveSpeed", 0.1); + player.MaxSpeed = 0.1; + player.CurrentTauntMoveSpeed = 0.1; return; } @@ -813,46 +820,178 @@ static void Hook_SpeedThink(int client) { if (!player.InCondition(TFCond_Charging)) { - player.SetPropFloat(Prop_Send, "m_flMaxspeed", sprintSpeed); + player.MaxSpeed = sprintSpeed; } else { if (SF_IsBoxingMap() || SF_IsRaidMap()) { - player.SetPropFloat(Prop_Send, "m_flMaxspeed", sprintSpeed * 2.5); + player.MaxSpeed = sprintSpeed * 2.5; } else { - player.SetPropFloat(Prop_Send, "m_flMaxspeed", sprintSpeed / 2.5); + player.MaxSpeed = sprintSpeed / 2.5; } } - player.SetPropFloat(Prop_Send, "m_flCurrentTauntMoveSpeed", 190.0); } else { - player.SetPropFloat(Prop_Send, "m_flMaxspeed", 520.0); - player.SetPropFloat(Prop_Send, "m_flCurrentTauntMoveSpeed", 190.0); + player.MaxSpeed = 520.0; } } else { if (!player.InCondition(TFCond_Charging)) { - player.SetPropFloat(Prop_Send, "m_flMaxspeed", walkSpeed); + player.MaxSpeed = walkSpeed; } else { if (SF_IsBoxingMap() || SF_IsRaidMap()) { - player.SetPropFloat(Prop_Send, "m_flMaxspeed", walkSpeed * 2.5); + player.MaxSpeed = walkSpeed * 2.5; } else { - player.SetPropFloat(Prop_Send, "m_flMaxspeed", 190.0); + player.MaxSpeed = walkSpeed / 2.5; } } - player.SetPropFloat(Prop_Send, "m_flCurrentTauntMoveSpeed", 190.0); } + player.CurrentTauntMoveSpeed = 190.0; +} + +static void Client90sMusicReset(int client) +{ + char oldMusic[PLATFORM_MAX_PATH]; + strcopy(oldMusic, sizeof(oldMusic), g_Player90sMusicString[client]); + g_Player90sMusicString[client][0] = '\0'; + if (IsValidClient(client) && oldMusic[0] != '\0') + { + StopSound(client, MUSIC_CHAN, oldMusic); + } + + g_Player90sMusicTimer[client] = null; + g_Player90sMusicVolumes[client] = 0.0; + + if (IsValidClient(client)) + { + oldMusic = NINETYSMUSIC; + if (oldMusic[0] != '\0') + { + StopSound(client, MUSIC_CHAN, oldMusic); + } + } +} + +static void Client90sMusicStart(int client) +{ + if (!IsValidClient(client) || !IsPlayerAlive(client)) + { + return; + } + + char buffer[PLATFORM_MAX_PATH]; + buffer = NINETYSMUSIC; + + if (buffer[0] == '\0') + { + return; + } + + strcopy(g_Player90sMusicString[client], sizeof(g_Player90sMusicString[]), buffer); + g_Player90sMusicTimer[client] = CreateTimer(0.01, Timer_PlayerFadeIn90sMusic, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + TriggerTimer(g_Player90sMusicTimer[client], true); +} + +static void Client90sMusicStop(int client) +{ + if (!IsValidClient(client)) + { + return; + } + + if (!IsClientSprinting(client)) + { + g_Player90sMusicString[client][0] = '\0'; + } + + g_Player90sMusicTimer[client]= CreateTimer(0.01, Timer_PlayerFadeOut90sMusic, GetClientUserId(client), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + TriggerTimer(g_Player90sMusicTimer[client], true); +} + +static Action Timer_PlayerFadeIn90sMusic(Handle timer, any userid) +{ + int client = GetClientOfUserId(userid); + if (client <= 0) + { + return Plugin_Stop; + } + + if (g_Player90sMusicTimer[client] != timer) + { + return Plugin_Stop; + } + + g_Player90sMusicVolumes[client] += 0.28; + if (g_Player90sMusicVolumes[client] > 0.5) + { + g_Player90sMusicVolumes[client] = 0.5; + } + + if (g_Player90sMusicString[client][0] != '\0') + { + EmitSoundToClient(client, g_Player90sMusicString[client], _, MUSIC_CHAN, _, SND_CHANGEVOL, g_Player90sMusicVolumes[client] * g_PlayerPreferences[client].PlayerPreference_MusicVolume, 100); + } + + if (g_Player90sMusicVolumes[client] >= 0.5) + { + g_Player90sMusicTimer[client] = null; + return Plugin_Stop; + } + + return Plugin_Continue; +} + +static Action Timer_PlayerFadeOut90sMusic(Handle timer, any userid) +{ + int client = GetClientOfUserId(userid); + if (client <= 0) + { + return Plugin_Stop; + } + + if (g_Player90sMusicTimer[client] != timer) + { + return Plugin_Stop; + } + + char buffer[PLATFORM_MAX_PATH]; + buffer = NINETYSMUSIC; + + if (strcmp(buffer, g_Player90sMusicString[client], false) == 0) + { + g_Player90sMusicTimer[client] = null; + return Plugin_Stop; + } + + g_Player90sMusicVolumes[client] -= 0.28; + if (g_Player90sMusicVolumes[client] < 0.0) + { + g_Player90sMusicVolumes[client] = 0.0; + } + + if (buffer[0] != '\0') + { + EmitSoundToClient(client, buffer, _, MUSIC_CHAN, _, SND_CHANGEVOL, g_Player90sMusicVolumes[client] * g_PlayerPreferences[client].PlayerPreference_MusicVolume, 100); + } + + if (g_Player90sMusicVolumes[client] <= 0.0) + { + g_Player90sMusicTimer[client] = null; + return Plugin_Stop; + } + + return Plugin_Continue; } void Sprint_SetupAPI() diff --git a/addons/sourcemod/scripting/sf2/client/static.sp b/addons/sourcemod/scripting/sf2/client/static.sp index 1bc02aac..1620d06c 100644 --- a/addons/sourcemod/scripting/sf2/client/static.sp +++ b/addons/sourcemod/scripting/sf2/client/static.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required // Player static data. int g_PlayerStaticMode[MAXTF2PLAYERS][MAX_BOSSES]; @@ -154,31 +155,6 @@ void ClientProcessStaticShake(int client) newPunchAngVel[i] = oldPunchAngVel[i]; } - int staticMaster = NPCGetFromUniqueID(g_PlayerStaticMaster[client]); - if (staticMaster != -1 && NPCGetFlags(staticMaster) & SFF_HASSTATICSHAKE) - { - if (g_PlayerStaticMode[client][staticMaster] == Static_Increase) - { - newStaticShakeMaster = staticMaster; - } - } - for (int i = 0; i < MAX_BOSSES; i++) - { - if (NPCGetUniqueID(i) == -1) - { - continue; - } - - if (NPCGetFlags(i) & SFF_HASSTATICSHAKE) - { - int master = NPCGetFromUniqueID(g_SlenderCopyMaster[i]); - if (master == -1) - { - master = i; - } - } - } - if (newStaticShakeMaster != -1) { g_PlayerStaticShakeMaster[client] = NPCGetUniqueID(newStaticShakeMaster); @@ -187,17 +163,18 @@ void ClientProcessStaticShake(int client) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(newStaticShakeMaster, profile, sizeof(profile)); + BaseBossProfile profileData = GetBossProfile(profile); if (g_PlayerStaticShakeSound[client][0] != '\0') { StopSound(client, SNDCHAN_STATIC, g_PlayerStaticShakeSound[client]); } - g_PlayerStaticShakeMinVolume[client] = GetBossProfileStaticShakeLocalVolumeMin(profile); - g_PlayerStaticShakeMaxVolume[client] = GetBossProfileStaticShakeLocalVolumeMax(profile); + g_PlayerStaticShakeMinVolume[client] = profileData.StaticShakeMinVolume; + g_PlayerStaticShakeMaxVolume[client] = profileData.StaticShakeMaxVolume; char staticSound[PLATFORM_MAX_PATH]; - GetBossProfileStaticShakeSound(profile, staticSound, sizeof(staticSound)); + profileData.GetStaticShakeLocalSound(staticSound, sizeof(staticSound)); if (staticSound[0] != '\0') { strcopy(g_PlayerStaticShakeSound[client], sizeof(g_PlayerStaticShakeSound[]), staticSound); @@ -292,9 +269,9 @@ void ClientProcessStaticShake(int client) NormalizeVector(newPunchAng, newPunchAng); float angVelocityScalar = 5.0 * g_PlayerStaticAmount[client]; - if (angVelocityScalar < 0.0) + if (angVelocityScalar < 1.0) { - angVelocityScalar = 0.0; + angVelocityScalar = 1.0; } ScaleVector(newPunchAng, angVelocityScalar); diff --git a/addons/sourcemod/scripting/sf2/client/think.sp b/addons/sourcemod/scripting/sf2/client/think.sp index e4cad356..f15eafa6 100644 --- a/addons/sourcemod/scripting/sf2/client/think.sp +++ b/addons/sourcemod/scripting/sf2/client/think.sp @@ -4,6 +4,7 @@ #define _sf2_clients_think_included #pragma semicolon 1 +#pragma newdecls required void Hook_ClientPreThink(int client) { @@ -12,676 +13,89 @@ void Hook_ClientPreThink(int client) return; } - ClientProcessFlashlightAngles(client); - ClientProcessStaticShake(client); - ClientProcessViewAngles(client); + SF2_BasePlayer player = SF2_BasePlayer(client); + float gameTime = GetGameTime(); - if ((g_PlayerTrapped[client] || g_PlayerLatchedByTongue[client]) && !IsClientInGhostMode(client)) - { - TF2Attrib_SetByName(client, "increased jump height", 0.0); - } - else - { - TF2Attrib_SetByName(client, "increased jump height", 1.0); - } + ClientProcessStaticShake(player.index); + ClientProcessViewAngles(player.index); - if (IsClientInGhostMode(client)) + if (!player.IsEliminated && !player.HasEscaped) { - SetEntityFlags(client, GetEntityFlags(client) ^ FL_EDICT_ALWAYS); - SetEntPropFloat(client, Prop_Send, "m_flNextAttack", GetGameTime() + 2.0); - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", 520.0); - SetEntPropFloat(client, Prop_Send, "m_flModelScale", 1.0); - SetEntPropFloat(client, Prop_Send, "m_flHeadScale", 1.0); - SetEntPropFloat(client, Prop_Send, "m_flTorsoScale", 1.0); - SetEntPropFloat(client, Prop_Send, "m_flHandScale", 1.0); - if (IsClientInKart(client) || !TF2_IsPlayerInCondition(client, TFCond_Stealthed)) + if (!IsRoundEnding() && !IsRoundInWarmup()) { - TF2_RemoveCondition(client,TFCond_HalloweenKart); - TF2_RemoveCondition(client,TFCond_HalloweenKartDash); - TF2_RemoveCondition(client,TFCond_HalloweenKartNoTurn); - TF2_RemoveCondition(client,TFCond_HalloweenKartCage); - TF2_RemoveCondition(client, TFCond_Taunting); - ClientHandleGhostMode(client, true); - } - TF2_RemoveCondition(client, TFCond_Taunting); - } - else if (!g_PlayerEliminated[client] || g_PlayerProxy[client]) - { - if (!IsRoundEnding() && !IsRoundInWarmup() && !DidClientEscape(client)) - { - SetEntPropFloat(client, Prop_Send, "m_flModelScale", 1.0); - SetEntPropFloat(client, Prop_Send, "m_flHeadScale", 1.0); - SetEntPropFloat(client, Prop_Send, "m_flTorsoScale", 1.0); - SetEntPropFloat(client, Prop_Send, "m_flHandScale", 1.0); - - int roundState = view_as(GameRules_GetRoundState()); - TFClassType class = TF2_GetPlayerClass(client); + player.SetPropFloat(Prop_Send, "m_flModelScale", 1.0); + player.SetPropFloat(Prop_Send, "m_flHeadScale", 1.0); + player.SetPropFloat(Prop_Send, "m_flTorsoScale", 1.0); + player.SetPropFloat(Prop_Send, "m_flHandScale", 1.0); - if (!g_PlayerProxy[client] && GetClientTeam(client) == TFTeam_Red) + if (player.InCondition(TFCond_Disguised)) { - if (TF2_IsPlayerInCondition(client, TFCond_Disguised)) - { - TF2_RemoveCondition(client, TFCond_Disguised); - } - - if (TF2_IsPlayerInCondition(client, TFCond_Taunting) && (g_PlayerTrapped[client] || g_PlayerLatchedByTongue[client])) - { - TF2_RemoveCondition(client, TFCond_Taunting); - } - - if (TF2_IsPlayerInCondition(client, TFCond_Taunting) && class == TFClass_Soldier) - { - int weaponEnt = GetPlayerWeaponSlot(client, TFWeaponSlot_Melee); - if (weaponEnt && weaponEnt != INVALID_ENT_REFERENCE) - { - int itemDefInt = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); - if ((itemDefInt == 775 || itemDefInt == 128) && GetEntProp(client, Prop_Send, "m_iTauntIndex") == 0) - { - TF2_RemoveCondition(client, TFCond_Taunting); //Stop suiciding... - } - } - } - - if (roundState == 4) - { - - } + player.ChangeCondition(TFCond_Disguised, true); } - else if (g_PlayerProxy[client] && GetClientTeam(client) == TFTeam_Blue) - { - int master = NPCGetFromUniqueID(g_PlayerProxyMaster[client]); - - float maxSpeed; - bool override; - - if (master != -1) - { - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(master, profile, sizeof(profile)); - - override = GetBossProfileProxyOverrideMaxSpeed(profile); - if (override) - { - int difficulty = GetLocalGlobalDifficulty(master); - - maxSpeed = GetBossProfileProxyMaxSpeed(profile, difficulty); - } - } - - bool speedup = TF2_IsPlayerInCondition(client, TFCond_SpeedBuffAlly); - if (override) - { - if (speedup || g_InProxySurvivalRageMode) - { - float rageSpeed = maxSpeed + 30.0; - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", rageSpeed); - } - else - { - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", maxSpeed); - } - } - else + if (player.InCondition(TFCond_Taunting)) + { + int weaponEnt = player.GetWeaponSlot(TFWeaponSlot_Melee); + if (weaponEnt && weaponEnt != INVALID_ENT_REFERENCE) { - switch (class) + int itemDefInt = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); + if ((itemDefInt == 775 || itemDefInt == 128) && GetEntProp(client, Prop_Send, "m_iTauntIndex") == 0) { - case TFClass_Scout: - { - if (speedup || g_InProxySurvivalRageMode) - { - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", 390.0); - } - else - { - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", 300.0); - } - } - case TFClass_Medic: - { - if (speedup || g_InProxySurvivalRageMode) - { - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", 370.0); - } - else - { - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", 300.0); - } - } - case TFClass_Spy: - { - if (speedup || g_InProxySurvivalRageMode) - { - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", 370.0); - } - else - { - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", 300.0); - } - } - default: - { - if (g_InProxySurvivalRageMode) - { - float rageSpeed = ClientGetDefaultSprintSpeed(client) + 30.0; - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", rageSpeed); - } - } + player.ChangeCondition(TFCond_Taunting, true); //Stop suiciding... } } } } - } - if (g_PlayerEliminated[client] && (IsClientInPvP(client) || IsClientInPvE(client))) - { - SetEntPropFloat(client, Prop_Send, "m_flModelScale", 1.0); - SetEntPropFloat(client, Prop_Send, "m_flHeadScale", 1.0); - SetEntPropFloat(client, Prop_Send, "m_flTorsoScale", 1.0); - SetEntPropFloat(client, Prop_Send, "m_flHandScale", 1.0); - if (IsClientInKart(client)) - { - TF2_RemoveCondition(client, TFCond_HalloweenKart); - TF2_RemoveCondition(client, TFCond_HalloweenKartDash); - TF2_RemoveCondition(client, TFCond_HalloweenKartNoTurn); - TF2_RemoveCondition(client, TFCond_HalloweenKartCage); - } - } - if (IsRoundInWarmup() || (IsRoundInIntro() && !g_PlayerEliminated[client]) || IsRoundEnding()) //I told you, stop breaking my plugin - { - if (IsClientInKart(client)) - { - TF2_RemoveCondition(client, TFCond_HalloweenKart); - TF2_RemoveCondition(client, TFCond_HalloweenKartDash); - TF2_RemoveCondition(client, TFCond_HalloweenKartNoTurn); - TF2_RemoveCondition(client, TFCond_HalloweenKartCage); - } - } - - // Calculate player stress levels. - if (!g_PlayerEliminated[client] && !DidClientEscape(client) && GetGameTime() >= g_PlayerStressNextUpdateTime[client]) - { - g_PlayerStressNextUpdateTime[client] = GetGameTime() + 0.33; - ClientAddStress(client, -0.01); - - #if defined DEBUG - SendDebugMessageToPlayer(client, DEBUG_PLAYER_STRESS, 1, "g_PlayerStressAmount[%d]: %0.1f", client, g_PlayerStressAmount[client]); - #endif - } - if (g_LastVisibilityProcess[client] + 0.30 >= GetGameTime()) - { - return; - } - - /*if (!g_PlayerEliminated[client]) - { - CNavArea targetArea = SDK_GetLastKnownArea(client);//SDK_GetLastKnownArea(client) =/= g_lastNavArea[client], SDK_GetLastKnownArea() retrives the nav area stored by the server - if (targetArea != INVALID_NAV_AREA) - { - ClientNavAreaUpdate(client, g_lastNavArea[client], targetArea); - g_lastNavArea[client] = targetArea; - } - }*/ - - g_LastVisibilityProcess[client] = GetGameTime(); - - ClientProcessVisibility(client); -} - -Action Hook_ClientOnTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom) -{ - SF2_BasePlayer victimPlayer = SF2_BasePlayer(victim); - if (!g_Enabled) - { - if (NPCGetFromEntIndex(attacker) != -1 && GetEntProp(attacker, Prop_Data, "m_iTeamNum") == victimPlayer.Team) + if (gameTime >= g_PlayerStressNextUpdateTime[player.index]) { - damage = 0.0; - return Plugin_Changed; - } - return Plugin_Continue; - } + g_PlayerStressNextUpdateTime[player.index] = gameTime + 0.33; + ClientAddStress(player.index, -0.01); - Action action = Plugin_Continue; - - float damage2 = damage; - Call_StartForward(g_OnClientTakeDamageFwd); - Call_PushCell(victimPlayer.index); - Call_PushCellRef(attacker); - Call_PushCellRef(inflictor); - Call_PushFloatRef(damage2); - Call_Finish(action); - - if (action == Plugin_Changed) - { - damage = damage2; - return Plugin_Changed; - } - - Call_StartForward(g_OnPlayerTakeDamagePFwd); - Call_PushCell(victimPlayer); - Call_PushCellRef(attacker); - Call_PushCellRef(inflictor); - Call_PushFloatRef(damage2); - Call_PushCellRef(damagetype); - Call_Finish(action); - - if (action == Plugin_Changed) - { - damage = damage2; - return Plugin_Changed; - } - - TFClassType class = victimPlayer.Class; - int classToInt = view_as(class); - - if (IsRoundInWarmup() && IsValidClient(attacker)) - { - float modelScale = GetEntPropFloat(attacker, Prop_Send, "m_flModelScale"); - float headScale = GetEntPropFloat(attacker, Prop_Send, "m_flHeadScale"); - float torsoScale = GetEntPropFloat(attacker, Prop_Send, "m_flTorsoScale"); - float handScale = GetEntPropFloat(attacker, Prop_Send, "m_flHandScale"); - if (modelScale < 1.0 || modelScale > 1.0 || headScale < 1.0 || headScale > 1.0 || torsoScale < 1.0 || torsoScale > 1.0 || handScale < 1.0 || handScale > 1.0) - { - damage = 0.0; //So how does it feel? - return Plugin_Changed; + #if defined DEBUG + SendDebugMessageToPlayer(player.index, DEBUG_PLAYER_STRESS, 1, "g_PlayerStressAmount[%d]: %0.1f", player.index, g_PlayerStressAmount[player.index]); + #endif } } - - if (IsClientInKart(victim) && (attacker == -1 || inflictor == -1)) - { - damage = 0.0; - return Plugin_Changed; - } - - char inflictorClass[32]; - if (inflictor >= 0) + if (player.IsEliminated && (player.IsInPvP || player.IsInPvE)) { - GetEdictClassname(inflictor, inflictorClass, sizeof(inflictorClass)); - } - - if (IsValidClient(attacker) && IsValidClient(victim) && g_PlayerProxy[attacker] && GetClientTeam(victim) == TFTeam_Red && TF2_IsPlayerInCondition(victim, TFCond_Gas)) - { - TF2_IgnitePlayer(victim, victim); - TF2_RemoveCondition(victim, TFCond_Gas); - } - - if (TF2_IsPlayerInCondition(victim, TFCond_Gas) && SF2_ChaserEntity(attacker).IsValid()) - { - TF2_IgnitePlayer(victim, victim); - TF2_RemoveCondition(victim, TFCond_Gas); - } - - if (IsValidClient(attacker) && IsValidClient(victim) && (IsClientInPvP(victim) || IsClientInPvE(victim)) && GetClientTeam(victim) == TFTeam_Red && GetClientTeam(attacker) == TFTeam_Red && victim != attacker) - { - damage = 0.0; - return Plugin_Changed; - } - - if (IsValidClient(attacker) && victimPlayer.IsValid && victimPlayer.Team == TFTeam_Red && GetClientTeam(attacker) == TFTeam_Red && (victimPlayer.IsTrapped || victimPlayer.IsLatched)) - { - if (!g_PlayerEliminated[attacker] && !victimPlayer.IsEliminated && (damagetype & 0x80) != 0) + player.SetPropFloat(Prop_Send, "m_flModelScale", 1.0); + player.SetPropFloat(Prop_Send, "m_flHeadScale", 1.0); + player.SetPropFloat(Prop_Send, "m_flTorsoScale", 1.0); + player.SetPropFloat(Prop_Send, "m_flHandScale", 1.0); + if (IsClientInKart(player.index)) { - victimPlayer.IsTrapped = false; - if (victimPlayer.IsLatched) - { - victimPlayer.ChangeCondition(TFCond_Dazed, true); - for (int i = 0; i < MAX_BOSSES; i++) - { - SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(i); - if (!npc.IsValid()) - { - continue; - } - - if (victimPlayer.Latcher != npc.Index) - { - continue; - } - - SF2_ChaserEntity chaser = SF2_ChaserEntity(npc.EntIndex); - if (!chaser.IsValid()) - { - continue; - } - - chaser.MyNextBotPointer().GetIntentionInterface().OnCommandString("break tongue"); - } - } - victimPlayer.IsLatched = false; - victimPlayer.LatchCount = 0; - TF2_AddCondition(attacker, TFCond_SpeedBuffAlly, 4.0); - TF2_AddCondition(victim, TFCond_SpeedBuffAlly, 4.0); + player.ChangeCondition(TFCond_HalloweenKart, true); + player.ChangeCondition(TFCond_HalloweenKartDash, true); + player.ChangeCondition(TFCond_HalloweenKartNoTurn, true); + player.ChangeCondition(TFCond_HalloweenKartCage, true); } } - - if (IsValidClient(attacker) && !g_PlayerEliminated[attacker] && !DidClientEscape(attacker) && class == TFClass_Soldier && !(GetEntityFlags(attacker) & FL_ONGROUND)) + if (IsRoundInWarmup() || (IsRoundInIntro() && !player.IsEliminated) || IsRoundEnding()) //I told you, stop breaking my plugin { - int weaponEnt = GetPlayerWeaponSlot(attacker, TFWeaponSlot_Melee); - if (IsValidEntity(weaponEnt)) + if (IsClientInKart(player.index)) { - int itemDefInt = GetEntProp(weaponEnt, Prop_Send, "m_iItemDefinitionIndex"); - float zVelocity[3]; - GetEntPropVector(attacker, Prop_Data, "m_vecVelocity", zVelocity); - if (itemDefInt == 416 && zVelocity[2] < 0.0 && weaponEnt == GetEntPropEnt(attacker, Prop_Send, "m_hActiveWeapon")) //A soldier has the market gardener and is currently falling down, like Minecraft with it's critical hits. - { - damagetype |= DMG_ACID; - } + player.ChangeCondition(TFCond_HalloweenKart, true); + player.ChangeCondition(TFCond_HalloweenKartDash, true); + player.ChangeCondition(TFCond_HalloweenKartNoTurn, true); + player.ChangeCondition(TFCond_HalloweenKartCage, true); } } - if (!SF2_ChaserEntity(inflictor).IsValid() && !SF2_StatueEntity(inflictor).IsValid() && !IsValidClient(inflictor)) + if (g_LastVisibilityProcess[player.index] + 0.30 >= GetGameTime()) { - int npcIndex = NPCGetFromEntIndex(GetEntPropEnt(inflictor, Prop_Send, "m_hOwnerEntity")); - if (npcIndex != -1) - { - bool attackEliminated = (NPCGetFlags(npcIndex) & SFF_ATTACKWAITERS) != 0; - if (!attackEliminated && (GetClientTeam(victim) == TFTeam_Blue) && IsValidClient(victim) ) - { - damage = 0.0; - return Plugin_Changed; - } - } + return; } - bool canDamage = false; - if (attacker != victim && IsValidClient(attacker)) - { - if (IsClientInPvP(victim) && IsClientInPvP(attacker)) - { - canDamage = true; - } - if (IsClientLeavingPvP(victim) && !IsClientInPvP(attacker)) - { - canDamage = true; - } - if (!IsRoundEnding()) - { - if (canDamage) - { - if (attacker == inflictor) - { - if (IsValidEdict(weapon)) - { - char weaponClass[64]; - GetEdictClassname(weapon, weaponClass, sizeof(weaponClass)); - - // Backstab check! - if ((strcmp(weaponClass, "tf_weapon_knife") == 0 || (TF2_GetPlayerClass(attacker) == TFClass_Spy && strcmp(weaponClass, "saxxy") == 0)) && - (damagecustom != TF_CUSTOM_TAUNT_FENCING)) - { - float myPos[3], hisPos[3], myDirection[3]; - GetClientAbsOrigin(victim, myPos); - GetClientAbsOrigin(attacker, hisPos); - GetClientEyeAngles(victim, myDirection); - GetAngleVectors(myDirection, myDirection, NULL_VECTOR, NULL_VECTOR); - NormalizeVector(myDirection, myDirection); - ScaleVector(myDirection, 32.0); - AddVectors(myDirection, myPos, myDirection); - - float p[3], s[3]; - MakeVectorFromPoints(myPos, hisPos, p); - MakeVectorFromPoints(myPos, myDirection, s); - if (GetVectorDotProduct(p, s) <= 0.0)//We can backstab him m8 - { - if (GetClientTeam(victim) == GetClientTeam(attacker) && class == TFClass_Sniper) - { - //look if the player has a razorback - int wearableEnt = INVALID_ENT_REFERENCE; - while ((wearableEnt = FindEntityByClassname(wearableEnt, "tf_wearable")) != -1) - { - if (GetEntPropEnt(wearableEnt, Prop_Send, "m_hOwnerEntity") == victim && GetEntProp(wearableEnt, Prop_Send, "m_iItemDefinitionIndex") == 57) - { - RemoveEntity(wearableEnt); - damage = 0.0; - EmitSoundToClient(victim, "player/spy_shield_break.wav", _, _, SNDLEVEL_TRAFFIC, SND_NOFLAGS, 0.7, 100); - EmitSoundToClient(attacker, "player/spy_shield_break.wav", _, _, SNDLEVEL_TRAFFIC, SND_NOFLAGS, 0.7, 100); - - SetEntPropFloat(weapon, Prop_Send, "m_flNextPrimaryAttack", GetGameTime() + 2.0); - SetEntPropFloat(attacker, Prop_Send, "m_flNextAttack", GetGameTime() + 2.0); - SetEntPropFloat(attacker, Prop_Send, "m_flStealthNextChangeTime", GetGameTime() + 2.0); - int vm = GetEntPropEnt(attacker, Prop_Send, "m_hViewModel"); - if (vm > MaxClients) - { - int meleeIndex = GetEntProp(weapon, Prop_Send, "m_iItemDefinitionIndex"); - int anim = 41; - switch (meleeIndex) - { - case 4, 194, 225, 356, 461, 574, 649, 665, 794, 803, 883, 892, 901, 910, 959, 968: - { - anim = 15; - } - case 638: - { - anim = 31; - } - } - SetEntProp(vm, Prop_Send, "m_nSequence", anim); - } - return Plugin_Changed; - } - } - } - if (damagecustom == TF_CUSTOM_BACKSTAB) // Modify backstab damage. - { - damage = 120.0; - if (damagetype & DMG_ACID) - { - damage = 120.0; - } - } - - if (g_WeaponCriticalsConVar != null && g_WeaponCriticalsConVar.BoolValue) - { - damagetype |= DMG_ACID; - } - - if (!IsClientCritUbercharged(victim)) - { - if (GetClientTeam(victim) == GetClientTeam(attacker)) - { - int pistol = GetPlayerWeaponSlot(attacker, TFWeaponSlot_Primary); - if (pistol > MaxClients && GetEntProp(pistol, Prop_Send, "m_iItemDefinitionIndex") == 525) //Give one crit fort the backstab - { - int crits = GetEntProp(attacker, Prop_Send, "m_iRevengeCrits"); - crits++; - SetEntProp(attacker, Prop_Send, "m_iRevengeCrits", crits); - } - } - if (GetEntProp(victim, Prop_Send, "m_iHealth") <= 120) - { - g_PlayerBackStabbed[victim] = true; - } - else - { - g_PlayerBackStabbed[victim] = false; - } - } - return Plugin_Changed; - } - } - } - } - } - else if (g_PlayerProxy[victim] || g_PlayerProxy[attacker]) - { - if (g_PlayerEliminated[attacker] == g_PlayerEliminated[victim]) - { - damage = 0.0; - return Plugin_Changed; - } - - if (attacker == victim)//Don't allow proxy to self regenerate control. - { - return Plugin_Continue; - } - - if (g_PlayerProxy[attacker]) - { - int maxHealth = SDKCall(g_SDKGetMaxHealth, victim); - int master = NPCGetFromUniqueID(g_PlayerProxyMaster[attacker]); - if (master != -1) - { - int difficulty = GetLocalGlobalDifficulty(master); - if (damagecustom == TF_CUSTOM_TAUNT_GRAND_SLAM || - damagecustom == TF_CUSTOM_TAUNT_FENCING || - damagecustom == TF_CUSTOM_TAUNT_ARROW_STAB || - damagecustom == TF_CUSTOM_TAUNT_GRENADE || - damagecustom == TF_CUSTOM_TAUNT_BARBARIAN_SWING || - damagecustom == TF_CUSTOM_TAUNT_ENGINEER_ARM || - damagecustom == TF_CUSTOM_TAUNT_ARMAGEDDON) - { - if (damage >= float(maxHealth)) - { - damage = float(maxHealth) * 0.5; - } - else - { - damage = 0.0; - } - } - else if (damagecustom == TF_CUSTOM_BACKSTAB) // Modify backstab damage. - { - damage = float(maxHealth) * g_SlenderProxyDamageVsBackstab[master][difficulty]; - if (damagetype & DMG_ACID) - { - damage /= 2.0; - } - } - - g_PlayerProxyControl[attacker] += g_SlenderProxyControlGainHitEnemy[master][difficulty]; - if (g_PlayerProxyControl[attacker] > 100) - { - g_PlayerProxyControl[attacker] = 100; - } - - float originalPercentage = g_SlenderProxyDamageVsEnemy[master][difficulty]; - float additionPercentage = 0.15; - if (!IsClassConfigsValid()) - { - if (class == TFClass_Medic) - { - damage *= (originalPercentage + additionPercentage); - } - else - { - damage *= originalPercentage; - } - } - else - { - damage *= originalPercentage + g_ClassProxyDamageVulnerability[classToInt]; - } - } - return Plugin_Changed; - } - else if (g_PlayerProxy[victim]) - { - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - int master = NPCGetFromUniqueID(g_PlayerProxyMaster[victim]); - if (master != -1) - { - NPCGetProfile(master, profile, sizeof(profile)); - int difficulty = GetLocalGlobalDifficulty(master); - g_PlayerProxyControl[attacker] += g_SlenderProxyControlGainHitByEnemy[master][difficulty]; - if (g_PlayerProxyControl[attacker] > 100) - { - g_PlayerProxyControl[attacker] = 100; - } - - damage *= g_SlenderProxyDamageVsSelf[master][difficulty]; - } - if (TF2_IsPlayerInCondition(victim, view_as(87))) - { - damage = 0.0; - } - if (damage * (damagetype & DMG_CRIT ? 3.0 : 1.0) >= float(GetClientHealth(victim)) && !TF2_IsPlayerInCondition(victim, view_as(87)))//The proxy is about to die - { - char buffer[PLATFORM_MAX_PATH]; - int classIndex = view_as(TF2_GetPlayerClass(victim)); - ArrayList deathAnims = GetBossProfileProxyDeathAnimations(profile); - if (deathAnims != null) - { - deathAnims.GetString(classIndex, buffer, sizeof(buffer)); - if (buffer[0] == '\0') - { - deathAnims.GetString(0, buffer, sizeof(buffer)); - } - if (buffer[0] != '\0') - { - g_ClientMaxFrameDeathAnim[victim]=GetBossProfileProxyDeathAnimFrames(profile, classIndex); - if (g_ClientMaxFrameDeathAnim[victim] == 0) - { - g_ClientMaxFrameDeathAnim[victim] = GetBossProfileProxyDeathAnimFrames(profile, 0); - } - if (g_ClientMaxFrameDeathAnim[victim] > 0) - { - // Cancel out any other taunts. - if (TF2_IsPlayerInCondition(victim, TFCond_Taunting)) - { - TF2_RemoveCondition(victim, TFCond_Taunting); - } - //The model has a death anim play it. - SDK_PlaySpecificSequence(victim,buffer); - g_ClientFrame[victim] = 0; - RequestFrame(ProxyDeathAnimation,victim); - TF2_AddCondition(victim, view_as(87), 5.0); - //Prevent death, and show the damage to the attacker. - TF2_AddCondition(victim, view_as(70), 0.5); - return Plugin_Changed; - } - } - } - - //the player has no death anim leave him die. - } - return Plugin_Changed; - } - } - else - { - damage = 0.0; - return Plugin_Changed; - } - } - else - { - if (g_PlayerEliminated[attacker] == g_PlayerEliminated[victim]) - { - damage = 0.0; - return Plugin_Changed; - } - } - - if (IsClientInGhostMode(victim)) - { - damage = 0.0; - return Plugin_Changed; - } - } + g_LastVisibilityProcess[player.index] = GetGameTime(); - return Plugin_Continue; + ClientProcessVisibility(player.index); } void ClientOnButtonPress(SF2_BasePlayer client, int button) { switch (button) { - case IN_ATTACK2: - { - if (client.IsAlive) - { - if (!IsRoundInWarmup() && - !IsRoundInIntro() && - !IsRoundEnding() && - !client.HasEscaped) - { - if (GetGameTime() >= client.GetFlashlightNextInputTime()) - { - client.HandleFlashlight(); - } - } - } - } case IN_ATTACK3: { client.HandleSprint(true); @@ -855,28 +269,7 @@ Action Timer_ClientAverageUpdate(Handle timer) char buffer2[64]; if (!SF_IsRaidMap() && !SF_IsBoxingMap()) { - float percent = float(player.Health) / float(player.MaxHealth); - if (percent > 1.0) - { - percent = 1.0; - } - bars = RoundToCeil(float(maxBars) * percent); - if (bars > maxBars) - { - bars = maxBars; - } - FormatEx(buffer, sizeof(buffer), "%s ", SF2_PLAYER_HUD_HEALTH_SYMBOL); - for (int i2 = 0; i2 < maxBars; i2++) - { - if (i2 < bars) - { - StrCat(buffer, sizeof(buffer), (!g_PlayerPreferences[player.index].PlayerPreference_LegacyHud ? SF2_PLAYER_HUD_BAR_SYMBOL : SF2_PLAYER_HUD_BAR_SYMBOL_OLD)); - } - else - { - StrCat(buffer, sizeof(buffer), (!g_PlayerPreferences[player.index].PlayerPreference_LegacyHud ? SF2_PLAYER_HUD_BAR_MISSING_SYMBOL : SF2_PLAYER_HUD_BAR_MISSING_SYMBOL_OLD)); - } - } + FormatEx(buffer, sizeof(buffer), "%s %i/%i", SF2_PLAYER_HUD_HEALTH_SYMBOL, player.Health, player.MaxHealth); } if (!SF_SpecialRound(SPECIALROUND_LIGHTSOUT) && !SF_IsRaidMap() && !SF_IsBoxingMap()) @@ -1061,7 +454,6 @@ Action Timer_ClientAverageUpdate(Handle timer) } } player.UpdateListeningFlags(); - player.UpdateMusicSystem(); Call_StartForward(g_OnPlayerAverageUpdatePFwd); Call_PushCell(player); diff --git a/addons/sourcemod/scripting/sf2/client/ultravision.sp b/addons/sourcemod/scripting/sf2/client/ultravision.sp index 53b1209e..bcc7a045 100644 --- a/addons/sourcemod/scripting/sf2/client/ultravision.sp +++ b/addons/sourcemod/scripting/sf2/client/ultravision.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required #define SF2_ULTRAVISION_CONE 180.0 #define SF2_ULTRAVISION_WIDTH 800.0 diff --git a/addons/sourcemod/scripting/sf2/client/weapons.sp b/addons/sourcemod/scripting/sf2/client/weapons.sp index e2070e50..ef496347 100644 --- a/addons/sourcemod/scripting/sf2/client/weapons.sp +++ b/addons/sourcemod/scripting/sf2/client/weapons.sp @@ -16,7 +16,7 @@ void SF2_RefreshRestrictions() { for(int client = 1; client <= MaxClients; client++) { - if (IsValidClient(client) && (!g_PlayerEliminated[client] || (g_PlayerEliminated[client] && !IsClientInPvP(client) && !IsClientInPvE(client)))) + if (IsValidClient(client) && (!g_PlayerEliminated[client] || (g_PlayerEliminated[client] && !IsClientInPvP(client) && !IsClientInPvE(client) && !IsClientInWeaponsTrigger(client)))) { ClientSwitchToWeaponSlot(client, TFWeaponSlot_Melee); g_PlayerPostWeaponsTimer[client] = CreateTimer(1.0, Timer_ClientPostWeapons, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); @@ -165,7 +165,7 @@ Action Timer_ClientPostWeapons(Handle timer, any userid) } // pvp - if (client.IsInPvP || (SF_IsRaidMap() && !client.IsEliminated)) + if (client.IsInPvP || (SF_IsRaidMap() && !client.IsEliminated && !client.HasEscaped) || IsClientInWeaponsTrigger(client.index)) { removeWeapons = false; restrictWeapons = true; @@ -511,7 +511,7 @@ Action Timer_ClientPostWeapons(Handle timer, any userid) } //Remove the teleport ability - if (client.IsInPvP || client.IsInPvE || ((SF_IsRaidMap() || SF_IsBoxingMap()) && !client.IsEliminated)) + if (client.IsInPvP || client.IsInPvE || ((SF_IsRaidMap() || SF_IsBoxingMap()) && !client.IsEliminated && !client.HasEscaped)) { int weaponEnt = INVALID_ENT_REFERENCE; Handle weaponHandle = null; @@ -713,7 +713,7 @@ bool IsWeaponRestricted(SF2_BasePlayer client, int itemDefInt) { continue; } - if (Npc.GetProfileDataEx().GetProxies().IsEnabled(Npc.Difficulty)) + if (Npc.GetProfileData().GetProxies().IsEnabled(Npc.Difficulty)) { proxyBoss = true; break; diff --git a/addons/sourcemod/scripting/sf2/debug.sp b/addons/sourcemod/scripting/sf2/debug.sp index e08792a0..c398c898 100644 --- a/addons/sourcemod/scripting/sf2/debug.sp +++ b/addons/sourcemod/scripting/sf2/debug.sp @@ -4,6 +4,7 @@ #define _sf2_debug_included #pragma semicolon 1 +#pragma newdecls required #include diff --git a/addons/sourcemod/scripting/sf2/effects.sp b/addons/sourcemod/scripting/sf2/effects.sp index 4da83b6f..b764a024 100644 --- a/addons/sourcemod/scripting/sf2/effects.sp +++ b/addons/sourcemod/scripting/sf2/effects.sp @@ -4,11 +4,768 @@ #define _sf2_effects_included #pragma semicolon 1 +#pragma newdecls required static EffectEvent g_EntityEffectEvent[2049]; static EffectType g_EntityEffectType[2049]; static ArrayList g_NpcEffectsArray[MAX_BOSSES]; +methodmap ProfileEffectMaster < ProfileObject +{ + public void Precache() + { + for (int i = 0; i < this.SectionLength; i++) + { + char key[128]; + this.GetSectionNameFromIndex(i, key, sizeof(key)); + ProfileEffect effect = view_as(this.GetSection(key)); + if (effect != null) + { + effect.Precache(); + } + } + } +} + +methodmap ProfileEffect < ProfileObject +{ + property EffectType Type + { + public get() + { + char effectTypeString[64]; + this.GetString("type", effectTypeString, sizeof(effectTypeString)); + if (strcmp(effectTypeString, "steam", false) == 0) + { + return EffectType_Steam; + } + else if (strcmp(effectTypeString, "dynamiclight", false) == 0) + { + return EffectType_DynamicLight; + } + else if (strcmp(effectTypeString, "particle", false) == 0) + { + return EffectType_Particle; + } + else if (strcmp(effectTypeString, "trail", false) == 0) + { + return EffectType_Trail; + } + else if (strcmp(effectTypeString, "propdynamic", false) == 0) + { + return EffectType_PropDynamic; + } + else if (strcmp(effectTypeString, "pointspotlight", false) == 0) + { + return EffectType_PointSpotlight; + } + else if (strcmp(effectTypeString, "sprite", false) == 0) + { + return EffectType_Sprite; + } + else if (strcmp(effectTypeString, "te_beamring", false) == 0) + { + return EffectType_TempEntBeamRing; + } + else if (strcmp(effectTypeString, "te_particle", false) == 0) + { + return EffectType_TempEntParticle; + } + else if (strcmp(effectTypeString, "sound", false) == 0) + { + return EffectType_Sound; + } + else if (strcmp(effectTypeString, "screen_shake", false) == 0) + { + return EffectType_ScreenShake; + } + } + } + + property EffectEvent Event + { + public get() + { + char effectTypeString[64]; + this.GetString("event", effectTypeString, sizeof(effectTypeString), "constant"); + if (strcmp(effectTypeString, "constant", false) == 0) + { + return EffectEvent_Constant; + } + if (strcmp(effectTypeString, "boss_hitplayer", false) == 0) + { + return EffectEvent_HitPlayer; + } + if (strcmp(effectTypeString, "boss_seenbyplayer", false) == 0) + { + return EffectEvent_PlayerSeesBoss; + } + } + } + + property int DifficultyIndexes + { + public get() + { + return this.GetInt("difficulty_indexes", 123456); + } + } + + property RenderMode RenderMode + { + public get() + { + RenderMode val = view_as(this.GetInt("rendermode", view_as(RENDER_TRANSCOLOR))); + if (this.Type == EffectType_PointSpotlight) + { + val = RENDER_TRANSCOLOR; + } + return val; + } + } + + property RenderFx RenderEffect + { + public get() + { + return view_as(this.GetInt("renderfx", view_as(RENDERFX_NONE))); + } + } + + property int SpawnFlags + { + public get() + { + return this.GetInt("spawnflags", 0); + } + } + + property int FadeAlpha + { + public get() + { + return this.GetInt("renderamt", 255); + } + } + + property float LifeTime + { + public get() + { + return this.GetFloat("lifetime", -1.0); + } + } + + property float Delay + { + public get() + { + return this.GetFloat("delay", 0.0); + } + } + + property bool AttachPlayer + { + public get() + { + return this.GetBool("attach_player", false); + } + } + + public void GetOrigin(float buffer[3]) + { + this.GetVector("origin", buffer); + } + + public void GetAngles(float buffer[3]) + { + this.GetVector("angles", buffer); + } + + public void GetAttachment(char[] buffer, int bufferSize) + { + this.GetString("attachment_point", buffer, bufferSize); + } + + public void GetRenderColor(int difficulty, int buffer[4]) + { + this.GetDifficultyColor("rendercolor", difficulty, buffer); + } + + public ProfileInputsList GetInputs() + { + return view_as(this.GetSection("inputs")); + } + + public ProfileOutputsList GetOutputs() + { + return view_as(this.GetSection("outputs")); + } + + public void Precache() + { + switch (this.Type) + { + case EffectType_PropDynamic: + { + view_as(this).Precache(); + } + + case EffectType_TempEntBeamRing: + { + view_as(this).Precache(); + } + + case EffectType_Sound: + { + view_as(this).Precache(); + } + } + } +} + +methodmap ProfileEffect_Steam < ProfileEffect +{ + property int SpreadSpeed + { + public get() + { + return this.GetInt("spreadspeed", 0); + } + } + + property int Speed + { + public get() + { + return this.GetInt("speed", 0); + } + } + + property int StartSize + { + public get() + { + return this.GetInt("startsize", 0); + } + } + + property int EndSize + { + public get() + { + return this.GetInt("endsize", 0); + } + } + + property int Rate + { + public get() + { + return this.GetInt("rate", 0); + } + } + + property int JetLength + { + public get() + { + return this.GetInt("jetlength", 0); + } + } + + property float RollSpeed + { + public get() + { + return this.GetFloat("rollspeed", 0.0); + } + } + + property int ParticleType + { + public get() + { + return this.GetInt("particletype", 0); + } + } +} + +methodmap ProfileEffect_DynamicLight < ProfileEffect +{ + property int Brightness + { + public get() + { + return this.GetInt("brightness", 0); + } + } + + property float Distance + { + public get() + { + return this.GetFloat("distance", 0.0); + } + } + + property int Cone + { + public get() + { + return this.GetInt("cone", 0); + } + } + + property int LightStyle + { + public get() + { + return this.GetInt("lightstyle", 0); + } + } +} + +methodmap ProfileEffect_Particle < ProfileEffect +{ + public void GetName(char[] buffer, int bufferSize) + { + this.GetString("particlename", buffer, bufferSize); + } + + property bool HasControlPoint + { + public get() + { + return this.GetBool("control_point", false); + } + } + + public void GetControlPointOffset(float buffer[3]) + { + this.GetVector("control_point_offset", buffer); + } +} + +methodmap ProfileEffect_Trail < ProfileEffect +{ + property float Time + { + public get() + { + return this.GetFloat("trailtime", 1.0); + } + } + + property float StartWidth + { + public get() + { + return this.GetFloat("startwidth", 6.0); + } + } + + property float EndWidth + { + public get() + { + return this.GetFloat("endwidth", 15.0); + } + } + + public void GetName(char[] buffer, int bufferSize) + { + this.GetString("spritename", buffer, bufferSize); + } +} + +methodmap ProfileEffect_PropDynamic < ProfileEffect +{ + property float Scale + { + public get() + { + return this.GetFloat("modelscale", 1.0); + } + } + + property int Skin + { + public get() + { + return this.GetInt("modelskin", 0); + } + } + + public void GetName(char[] buffer, int bufferSize) + { + this.GetString("modelname", buffer, bufferSize); + } + + public void GetAnimation(char[] buffer, int bufferSize) + { + this.GetString("modelanimation", buffer, bufferSize); + } + + public void Precache() + { + char path[PLATFORM_MAX_PATH]; + this.GetName(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheModel2(path, _, _, g_FileCheckConVar.BoolValue); + } + } +} + +methodmap ProfileEffect_PointSpotlight < ProfileEffect +{ + property float Width + { + public get() + { + return this.GetFloat("spotlightwidth", 40.0); + } + } + + property float Length + { + public get() + { + return this.GetFloat("spotlightlength", 512.0); + } + } + + property float HaloScale + { + public get() + { + return this.GetFloat("halo_scale", 40.0); + } + } + + property int Brightness + { + public get() + { + return this.GetInt("brightness", 0); + } + } + + property float Distance + { + public get() + { + return this.GetFloat("distance", 0.0); + } + } + + property int Cone + { + public get() + { + return this.GetInt("cone", 0); + } + } +} + +methodmap ProfileEffect_Sprite < ProfileEffect +{ + property float Scale + { + public get() + { + return this.GetFloat("spritescale", 1.0); + } + } + + public void GetName(char[] buffer, int bufferSize) + { + this.GetString("spritename", buffer, bufferSize); + } +} + +methodmap ProfileEffect_TEBeamRing < ProfileEffect +{ + property float StartRadius + { + public get() + { + return this.GetFloat("start_radius", 5.0); + } + } + + property float EndRadius + { + public get() + { + return this.GetFloat("end_radius", 10.0); + } + } + + property int StartFrame + { + public get() + { + return this.GetInt("start_frame", 0); + } + } + + property int FrameRate + { + public get() + { + return this.GetInt("framerate", 12); + } + } + + property float Width + { + public get() + { + return this.GetFloat("width", 100.0); + } + } + + property float Amplitude + { + public get() + { + return this.GetFloat("amplitude", 1.0); + } + } + + property int Speed + { + public get() + { + return this.GetInt("speed", 5); + } + } + + property int Flags + { + public get() + { + return this.GetInt("flags", 0); + } + } + + public void GetRingColor(int buffer[4]) + { + this.GetColor("color", buffer); + } + + public void GetBeamSprite(char[] buffer, int bufferSize) + { + this.GetString("beam_sprite", buffer, bufferSize, "sprites/laser.vmt"); + } + + public void GetHaloSprite(char[] buffer, int bufferSize) + { + this.GetString("halo_sprite", buffer, bufferSize, "sprites/halo01.vmt"); + } + + property int BeamModel + { + public get() + { + return this.GetInt("__beam_model", -1); + } + + public set(int value) + { + this.SetInt("__beam_model", value); + } + } + + property int HaloModel + { + public get() + { + return this.GetInt("__halo_model", -1); + } + + public set(int value) + { + this.SetInt("__halo_model", value); + } + } + + public void Precache() + { + char sprite[PLATFORM_MAX_PATH], buffer[PLATFORM_MAX_PATH]; + this.GetBeamSprite(sprite, sizeof(sprite)); + if (sprite[0] != '\0') + { + this.BeamModel = PrecacheModel(sprite, true); + FormatEx(buffer, sizeof(buffer), "materials/%s", sprite); + AddFileToDownloadsTable(buffer); + } + + this.GetHaloSprite(sprite, sizeof(sprite)); + if (sprite[0] != '\0') + { + this.HaloModel = PrecacheModel(sprite, true); + FormatEx(buffer, sizeof(buffer), "materials/%s", sprite); + AddFileToDownloadsTable(buffer); + } + } +} + +methodmap ProfileEffect_TEParticle < ProfileEffect +{ + property int AttachType + { + public get() + { + int val = 0; + char attachType[64]; + this.GetString("attach_type", attachType, sizeof(attachType)); + if (strcmp(attachType, "follow_origin", false) == 0) + { + val = 1; + } + else if (strcmp(attachType, "start_at_customorigin", false) == 0) + { + val = 2; + } + else if (strcmp(attachType, "start_at_attachment", false) == 0) + { + val = 3; + } + else if (strcmp(attachType, "follow_attachment", false) == 0) + { + val = 4; + } + else if (strcmp(attachType, "start_at_worldorigin", false) == 0) + { + val = 5; + } + else if (strcmp(attachType, "follow_rootbone", false) == 0) + { + val = 6; + } + return val; + } + } + + property bool ResetParticles + { + public get() + { + return this.GetBool("reset_particles", true); + } + } + + property bool HasControlPoint + { + public get() + { + return this.GetBool("control_point", false); + } + } + + property int ControlPointAttachType + { + public get() + { + return this.GetInt("control_point_attach_type", 0); + } + } + + public void GetName(char[] buffer, int bufferSize) + { + this.GetString("particlename", buffer, bufferSize); + } + + public void GetStartPos(float buffer[3]) + { + this.GetVector("start", buffer); + } + + public void GetControlPointOffset(float buffer[3]) + { + this.GetVector("control_point_offset", buffer); + } +} + +methodmap ProfileEffect_Sound < ProfileEffect +{ + property ProfileSound Sounds + { + public get() + { + return view_as(this); + } + } + + public void Precache() + { + if (this.Sounds != null) + { + this.Sounds.Precache(); + } + } +} + +methodmap ProfileEffect_ScreenShake < ProfileEffect +{ + property float Amplitude + { + public get() + { + return this.GetFloat("amplitude", 25.0); + } + } + + property float Frequency + { + public get() + { + return this.GetFloat("frequency", 5.0); + } + } + + property float Duration + { + public get() + { + return this.GetFloat("duration", 1.0); + } + } + + property float Radius + { + public get() + { + return this.GetFloat("radius", 1000.0); + } + } + + /*SHAKE_START = 0, // Starts the screen shake for all players within the radius. + SHAKE_STOP, // Stops the screen shake for all players within the radius. + SHAKE_AMPLITUDE, // Modifies the amplitude of an active screen shake for all players within the radius. + SHAKE_FREQUENCY, // Modifies the frequency of an active screen shake for all players within the radius. + SHAKE_START_RUMBLEONLY, // Starts a shake effect that only rumbles the controller, no screen effect. + SHAKE_START_NORUMBLE, // Starts a shake that does NOT rumble the controller.*/ + property int Command + { + public get() + { + return this.GetInt("command", 0); + } + } + + property bool AirShake + { + public get() + { + return this.GetBool("air_shake", true); + } + } +} + void InitializeEffects() { g_OnEntityDestroyedPFwd.AddFunction(null, EntityDestroyed); @@ -23,7 +780,7 @@ static void EntityDestroyed(CBaseEntity ent, const char[] classname) continue; } - int index = g_NpcEffectsArray[i].FindValue(ent.index); + int index = g_NpcEffectsArray[i].FindValue(EntIndexToEntRef(ent.index)); if (index != -1) { g_NpcEffectsArray[i].Erase(index); @@ -31,7 +788,7 @@ static void EntityDestroyed(CBaseEntity ent, const char[] classname) } } -void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = true, const float overridePos[3] = { 0.0, 0.0, 0.0 }, const float overrideAng[3] = { 0.0, 0.0, 0.0 }, ArrayList &output = null, int entityOverride = INVALID_ENT_REFERENCE, bool noParenting = false) +void SlenderSpawnEffects(ProfileEffectMaster effects, int bossIndex, bool nonEffects = true, const float overridePos[3] = { 0.0, 0.0, 0.0 }, const float overrideAng[3] = { 0.0, 0.0, 0.0 }, ArrayList &output = null, int entityOverride = INVALID_ENT_REFERENCE, bool noParenting = false) { if (bossIndex < 0 || bossIndex >= MAX_BOSSES) { @@ -48,6 +805,7 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); + BaseBossProfile profileData = GetBossProfile(profile); if (g_NpcEffectsArray[bossIndex] == null) { @@ -56,7 +814,7 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru if (nonEffects) { - if (NPCGetDiscoModeState(bossIndex)) + if (profileData.DiscoMode) { int light = CreateEntityByName("light_dynamic"); int particle = CreateEntityByName("info_particle_system"); @@ -71,9 +829,9 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru TeleportEntity(light, effectPos, effectAng, NULL_VECTOR); SetVariantInt(5); AcceptEntityInput(light, "Brightness"); - SetVariantFloat(GetRandomFloat(NPCGetDiscoModeRadiusMin(bossIndex), NPCGetDiscoModeRadiusMax(bossIndex))); + SetVariantFloat(GetRandomFloat(profileData.DiscoDistanceMin, profileData.DiscoDistanceMax)); AcceptEntityInput(light, "Distance"); - SetVariantFloat(GetRandomFloat(NPCGetDiscoModeRadiusMin(bossIndex), NPCGetDiscoModeRadiusMax(bossIndex))); + SetVariantFloat(GetRandomFloat(profileData.DiscoDistanceMin, profileData.DiscoDistanceMax)); AcceptEntityInput(light, "spotlight_radius"); SetVariantInt(1); AcceptEntityInput(light, "cone"); @@ -90,7 +848,7 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru CreateTimer(0.1, Timer_DiscoLight, EntIndexToEntRef(light), TIMER_REPEAT); SDKHook(light, SDKHook_SetTransmit, Hook_EffectTransmitX); g_EntityEffectType[light] = EffectType_DynamicLight; - g_NpcEffectsArray[bossIndex].Push(light); + g_NpcEffectsArray[bossIndex].Push(EntIndexToEntRef(light)); } if (particle != -1) @@ -98,7 +856,7 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru float effectPos[3], effectAng[3], basePosX[3], baseAngX[3]; GetEntPropVector(slenderEnt, Prop_Data, "m_vecAbsOrigin", basePosX); GetEntPropVector(slenderEnt, Prop_Data, "m_angAbsRotation", baseAngX); - CopyVector(NPCGetDiscoModePos(bossIndex), effectPos); + profileData.GetDiscoPos(effectPos); VectorTransform(effectPos, basePosX, baseAngX, effectPos); AddVectors(effectAng, baseAngX, effectAng); TeleportEntity(particle, effectPos, effectAng, NULL_VECTOR); @@ -111,10 +869,10 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru AcceptEntityInput(particle, "SetParent", slenderEnt); SDKHook(particle, SDKHook_SetTransmit, Hook_EffectTransmitX); g_EntityEffectType[particle] = EffectType_Particle; - g_NpcEffectsArray[bossIndex].Push(particle); + g_NpcEffectsArray[bossIndex].Push(EntIndexToEntRef(particle)); } } - if (NPCGetFestiveLightState(bossIndex)) + if (profileData.FestiveLights) { int light = CreateEntityByName("light_dynamic"); if (light != -1) @@ -122,16 +880,16 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru float effectPos[3], effectAng[3], basePosX[3], baseAngX[3]; GetEntPropVector(slenderEnt, Prop_Data, "m_vecAbsOrigin", basePosX); GetEntPropVector(slenderEnt, Prop_Data, "m_angAbsRotation", baseAngX); - CopyVector(NPCGetFestiveLightPosition(bossIndex), effectPos); - CopyVector(NPCGetFestiveLightAngle(bossIndex), effectAng); + profileData.GetFestiveLightPos(effectPos); + profileData.GetFestiveLightAng(effectPos); VectorTransform(effectPos, basePosX, baseAngX, effectPos); AddVectors(effectAng, baseAngX, effectAng); TeleportEntity(light, effectPos, effectAng, NULL_VECTOR); - SetVariantInt(NPCGetFestiveLightBrightness(bossIndex)); + SetVariantInt(profileData.FestiveLightBrightness); AcceptEntityInput(light, "Brightness"); - SetVariantFloat(NPCGetFestiveLightDistance(bossIndex)); + SetVariantFloat(profileData.FestiveLightDistance); AcceptEntityInput(light, "Distance"); - SetVariantFloat(NPCGetFestiveLightRadius(bossIndex)); + SetVariantFloat(profileData.FestiveLightRadius); AcceptEntityInput(light, "spotlight_radius"); SetVariantInt(1); AcceptEntityInput(light, "cone"); @@ -160,7 +918,7 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru AcceptEntityInput(light, "SetParent", slenderEnt); SDKHook(light, SDKHook_SetTransmit, Hook_EffectTransmitX); g_EntityEffectType[light] = EffectType_Particle; - g_NpcEffectsArray[bossIndex].Push(light); + g_NpcEffectsArray[bossIndex].Push(EntIndexToEntRef(light)); } } } @@ -170,28 +928,35 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru return; } - SF2BossProfileBaseEffectInfo effectsInfo; - for (int i = 0, size = effects.Length; i < size; i++) + ProfileEffect effect; + for (int i = 0, size = effects.SectionLength; i < size; i++) { - effects.GetArray(i, effectsInfo, sizeof(effectsInfo)); + char key[128]; + effects.GetSectionNameFromIndex(i, key, sizeof(key)); + effect = view_as(effects.GetSection(key)); + + if (effect == null) + { + continue; + } // Validate effect event and type. Check to see if it matches with ours. - if (effectsInfo.Event != EffectEvent_Invalid) + if (effect.Event != EffectEvent_Invalid) { - if (effectsInfo.Type != EffectType_Invalid) + if (effect.Type != EffectType_Invalid) { // Check base position behavior. if (!slenderEnt || slenderEnt == INVALID_ENT_REFERENCE) { - LogError("Could not spawn effect %s for boss %d: unable to read position and angles due to boss entity not in game!"); + LogError("Could not spawn effect %s for boss %d: unable to read position and angles due to boss entity not in game!", key, bossIndex); continue; } - if (effectsInfo.Delay > 0.0) + if (effect.Delay > 0.0) { DataPack pack; - CreateDataTimer(effectsInfo.Delay, Timer_SpawnEffect, pack, TIMER_FLAG_NO_MAPCHANGE); - pack.WriteCellArray(effectsInfo, sizeof(effectsInfo)); + CreateDataTimer(effect.Delay, Timer_SpawnEffect, pack, TIMER_FLAG_NO_MAPCHANGE); + pack.WriteCell(effect); pack.WriteCell(bossIndex); pack.WriteCellArray(overridePos, sizeof(overridePos)); pack.WriteCellArray(overrideAng, sizeof(overrideAng)); @@ -201,12 +966,14 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru } else { - SpawnEffect(effectsInfo, bossIndex, overridePos, overrideAng, output, entityOverride, noParenting); + SpawnEffect(effect, bossIndex, overridePos, overrideAng, output, entityOverride, noParenting); } } else { - LogError("Could not spawn effect %s for boss %d: invalid type!", effectsInfo.SectionName, bossIndex); + char section[64]; + effect.GetSectionName(section, sizeof(section)); + LogError("Could not spawn effect %s for boss %d: invalid type!", section, bossIndex); } } } @@ -215,8 +982,7 @@ void SlenderSpawnEffects(ArrayList effects, int bossIndex, bool nonEffects = tru static Action Timer_SpawnEffect(Handle timer, DataPack pack) { pack.Reset(); - SF2BossProfileBaseEffectInfo effect; - pack.ReadCellArray(effect, sizeof(effect)); + ProfileEffect effect = pack.ReadCell(); int bossIndex = pack.ReadCell(); float overridePos[3], overrideAng[3]; pack.ReadCellArray(overridePos, sizeof(overridePos)); @@ -228,11 +994,11 @@ static Action Timer_SpawnEffect(Handle timer, DataPack pack) return Plugin_Stop; } -static void SpawnEffect(SF2BossProfileBaseEffectInfo effectsInfo, int bossIndex, const float overridePos[3] = { 0.0, 0.0, 0.0 }, const float overrideAng[3] = { 0.0, 0.0, 0.0 }, ArrayList &output = null, int entityOverride = INVALID_ENT_REFERENCE, bool noParenting = false) +static void SpawnEffect(ProfileEffect effect, int bossIndex, const float overridePos[3] = { 0.0, 0.0, 0.0 }, const float overrideAng[3] = { 0.0, 0.0, 0.0 }, ArrayList &output = null, int entityOverride = INVALID_ENT_REFERENCE, bool noParenting = false) { int slenderEnt = NPCGetEntIndex(bossIndex); int attacher = slenderEnt; - if (entityOverride != INVALID_ENT_REFERENCE && effectsInfo.AttachPlayer) + if (entityOverride != INVALID_ENT_REFERENCE && effect.AttachPlayer) { attacher = entityOverride; } @@ -255,7 +1021,7 @@ static void SpawnEffect(SF2BossProfileBaseEffectInfo effectsInfo, int bossIndex, GetEntPropVector(attacher, Prop_Data, "m_angAbsRotation", baseAng); } - int difficultyIndex = effectsInfo.DifficultyIndexes; + int difficultyIndex = effect.DifficultyIndexes; char indexes[8], currentIndex[2]; FormatEx(indexes, sizeof(indexes), "%d", difficultyIndex); FormatEx(currentIndex, sizeof(currentIndex), "%d", g_DifficultyConVar.IntValue); @@ -274,40 +1040,43 @@ static void SpawnEffect(SF2BossProfileBaseEffectInfo effectsInfo, int bossIndex, } } - int entity = -1; + CBaseEntity entity = view_as(-1); bool isEntity = true; - switch (effectsInfo.Type) + char section[64]; + effect.GetSectionName(section, sizeof(section)); + + switch (effect.Type) { case EffectType_Steam: { - entity = CreateEntityByName("env_steam"); + entity = CBaseEntity(CreateEntityByName("env_steam")); } case EffectType_DynamicLight: { - entity = CreateEntityByName("light_dynamic"); + entity = CBaseEntity(CreateEntityByName("light_dynamic")); } case EffectType_Particle: { - entity = CreateEntityByName("info_particle_system"); + entity = CBaseEntity(CreateEntityByName("info_particle_system")); } case EffectType_Trail: { - entity = CreateEntityByName("env_spritetrail"); + entity = CBaseEntity(CreateEntityByName("env_spritetrail")); } case EffectType_PropDynamic: { - entity = CreateEntityByName("prop_dynamic"); + entity = CBaseEntity(CreateEntityByName("prop_dynamic")); } case EffectType_PointSpotlight: { - entity = CreateEntityByName("sf2_point_spotlight"); + entity = CBaseEntity(CreateEntityByName("sf2_point_spotlight")); } case EffectType_Sprite: { - entity = CreateEntityByName("env_sprite"); + entity = CBaseEntity(CreateEntityByName("env_sprite")); } - case EffectType_TempEntBeamRing, EffectType_TempEntParticle, EffectType_Sound: + case EffectType_TempEntBeamRing, EffectType_TempEntParticle, EffectType_Sound, EffectType_ScreenShake: { isEntity = false; } @@ -315,261 +1084,325 @@ static void SpawnEffect(SF2BossProfileBaseEffectInfo effectsInfo, int bossIndex, float effectPos[3], effectAng[3]; - effectPos = effectsInfo.Origin; - effectAng = effectsInfo.Angles; + effect.GetOrigin(effectPos); + effect.GetAngles(effectAng); VectorTransform(effectPos, basePos, baseAng, effectPos); AddVectors(effectAng, baseAng, effectAng); - if (entity != -1 && isEntity) + if (entity.IsValid() && isEntity) { - TeleportEntity(entity, effectPos, effectAng, NULL_VECTOR); + entity.Teleport(effectPos, effectAng, NULL_VECTOR); + DispatchKeyValueInt(entity.index, "renderamt", effect.FadeAlpha); + DispatchKeyValueInt(entity.index, "rendermode", view_as(effect.RenderMode)); + DispatchKeyValueInt(entity.index, "renderfx", view_as(effect.RenderEffect)); + DispatchKeyValueInt(entity.index, "spawnflags", effect.SpawnFlags); + float lifeTime = effect.LifeTime; + char value[PLATFORM_MAX_PATH]; - DispatchKeyValueInt(entity, "renderamt", effectsInfo.FadeAlpha); - DispatchKeyValueInt(entity, "rendermode", view_as(effectsInfo.RenderModes)); - DispatchKeyValueInt(entity, "renderfx", view_as(effectsInfo.RenderEffects)); - DispatchKeyValueInt(entity, "spawnflags", effectsInfo.SpawnFlags); + FormatEx(value, sizeof(value), "%s%u", section, EntIndexToEntRef(attacher)); + entity.SetPropString(Prop_Data, "m_iName", value); + + if (effect.GetOutputs() != null) + { + effect.GetOutputs().AddOutputs(entity); + } - switch (effectsInfo.Type) + switch (effect.Type) { case EffectType_Steam: { - DispatchKeyValueInt(entity, "SpreadSpeed", effectsInfo.SteamSpreadSpeed); - DispatchKeyValueInt(entity, "Speed", effectsInfo.SteamSpeed); - DispatchKeyValueInt(entity, "StartSize", effectsInfo.SteamStartSize); - DispatchKeyValueInt(entity, "EndSize", effectsInfo.SteamEndSize); - DispatchKeyValueInt(entity, "Rate", effectsInfo.SteamRate); - DispatchKeyValueInt(entity, "Jetlength", effectsInfo.SteamJetLength); - DispatchKeyValueFloat(entity, "RollSpeed", effectsInfo.SteamRollSpeed); - DispatchKeyValueInt(entity, "type", effectsInfo.SteamType); - DispatchSpawn(entity); - ActivateEntity(entity); + ProfileEffect_Steam steam = view_as(effect); + DispatchKeyValueInt(entity.index, "SpreadSpeed", steam.SpreadSpeed); + DispatchKeyValueInt(entity.index, "Speed", steam.Speed); + DispatchKeyValueInt(entity.index, "StartSize", steam.StartSize); + DispatchKeyValueInt(entity.index, "EndSize", steam.EndSize); + DispatchKeyValueInt(entity.index, "Rate", steam.Rate); + DispatchKeyValueInt(entity.index, "Jetlength", steam.JetLength); + entity.KeyValueFloat("RollSpeed", steam.RollSpeed); + DispatchKeyValueInt(entity.index, "type", steam.ParticleType); + entity.Spawn(); + entity.Activate(); } case EffectType_DynamicLight: { - SetVariantInt(effectsInfo.LightBrightness); - AcceptEntityInput(entity, "Brightness"); - SetVariantFloat(effectsInfo.LightMaxDistance); - AcceptEntityInput(entity, "Distance"); - SetVariantFloat(effectsInfo.LightMaxDistance); - AcceptEntityInput(entity, "spotlight_radius"); - SetVariantInt(effectsInfo.LightCone); - AcceptEntityInput(entity, "cone"); - DispatchSpawn(entity); - ActivateEntity(entity); + ProfileEffect_DynamicLight light = view_as(effect); + SetVariantInt(light.Brightness); + entity.AcceptInput("Brightness"); + SetVariantFloat(light.Distance); + entity.AcceptInput("Distance"); + SetVariantFloat(light.Distance); + entity.AcceptInput("spotlight_radius"); + SetVariantInt(light.Cone); + entity.AcceptInput("cone"); + entity.Spawn(); + entity.Activate(); int renderColor[4]; - effectsInfo.Colors.GetArray(difficulty, renderColor, sizeof(renderColor)); + light.GetRenderColor(difficulty, renderColor); - SetEntityRenderColor(entity, renderColor[0], renderColor[1], renderColor[2], renderColor[3]); - SetEntProp(entity, Prop_Data, "m_LightStyle", effectsInfo.LightStyle); + entity.SetRenderColor(renderColor[0], renderColor[1], renderColor[2], renderColor[3]); + entity.SetProp(Prop_Data, "m_LightStyle", light.LightStyle); } case EffectType_Particle: { - DispatchKeyValue(entity, "effect_name", effectsInfo.ParticleName); - DispatchSpawn(entity); - ActivateEntity(entity); + ProfileEffect_Particle particle = view_as(effect); + char name[64]; + particle.GetName(name, sizeof(name)); + entity.KeyValue("effect_name", name); + if (particle.HasControlPoint) + { + int point = CreateEntityByName("info_particle_system"); // Sadly cannot use info_targets + if (IsValidEntity(point)) + { + float pointPos[3]; + particle.GetControlPointOffset(pointPos); + TeleportEntity(point, pointPos); + FormatEx(value, sizeof(value), "%s%u_controlpoint", section, EntIndexToEntRef(attacher)); + DispatchKeyValue(point, "targetname", value); + entity.KeyValue("cpoint1", value); + if (lifeTime > 0.0) + { + CreateTimer(lifeTime, Timer_KillEntity, EntIndexToEntRef(point), TIMER_FLAG_NO_MAPCHANGE); + } + + if (!noParenting) + { + g_NpcEffectsArray[bossIndex].Push(EntIndexToEntRef(point)); + } + + if (output != null) + { + output.Push(EntIndexToEntRef(point)); + } + } + } + entity.Spawn(); + entity.Activate(); } case EffectType_Trail: { - DispatchKeyValueFloat(entity, "lifetime", effectsInfo.TrailTime); - DispatchKeyValueFloat(entity, "startwidth", effectsInfo.TrailStartWidth); - DispatchKeyValueFloat(entity, "endwidth", effectsInfo.TrailEndWidth); - DispatchKeyValue(entity, "spritename", effectsInfo.TrailName); - SetEntPropFloat(entity, Prop_Send, "m_flTextureRes", 0.05); + ProfileEffect_Trail trail = view_as(effect); + char name[64]; + trail.GetName(name, sizeof(name)); + entity.KeyValueFloat("lifetime", trail.Time); + entity.KeyValueFloat("startwidth", trail.StartWidth); + entity.KeyValueFloat("endwidth", trail.EndWidth); + entity.KeyValue("spritename", name); + entity.SetPropFloat(Prop_Send, "m_flTextureRes", 0.05); int renderColor[4]; - effectsInfo.Colors.GetArray(difficulty, renderColor, sizeof(renderColor)); + trail.GetRenderColor(difficulty, renderColor); - SetEntityRenderColor(entity, renderColor[0], renderColor[1], renderColor[2], renderColor[3]); - DispatchSpawn(entity); - ActivateEntity(entity); + entity.SetRenderColor(renderColor[0], renderColor[1], renderColor[2], renderColor[3]); + entity.Spawn(); + entity.Activate(); } case EffectType_PropDynamic: { - DispatchKeyValue(entity, "model", effectsInfo.ModelName); - float modelScale = effectsInfo.ModelScale; - if (SF_SpecialRound(SPECIALROUND_TINYBOSSES) && modelScale != GetEntPropFloat(attacher, Prop_Send, "m_flModelScale") && !effectsInfo.AttachPlayer) + ProfileEffect_PropDynamic prop = view_as(effect); + char name[64]; + prop.GetName(name, sizeof(name)); + entity.KeyValue("model", name); + float modelScale = prop.Scale; + if (SF_SpecialRound(SPECIALROUND_TINYBOSSES) && modelScale != GetEntPropFloat(attacher, Prop_Send, "m_flModelScale") && !prop.AttachPlayer) { modelScale *= 0.5; } - DispatchKeyValueFloat(entity, "modelscale", modelScale); - SetEntProp(entity, Prop_Send, "m_nSkin", effectsInfo.ModelSkin); - SetEntProp(entity, Prop_Send, "m_fEffects", EF_BONEMERGE|EF_PARENT_ANIMATES); - if (effectsInfo.ModelAnimation[0] != '\0') + entity.KeyValueFloat("modelscale", modelScale); + entity.SetProp(Prop_Send, "m_nSkin", prop.Skin); + entity.SetProp(Prop_Send, "m_fEffects", EF_BONEMERGE | EF_PARENT_ANIMATES); + char animation[64]; + prop.GetAnimation(animation, sizeof(animation)); + if (animation[0] != '\0') { - SetVariantString(value); - AcceptEntityInput(entity, "SetAnimation"); + SetVariantString(animation); + entity.AcceptInput("SetAnimation"); } int renderColor[4]; - effectsInfo.Colors.GetArray(difficulty, renderColor, sizeof(renderColor)); + prop.GetRenderColor(difficulty, renderColor); - SetEntityRenderColor(entity, renderColor[0], renderColor[1], renderColor[2], renderColor[3]); - DispatchSpawn(entity); - ActivateEntity(entity); + entity.SetRenderColor(renderColor[0], renderColor[1], renderColor[2], renderColor[3]); + entity.Spawn(); + entity.Activate(); } case EffectType_PointSpotlight: { + ProfileEffect_PointSpotlight light = view_as(effect); int renderColor[4]; - effectsInfo.Colors.GetArray(difficulty, renderColor, sizeof(renderColor)); + light.GetRenderColor(difficulty, renderColor); - SF2PointSpotlightEntity spotlight = SF2PointSpotlightEntity(entity); - if (effectsInfo.AttachmentName[0] != '\0') + char attachment[64]; + light.GetAttachment(attachment, sizeof(attachment)); + + SF2PointSpotlightEntity spotlight = SF2PointSpotlightEntity(entity.index); + if (attachment[0] != '\0') { SetVariantString("!activator"); spotlight.Start.AcceptInput("ClearParent"); SetVariantString("!activator"); spotlight.Start.AcceptInput("SetParent", attacher); - SetVariantString(effectsInfo.AttachmentName); + SetVariantString(attachment); spotlight.Start.AcceptInput("SetParentAttachmentMaintainOffset"); SetVariantString("!activator"); spotlight.End.AcceptInput("ClearParent"); SetVariantString("!activator"); spotlight.End.AcceptInput("SetParent", attacher); - SetVariantString(effectsInfo.AttachmentName); + SetVariantString(attachment); spotlight.End.AcceptInput("SetParentAttachmentMaintainOffset"); } - spotlight.SetRenderColor(renderColor[0], renderColor[1], renderColor[2], renderColor[3]); - spotlight.Length = effectsInfo.SpotlightLength; - spotlight.Width = effectsInfo.SpotlightWidth; + spotlight.Length = light.Length; + spotlight.Width = light.Width; spotlight.EndWidth = spotlight.Width * 2.0; - spotlight.Brightness = effectsInfo.LightBrightness; - spotlight.Distance = effectsInfo.LightMaxDistance; - spotlight.SpotlightRadius = effectsInfo.LightMaxDistance; - spotlight.Cone = effectsInfo.LightCone; - spotlight.HaloScale = effectsInfo.SpotlightHaloScale; + spotlight.Brightness = light.Brightness; + spotlight.Distance = light.Distance; + spotlight.SpotlightRadius = light.Distance; + spotlight.Cone = light.Cone; + spotlight.HaloScale = light.HaloScale; + + spotlight.SetColor(renderColor); spotlight.Spawn(); spotlight.Activate(); spotlight.TurnOn(); - - //SDKHook(spotlight.Spotlight.index, SDKHook_SetTransmit, Hook_FlashlightSpotlightEffectSetTransmit); } case EffectType_Sprite: { - DispatchKeyValue(entity, "classname", "env_sprite"); - DispatchKeyValue(entity, "model", effectsInfo.SpriteName); - DispatchKeyValueFloat(entity, "scale", effectsInfo.SpriteScale); + ProfileEffect_Sprite sprite = view_as(effect); + char name[64]; + sprite.GetName(name, sizeof(name)); + entity.KeyValue("classname", "env_sprite"); + entity.KeyValue("model", name); + entity.KeyValueFloat("scale", sprite.Scale); int renderColor[4]; - effectsInfo.Colors.GetArray(difficulty, renderColor, sizeof(renderColor)); + sprite.GetRenderColor(difficulty, renderColor); - SetEntityRenderColor(entity, renderColor[0], renderColor[1], renderColor[2], renderColor[3]); + entity.SetRenderColor(renderColor[0], renderColor[1], renderColor[2], renderColor[3]); - DispatchSpawn(entity); - ActivateEntity(entity); + entity.Spawn(); + entity.Activate(); } } - float lifeTime = effectsInfo.LifeTime; if (lifeTime > 0.0) { - CreateTimer(lifeTime, Timer_KillEntity, EntIndexToEntRef(entity), TIMER_FLAG_NO_MAPCHANGE); + CreateTimer(lifeTime, Timer_KillEntity, EntIndexToEntRef(entity.index), TIMER_FLAG_NO_MAPCHANGE); } if (!noParenting) { if (!attacher || attacher == INVALID_ENT_REFERENCE) { - LogError("Could not parent effect %s of boss %d to itself: boss entity does not exist!", effectsInfo.SectionName, bossIndex); + LogError("Could not parent effect %s of boss %d to itself: boss entity does not exist!", section, bossIndex); return; } SetVariantString("!activator"); - AcceptEntityInput(entity, "SetParent", attacher); - if (effectsInfo.Attachment) + entity.AcceptInput("SetParent", attacher); + char attachment[64]; + effect.GetAttachment(attachment, sizeof(attachment)); + if (attachment[0] != '\0') { - if (effectsInfo.AttachmentName[0] != '\0') + SetVariantString(attachment); + if (effect.Type != EffectType_PropDynamic && effect.Type != EffectType_PointSpotlight) { - SetVariantString(effectsInfo.AttachmentName); - if (effectsInfo.Type != EffectType_PropDynamic && effectsInfo.Type != EffectType_PointSpotlight) - { - AcceptEntityInput(entity, "SetParentAttachment"); - } - else - { - AcceptEntityInput(entity, "SetParentAttachmentMaintainOffset"); - } + entity.AcceptInput("SetParentAttachment"); + } + else + { + entity.AcceptInput("SetParentAttachmentMaintainOffset"); } } } - switch (effectsInfo.Type) + switch (effect.Type) { case EffectType_Steam, EffectType_DynamicLight: { - AcceptEntityInput(entity, "TurnOn"); + entity.AcceptInput("TurnOn"); } case EffectType_Particle: { - AcceptEntityInput(entity, "start"); + entity.AcceptInput("start"); } case EffectType_Trail: { - AcceptEntityInput(entity, "showsprite"); + entity.AcceptInput("showsprite"); } case EffectType_PointSpotlight: { - AcceptEntityInput(entity, "TurnOn"); + entity.AcceptInput("TurnOn"); } case EffectType_Sprite: { - AcceptEntityInput(entity, "ShowSprite"); + entity.AcceptInput("ShowSprite"); } } - SDKHook(entity, SDKHook_SetTransmit, Hook_EffectTransmit); - g_EntityEffectType[entity] = effectsInfo.Type; - g_EntityEffectEvent[entity] = effectsInfo.Event; + + SDKHook(entity.index, SDKHook_SetTransmit, Hook_EffectTransmit); + g_EntityEffectType[entity.index] = effect.Type; + g_EntityEffectEvent[entity.index] = effect.Event; + + if (effect.GetInputs() != null) + { + effect.GetInputs().AcceptInputs(entity); + } + if (!noParenting) { - g_NpcEffectsArray[bossIndex].Push(entity); + g_NpcEffectsArray[bossIndex].Push(EntIndexToEntRef(entity.index)); } if (output != null) { - output.Push(EntIndexToEntRef(entity)); + output.Push(EntIndexToEntRef(entity.index)); } } else { - switch (effectsInfo.Type) + switch (effect.Type) { case EffectType_TempEntBeamRing: { - char colorString[32]; - strcopy(colorString, sizeof(colorString), effectsInfo.BeamRingColor); - char keys[4][4]; - ExplodeString(colorString, " ", keys, sizeof(keys), sizeof(keys)); + ProfileEffect_TEBeamRing ring = view_as(effect); int color[4]; - for (int i2 = 0; i2 < 4; i2++) - { - color[i2] = StringToInt(keys[i2]); - } + ring.GetRingColor(color); - TE_SetupBeamRingPoint(effectPos, effectsInfo.BeamRingStartRadius, effectsInfo.BeamRingEndRadius, effectsInfo.BeamRingBeamModel, effectsInfo.BeamRingHaloModel, effectsInfo.BeamRingStartFrame, effectsInfo.BeamRingFrameRate, effectsInfo.LifeTime, effectsInfo.BeamRingWidth, effectsInfo.BeamRingAmplitude, color, effectsInfo.BeamRingSpeed, effectsInfo.BeamRingFlags); + TE_SetupBeamRingPoint(effectPos, ring.StartRadius, ring.EndRadius, ring.BeamModel, ring.HaloModel, ring.StartFrame, ring.FrameRate, ring.LifeTime, ring.Width, ring.Amplitude, color, ring.Speed, ring.Flags); TE_SendToAll(); } case EffectType_TempEntParticle: { - if (effectsInfo.ParticleName[0] == '\0') + ProfileEffect_TEParticle particle = view_as(effect); + char name[64]; + particle.GetName(name, sizeof(name)); + if (name[0] == '\0') { return; } - int particle = PrecacheParticleSystem(effectsInfo.ParticleName); - if (particle == -1) + int particleIndex = PrecacheParticleSystem(name); + if (particleIndex == -1) { return; } - int attachment = LookupEntityAttachment(attacher, effectsInfo.AttachmentName); + char attachment[64]; + particle.GetAttachment(attachment, sizeof(attachment)); + int attachmentIndex = -1; + if (!noParenting) + { + attachmentIndex = LookupEntityAttachment(attacher, attachment); + } - float start[3], pos[3], ang[3]; - start = effectsInfo.TEParticleStartPos; - if (effectsInfo.TEParticleAttachType == 2) + float start[3], pos[3], ang[3], offset[3]; + particle.GetStartPos(start); + particle.GetControlPointOffset(offset); + if (particle.AttachType == 2) { VectorTransform(start, basePos, baseAng, start); pos = start; @@ -580,12 +1413,18 @@ static void SpawnEffect(SF2BossProfileBaseEffectInfo effectsInfo, int bossIndex, pos = effectPos; ang = effectAng; } - TE_Particle(particle, pos, start, ang, attacher, effectsInfo.TEParticleAttachType, attachment, effectsInfo.TEParticleReset, effectsInfo.TEParticleHasControlPoint, effectsInfo.TEParticleControlPointAttachType, effectsInfo.TEParticleControlPointOffset); + TE_Particle(particleIndex, pos, start, ang, noParenting ? -1 : attacher, particle.AttachType, attachmentIndex, particle.ResetParticles, particle.HasControlPoint, particle.ControlPointAttachType, offset); TE_SendToAll(); } case EffectType_Sound: { - effectsInfo.SoundSounds.EmitSound(_, attacher); + ProfileEffect_Sound sound = view_as(effect); + sound.Sounds.EmitSound(_, attacher); + } + case EffectType_ScreenShake: + { + ProfileEffect_ScreenShake shake = view_as(effect); + UTIL_ScreenShake(effectPos, shake.Amplitude, shake.Frequency, shake.Duration, shake.Radius, shake.Command, shake.AirShake); } } } @@ -615,7 +1454,7 @@ static Action Hook_EffectTransmit(int ent, int other) SF2_BasePlayer player = SF2_BasePlayer(other); if (player.IsValid && !player.IsEliminated && !player.IsInGhostMode && !player.HasEscaped) { - if (g_EntityEffectEvent[ent] == EffectEvent_PlayerSeesBoss && !controller.PlayerCanSee(player.index)) + if (g_EntityEffectEvent[ent] == EffectEvent_PlayerSeesBoss && !controller.PlayerCanSee(player.index) && !player.IsInDeathCam) { return Plugin_Handled; } @@ -669,14 +1508,9 @@ void SlenderRemoveEffects(int bossIndex, bool kill = false) return; } - if (g_NpcEffectsArray[bossIndex] != null && g_NpcEffectsArray[bossIndex].Length <= 0) - { - return; - } - - for (int effect = 0; effect < g_NpcEffectsArray[bossIndex].Length; effect++) + for (int effect = 0; effect < g_NpcEffectsArray[bossIndex].Length;) { - int ent = g_NpcEffectsArray[bossIndex].Get(effect); + int ent = EntRefToEntIndex(g_NpcEffectsArray[bossIndex].Get(effect)); if (!IsValidEntity(ent)) { continue; @@ -741,12 +1575,16 @@ static Action Timer_DiscoLight(Handle timer, any effect) return Plugin_Stop; } + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(bossIndex, profile, sizeof(profile)); + BaseBossProfile profileData = GetBossProfile(profile); + int rChase = GetRandomInt(75, 250); int gChase = GetRandomInt(75, 250); int bChase = GetRandomInt(75, 250); SetEntityRenderColor(effect, rChase, gChase, bChase, 255); - float distanceRNG = GetRandomFloat(NPCGetDiscoModeRadiusMin(bossIndex), NPCGetDiscoModeRadiusMax(bossIndex)); + float distanceRNG = GetRandomFloat(profileData.DiscoDistanceMin, profileData.DiscoDistanceMax); SetVariantFloat(distanceRNG); AcceptEntityInput(effect, "Distance"); @@ -788,3 +1626,63 @@ static Action Timer_FestiveLight(Handle timer, any effect) return Plugin_Continue; } + +void SetupNPCEffectsAPI() +{ + CreateNative("SF2_ProfileEffect.Type.get", Native_GetEffectType); + CreateNative("SF2_ProfileEffect.Precache", Native_EffectPrecache); + + CreateNative("SF2_ProfileEffectMaster.Precache", Native_MasterEffectPrecache); + CreateNative("SF2_ProfileEffectMaster.Spawn", Native_MasterEffectSpawn); +} + +static any Native_GetEffectType(Handle plugin, int numParams) +{ + ProfileEffect effect = GetNativeCell(1); + if (effect == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid Handle %x", effect); + } + + return effect.Type; +} + +static any Native_EffectPrecache(Handle plugin, int numParams) +{ + ProfileEffect effect = GetNativeCell(1); + if (effect == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid Handle %x", effect); + } + + effect.Precache(); + return 0; +} + +static any Native_MasterEffectPrecache(Handle plugin, int numParams) +{ + ProfileEffectMaster master = GetNativeCell(1); + if (master == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid Handle %x", master); + } + + master.Precache(); + return 0; +} + +static any Native_MasterEffectSpawn(Handle plugin, int numParams) +{ + ProfileEffectMaster master = GetNativeCell(1); + if (master == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid Handle %x", master); + } + + float pos[3], ang[3]; + GetNativeArray(3, pos, 3); + GetNativeArray(4, ang, 3); + ArrayList output = GetNativeCellRef(5); + SlenderSpawnEffects(master, GetNativeCell(2), false, pos, ang, output, GetNativeCell(6)); + return 0; +} diff --git a/addons/sourcemod/scripting/sf2/entities/initialize.sp b/addons/sourcemod/scripting/sf2/entities/initialize.sp index 87834773..d10d2cc9 100644 --- a/addons/sourcemod/scripting/sf2/entities/initialize.sp +++ b/addons/sourcemod/scripting/sf2/entities/initialize.sp @@ -1,3 +1,6 @@ +#pragma semicolon 1 +#pragma newdecls required + #include "sf2_base_projectile.sp" void InitializeCustomEntities() diff --git a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_arrow_projectile.sp b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_arrow_projectile.sp index 87d2464b..56d49e06 100644 --- a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_arrow_projectile.sp +++ b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_arrow_projectile.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_projectile_arrow"; @@ -71,6 +72,7 @@ methodmap SF2_ProjectileArrow < SF2_ProjectileBase arrow.Type = SF2BossProjectileType_Arrow; arrow.Speed = speed; arrow.Damage = damage; + arrow.IsCrits = isCrits; if (arrow.IsCrits) { CBaseEntity critParticle = arrow.CreateParticle("critical_rocket_blue"); @@ -87,7 +89,7 @@ methodmap SF2_ProjectileArrow < SF2_ProjectileBase arrow.SetMoveType(MOVETYPE_FLYGRAVITY); arrow.SetProp(Prop_Send, "m_usSolidFlags", 12); arrow.Teleport(pos, ang, NULL_VECTOR); - arrow.CreateTrail(true, "effects/arrowtrail_red.vmt", "255", "1"); + arrow.CreateTrail(true, trail, "255", "1"); arrow.SetVelocity(); SDKHook(arrow.index, SDKHook_StartTouch, StartTouch); @@ -179,7 +181,7 @@ static void StartTouch(int entity, int other) } else { - SDKHooks_TakeDamage(other, !IsValidEntity(owner) ? projectile.index : owner, !IsValidEntity(owner) ? projectile.index : owner, projectile.Damage, flags, _, _, pos); + SDKHooks_TakeDamage(other, !IsValidEntity(owner) ? projectile.index : owner, !IsValidEntity(owner) ? projectile.index : owner, projectile.Damage, flags, _, _, pos, .bypassHooks = false); } EmitSoundToAll(projectile.GetImpactSound(), projectile.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); RemoveEntity(projectile.index); diff --git a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_baseball_projectile.sp b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_baseball_projectile.sp index a9f6f33d..1b3833b3 100644 --- a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_baseball_projectile.sp +++ b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_baseball_projectile.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_projectile_baseball"; @@ -69,6 +70,7 @@ methodmap SF2_ProjectileBaseball < SF2_ProjectileGrenade ball.Type = SF2BossProjectileType_Baseball; ball.Speed = speed; ball.Damage = damage; + ball.IsCrits = isCrits; if (ball.IsCrits) { CBaseEntity critParticle = ball.CreateParticle("critical_rocket_blue"); @@ -81,7 +83,7 @@ methodmap SF2_ProjectileBaseball < SF2_ProjectileGrenade ball.SetProp(Prop_Send, "m_usSolidFlags", 12); ball.KeyValue("solid", "2"); ball.KeyValue("spawnflags", "4"); - SetEntityCollisionGroup(ball.index, COLLISION_GROUP_DEBRIS_TRIGGER); + SetEntityCollisionGroup(ball.index, 13); ball.SetProp(Prop_Send, "m_usSolidFlags", 0); ball.Spawn(); @@ -117,9 +119,10 @@ static void Think(int entity) projectile.GetAbsOrigin(pos); projectile.GetPropVector(Prop_Send, "m_vecMins", mins); projectile.GetPropVector(Prop_Send, "m_vecMaxs", maxs); - TR_TraceHullFilter(pos, pos, mins, maxs, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP, TraceRayGrenade, projectile.index); + Handle trace = TR_TraceHullFilterEx(pos, pos, mins, maxs, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP, TraceRayGrenade, projectile.index); - int hitIndex = TR_GetEntityIndex(); + int hitIndex = TR_GetEntityIndex(trace); + delete trace; if (IsValidEntity(hitIndex)) { Call_StartForward(g_OnProjectileTouchFwd); @@ -187,7 +190,7 @@ static void Think(int entity) } else { - SDKHooks_TakeDamage(hitIndex, !IsValidEntity(owner) ? projectile.index : owner, !IsValidEntity(owner) ? projectile.index : owner, projectile.Damage, flags, _, _, pos); + SDKHooks_TakeDamage(hitIndex, !IsValidEntity(owner) ? projectile.index : owner, !IsValidEntity(owner) ? projectile.index : owner, projectile.Damage, flags, _, _, pos, .bypassHooks = false); } } } diff --git a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_cow_mangler_projectile.sp b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_cow_mangler_projectile.sp index 44401841..593d6392 100644 --- a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_cow_mangler_projectile.sp +++ b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_cow_mangler_projectile.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_projectile_cowmangler"; diff --git a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_fireball_projectile.sp b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_fireball_projectile.sp index 3206cdbf..b348ee10 100644 --- a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_fireball_projectile.sp +++ b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_fireball_projectile.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_projectile_fireball"; @@ -35,11 +36,6 @@ methodmap SF2_ProjectileFireball < SF2_ProjectileBase CreateNative("SF2_Projectile_Fireball.IsValid.get", Native_IsValid); } - public void OnPlayerDamaged(SF2_BasePlayer player) - { - player.Ignite(true); - } - public static SF2_ProjectileFireball Create( const CBaseEntity owner, const float pos[3], @@ -58,7 +54,7 @@ methodmap SF2_ProjectileFireball < SF2_ProjectileBase } fireball.InitializeProjectile(SF2BossProjectileType_Fireball, owner, pos, ang, speed, damage, blastRadius, - false, "spell_fireball_small_red", "bombinomicon_burningdebris", impactSound, "models/roller.mdl", attackWaiters); + false, trail, "bombinomicon_burningdebris", impactSound, "models/roller.mdl", attackWaiters); return fireball; } diff --git a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_grenade_projectile.sp b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_grenade_projectile.sp index 65dcb688..114338dc 100644 --- a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_grenade_projectile.sp +++ b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_grenade_projectile.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_projectile_grenade"; @@ -258,6 +259,7 @@ methodmap SF2_ProjectileGrenade < SF2_ProjectileBase grenade.Speed = speed; grenade.Damage = damage; grenade.BlastRadius = blastRadius; + grenade.IsCrits = isCrits; if (grenade.IsCrits) { CBaseEntity critParticle = grenade.CreateParticle("critical_grenade_blue"); @@ -272,7 +274,7 @@ methodmap SF2_ProjectileGrenade < SF2_ProjectileBase grenade.SetProp(Prop_Send, "m_nSkin", 1); grenade.KeyValue("solid", "2"); grenade.KeyValue("spawnflags", "4"); - SetEntityCollisionGroup(grenade.index, COLLISION_GROUP_DEBRIS_TRIGGER); + SetEntityCollisionGroup(grenade.index, 13); grenade.SetProp(Prop_Send, "m_usSolidFlags", 0); CBaseEntity particle = grenade.CreateParticle(grenade.GetTrailName()); @@ -287,39 +289,29 @@ methodmap SF2_ProjectileGrenade < SF2_ProjectileBase grenade.Timer = GetGameTime() + 2.0; CreateTimer(0.1, Timer_Think, EntIndexToEntRef(grenade.index), TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); + SDKHook(grenade.index, SDKHook_VPhysicsUpdate, OnVPhysicsUpdate); } } -static Action Timer_Think(Handle timer, any ref) +static void OnVPhysicsUpdate(int entity) { - int entity = EntRefToEntIndex(ref); - if (!entity || entity == INVALID_ENT_REFERENCE) - { - return Plugin_Stop; - } SF2_ProjectileGrenade projectile = SF2_ProjectileGrenade(entity); - if (projectile.Timer < GetGameTime()) - { - projectile.DoExplosion(); - if (projectile.IsValid()) - { - RemoveEdict(projectile.index); - } - } - if (projectile.Touched) { - return Plugin_Continue; + return; } float pos[3], mins[3], maxs[3]; projectile.GetAbsOrigin(pos); projectile.GetPropVector(Prop_Send, "m_vecMins", mins); projectile.GetPropVector(Prop_Send, "m_vecMaxs", maxs); - TR_TraceHullFilter(pos, pos, mins, maxs, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP, TraceRayGrenade, projectile.index); + ScaleVector(mins, 1.1); + ScaleVector(maxs, 1.1); + Handle trace = TR_TraceHullFilterEx(pos, pos, mins, maxs, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP, TraceRayGrenade, projectile.index); - int hitIndex = TR_GetEntityIndex(); + int hitIndex = TR_GetEntityIndex(trace); + delete trace; if (IsValidEntity(hitIndex)) { Call_StartForward(g_OnProjectileTouchFwd); @@ -330,7 +322,7 @@ static Action Timer_Think(Handle timer, any ref) if (hitIndex == 0) { projectile.Touched = true; - return Plugin_Continue; + return; } else { @@ -358,6 +350,30 @@ static Action Timer_Think(Handle timer, any ref) } } } +} + +static Action Timer_Think(Handle timer, any ref) +{ + int entity = EntRefToEntIndex(ref); + if (!entity || entity == INVALID_ENT_REFERENCE) + { + return Plugin_Stop; + } + SF2_ProjectileGrenade projectile = SF2_ProjectileGrenade(entity); + + if (projectile.Timer < GetGameTime()) + { + projectile.DoExplosion(); + if (projectile.IsValid()) + { + RemoveEdict(projectile.index); + } + } + + if (projectile.Touched) + { + return Plugin_Continue; + } return Plugin_Continue; } diff --git a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_iceball_projectile.sp b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_iceball_projectile.sp index 664e40b4..f8175c30 100644 --- a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_iceball_projectile.sp +++ b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_iceball_projectile.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_projectile_iceball"; @@ -78,12 +79,6 @@ methodmap SF2_ProjectileIceball < SF2_ProjectileBase this.SetPropString(Prop_Data, "m_FreezeSound", value); } - public void OnPlayerDamaged(SF2_BasePlayer player) - { - EmitSoundToClient(player.index, this.GetFreezeSound(), _, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); - player.Stun(this.SlowDuration, this.SlowMultiplier, TF_STUNFLAG_SLOWDOWN, player.index); - } - public static SF2_ProjectileIceball Create( const CBaseEntity owner, const float pos[3], @@ -108,7 +103,7 @@ methodmap SF2_ProjectileIceball < SF2_ProjectileBase iceball.SlowMultiplier = slowMultiplier; iceball.SetFreezeSound(freezeSound); iceball.InitializeProjectile(SF2BossProjectileType_Iceball, owner, pos, ang, speed, damage, blastRadius, - false, "spell_fireball_small_blue", "spell_batball_impact_blue", impactSound, "models/roller.mdl", attackWaiters); + false, trail, "spell_batball_impact_blue", impactSound, "models/roller.mdl", attackWaiters); return iceball; } } diff --git a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_rocket_projectile.sp b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_rocket_projectile.sp index 21ddebee..182aeade 100644 --- a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_rocket_projectile.sp +++ b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_rocket_projectile.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_projectile_rocket"; diff --git a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_sentry_rocket_projectile.sp b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_sentry_rocket_projectile.sp index 400c5ec4..c51999f3 100644 --- a/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_sentry_rocket_projectile.sp +++ b/addons/sourcemod/scripting/sf2/entities/projectiles/sf2_sentry_rocket_projectile.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_projectile_sentryrocket"; diff --git a/addons/sourcemod/scripting/sf2/entities/sf2_base_projectile.sp b/addons/sourcemod/scripting/sf2/entities/sf2_base_projectile.sp index f6b6efde..0beaddf6 100644 --- a/addons/sourcemod/scripting/sf2/entities/sf2_base_projectile.sp +++ b/addons/sourcemod/scripting/sf2/entities/sf2_base_projectile.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_Factory; @@ -316,11 +317,6 @@ methodmap SF2_ProjectileBase < CBaseAnimating ScaleVector(buffer, 2.0); } - public void OnPlayerDamaged(SF2_BasePlayer player) - { - // Do nothing - } - public void DoExplosion() { int owner = this.GetPropEnt(Prop_Send, "m_hOwnerEntity"); @@ -437,16 +433,18 @@ methodmap SF2_ProjectileBase < CBaseAnimating float targetPos[3]; valid.WorldSpaceCenter(targetPos); - TR_TraceRayFilter(pos, targetPos, + Handle trace = TR_TraceRayFilterEx(pos, targetPos, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_GRATE, RayType_EndPoint, TraceRayDontHitAnyEntity, this.index); - if (TR_DidHit() && TR_GetEntityIndex() != valid.index) + if (TR_DidHit(trace) && TR_GetEntityIndex(trace) != valid.index) { + delete trace; continue; } - TR_GetEndPosition(subtracted); + TR_GetEndPosition(subtracted, trace); + delete trace; SubtractVectors(pos, subtracted, subtracted); adjustedDamage = GetVectorLength(subtracted) * falloff; @@ -464,7 +462,7 @@ methodmap SF2_ProjectileBase < CBaseAnimating } float force[3]; this.GetDamageForce(valid, force); - SDKHooks_TakeDamage(valid.index, !IsValidEntity(owner) ? this.index : owner, !IsValidEntity(owner) ? this.index : owner, adjustedDamage, flags, _, force, pos); + SDKHooks_TakeDamage(valid.index, !IsValidEntity(owner) ? this.index : owner, !IsValidEntity(owner) ? this.index : owner, adjustedDamage, flags, _, force, pos, .bypassHooks = false); if (SF2_BasePlayer(valid.index).IsValid) { Call_StartForward(g_OnPlayerDamagedByProjectilePFwd); diff --git a/addons/sourcemod/scripting/sf2/extras/afk_mode.sp b/addons/sourcemod/scripting/sf2/extras/afk_mode.sp index b81d33b2..568e9bed 100644 --- a/addons/sourcemod/scripting/sf2/extras/afk_mode.sp +++ b/addons/sourcemod/scripting/sf2/extras/afk_mode.sp @@ -4,6 +4,7 @@ #define _sf2_afk_mode_included #pragma semicolon 1 +#pragma newdecls required static float g_AfkAtGameTime[MAXTF2PLAYERS]; diff --git a/addons/sourcemod/scripting/sf2/extras/commands.sp b/addons/sourcemod/scripting/sf2/extras/commands.sp index 8c0792a0..049a2bbb 100644 --- a/addons/sourcemod/scripting/sf2/extras/commands.sp +++ b/addons/sourcemod/scripting/sf2/extras/commands.sp @@ -4,6 +4,7 @@ #define _sf2_commands_included #pragma semicolon 1 +#pragma newdecls required public void OnPluginStart() { @@ -71,8 +72,9 @@ public void OnPluginStart() } g_Pages = new ArrayList(sizeof(SF2PageEntityData)); - g_PageMusicRanges = new ArrayList(3); g_EmptySpawnPagePoints = new ArrayList(); + g_PageLocations = new ArrayList(); + g_PageLocationsGlow = new ArrayList(); char valueToString[32]; @@ -112,6 +114,8 @@ public void OnPluginStart() g_DragonsFuryBurningBonusConVar = FindConVar("tf_fireball_burning_bonus"); g_DragonsFuryBurnDurationConVar = FindConVar("tf_fireball_burn_duration"); + g_TFBotForceClassConVar = FindConVar("tf_bot_force_class"); + g_PlayerShakeEnabledConVar = CreateConVar("sf2_player_shake_enabled", "1", "Enable/Disable player view shake during boss encounters.", _, true, 0.0, true, 1.0); g_PlayerShakeEnabledConVar.AddChangeHook(OnConVarChanged); g_PlayerShakeFrequencyMaxConVar = CreateConVar("sf2_player_shake_frequency_max", "255", "Maximum frequency value of the shake. Should be a value between 1-255.", _, true, 1.0, true, 255.0); @@ -297,7 +301,6 @@ public void OnPluginStart() RegConsoleCmd("sm_slviewbosslist", Command_BossList); RegConsoleCmd("sm_slbosslist", Command_BossList); RegConsoleCmd("sm_slafk", Command_NoPoints); - RegConsoleCmd("sm_flashlight", Command_ToggleFlashlight); RegConsoleCmd("sm_slhud", Command_MenuSwitchHud); RegConsoleCmd("sm_slviewbob", Command_MenuViewBob); RegConsoleCmd("+sprint", Command_SprintOn); @@ -347,10 +350,13 @@ public void OnPluginStart() RegAdminCmd("sm_sf2_player_infinite_blink_override", Command_InfiniteBlink, ADMFLAG_SLAY); RegAdminCmd("sm_sf2_wall_hax", Command_WallHax, ADMFLAG_SLAY); RegAdminCmd("sm_sf2_keep_weapons", Command_KeepWeapons, ADMFLAG_SLAY); + RegAdminCmd("sm_sf2_reveal_page_locations", Command_RevealPageLocations, ADMFLAG_CHEATS); RegAdminCmd("+alltalk", Command_AllTalkOn, ADMFLAG_SLAY); RegAdminCmd("-alltalk", Command_AllTalkOff, ADMFLAG_SLAY); RegAdminCmd("+slalltalk", Command_AllTalkOn, ADMFLAG_SLAY, _, _, FCVAR_HIDDEN); RegAdminCmd("-slalltalk", Command_AllTalkOff, ADMFLAG_SLAY, _, _, FCVAR_HIDDEN); + RegAdminCmd("sm_sf2_do_trace", Command_DoTrace, ADMFLAG_CHEATS); + RegAdminCmd("sm_sf2_steamid", Command_SteamID, ADMFLAG_CHEATS); RegServerCmd("load_itempreset", Command_BlockCommand); @@ -407,12 +413,18 @@ public void OnPluginStart() g_OnGameFramePFwd = new PrivateForward(ET_Ignore); g_OnRoundStartPFwd = new PrivateForward(ET_Ignore); g_OnRoundEndPFwd = new PrivateForward(ET_Ignore); + g_OnConfigsExecutedPFwd = new PrivateForward(ET_Ignore); g_OnEntityCreatedPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_String); g_OnEntityDestroyedPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_String); g_OnEntityTeleportedPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell); + g_OnBuildingDestroyedPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell); + g_OnPostInitMapEntitiesPFwd = new PrivateForward(ET_Ignore); + g_OnPostInitNewGamePFwd = new PrivateForward(ET_Ignore); + g_OnRoundStateChangePFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell); g_OnPlayerJumpPFwd = new PrivateForward(ET_Ignore, Param_Cell); g_OnPlayerSpawnPFwd = new PrivateForward(ET_Ignore, Param_Cell); - g_OnPlayerTakeDamagePFwd = new PrivateForward(ET_Hook, Param_Cell, Param_CellByRef, Param_CellByRef, Param_FloatByRef, Param_CellByRef); + g_OnPlayerTakeDamagePFwd = new PrivateForward(ET_Hook, Param_Cell, Param_CellByRef, Param_CellByRef, Param_FloatByRef, Param_CellByRef, Param_Cell); + g_OnPlayerTakeDamagePostPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Float, Param_Cell); g_OnPlayerDeathPrePFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell); g_OnPlayerDeathPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell); g_OnPlayerPutInServerPFwd = new PrivateForward(ET_Ignore, Param_Cell); @@ -420,6 +432,8 @@ public void OnPluginStart() g_OnPlayerEscapePFwd = new PrivateForward(ET_Ignore, Param_Cell); g_OnPlayerTeamPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell); g_OnPlayerClassPFwd = new PrivateForward(ET_Ignore, Param_Cell); + g_OnPlayerPressButtonPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell); + g_OnPlayerReleaseButtonPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell); g_OnPlayerLookAtBossPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell); g_OnPlayerChangePlayStatePFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell); g_OnPlayerChangeGhostStatePFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell); @@ -430,7 +444,10 @@ public void OnPluginStart() g_OnPlayerTurnOffFlashlightPFwd = new PrivateForward(ET_Ignore, Param_Cell); g_OnPlayerFlashlightBreakPFwd = new PrivateForward(ET_Ignore, Param_Cell); g_OnPlayerAverageUpdatePFwd = new PrivateForward(ET_Ignore, Param_Cell); + g_OnPlayerPostWeaponsPFwd = new PrivateForward(ET_Ignore, Param_Cell); + g_OnPageCountChangedPFwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell); g_OnSpecialRoundStartPFwd = new PrivateForward(ET_Ignore, Param_Cell); + g_OnBossAddedPFwd = new PrivateForward(ET_Ignore, Param_Cell); g_OnBossSpawnPFwd = new PrivateForward(ET_Ignore, Param_Cell); g_OnBossRemovedPFwd = new PrivateForward(ET_Ignore, Param_Cell); g_OnChaserGetAttackActionPFwd = new PrivateForward(ET_Hook, Param_Cell, Param_String, Param_CellByRef); @@ -462,16 +479,19 @@ public void OnPluginStart() InitializeEffects(); + SetupClients(); + SetupAntiCamping(); SetupBlink(); SetupBreathing(); SetupDeathCams(); SetupGhost(); SetupProxy(); + SetupPlayerWeapons(); SetupHints(); SetupStatic(); SetupFlashlight(); - SetupMusic(); + SetupNewMusic(); SetupSprint(); SetupUltravision(); SetupPlayerGlows(); @@ -657,29 +677,6 @@ static Action Command_NoPoints(int client, int args) return Plugin_Handled; } -static Action Command_ToggleFlashlight(int client, int args) -{ - if (!g_Enabled) - { - return Plugin_Continue; - } - - if (!IsValidClient(client) || !IsPlayerAlive(client)) - { - return Plugin_Handled; - } - - if (!IsRoundInWarmup() && !IsRoundInIntro() && !IsRoundEnding() && !DidClientEscape(client)) - { - if (GetGameTime() >= ClientGetFlashlightNextInputTime(client)) - { - ClientHandleFlashlight(client); - } - } - - return Plugin_Handled; -} - static Action Command_SprintOn(int client, int args) { if (!g_Enabled) @@ -1128,14 +1125,14 @@ static Action Hook_CommandVoiceMenu(int client, const char[] command,int argc) int master = NPCGetFromUniqueID(g_PlayerProxyMaster[client]); if (master != -1) { - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(master, profile, sizeof(profile)); - SF2BossProfileSoundInfo soundInfo; - GetBossProfileProxyIdleSounds(profile, soundInfo); + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(master); + BossProfileProxyData proxyData = npc.GetProfileData().GetProxies(); + BossProfileProxyClass classData = proxyData.GetClassData(TF2_GetPlayerClass(client)); + ProfileSound soundInfo = classData.GetIdleSounds(); if (soundInfo.Paths != null && soundInfo.Paths.Length > 0 && GetGameTime() >= g_PlayerProxyNextVoiceSound[client]) { soundInfo.EmitSound(_, client); - g_PlayerProxyNextVoiceSound[client] = GetGameTime() + GetRandomFloat(soundInfo.CooldownMin, soundInfo.CooldownMax); + g_PlayerProxyNextVoiceSound[client] = GetGameTime() + GetRandomFloat(soundInfo.GetCooldownMin(npc.Difficulty), soundInfo.GetCooldownMax(npc.Difficulty)); } } } @@ -1181,7 +1178,22 @@ static Action Command_ClientKillDeathcam(int client, int args) for (int i = 0; i < target_count; i++) { int target = target_list[i]; - if (!IsValidClient(target) || !IsPlayerAlive(target) || g_PlayerEliminated[target] || IsClientInGhostMode(target)) + if (!IsValidClient(target) || !IsPlayerAlive(target) || IsClientInGhostMode(target)) + { + continue; + } + + bool eliminated = false; + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(StringToInt(arg2)); + if (npc.IsValid()) + { + if ((npc.Flags & SFF_ATTACKWAITERS) != 0 || npc.GetProfileData().IsPvEBoss) + { + eliminated = true; + } + } + + if (!eliminated && g_PlayerEliminated[target]) { continue; } @@ -1263,9 +1275,7 @@ static Action Command_SpawnSlender(int client, int args) return Plugin_Handled; } - SF2BossProfileData data; - data = npc.GetProfileData(); - if (data.IsPvEBoss) + if (npc.GetProfileData().IsPvEBoss) { ReplyToCommand(client, "You may not spawn PvE bosses!"); return Plugin_Handled; @@ -1288,6 +1298,7 @@ static Action Command_SpawnSlender(int client, int args) { CPrintToChat(client, "{royalblue}%t {default}%T", "SF2 Prefix", "SF2 Spawned Boss", client); } + LogAction(client, -1, "%N spawned boss %d! (%s)", client, npc.Index, profile); return Plugin_Handled; } @@ -1329,9 +1340,7 @@ static Action Command_SpawnAllSlenders(int client, int args) npc = SF2NPC_BaseNPC(npcIndex); if (npc.IsValid()) { - SF2BossProfileData data; - data = npc.GetProfileData(); - if (data.IsPvEBoss) + if (npc.GetProfileData().IsPvEBoss) { continue; } @@ -1386,9 +1395,7 @@ static Action Timer_SpawnAllSlenders(Handle timer, any userid) SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(g_SpawnAllBossesCount); if (npc.IsValid()) { - SF2BossProfileData data; - data = npc.GetProfileData(); - if (!data.IsPvEBoss) + if (!npc.GetProfileData().IsPvEBoss) { npc.Spawn(endPos); } @@ -1422,8 +1429,7 @@ static Action Command_RemoveSlender(int client, int args) char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); - SF2BossProfileData data; - g_BossProfileData.GetArray(profile, data, sizeof(data)); + BaseBossProfile data = GetBossProfile(profile); if (data.IsPvEBoss) { ReplyToCommand(client, "You may not remove PvE bosses!"); @@ -1436,6 +1442,7 @@ static Action Command_RemoveSlender(int client, int args) { CPrintToChat(client, "{royalblue}%t {default}%T", "SF2 Prefix", "SF2 Removed Boss", client); } + LogAction(client, -1, "%N removed boss %d! (%s)", client, bossIndex, profile); return Plugin_Handled; } @@ -1457,9 +1464,7 @@ static Action Command_RemoveAllSlenders(int client, int args) continue; } - SF2BossProfileData data; - data = npc.GetProfileData(); - if (data.IsPvEBoss) + if (npc.GetProfileData().IsPvEBoss) { continue; } @@ -1478,11 +1483,6 @@ static Action Command_RemoveAllSlenders(int client, int args) } } - if (MusicActive()) - { - NPCStopMusic(); - } - return Plugin_Handled; } @@ -1770,34 +1770,6 @@ static Action Command_ReloadProfiles(int client, int args) ReloadClassConfigs(); } - for (int i = 0; i < MAX_BOSSES; i++) - { - SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(i); - - if (!npc.IsValid()) - { - continue; - } - - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - npc.GetProfile(profile, sizeof(profile)); - SF2BossProfileData data; - g_BossProfileData.GetArray(profile, data, sizeof(data)); - NPCSetProfileData(npc.Index, data); - - if (npc.Type == SF2BossType_Chaser) - { - SF2ChaserBossProfileData chaserData; - g_ChaserBossProfileData.GetArray(profile, chaserData, sizeof(chaserData)); - NPCChaserSetProfileData(npc.Index, chaserData); - } - else if (npc.Type == SF2BossType_Statue) - { - SF2StatueBossProfileData statueData; - g_StatueBossProfileData.GetArray(profile, statueData, sizeof(statueData)); - NPCStatueSetProfileData(npc.Index, statueData); - } - } CPrintToChatAll("{royalblue}%t {default} Reloaded all profiles successfully.", "SF2 Prefix"); return Plugin_Handled; @@ -2272,8 +2244,7 @@ static Action Command_AddSlender(int client, int args) return Plugin_Handled; } - SF2BossProfileData data; - g_BossProfileData.GetArray(profile, data, sizeof(data)); + BaseBossProfile data = GetBossProfile(profile); if (data.IsPvEBoss) { ReplyToCommand(client, "You may not spawn PvE bosses!"); @@ -2283,10 +2254,12 @@ static Action Command_AddSlender(int client, int args) SF2NPC_BaseNPC npc = AddProfile(profile); if (npc.IsValid()) { - if (SF_IsBoxingMap() && (GetRoundState() == SF2RoundState_Escape) && NPCChaserIsBoxingBoss(npc.Index)) + if (SF_IsBoxingMap() && (GetRoundState() == SF2RoundState_Escape) && view_as(data).BoxingBoss) { g_SlenderBoxingBossCount++; } + + LogAction(client, -1, "%N added a boss! (%s)", client, profile); } return Plugin_Handled; @@ -2390,8 +2363,7 @@ static Action Command_AddSlenderFake(int client, int args) return Plugin_Handled; } - SF2BossProfileData data; - g_BossProfileData.GetArray(profile, data, sizeof(data)); + BaseBossProfile data = GetBossProfile(profile); if (data.IsPvEBoss) { ReplyToCommand(client, "You may not spawn PvE bosses!"); @@ -2410,6 +2382,8 @@ static Action Command_AddSlenderFake(int client, int args) delete trace; npc.Spawn(pos); + + LogAction(client, -1, "%N added a fake boss! (%s)", client, profile); } return Plugin_Handled; @@ -2556,6 +2530,72 @@ static Action Command_AllTalkOff(int client, int args) return Plugin_Handled; } +static Action Command_DoTrace(int client, int args) +{ + if (!IsValidClient(client)) + { + return Plugin_Handled; + } + + float eyes[3], end[3], ang[3]; + end[0] = 5000.0; + GetClientEyePosition(client, eyes); + GetClientEyeAngles(client, ang); + VectorTransform(end, eyes, ang, end); + + Profiler profiler = new Profiler(); + + profiler.Start(); + Handle trace = TR_TraceHullFilterEx(eyes, end, { -16.0, -16.0, -16.0 }, { 16.0, 16.0, 16.0 }, MASK_NPCSOLID, TraceRayBossVisibility, client); + PrintToServer("%b %i", TR_DidHit(trace), TR_GetEntityIndex(trace)); + profiler.Stop(); + PrintToServer("Time it took to trace handle 1: %f", profiler.Time); + + profiler.Start(); + TR_TraceHullFilter(eyes, end, { -16.0, -16.0, -16.0 }, { 16.0, 16.0, 16.0 }, MASK_NPCSOLID, TraceRayBossVisibility, client); + PrintToServer("%b %i", TR_DidHit(), TR_GetEntityIndex()); + profiler.Stop(); + PrintToServer("Time it took to trace global 1: %f", profiler.Time); + + delete trace; + + profiler.Start(); + trace = TR_TraceHullFilterEx(eyes, end, { -16.0, -16.0, -16.0 }, { 16.0, 16.0, 16.0 }, MASK_NPCSOLID, TraceRayBossVisibility, client); + PrintToServer("%b %i", TR_DidHit(trace), TR_GetEntityIndex(trace)); + profiler.Stop(); + PrintToServer("Time it took to trace handle 2: %f", profiler.Time); + + profiler.Start(); + TR_TraceHullFilter(eyes, end, { -16.0, -16.0, -16.0 }, { 16.0, 16.0, 16.0 }, MASK_NPCSOLID, TraceRayBossVisibility, client); + PrintToServer("%b %i", TR_DidHit(), TR_GetEntityIndex()); + profiler.Stop(); + PrintToServer("Time it took to trace global 2: %f", profiler.Time); + + delete trace; + delete profiler; + return Plugin_Handled; +} + +static Action Command_SteamID(int client, int args) +{ + if (!IsValidClient(client)) + { + return Plugin_Handled; + } + + char authA[64], authB[64], authC[64], authD[4]; + GetClientAuthId(client, AuthId_Engine, authA, sizeof(authA)); + GetClientAuthId(client, AuthId_Steam2, authB, sizeof(authB)); + GetClientAuthId(client, AuthId_Steam3, authC, sizeof(authC)); + GetClientAuthId(client, AuthId_SteamID64, authD, sizeof(authD)); + PrintToChatAll("Engine: %s", authA); + PrintToChatAll("Steam2: %s", authB); + PrintToChatAll("Steam3: %s", authC); + PrintToChatAll("Steam64: %s", authD); + PrintToChatAll("SteamID: %i", GetSteamAccountID(client)); + return Plugin_Handled; +} + static Action Command_ConditionToggle(int client, int args) { g_IgnoreRoundWinConditionsConVar.BoolValue = !g_IgnoreRoundWinConditionsConVar.BoolValue; @@ -2879,3 +2919,113 @@ static Action Command_KeepWeapons(int client, int args) CPrintToChat(client, "{royalblue}%t {default}Lobby players can %s keep their weapons.", "SF2 Prefix", g_PlayerKeepWeaponsConVar.BoolValue ? "now" : "no longer"); return Plugin_Handled; } + +static Action Command_RevealPageLocations(int client, int args) +{ + if (!IsValidClient(client)) + { + return Plugin_Handled; + } + + if (!g_PagesRevealed) + { + int count = 0; + EmitSoundToClient(client, DEBUG_PAGEREVEALSOUND); + for (int i = 0; i < g_EmptySpawnPagePoints.Length; i++) + { + float pos[3], ang[3]; + CBaseEntity location = CBaseEntity(g_EmptySpawnPagePoints.Get(i)); + location.GetAbsOrigin(pos); + location.GetAbsAngles(ang); + CBaseEntity page = CBaseEntity(CreateEntityByName("prop_dynamic_override")); + page.KeyValue("solid", "0"); + page.SetModel(PAGE_MODEL); + page.Teleport(pos, ang); + page.Spawn(); + page.Activate(); + page.AcceptInput("DisableCollision"); + page.SetPropFloat(Prop_Send, "m_flModelScale", PAGE_MODELSCALE); + SetEntityTransmitState(page.index, FL_EDICT_ALWAYS); + + g_PageLocations.Push(EntIndexToEntRef(page.index)); + CBaseEntity glow = CBaseEntity(CreateGlowEntityDataless(page.index)); + glow.SetPropEnt(Prop_Data, "m_hOwnerEntity", page.index); + g_PageLocationsGlow.Push(EntIndexToEntRef(glow.index)); + count++; + } + + for (int i = 0; i < g_Pages.Length; i++) + { + SF2PageEntityData pageData; + g_Pages.GetArray(i, pageData, sizeof(pageData)); + + CBaseEntity page = CBaseEntity(EntRefToEntIndex(pageData.EntRef)); + if (!page.IsValid()) + { + page = CBaseEntity(CreateEntityByName("prop_dynamic_override")); + page.KeyValue("solid", "0"); + page.SetModel(PAGE_MODEL); + page.Teleport(pageData.Pos, pageData.Ang); + page.Spawn(); + page.Activate(); + page.AcceptInput("DisableCollision"); + page.SetPropFloat(Prop_Send, "m_flModelScale", PAGE_MODELSCALE); + SetEntityTransmitState(page.index, FL_EDICT_ALWAYS); + g_PageLocations.Push(EntIndexToEntRef(page.index)); + } + else + { + page = CBaseEntity(page.GetPropEnt(Prop_Send, "m_hOwnerEntity")); + SetEntityTransmitState(page.index, FL_EDICT_ALWAYS); + } + + CBaseEntity glow = CBaseEntity(CreateGlowEntityDataless(page.index, { 255, 0, 0, 255 })); + glow.SetPropEnt(Prop_Data, "m_hOwnerEntity", page.index); + g_PageLocationsGlow.Push(EntIndexToEntRef(glow.index)); + count++; + } + CPrintToChat(client, "{royalblue}%t {default}Revealed %i page locations.", "SF2 Prefix", count); + g_PagesRevealed = true; + } + else + { + for (int i = 0; i < g_PageLocations.Length; i++) + { + int entity = EntRefToEntIndex(g_PageLocations.Get(i)); + if (!entity || entity == INVALID_ENT_REFERENCE) + { + continue; + } + + RemoveEntity(entity); + } + + for (int i = 0; i < g_PageLocationsGlow.Length; i++) + { + int entity = EntRefToEntIndex(g_PageLocationsGlow.Get(i)); + if (!entity || entity == INVALID_ENT_REFERENCE) + { + continue; + } + + RemoveEntity(entity); + } + + for (int i = 0; i < g_Pages.Length; i++) + { + SF2PageEntityData pageData; + g_Pages.GetArray(i, pageData, sizeof(pageData)); + + CBaseEntity page = CBaseEntity(EntRefToEntIndex(pageData.EntRef)); + if (page.IsValid()) + { + page = CBaseEntity(page.GetPropEnt(Prop_Send, "m_hOwnerEntity")); + page.DispatchUpdateTransmitState(); + } + } + g_PageLocations.Clear(); + g_PageLocationsGlow.Clear(); + g_PagesRevealed = false; + } + return Plugin_Handled; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/extras/game_events.sp b/addons/sourcemod/scripting/sf2/extras/game_events.sp index e2e4d491..6ce4a964 100644 --- a/addons/sourcemod/scripting/sf2/extras/game_events.sp +++ b/addons/sourcemod/scripting/sf2/extras/game_events.sp @@ -4,6 +4,7 @@ #define _sf2_game_events_included #pragma semicolon 1 +#pragma newdecls required Action Event_RoundStart(Handle event, const char[] name, bool dB) { @@ -38,8 +39,6 @@ Action Event_RoundStart(Handle event, const char[] name, bool dB) g_PageMax = 0; g_VoteTimer = null; - //Stop the music if needed. - NPCStopMusic(); // Remove all bosses from the game. NPCRemoveAll(); // Collect trigger_multiple to prevent touch bug. @@ -64,21 +63,10 @@ Action Event_RoundStart(Handle event, const char[] name, bool dB) // Calculate the new round state. if (g_RoundWaitingForPlayers) { - int ent = -1; - while ((ent = FindEntityByClassname(ent, "func_regenerate")) != -1) - { - AcceptEntityInput(ent, "Disable"); - } SetRoundState(SF2RoundState_Waiting); } else if (g_WarmupRoundConVar.BoolValue && g_RoundWarmupRoundCount < g_WarmupRoundNumConVar.IntValue) { - int ent = -1; - while ((ent = FindEntityByClassname(ent, "func_regenerate")) != -1) - { - AcceptEntityInput(ent, "Disable"); - } - g_RoundWarmupRoundCount++; SetRoundState(SF2RoundState_Waiting); @@ -134,29 +122,37 @@ Action Event_WinPanel(Event event, const char[] name, bool dontBroadcast) Action Event_Audio(Event event, const char[] name, bool dB) { + if (!g_Enabled) + { + return Plugin_Continue; + } + char audio[PLATFORM_MAX_PATH]; - GetEventString(event, "sound", audio, sizeof(audio)); - if (strncmp(audio, "Game.Your", 9) == 0 || strcmp(audio, "Game.Stalemate") == 0) + event.GetString("sound", audio, sizeof(audio)); + if (StrContains(audio, "Game.Your", false) == -1) { - for (int bossIndex = 0; bossIndex < MAX_BOSSES; bossIndex++) - { - if (NPCGetUniqueID(bossIndex) == -1) - { - continue; - } - if (!g_SlenderCustomOutroSong[bossIndex]) - { - continue; - } + return Plugin_Continue; + } - return Plugin_Handled; + for (int bossIndex = 0; bossIndex < MAX_BOSSES; bossIndex++) + { + if (NPCGetUniqueID(bossIndex) == -1) + { + continue; + } + BaseBossProfile data = SF2NPC_BaseNPC(bossIndex).GetProfileData(); + if (data.GetOutroLoseSounds() == null && data.GetOutroWinSounds() == null) + { + continue; } + + return Plugin_Handled; } return Plugin_Continue; } -Action Event_RoundEnd(Handle event, const char[] name, bool dB) +Action Event_RoundEnd(Event event, const char[] name, bool dB) { if (!g_Enabled) { @@ -183,8 +179,9 @@ Action Event_RoundEnd(Handle event, const char[] name, bool dB) g_PlayerFullyDied1Up[i] = true; } + int wonTeam = event.GetInt("team"); + ArrayList randomBosses = new ArrayList(); - char music[MAX_BOSSES][PLATFORM_MAX_PATH]; for (int npcIndex = 0; npcIndex < MAX_BOSSES; npcIndex++) { @@ -193,28 +190,40 @@ Action Event_RoundEnd(Handle event, const char[] name, bool dB) continue; } - if (g_SlenderCustomOutroSong[npcIndex]) + BaseBossProfile data = SF2NPC_BaseNPC(npcIndex).GetProfileData(); + if (data.IsPvEBoss) { - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(npcIndex, profile, sizeof(profile)); - SF2BossProfileSoundInfo soundInfo; - GetBossProfileOutroMusics(profile, soundInfo); - if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) + continue; + } + + if (wonTeam == TFTeam_Blue) + { + if (data.GetOutroLoseSounds() == null) { - soundInfo.Paths.GetString(GetRandomInt(0, soundInfo.Paths.Length - 1), music[npcIndex], sizeof(music[])); + continue; } - if (music[npcIndex][0] != '\0') + randomBosses.Push(data.GetOutroLoseSounds()); + } + else + { + if (data.GetOutroWinSounds() == null) { - randomBosses.Push(npcIndex); + continue; } + randomBosses.Push(data.GetOutroWinSounds()); } } + if (randomBosses.Length > 0) { - int newBossIndex = randomBosses.Get(GetRandomInt(0, randomBosses.Length - 1)); - if (NPCGetUniqueID(newBossIndex) != -1) + ProfileSound sound = randomBosses.Get(GetRandomInt(0, randomBosses.Length - 1)); + for (int i = 1; i <= MaxClients; i++) { - EmitSoundToAll(music[newBossIndex], _, SNDCHAN_AUTO, SNDLEVEL_SCREAMING); + if (!IsValidClient(i)) + { + continue; + } + sound.EmitSound(true, i); } } @@ -236,7 +245,6 @@ Action Event_RoundEnd(Handle event, const char[] name, bool dB) DebugMessage("EVENT END: Event_RoundEnd"); } #endif - delete event; return Plugin_Continue; } @@ -353,22 +361,23 @@ Action Event_PlayerTeam(Handle event, const char[] name, bool dB) Action Event_PlayerSpawn(Handle event, const char[] name, bool dB) { + int client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client <= 0) + { + return Plugin_Continue; + } + if (!g_Enabled) { - if (g_LoadOutsideMapsConVar.BoolValue && GetClientOfUserId(GetEventInt(event, "userid")) > 0) + if (g_LoadOutsideMapsConVar.BoolValue) { Call_StartForward(g_OnPlayerSpawnPFwd); - Call_PushCell(SF2_BasePlayer(GetClientOfUserId(GetEventInt(event, "userid")))); + Call_PushCell(SF2_BasePlayer(client)); Call_Finish(); } return Plugin_Continue; } - int client = GetClientOfUserId(GetEventInt(event, "userid")); - if (client <= 0) - { - return Plugin_Continue; - } #if defined DEBUG Handle profiler = CreateProfiler(); @@ -392,7 +401,6 @@ Action Event_PlayerSpawn(Handle event, const char[] name, bool dB) } if (!IsClientParticipating(client)) { - TF2Attrib_SetByName(client, "increased jump height", 1.0); TF2Attrib_RemoveByDefIndex(client, 10); SetEntityGravity(client, 1.0); @@ -435,26 +443,10 @@ Action Event_PlayerSpawn(Handle event, const char[] name, bool dB) g_PlayerHitsToCrits[client] = 0; g_PlayerHitsToHeads[client] = 0; - g_PlayerTrapped[client] = false; - g_PlayerTrapCount[client] = 0; - - g_PlayerLatchedByTongue[client] = false; - g_PlayerLatchCount[client] = 0; - g_PlayerLatcher[client] = -1; - g_PlayerRandomClassNumber[client] = 1; if (IsPlayerAlive(client) && IsClientParticipating(client)) { - if (MusicActive()) //A boss is overriding the music. - { - char path[PLATFORM_MAX_PATH]; - GetBossMusic(path, sizeof(path)); - if (path[0] != '\0') - { - StopSound(client, MUSIC_CHAN, path); - } - } g_PlayerBackStabbed[client] = false; TF2_RemoveCondition(client, TFCond_HalloweenKart); TF2_RemoveCondition(client, TFCond_HalloweenKartDash); @@ -498,8 +490,6 @@ Action Event_PlayerSpawn(Handle event, const char[] name, bool dB) } } - TF2Attrib_SetByName(client, "increased jump height", 1.0); - if (!g_PlayerEliminated[client]) { if (IsFakeClient(client)) @@ -530,12 +520,6 @@ Action Event_PlayerSpawn(Handle event, const char[] name, bool dB) CreateTimer(0.1, Timer_SwitchBot, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); } - // screen overlay timer - if (!SF_IsRaidMap() && !SF_IsBoxingMap()) - { - g_PlayerOverlayCheck[client] = CreateTimer(0.0, Timer_PlayerOverlayCheck, GetClientUserId(client), TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); - TriggerTimer(g_PlayerOverlayCheck[client], true); - } if (DidClientEscape(client)) { CreateTimer(0.1, Timer_TeleportPlayerToEscapePoint, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); @@ -548,6 +532,10 @@ Action Event_PlayerSpawn(Handle event, const char[] name, bool dB) TF2Attrib_RemoveByDefIndex(client, 49); TF2Attrib_RemoveByDefIndex(client, 28); } + + g_PlayerOverlayCheck[client] = CreateTimer(0.0, Timer_PlayerOverlayCheck, GetClientUserId(client), TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); + TriggerTimer(g_PlayerOverlayCheck[client], true); + ClientSwitchToWeaponSlot(client, TFWeaponSlot_Melee); g_PlayerPostWeaponsTimer[client] = CreateTimer(0.1, Timer_ClientPostWeapons, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); @@ -702,7 +690,8 @@ Action Event_PlayerDeathPre(Event event, const char[] name, bool dB) char bossName[SF2_MAX_NAME_LENGTH], profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(npcIndex, profile, sizeof(profile)); - NPCGetBossName(npcIndex, bossName, sizeof(bossName)); + BaseBossProfile data = GetBossProfile(profile); + data.GetName(GetLocalGlobalDifficulty(npcIndex), bossName, sizeof(bossName)); SetClientName(target, bossName); SetEntPropString(target, Prop_Data, "m_szNetname", bossName); @@ -710,22 +699,20 @@ Action Event_PlayerDeathPre(Event event, const char[] name, bool dB) event.SetString("assister_fallback", ""); if ((NPCGetFlags(npcIndex) & SFF_WEAPONKILLS) || (NPCGetFlags(npcIndex) & SFF_WEAPONKILLSONRADIUS)) { + char weaponType[64]; if (NPCGetFlags(npcIndex) & SFF_WEAPONKILLS && SF2_ChaserEntity(owner).IsValid()) { SF2_ChaserEntity chaser = SF2_ChaserEntity(owner); - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(npcIndex); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(chaser.GetAttackName(), attackData); - event.SetString("weapon_logclassname", attackData.WeaponString); - event.SetString("weapon", attackData.WeaponString); + ChaserBossProfileBaseAttack attackData = SF2NPC_Chaser(npcIndex).GetProfileData().GetAttack(chaser.GetAttackName()); + attackData.GetWeaponString(weaponType, sizeof(weaponType)); + event.SetString("weapon_logclassname", weaponType); + event.SetString("weapon", weaponType); event.SetInt("customkill", attackData.WeaponInt); } else if (NPCGetFlags(npcIndex) & SFF_WEAPONKILLSONRADIUS) { - char weaponType[PLATFORM_MAX_PATH]; - int weaponNum = GetBossProfileWeaponInt(profile); - GetBossProfileWeaponString(profile, weaponType, sizeof(weaponType)); + int weaponNum = data.WeaponInt; + data.GetWeaponString(weaponType, sizeof(weaponType)); event.SetString("weapon_logclassname", weaponType); event.SetString("weapon", weaponType); event.SetInt("customkill", weaponNum); @@ -777,8 +764,9 @@ Action Event_PlayerDeathPre(Event event, const char[] name, bool dB) if (npcIndex != -1) { - SF2BossProfileData data; - data = NPCGetProfileData(npcIndex); + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + NPCGetProfile(npcIndex, profile, sizeof(profile)); + BaseBossProfile data = GetBossProfile(profile); g_PlayerBossKillSubject[client] = npcIndex; if (!modify && (data.AshRagdoll || data.CloakRagdoll || data.DecapRagdoll || data.DeleteRagdoll || data.DissolveRagdoll || @@ -838,32 +826,6 @@ Action Event_PlayerHurt(Handle event, const char[] name, bool dB) } #endif - int attacker = GetClientOfUserId(GetEventInt(event, "attacker")); - if (attacker > 0) - { - if (g_PlayerProxy[attacker]) - { - g_PlayerProxyControl[attacker] = 100; - } - } - - // Play any sounds, if any. - if (g_PlayerProxy[client]) - { - int proxyMaster = NPCGetFromUniqueID(g_PlayerProxyMaster[client]); - if (proxyMaster != -1) - { - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(proxyMaster, profile, sizeof(profile)); - - SF2BossProfileSoundInfo soundInfo; - GetBossProfileProxyHurtSounds(profile, soundInfo); - if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) - { - soundInfo.EmitSound(_, client); - } - } - } delete event; #if defined DEBUG if (g_DebugDetailConVar.IntValue > 0) diff --git a/addons/sourcemod/scripting/sf2/extras/natives.sp b/addons/sourcemod/scripting/sf2/extras/natives.sp index 952821e9..ad8e8373 100644 --- a/addons/sourcemod/scripting/sf2/extras/natives.sp +++ b/addons/sourcemod/scripting/sf2/extras/natives.sp @@ -4,6 +4,7 @@ #define _sf2_natives_included #pragma semicolon 1 +#pragma newdecls required // ========================================================== // GENERAL PLUGIN HOOK FUNCTIONS @@ -57,11 +58,10 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max g_OnGroupEnterGameFwd = new GlobalForward("SF2_OnGroupEnterGame", ET_Hook, Param_Cell); g_OnEverythingLoadedFwd = new GlobalForward("SF2_OnEverythingLoaded", ET_Ignore); g_OnDifficultyVoteFinishedFwd = new GlobalForward("SF2_OnDifficultyVoteFinished", ET_Ignore, Param_Cell, Param_Cell); - g_OnIsBossCustomAttackPossibleFwd = new GlobalForward("SF2_OnIsBossCustomAttackPossible", ET_Hook, Param_Cell, Param_String, Param_Array, Param_Cell); - g_OnBossGetCustomAttackActionFwd = new GlobalForward("SF2_OnBossGetCustomAttackAction", ET_Hook, Param_Cell, Param_String, Param_Array, Param_Cell, Param_CellByRef); + g_OnIsBossCustomAttackPossibleFwd = new GlobalForward("SF2_OnIsBossCustomAttackPossible", ET_Hook, Param_Cell, Param_String, Param_Cell, Param_Cell); + g_OnBossGetCustomAttackActionFwd = new GlobalForward("SF2_OnBossGetCustomAttackAction", ET_Hook, Param_Cell, Param_String, Param_Cell, Param_Cell, Param_CellByRef); g_OnProjectileTouchFwd = new GlobalForward("SF2_OnProjectileTouch", ET_Ignore, Param_Cell, Param_Cell); - CreateNative("SF2_GetConfig", Native_GetConfig); CreateNative("SF2_IsRunning", Native_IsRunning); CreateNative("SF2_GetRoundState", Native_GetRoundState); CreateNative("SF2_IsRoundInGracePeriod", Native_IsRoundInGracePeriod); @@ -225,7 +225,7 @@ void SDK_Init() PrepSDKCall_SetFromConf(gameData, SDKConf_Virtual, "CTFPlayer::EquipWearable"); PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); g_SDKEquipWearable = EndPrepSDKCall(); - if (g_SDKEquipWearable == null)//In case the offset is missing, look if the server has the tf2 randomizer's gamedata. + if (g_SDKEquipWearable == null) // In case the offset is missing, look if the server has the tf2 randomizer's gamedata. { char strFilePath[PLATFORM_MAX_PATH]; BuildPath(Path_SM, strFilePath, sizeof(strFilePath), "gamedata/tf2items.randomizer.txt"); @@ -255,6 +255,13 @@ void SDK_Init() SetFailState("Failed to retrieve CTFPlayer::EquipWearable offset from SF2 gamedata!"); } + StartPrepSDKCall(SDKCall_Player); + PrepSDKCall_SetFromConf(gameData, SDKConf_Signature, "CTFPlayer::TeamFortress_SetSpeed"); + if ((g_SDKUpdateSpeed = EndPrepSDKCall()) == null) + { + LogError("Failed to retrieve CTFPlayer::TeamFortress_SetSpeed offset from SDKHooks gamedata!"); + } + StartPrepSDKCall(SDKCall_Player); PrepSDKCall_SetFromConf(gameData, SDKConf_Signature, "CTFPlayer::PlaySpecificSequence"); PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); @@ -262,7 +269,7 @@ void SDK_Init() if (g_SDKPlaySpecificSequence == null) { PrintToServer("Failed to retrieve CTFPlayer::PlaySpecificSequence signature from SF2 gamedata!"); - //Don't have to call SetFailState, since this function is used in a minor part of the code. + // Don't have to call SetFailState, since this function is used in a minor part of the code. } StartPrepSDKCall(SDKCall_Entity); @@ -273,7 +280,7 @@ void SDK_Init() if (g_SDKPointIsWithin == null) { PrintToServer("Failed to retrieve CBaseTrigger::PointIsWithin signature from SF2 gamedata!"); - //Don't have to call SetFailState, since this function is used in a minor part of the code. + // Don't have to call SetFailState, since this function is used in a minor part of the code. } StartPrepSDKCall(SDKCall_Entity); @@ -326,7 +333,7 @@ void SDK_Init() LogError("Failed to setup Studio_SeqVelocity call from gamedata"); } - //Hook_ClientWantsLagCompensationOnEntity + // Hook_ClientWantsLagCompensationOnEntity int offset = gameData.GetOffset("CTFPlayer::WantsLagCompensationOnEntity"); g_DHookWantsLagCompensationOnEntity = new DynamicHook(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity); if (g_DHookWantsLagCompensationOnEntity == null) @@ -337,7 +344,7 @@ void SDK_Init() DHookAddParam(g_DHookWantsLagCompensationOnEntity, HookParamType_CBaseEntity); DHookAddParam(g_DHookWantsLagCompensationOnEntity, HookParamType_ObjectPtr); DHookAddParam(g_DHookWantsLagCompensationOnEntity, HookParamType_Unknown); - //Hook_EntityShouldTransmit + // Hook_EntityShouldTransmit offset = gameData.GetOffset("CBaseEntity::ShouldTransmit"); g_DHookShouldTransmit = new DynamicHook(offset, HookType_Entity, ReturnType_Int, ThisPointer_CBaseEntity); if (g_DHookShouldTransmit == null) @@ -352,7 +359,7 @@ void SDK_Init() { SetFailState("Failed to create hook CBaseEntity::UpdateTransmitState offset from SF2 gamedata!"); } - //Hook_WeaponGetCustomDamageType + // Hook_WeaponGetCustomDamageType offset = gameData.GetOffset("CTFWeaponBase::GetCustomDamageType"); g_DHookWeaponGetCustomDamageType = new DynamicHook(offset, HookType_Entity, ReturnType_Int, ThisPointer_CBaseEntity); if (g_DHookWeaponGetCustomDamageType == null) @@ -367,9 +374,6 @@ void SDK_Init() SetFailState("Failed to create hook CBaseProjectile::CanCollideWithTeammates offset from SF2 gamedata!"); } - //Initialize tutorial detours & calls - //Tutorial_SetupSDK(gameData); - delete gameData; } @@ -377,11 +381,6 @@ void SDK_Init() // API // ========================================================== -static any Native_GetConfig(Handle plugin, int numParams) -{ - return g_Config; -} - static any Native_IsRunning(Handle plugin, int numParams) { return g_Enabled; @@ -677,14 +676,12 @@ static any Native_SetBossTarget(Handle plugin, int numParams) static any Native_IsBossStunnable(Handle plugin, int numParams) { - return SF2NPC_Chaser(GetNativeCell(1)).GetProfileData().StunData.Enabled[1]; + return SF2NPC_Chaser(GetNativeCell(1)).GetProfileData().GetStunBehavior().IsEnabled(1); } static any Native_IsBossStunnableByFlashlight(Handle plugin, int numParams) { - SF2ChaserBossProfileData data; - data = NPCChaserGetProfileData(GetNativeCell(1)); - return data.StunData.FlashlightStun[1]; + return SF2NPC_Chaser(GetNativeCell(1)).GetProfileData().GetStunBehavior().CanFlashlightStun(1); } static any Native_IsBossCloaked(Handle plugin, int numParams) diff --git a/addons/sourcemod/scripting/sf2/gamemodes/renevant.sp b/addons/sourcemod/scripting/sf2/gamemodes/renevant.sp index feb01335..278ff735 100644 --- a/addons/sourcemod/scripting/sf2/gamemodes/renevant.sp +++ b/addons/sourcemod/scripting/sf2/gamemodes/renevant.sp @@ -4,6 +4,7 @@ #define _sf2_renevant_mode_included #pragma semicolon 1 +#pragma newdecls required static GlobalForward g_OnRenevantTriggerWaveFwd; @@ -71,12 +72,14 @@ bool SF_IsRenevantMap() static bool Renevant_TryAddBossProfile(char profile[SF2_MAX_PROFILE_NAME_LENGTH], int profileLen, char[] name, int nameLen, bool playSpawnSound = true) { - if (!GetRandomRenevantBossProfile(profile, profileLen)) + if (GetSelectableBossProfileList().Length == 0) { return false; } - NPCGetBossName(_, name, nameLen, profile); + GetSelectableBossProfileList().GetString(GetRandomInt(0, GetSelectableBossProfileList().Length - 1), profile, profileLen); + + GetBossProfile(profile).GetName(1, name, nameLen); if (name[0] == '\0') { strcopy(name, nameLen, profile); @@ -86,7 +89,6 @@ static bool Renevant_TryAddBossProfile(char profile[SF2_MAX_PROFILE_NAME_LENGTH] return true; } - static void ShowRenevantMessageToClient(int client, const char[] message, int params, any ...) { char messageDisplay[512]; @@ -105,7 +107,7 @@ static void ShowRenevantMessageToClient(int client, const char[] message, int pa ShowSyncHudText(client, g_HudSync3, messageDisplay); } -static void Renevant_BroadcastMessage(const char[] message, int params, ...) +static void Renevant_BroadcastMessage(const char[] message, int params, any ...) { char format[512]; VFormat(format, sizeof(format), message, params); @@ -376,14 +378,14 @@ static void Renevant_DoWaveAction(RenevantWave action) } case RenevantWave_AdminBoss: { - ArrayList selectableBosses = GetSelectableRenevantBossAdminProfileList().Clone(); + ArrayList selectableBosses = GetSelectableRenevantBossAdminProfileList(); if (selectableBosses.Length > 0) { selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); SF2NPC_BaseNPC Npc = AddProfile(buffer); if (Npc.IsValid()) { - NPCGetBossName(_, name, sizeof(name), buffer); + GetBossProfile(buffer).GetName(1, name, sizeof(name)); if (name[0] == '\0') { strcopy(name, sizeof(name), buffer); @@ -405,7 +407,6 @@ static void Renevant_DoWaveAction(RenevantWave action) { g_RenevantWaveList.Erase(eraseWave); } - delete selectableBosses; } case RenevantWave_WallHax: { @@ -473,7 +474,7 @@ void Renevant_SetWave(int wave, bool resetTimer = false) g_RenevantWaveList.Erase(eraseWave); } } - ArrayList selectableBosses = GetSelectableRenevantBossAdminProfileList().Clone(); + ArrayList selectableBosses = GetSelectableRenevantBossAdminProfileList(); if (selectableBosses.Length <= 0) { int eraseWave = g_RenevantWaveList.FindValue(RenevantWave_AdminBoss); @@ -482,7 +483,6 @@ void Renevant_SetWave(int wave, bool resetTimer = false) g_RenevantWaveList.Erase(eraseWave); } } - delete selectableBosses; } case 0: { diff --git a/addons/sourcemod/scripting/sf2/glow.sp b/addons/sourcemod/scripting/sf2/glow.sp index 311d9290..0b9e8a09 100644 --- a/addons/sourcemod/scripting/sf2/glow.sp +++ b/addons/sourcemod/scripting/sf2/glow.sp @@ -1,3 +1,6 @@ +#pragma semicolon 1 +#pragma newdecls required + enum struct SF2GlowData { int Ref; diff --git a/addons/sourcemod/scripting/sf2/logging.sp b/addons/sourcemod/scripting/sf2/logging.sp index b57fd8ce..d4b978d4 100644 --- a/addons/sourcemod/scripting/sf2/logging.sp +++ b/addons/sourcemod/scripting/sf2/logging.sp @@ -4,6 +4,7 @@ #define _sf2_logging_included #pragma semicolon 1 +#pragma newdecls required static char g_LogFilePath[512] = ""; diff --git a/addons/sourcemod/scripting/sf2/mapentities.sp b/addons/sourcemod/scripting/sf2/mapentities.sp index 0c58b292..9c027c0a 100644 --- a/addons/sourcemod/scripting/sf2/mapentities.sp +++ b/addons/sourcemod/scripting/sf2/mapentities.sp @@ -4,6 +4,7 @@ #define _sf2_mapentities_included #pragma semicolon 1 +#pragma newdecls required //#define DEBUG_MAPENTITIES @@ -99,6 +100,7 @@ void SF2MapEntity_AddHook(SF2MapEntityHook hookType, Function hookFunc) #include "mapentities/sf2_info_player_escapespawn.sp" #include "mapentities/sf2_trigger_pvp.sp" #include "mapentities/sf2_trigger_pve.sp" +#include "mapentities/sf2_trigger_weapons.sp" #include "mapentities/sf2_info_player_pvpspawn.sp" #include "mapentities/sf2_info_player_proxyspawn.sp" #include "mapentities/sf2_info_boss_spawn.sp" @@ -151,6 +153,9 @@ void SetupCustomMapEntities() // sf2_trigger_pve SF2TriggerPvEEntity.Initialize(); + // sf2_trigger_weapons + SF2TriggerWeaponsEntity.Initialize(); + // sf2_info_player_pvpspawn SF2PlayerPvPSpawnEntity.Initialize(); diff --git a/addons/sourcemod/scripting/sf2/mapentities/base.sp b/addons/sourcemod/scripting/sf2/mapentities/base.sp index 8c6916cb..c0410980 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/base.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/base.sp @@ -6,6 +6,7 @@ // To initialize, call the SF2MapEntityBase.Initialize() function from SetupCustomMapEntities(). #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = ""; // The custom classname of the entity. Should be prefixed with "sf2_" diff --git a/addons/sourcemod/scripting/sf2/mapentities/base_spawnpoint.sp b/addons/sourcemod/scripting/sf2/mapentities/base_spawnpoint.sp index 0e2d2451..a4686b8a 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/base_spawnpoint.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/base_spawnpoint.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_boss_maker.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_boss_maker.sp index 6ceb00b2..55f95275 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_boss_maker.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_boss_maker.sp @@ -1,6 +1,7 @@ // sf2_boss_maker #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_boss_maker"; // The custom classname of the entity. Should be prefixed with "sf2_" @@ -235,9 +236,10 @@ methodmap SF2BossMakerEntity < SF2SpawnPointBaseEntity endPos[1] = pos[1]; endPos[2] = pos[2] - 1024.0; - TR_TraceHullFilter(pos, endPos, mins, maxs, MASK_PLAYERSOLID_BRUSHONLY, TraceRayDontHitEntity, bossEntity.index); - bool traceHit = TR_DidHit(); - TR_GetEndPosition(endPos); + Handle trace = TR_TraceHullFilterEx(pos, endPos, mins, maxs, MASK_PLAYERSOLID_BRUSHONLY, TraceRayDontHitEntity, bossEntity.index); + bool traceHit = TR_DidHit(trace); + TR_GetEndPosition(endPos, trace); + delete trace; if (traceHit) { @@ -245,7 +247,7 @@ methodmap SF2BossMakerEntity < SF2SpawnPointBaseEntity } } - if (NPCGetType(bossIndex) == SF2BossType_Chaser) + if (SF2NPC_BaseNPC(bossIndex).GetProfileData().Type == SF2BossType_Chaser) { char spawnAnim[64]; this.GetSpawnAnimation(spawnAnim, sizeof(spawnAnim)); @@ -304,7 +306,7 @@ methodmap SF2BossMakerEntity < SF2SpawnPointBaseEntity continue; } - boss.RemoveFromGame(); + boss.Remove(); } this.Bosses.Clear(); diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_game_text.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_game_text.sp index 7bc10f88..819f0e79 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_game_text.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_game_text.sp @@ -6,6 +6,7 @@ // constant displaying of HUD text by SF2, rendering the text unreadable and useless. #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_game_text"; // The custom classname of the entity. Should be prefixed with "sf2_" diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_gamerules.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_gamerules.sp index 715596be..0e656521 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_gamerules.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_gamerules.sp @@ -1,6 +1,7 @@ // sf2_gamerules #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_gamerules"; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_boss_spawn.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_boss_spawn.sp index f181526b..fa86e076 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_boss_spawn.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_boss_spawn.sp @@ -1,6 +1,7 @@ // sf2_info_boss_spawn #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_info_boss_spawn"; // The custom classname of the entity. Should be prefixed with "sf2_" diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_page_music.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_page_music.sp index f52702fc..4f5998d5 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_page_music.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_page_music.sp @@ -1,6 +1,7 @@ // sf2_info_page_music #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_info_page_music"; // The custom classname of the entity. Should be prefixed with "sf2_" diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_page_spawn.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_page_spawn.sp index d63e840c..cc5c552d 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_page_spawn.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_page_spawn.sp @@ -1,6 +1,7 @@ // sf2_info_page_spawn #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_escapespawn.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_escapespawn.sp index 4f9fe452..4c5c7972 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_escapespawn.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_escapespawn.sp @@ -1,6 +1,7 @@ // sf2_info_player_escapespawn #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_proxyspawn.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_proxyspawn.sp index 2a1fbcbc..920ad81f 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_proxyspawn.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_proxyspawn.sp @@ -1,6 +1,7 @@ // sf2_info_player_proxyspawn #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_pvpspawn.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_pvpspawn.sp index 3a39cf87..43794c1f 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_pvpspawn.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_info_player_pvpspawn.sp @@ -1,6 +1,7 @@ // sf2_info_player_pvpspawn #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_arena.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_arena.sp index 2d3f7434..5b840e63 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_arena.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_arena.sp @@ -1,6 +1,7 @@ // sf2_logic_arena #pragma semicolon 1 +#pragma newdecls required static const char g_EntityClassname[] = "sf2_logic_arena"; // The custom classname of the entity. Should be prefixed with "sf2_" diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_boxing.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_boxing.sp index 4a2bfc1d..31855331 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_boxing.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_boxing.sp @@ -1,6 +1,7 @@ // sf2_logic_boxing #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_proxy.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_proxy.sp index 4c55c7b6..3a49eb63 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_proxy.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_proxy.sp @@ -1,6 +1,7 @@ // sf2_logic_proxy #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_raid.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_raid.sp index 2bdce2e8..0176f247 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_raid.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_raid.sp @@ -1,6 +1,7 @@ // sf2_logic_raid #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_slaughter.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_slaughter.sp index a6d5ad9f..1684d49a 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_slaughter.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_logic_slaughter.sp @@ -1,6 +1,7 @@ // sf2_logic_slaughter #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_point_spotlight.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_point_spotlight.sp index 3bff8c78..ea5f2ef4 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_point_spotlight.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_point_spotlight.sp @@ -1,3 +1,5 @@ +#pragma semicolon 1 +#pragma newdecls required static const char g_Classname[] = "sf2_point_spotlight"; @@ -192,7 +194,9 @@ methodmap SF2PointSpotlightEntity < CBaseEntity this.IsOn = true; - this.AcceptInput("TurnOn"); + int color[4]; + this.End.GetRenderColor(color[0], color[1], color[2], color[3]); + this.SetRenderColor(color[0], color[1], color[2], color[3]); this.End.AcceptInput("TurnOn"); } @@ -205,7 +209,7 @@ methodmap SF2PointSpotlightEntity < CBaseEntity this.IsOn = false; - this.AcceptInput("TurnOff"); + this.SetRenderColor(0, 0, 0, 0); this.End.AcceptInput("TurnOff"); } @@ -220,6 +224,12 @@ methodmap SF2PointSpotlightEntity < CBaseEntity SetVariantInt(this.Cone); this.End.AcceptInput("cone"); } + + public void SetColor(int color[4]) + { + this.SetRenderColor(color[0], color[1], color[2], color[3]); + this.End.SetRenderColor(color[0], color[1], color[2], color[3]); + } } static void OnCreate(SF2PointSpotlightEntity ent) @@ -253,8 +263,8 @@ static void OnSpawn(int entIndex) { SF2PointSpotlightEntity entity = SF2PointSpotlightEntity(entIndex); - entity.SetPropEnt(Prop_Send, "m_hAttachEntity", entity.Start.index, 0); - entity.SetPropEnt(Prop_Send, "m_hAttachEntity", entity.End.index, 1); + entity.SetPropEnt(Prop_Send, "m_hAttachEntity", EntIndexToEntRef(entity.Start.index), 0); + entity.SetPropEnt(Prop_Send, "m_hAttachEntity", EntIndexToEntRef(entity.End.index), 1); entity.SetProp(Prop_Send, "m_nNumBeamEnts", 2); entity.SetProp(Prop_Send, "m_nBeamType", 2); @@ -282,21 +292,22 @@ static void UpdateSpotlight(SF2PointSpotlightEntity entity) CBaseEntity spotlightEnd = entity.End; if (spotlightEnd.IsValid()) { - float pos[3], dir[3]; + float pos[3], dir[3], startPos[3]; + entity.Start.GetAbsOrigin(startPos); entity.GetAbsOrigin(pos); entity.GetAbsAngles(dir); float endPos[3]; endPos[0] = entity.Length; VectorTransform(endPos, pos, dir, endPos); - TR_TraceRayFilter(pos, endPos, MASK_SOLID_BRUSHONLY, RayType_EndPoint, Trace, entity.index); + Handle trace = TR_TraceRayFilterEx(pos, endPos, MASK_OPAQUE_AND_NPCS, RayType_EndPoint, Trace, entity.index); float hitPos[3]; - TR_GetEndPosition(hitPos); -/* - int color[4] = { 255, 0, 0, 255 }; + TR_GetEndPosition(hitPos, trace); + + /*int color[4] = { 255, 0, 0, 255 }; TE_SetupBeamPoints(pos, - endPos, + hitPos, g_ShockwaveBeam, g_ShockwaveHalo, 0, @@ -308,9 +319,11 @@ static void UpdateSpotlight(SF2PointSpotlightEntity entity) 0.0, color, 1); - TE_SendToAll(); -*/ + TE_SendToAll();*/ + spotlightEnd.SetAbsOrigin(hitPos); + entity.Start.SetAbsOrigin(pos); + delete trace; } } @@ -336,7 +349,7 @@ static bool Trace(int entity, int mask, any data) { return false; } - if (IsValidEntity(entity) && NPCGetFromEntIndex(entity) != -1) + if (SF2_ChaserEntity(entity).IsValid() || SF2_StatueEntity(entity).IsValid()) { return false; } diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_boss_despawn.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_boss_despawn.sp index 386bbaaf..2afed005 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_boss_despawn.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_boss_despawn.sp @@ -1,6 +1,7 @@ // sf2_trigger_boss_despawn #pragma semicolon 1 +#pragma newdecls required // A trigger that when touched by a boss will despawn the boss from the map. diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_escape.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_escape.sp index 0a7a8b1f..38ae0858 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_escape.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_escape.sp @@ -1,6 +1,7 @@ // sf2_trigger_escape #pragma semicolon 1 +#pragma newdecls required // A trigger that when touched by a player on RED will let the player escape. // Escaping can only occur during the Escape phase. diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_pve.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_pve.sp index b29ee97e..2aecfbd6 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_pve.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_pve.sp @@ -1,6 +1,7 @@ // sf2_trigger_pve #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_pvp.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_pvp.sp index d08c8aae..14adcfe5 100644 --- a/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_pvp.sp +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_pvp.sp @@ -1,6 +1,7 @@ // sf2_trigger_pvp #pragma semicolon 1 +#pragma newdecls required static CEntityFactory g_EntityFactory; diff --git a/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_weapons.sp b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_weapons.sp new file mode 100644 index 00000000..ec81c29c --- /dev/null +++ b/addons/sourcemod/scripting/sf2/mapentities/sf2_trigger_weapons.sp @@ -0,0 +1,189 @@ +// sf2_trigger_weapons + +#pragma semicolon 1 +#pragma newdecls required + +static CEntityFactory g_EntityFactory; + +static bool g_PlayerInWeaponsTrigger[MAXTF2PLAYERS]; +static ArrayList g_PlayerEnteredWeaponTriggers[MAXTF2PLAYERS] = { null, ... }; + +/** + * Interface that exposes public methods for interacting with the entity. + */ +methodmap SF2TriggerWeaponsEntity < SF2TriggerMapEntity +{ + public SF2TriggerWeaponsEntity(int entIndex) + { + return view_as(SF2TriggerMapEntity(entIndex)); + } + + public bool IsValid() + { + if (!CBaseEntity(this.index).IsValid()) + { + return false; + } + + return CEntityFactory.GetFactoryOfEntity(this.index) == g_EntityFactory; + } + + public static void Initialize() + { + Initialize(); + } +} + +static void Initialize() +{ + g_EntityFactory = new CEntityFactory("sf2_trigger_weapons", OnCreate); + g_EntityFactory.DeriveFromClass("trigger_multiple"); + + g_EntityFactory.Install(); + + g_OnPlayerPutInServerPFwd.AddFunction(null, OnPutInServer); + g_OnPlayerDisconnectedPFwd.AddFunction(null, OnDisconnected); + g_OnPlayerSpawnPFwd.AddFunction(null, OnPlayerSpawn); + g_OnPlayerDeathPFwd.AddFunction(null, OnPlayerDeath); +} + +static void OnPutInServer(SF2_BasePlayer client) +{ + if (!g_Enabled) + { + return; + } + + g_PlayerEnteredWeaponTriggers[client.index] = new ArrayList(); + g_PlayerInWeaponsTrigger[client.index] = false; +} + +static void OnDisconnected(SF2_BasePlayer client) +{ + SetWeaponsState(client.index, false, false); + + if (g_PlayerEnteredWeaponTriggers[client.index] != null) + { + delete g_PlayerEnteredWeaponTriggers[client.index]; + g_PlayerEnteredWeaponTriggers[client.index] = null; + } +} + +static void OnPlayerSpawn(SF2_BasePlayer client) +{ + if (IsRoundInWarmup() || GameRules_GetProp("m_bInWaitingForPlayers")) + { + return; + } + + SetWeaponsState(client.index, false, false); +} + +static void OnPlayerDeath(SF2_BasePlayer client, int attacker, int inflictor, bool fake) +{ + if (!g_Enabled) + { + return; + } + + if (!fake) + { + SetWeaponsState(client.index, false, false); + } +} + +static void OnCreate(int entity) +{ + SDKHook(entity, SDKHook_SpawnPost, OnSpawn); + SDKHook(entity, SDKHook_StartTouchPost, OnStartTouchPost); + SDKHook(entity, SDKHook_EndTouchPost, OnEndTouchPost); +} + +static void OnSpawn(int entity) +{ + int spawnFlags = GetEntProp(entity, Prop_Data, "m_spawnflags"); + SetEntProp(entity, Prop_Data, "m_spawnflags", spawnFlags | TRIGGER_CLIENTS); +} + +static void OnStartTouchPost(int entity, int toucher) +{ + if (!IsValidClient(toucher) || !IsPlayerAlive(toucher) || IsClientInGhostMode(toucher)) + { + return; + } + + if (!g_PlayerEliminated[toucher] && !DidClientEscape(toucher)) + { + return; + } + + int entRef = EnsureEntRef(entity); + if (g_PlayerEnteredWeaponTriggers[toucher].FindValue(entRef) == -1) + { + g_PlayerEnteredWeaponTriggers[toucher].Push(entRef); + } + + SetWeaponsState(toucher, true); +} + +static void OnEndTouchPost(int entity, int toucher) +{ + if (!IsValidClient(toucher)) + { + return; + } + + if (g_PlayerEnteredWeaponTriggers[toucher] != null) + { + int triggerEntRef = EnsureEntRef(entity); + for (int i = g_PlayerEnteredWeaponTriggers[toucher].Length - 1; i >= 0; i--) + { + int entRef = g_PlayerEnteredWeaponTriggers[toucher].Get(i); + if (entRef == triggerEntRef) + { + g_PlayerEnteredWeaponTriggers[toucher].Erase(i); + } + else if (EntRefToEntIndex(entRef) == INVALID_ENT_REFERENCE) + { + g_PlayerEnteredWeaponTriggers[toucher].Erase(i); + } + } + } + + if (IsClientInWeaponsTrigger(toucher) && g_PlayerEnteredWeaponTriggers[toucher].Length == 0) + { + SetWeaponsState(toucher, false); + } +} + +static void SetWeaponsState(int client, bool status, bool regenerate = true) +{ + SF2_BasePlayer player = SF2_BasePlayer(client); + if (!player.IsValid) + { + return; + } + + bool old = g_PlayerInWeaponsTrigger[player.index]; + if (status == old) + { + return; + } + + g_PlayerInWeaponsTrigger[player.index] = status; + + if (regenerate) + { + int health = player.GetProp(Prop_Send, "m_iHealth"); + player.RemoveWeaponSlot(TFWeaponSlot_Primary); + player.RemoveWeaponSlot(TFWeaponSlot_Secondary); + player.Regenerate(); + player.SetProp(Prop_Data, "m_iHealth", health); + player.SetProp(Prop_Send, "m_iHealth", health); + } +} + +bool IsClientInWeaponsTrigger(int client) +{ + return g_PlayerInWeaponsTrigger[client]; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/menus.sp b/addons/sourcemod/scripting/sf2/menus.sp index 04b80690..6457ca15 100644 --- a/addons/sourcemod/scripting/sf2/menus.sp +++ b/addons/sourcemod/scripting/sf2/menus.sp @@ -5,6 +5,7 @@ #define _sf2_menus #pragma semicolon 1 +#pragma newdecls required Menu g_MenuMain; Menu g_MenuVoteDifficulty; @@ -1962,14 +1963,13 @@ void DisplayBossList(int client) for (int i = 0; i < bossList.Length; i++) { bossList.GetString(i, profile, sizeof(profile)); - NPCGetBossName(_, displayName, sizeof(displayName), profile); + BaseBossProfile data = GetBossProfile(profile); + data.GetName(1, displayName, sizeof(displayName)); if (displayName[0] == '\0') { strcopy(displayName, sizeof(displayName), profile); } - SF2BossProfileData data; - g_BossProfileData.GetArray(profile, data, sizeof(data)); - if (data.Description.Hidden) + if (data.GetDescription().Hidden) { continue; } @@ -2001,29 +2001,40 @@ static int Menu_BossList(Menu menu, MenuAction action, int param1, int param2) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; menu.GetItem(param2, profile, sizeof(profile)); - SF2BossProfileData data; - g_BossProfileData.GetArray(profile, data, sizeof(data)); + BaseBossProfile data = GetBossProfile(profile); Menu newMenu = new Menu(Menu_BossDisplay); - char buffer[256], buffer2[128]; - data.Names.GetString(Difficulty_Normal, buffer2, sizeof(buffer2)); + char buffer[256], buffer2[128], buffer3[128], def[32]; + data.GetName(Difficulty_Normal, buffer2, sizeof(buffer2)); FormatEx(buffer, sizeof(buffer), "%t %s\n \n", "SF2 Prefix", buffer2); - FormatEx(buffer2, sizeof(buffer2), "Type: %s\n \n", data.Description.Type); + switch (data.Type) + { + case SF2BossType_Chaser: + { + def = "Chaser"; + } + + case SF2BossType_Statue: + { + def = "Statue"; + } + } + data.GetDescription().GetType(buffer3, sizeof(buffer3), def); + FormatEx(buffer2, sizeof(buffer2), "Type: %s\n \n", buffer3); StrCat(buffer, sizeof(buffer), buffer2); - SF2ChaserBossProfileData chaserData; - if (g_ChaserBossProfileData.GetArray(profile, chaserData, sizeof(chaserData))) + if (data.Type == SF2BossType_Chaser) { - FormatEx(buffer2, sizeof(buffer2), "Walk speed: %s\n", ConvertWalkSpeedToDescription(chaserData.WalkSpeed[Difficulty_Normal])); + FormatEx(buffer2, sizeof(buffer2), "Walk speed: %s\n", ConvertWalkSpeedToDescription(view_as(data).GetWalkSpeed(Difficulty_Normal))); } StrCat(buffer, sizeof(buffer), buffer2); - float speed = data.RunSpeed[Difficulty_Normal]; - SF2StatueBossProfileData statueData; - if (g_StatueBossProfileData.GetArray(profile, statueData, sizeof(statueData))) + float speed = data.GetRunSpeed(Difficulty_Normal); + if (data.Type == SF2BossType_Statue) { speed *= 10.0; } FormatEx(buffer2, sizeof(buffer2), "Run speed: %s\n \n", ConvertRunSpeedToDescription(speed)); StrCat(buffer, sizeof(buffer), buffer2); - FormatEx(buffer2, sizeof(buffer2), "%s", data.Description.Information); + data.GetDescription().GetDescription(buffer3, sizeof(buffer3)); + FormatEx(buffer2, sizeof(buffer2), "%s", buffer3); ReplaceString(buffer2, sizeof(buffer2), "\\n", "\n"); StrCat(buffer, sizeof(buffer), buffer2); newMenu.SetTitle(buffer); diff --git a/addons/sourcemod/scripting/sf2/methodmaps.sp b/addons/sourcemod/scripting/sf2/methodmaps.sp index 68cb4d7c..aa54ad45 100644 --- a/addons/sourcemod/scripting/sf2/methodmaps.sp +++ b/addons/sourcemod/scripting/sf2/methodmaps.sp @@ -1,10 +1,11 @@ #if defined _sf2_methodmaps_included - #endinput +#endinput #endif #define _sf2_methodmaps_included #pragma semicolon 1 +#pragma newdecls required const SF2NPC_BaseNPC SF2_INVALID_NPC = view_as(-1); const SF2_BasePlayer SF2_INVALID_PLAYER = view_as(-1); @@ -21,14 +22,6 @@ methodmap SF2NPC_BaseNPC } } - property int Type - { - public get() - { - return NPCGetType(this.Index); - } - } - property PathFollower Path { public get() @@ -37,22 +30,6 @@ methodmap SF2NPC_BaseNPC } } - property int ProfileIndex - { - public get() - { - return NPCGetProfileIndex(this.Index); - } - } - - property int uniqueProfileIndex - { - public get() - { - return NPCGetUniqueProfileIndex(this.Index); - } - } - property int UniqueID { public get() @@ -87,9 +64,11 @@ methodmap SF2NPC_BaseNPC } } - public SF2BossProfileData GetProfileData() + public BaseBossProfile GetProfileData() { - return NPCGetProfileData(this.Index); + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + this.GetProfile(profile, sizeof(profile)); + return GetBossProfile(profile); } property int Difficulty @@ -108,29 +87,13 @@ methodmap SF2NPC_BaseNPC property int Flags { public get() - { - return NPCGetFlags(this.Index); - } - - public set(int flags) { - NPCSetFlags(this.Index, flags); + return NPCGetFlags(this.Index); } - } - property float ModelScale - { - public get() - { - return NPCGetModelScale(this.Index); - } - } - - property int Skin - { - public get() + public set(int flags) { - return NPCGetModelSkin(this.Index); + NPCSetFlags(this.Index, flags); } } @@ -139,35 +102,6 @@ methodmap SF2NPC_BaseNPC return GetSlenderModel(this.Index, modelState, buffer, bufferLen); } - public int GetMaxCopies(int difficulty) - { - return g_SlenderMaxCopies[this.Index][difficulty]; - } - - property bool RaidHitbox - { - public get() - { - return NPCGetRaidHitbox(this.Index); - } - } - - property float ScareRadius - { - public get() - { - return NPCGetScareRadius(this.Index); - } - } - - property float ScareCooldown - { - public get() - { - return NPCGetScareCooldown(this.Index); - } - } - property SF2NPC_BaseNPC CopyMaster { public get() @@ -202,34 +136,6 @@ methodmap SF2NPC_BaseNPC } } - property int TeleportType - { - public get() - { - return NPCGetTeleportType(this.Index); - } - } - - public float GetTeleportRestPeriod(int difficulty) - { - return NPCGetTeleportRestPeriod(this.Index, difficulty); - } - - public float GetTeleportStressMin(int difficulty) - { - return NPCGetTeleportStressMin(this.Index, difficulty); - } - - public float GetTeleportStressMax(int difficulty) - { - return NPCGetTeleportStressMax(this.Index, difficulty); - } - - public float GetTeleportPersistencyPeriod(int difficulty) - { - return NPCGetTeleportPersistencyPeriod(this.Index, difficulty); - } - public int GetTeleporter(int teleporterNumber) { return NPCGetTeleporter(this.Index, teleporterNumber); @@ -240,19 +146,6 @@ methodmap SF2NPC_BaseNPC NPCSetTeleporter(this.Index, teleporterNumber, entity); } - property bool DeathCamEnabled - { - public get() - { - return NPCHasDeathCamEnabled(this.Index); - } - - public set(bool state) - { - NPCSetDeathCamEnabled(this.Index, state); - } - } - public SF2NPC_BaseNPC(int index) { return view_as(index); @@ -284,7 +177,7 @@ methodmap SF2NPC_BaseNPC NPCRemove(this.Index); } - property bool CanRemove + property bool CanDespawn { public get() { @@ -292,10 +185,10 @@ methodmap SF2NPC_BaseNPC } } - public void MarkAsFake() - { - SlenderMarkAsFake(this.Index); - } + public void MarkAsFake() + { + SlenderMarkAsFake(this.Index); + } public bool IsValid() { @@ -312,29 +205,39 @@ methodmap SF2NPC_BaseNPC NPCSetProfile(this.Index, profileName); } - public void GetName(char[] buffer, int bufferLen) + public float GetDistanceFrom(int entity) { - NPCGetBossName(this.Index, buffer, bufferLen); + return NPCGetDistanceFromEntity(this.Index, entity); } - public void RemoveFromGame() + public float GetAddSpeed() { - RemoveProfile(this.Index); + return NPCGetAddSpeed(this.Index); } - public float GetDistanceFrom(int entity) + public float GetPersistentAddSpeed() { - NPCGetDistanceFromEntity(this.Index, entity); + return NPCGetPersistentAddSpeed(this.Index); } - public float GetAddSpeed() + public void SetAddSpeed(float value) { - return NPCGetAddSpeed(this.Index); + NPCSetAddSpeed(this.Index, value); } - public void SetAddSpeed(float value) + public void SetPersistentAddSpeed(float value) { - NPCSetAddSpeed(this.Index, value); + NPCSetPersistentAddSpeed(this.Index, value); + } + + public float GetPersistentAddWalkSpeed() + { + return NPCGetPersistentAddWalkSpeed(this.Index); + } + + public void SetPersistentAddWalkSpeed(float value) + { + NPCSetPersistentAddWalkSpeed(this.Index, value); } public float GetAddAcceleration() @@ -342,22 +245,19 @@ methodmap SF2NPC_BaseNPC return NPCGetAddAcceleration(this.Index); } + public float GetPersistentAddAcceleration() + { + return NPCGetPersistentAddAcceleration(this.Index); + } + public void SetAddAcceleration(float value) { NPCSetAddAcceleration(this.Index, value); } - property float AddAcceleration + public void SetPersistentAddAcceleration(float value) { - public get() - { - NPCGetAddAcceleration(this.Index); - } - - public set(float amount) - { - NPCSetAddAcceleration(this.Index, amount); - } + NPCSetPersistentAddAcceleration(this.Index, value); } public void GetEyePosition(float buffer[3], const float defaultValue[3] = { 0.0, 0.0, 0.0 }) @@ -365,32 +265,6 @@ methodmap SF2NPC_BaseNPC NPCGetEyePosition(this.Index, buffer, defaultValue); } - public void GetEyePositionOffset(float buffer[3]) - { - NPCGetEyePositionOffset(this.Index, buffer); - } - - public int GetRenderColor(int cell) - { - return g_SlenderRenderColor[this.Index][cell]; - } - - property int GetRenderMode - { - public get() - { - return g_SlenderRenderMode[this.Index]; - } - } - - property int GetRenderFX - { - public get() - { - return g_SlenderRenderFX[this.Index]; - } - } - public bool HasAttribute(int attributeIndex) { return NPCHasAttribute(this.Index, attributeIndex); @@ -403,7 +277,7 @@ methodmap SF2NPC_BaseNPC public bool CanBeSeen(bool fov = true, bool blink = false, bool checkEliminated = true) { - return PeopleCanSeeSlender(this.Index, fov, blink); + return PeopleCanSeeSlender(this.Index, fov, blink, checkEliminated); } public bool PlayerCanSee(int client, bool fov = true, bool blink = false, bool eliminated = false) @@ -565,6 +439,14 @@ methodmap SF2_BasePlayer < CBaseCombatCharacter } } + property int ModifiedMaxHealth + { + public get() + { + return ClientGetModifiedMaxHealth(this.index); + } + } + property bool Ducking { public get() @@ -581,6 +463,37 @@ methodmap SF2_BasePlayer < CBaseCombatCharacter } } + property float MaxSpeed + { + public get() + { + return this.GetPropFloat(Prop_Send, "m_flMaxspeed"); + } + + public set(float value) + { + this.SetPropFloat(Prop_Send, "m_flMaxspeed", value); + } + } + + property float CurrentTauntMoveSpeed + { + public get() + { + return this.GetPropFloat(Prop_Send, "m_flCurrentTauntMoveSpeed"); + } + + public set(float value) + { + this.SetPropFloat(Prop_Send, "m_flCurrentTauntMoveSpeed", value); + } + } + + public void UpdateSpeed() + { + SDKCall(g_SDKUpdateSpeed, this.index); + } + public int GetDataEnt(int offset) { return GetEntDataEnt2(this.index, offset); @@ -731,7 +644,7 @@ methodmap SF2_BasePlayer < CBaseCombatCharacter public void UpdateListeningFlags(bool reset = false) { - ClientUpdateListeningFlags(this.index, false); + ClientUpdateListeningFlags(this.index, reset); } property bool IsParticipating @@ -854,6 +767,14 @@ methodmap SF2_BasePlayer < CBaseCombatCharacter } } + property bool IsInWeaponsTriggers + { + public get() + { + return IsClientInWeaponsTrigger(this.index); + } + } + property bool IsInDeathCam { public get() @@ -893,9 +814,9 @@ methodmap SF2_BasePlayer < CBaseCombatCharacter } } - public void HandleFlashlight() + public void ToggleFlashlight() { - ClientHandleFlashlight(this.index); + ClientToggleFlashlight(this); } property float FlashlightBatteryLife @@ -1110,11 +1031,6 @@ methodmap SF2_BasePlayer < CBaseCombatCharacter } } - public void UpdateMusicSystem(bool initialize = false) - { - ClientUpdateMusicSystem(this.index, initialize); - } - public void SetPlayState(bool state, bool enablePlay = true) { SetClientPlayState(this.index, state, enablePlay); @@ -1127,7 +1043,7 @@ methodmap SF2_BasePlayer < CBaseCombatCharacter public void SetAFKTime(bool reset = true) { - AFK_SetTime(this.index); + AFK_SetTime(this.index, reset); } public void SetAFKState() @@ -1158,9 +1074,11 @@ methodmap SF2_BasePlayer < CBaseCombatCharacter methodmap SF2NPC_Chaser < SF2NPC_BaseNPC { - public SF2ChaserBossProfileData GetProfileData() + public ChaserBossProfile GetProfileData() { - return NPCChaserGetProfileData(this.Index); + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + this.GetProfile(profile, sizeof(profile)); + return view_as(GetBossProfile(profile)); } public float GetInitialDeathHealth(int difficulty) @@ -1178,17 +1096,14 @@ methodmap SF2NPC_Chaser < SF2NPC_BaseNPC NPCChaserSetDeathHealth(this.Index, difficulty, amount); } - property float StunHealthAdd + public float GetAddStunHealth() { - public get() - { - return NPCChaserGetAddStunHealth(this.Index); - } + return NPCChaserGetAddStunHealth(this.Index); + } - public set(float value) - { - NPCChaserSetAddStunHealth(this.Index, value); - } + public void SetAddStunHealth(float value) + { + NPCChaserSetAddStunHealth(this.Index, value); } property ArrayList ChaseOnLookTargets @@ -1212,19 +1127,58 @@ methodmap SF2NPC_Statue < SF2NPC_BaseNPC return view_as(SF2NPC_BaseNPC(index)); } - public SF2StatueBossProfileData GetProfileData() - { - return NPCStatueGetProfileData(this.Index); - } - - public float GetIdleLifetime(int difficulty) + public StatueBossProfile GetProfileData() { - return NPCStatueGetIdleLifetime(this.Index, difficulty); + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + this.GetProfile(profile, sizeof(profile)); + return view_as(GetBossProfile(profile)); } } void SetupMethodmapAPI() { + CreateNative("SF2_BaseBossController.IsValid", Native_GetControllerIsValid); + CreateNative("SF2_BaseBossController.Index.get", Native_GetControllerIndex); + CreateNative("SF2_BaseBossController.Path.get", Native_GetControllerPathFollower); + CreateNative("SF2_BaseBossController.UniqueID.get", Native_GetControllerUniqueID); + CreateNative("SF2_BaseBossController.FromUniqueID", Native_GetControllerFromUniqueID); + CreateNative("SF2_BaseBossController.FromEntity", Native_GetControllerFromEntity); + CreateNative("SF2_BaseBossController.EntRef.get", Native_GetControllerEntRef); + CreateNative("SF2_BaseBossController.EntIndex.get", Native_GetControllerEntIndex); + CreateNative("SF2_BaseBossController.ProfileData", Native_GetControllerProfileData); + CreateNative("SF2_BaseBossController.Difficulty.get", Native_GetControllerDifficulty); + CreateNative("SF2_BaseBossController.Difficulty.set", Native_SetControllerDifficulty); + CreateNative("SF2_BaseBossController.Flags.get", Native_GetControllerFlags); + CreateNative("SF2_BaseBossController.Flags.set", Native_SetControllerFlags); + CreateNative("SF2_BaseBossController.CopyMaster.get", Native_GetControllerCopyMaster); + CreateNative("SF2_BaseBossController.CopyMaster.set", Native_SetControllerCopyMaster); + CreateNative("SF2_BaseBossController.IsCopy.get", Native_GetControllerIsCopy); + CreateNative("SF2_BaseBossController.CompanionMaster.get", Native_GetControllerCompanionMaster); + CreateNative("SF2_BaseBossController.CompanionMaster.set", Native_SetControllerCompanionMaster); + CreateNative("SF2_BaseBossController.GetTeleporter", Native_GetControllerTeleporter); + CreateNative("SF2_BaseBossController.SetTeleporter", Native_SetControllerTeleporter); + CreateNative("SF2_BaseBossController.Spawn", Native_ControllerSpawn); + CreateNative("SF2_BaseBossController.UnSpawn", Native_ControllerUnSpawn); + CreateNative("SF2_BaseBossController.Remove", Native_ControllerRemove); + CreateNative("SF2_BaseBossController.CanDespawn.get", Native_GetControllerCanDespawn); + CreateNative("SF2_BaseBossController.MarkAsFake", Native_ControllerMarkAsFake); + CreateNative("SF2_BaseBossController.GetProfile", Native_GetControllerProfile); + CreateNative("SF2_BaseBossController.SetProfile", Native_SetControllerProfile); + CreateNative("SF2_BaseBossController.GetAddSpeed", Native_GetControllerAddSpeed); + CreateNative("SF2_BaseBossController.GetPersistentAddSpeed", Native_GetControllerPersistentAddSpeed); + CreateNative("SF2_BaseBossController.SetAddSpeed", Native_SetControllerAddSpeed); + CreateNative("SF2_BaseBossController.SetPersistentAddSpeed", Native_SetControllerPersistentAddSpeed); + CreateNative("SF2_BaseBossController.GetPersistentAddWalkSpeed", Native_GetControllerPersistentAddWalkSpeed); + CreateNative("SF2_BaseBossController.SetPersistentAddWalkSpeed", Native_SetControllerPersistentAddWalkSpeed); + CreateNative("SF2_BaseBossController.GetAddAcceleration", Native_GetControllerAddAcceleration); + CreateNative("SF2_BaseBossController.GetPersistentAddAcceleration", Native_GetControllerPersistentAddAcceleration); + CreateNative("SF2_BaseBossController.SetAddAcceleration", Native_SetControllerAddAcceleration); + CreateNative("SF2_BaseBossController.SetPersistentAddAcceleration", Native_SetControllerPersistentAddAcceleration); + CreateNative("SF2_BaseBossController.CanBeSeen", Native_GetControllerCanBeSeen); + CreateNative("SF2_BaseBossController.PlayerCanSee", Native_GetControllerPlayerCanSee); + CreateNative("SF2_BaseBossController.IsAffectedBySight", Native_GetControllerIsAffectedBySight); + CreateNative("SF2_BaseBossController.SetAffectedBySight", Native_SetControllerAffectedBySight); + CreateNative("SF2_Player.UserID.get", Native_GetClientUserID); CreateNative("SF2_Player.IsValid.get", Native_GetIsValidClient); CreateNative("SF2_Player.IsAlive.get", Native_GetIsClientAlive); @@ -1290,7 +1244,7 @@ void SetupMethodmapAPI() CreateNative("SF2_Player.TeleportToEscapePoint", Native_ClientTeleportToEscapePoint); CreateNative("SF2_Player.ForceEscape", Native_ClientForceEscape); CreateNative("SF2_Player.UsingFlashlight.get", Native_GetClientUsingFlashlight); - CreateNative("SF2_Player.HandleFlashlight", Native_ClientHandleFlashlight); + CreateNative("SF2_Player.ToggleFlashlight", Native_ClientToggleFlashlight); CreateNative("SF2_Player.FlashlightBatteryLife.get", Native_GetClientFlashlightBatteryLife); CreateNative("SF2_Player.FlashlightBatteryLife.set", Native_SetClientFlashlightBatteryLife); CreateNative("SF2_Player.ResetFlashlight", Native_ClientResetFlashlight); @@ -1324,7 +1278,6 @@ void SetupMethodmapAPI() CreateNative("SF2_Player.LatchCount.set", Native_SetClientLatchCount); CreateNative("SF2_Player.Latcher.get", Native_GetClientLatcher); CreateNative("SF2_Player.Latcher.set", Native_SetClientLatcher); - CreateNative("SF2_Player.UpdateMusicSystem", Native_ClientUpdateMusicSystem); CreateNative("SF2_Player.HasConstantGlow.get", Native_GetClientHasConstantGlow); CreateNative("SF2_Player.SetPlayState", Native_SetClientPlayState); CreateNative("SF2_Player.CanSeeSlender", Native_GetClientCanSeeSlender); @@ -1334,6 +1287,568 @@ void SetupMethodmapAPI() CreateNative("SF2_Player.ShouldBeForceChased", Native_GetClientShouldBeForceChased); CreateNative("SF2_Player.SetForceChaseState", Native_SetClientForceChaseState); CreateNative("SF2_Player.IsLookingAtBoss", Native_GetClientIsLookingAtBoss); + + CreateNative("SF2_ChaserBossController.ProfileData", Native_GetChaserControllerProfileData); + CreateNative("SF2_ChaserBossController.GetDeathHealth", Native_GetChaserControllerDeathHealth); + CreateNative("SF2_ChaserBossController.SetDeathHealth", Native_SetChaserControllerDeathHealth); + CreateNative("SF2_ChaserBossController.GetAddStunHealth", Native_GetChaserControllerAddStunHealth); + CreateNative("SF2_ChaserBossController.SetAddStunHealth", Native_SetChaserControllerAddStunHealth); + CreateNative("SF2_ChaserBossController.ChaseOnLookTargets", Native_GetChaserControllerChaseOnLookTargets); + + CreateNative("SF2_StatueBossController.ProfileData", Native_GetStatueControllerProfileData); +} + +static any Native_GetControllerIsValid(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + return npc.IsValid(); +} + +static any Native_GetControllerIndex(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.Index; +} + +static any Native_GetControllerPathFollower(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.Path; +} + +static any Native_GetControllerUniqueID(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.UniqueID; +} + +static any Native_GetControllerFromUniqueID(Handle plugin, int numParams) +{ + return SF2NPC_BaseNPC.FromUniqueId(GetNativeCell(1)); +} + +static any Native_GetControllerFromEntity(Handle plugin, int numParams) +{ + return SF2NPC_BaseNPC.FromEntity(GetNativeCell(1)); +} + +static any Native_GetControllerEntRef(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.EntRef; +} + +static any Native_GetControllerEntIndex(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.EntIndex; +} + +static any Native_GetControllerProfileData(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.GetProfileData(); +} + +static any Native_GetControllerDifficulty(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.Difficulty; +} + +static any Native_SetControllerDifficulty(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.Difficulty = GetNativeCell(2); + return 0; +} + +static any Native_GetControllerFlags(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.Flags; +} + +static any Native_SetControllerFlags(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.Flags = GetNativeCell(2); + return 0; +} + +static any Native_GetControllerCopyMaster(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.CopyMaster; +} + +static any Native_SetControllerCopyMaster(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.CopyMaster = GetNativeCell(2); + return 0; +} + +static any Native_GetControllerIsCopy(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.IsCopy; +} + +static any Native_GetControllerCompanionMaster(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.CompanionMaster; +} + +static any Native_SetControllerCompanionMaster(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.CompanionMaster = GetNativeCell(2); + return 0; +} + +static any Native_GetControllerTeleporter(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.GetTeleporter(GetNativeCell(2)); +} + +static any Native_SetControllerTeleporter(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + int entity = EntRefToEntIndex(GetNativeCell(3)); + if (entity && entity != INVALID_ENT_REFERENCE) + { + char classname[64]; + GetEntityClassname(entity, classname, sizeof(classname)); + if (strcmp(classname, "trigger_teleport", false) != 0) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Entity index %d is not a trigger_teleport or INVALID_ENT_REFERENCE", entity); + } + } + npc.SetTeleporter(GetNativeCell(2), entity); + return 0; +} + +static any Native_ControllerSpawn(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + float pos[3]; + GetNativeArray(2, pos, 3); + npc.Spawn(pos); + return 0; +} + +static any Native_ControllerUnSpawn(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.UnSpawn(GetNativeCell(2)); + return 0; +} + +static any Native_ControllerRemove(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.Remove(); + return 0; +} + +static any Native_GetControllerCanDespawn(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.CanDespawn; +} + +static any Native_ControllerMarkAsFake(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.MarkAsFake(); + return 0; +} + +static any Native_GetControllerProfile(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + int size; + GetNativeStringLength(2, size); + size++; + char[] buffer = new char[size]; + GetNativeString(2, buffer, size); + npc.GetProfile(buffer, size); + SetNativeString(2, buffer, size); + return 0; +} + +static any Native_SetControllerProfile(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + int size; + GetNativeStringLength(2, size); + size++; + char[] buffer = new char[size]; + GetNativeString(2, buffer, size); + npc.SetProfile(buffer); + return 0; +} + +static any Native_GetControllerAddSpeed(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.GetAddSpeed(); +} + +static any Native_GetControllerPersistentAddSpeed(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.GetPersistentAddSpeed(); +} + +static any Native_SetControllerAddSpeed(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.SetAddSpeed(GetNativeCell(2)); + return 0; +} + +static any Native_SetControllerPersistentAddSpeed(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.SetPersistentAddSpeed(GetNativeCell(2)); + return 0; +} + +static any Native_GetControllerPersistentAddWalkSpeed(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.GetPersistentAddWalkSpeed(); +} + +static any Native_SetControllerPersistentAddWalkSpeed(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.SetPersistentAddWalkSpeed(GetNativeCell(2)); + return 0; +} + +static any Native_GetControllerAddAcceleration(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.GetAddAcceleration(); +} + +static any Native_GetControllerPersistentAddAcceleration(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.GetPersistentAddAcceleration(); +} + +static any Native_SetControllerAddAcceleration(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.SetAddAcceleration(GetNativeCell(2)); + return 0; +} + +static any Native_SetControllerPersistentAddAcceleration(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.SetPersistentAddAcceleration(GetNativeCell(2)); + return 0; +} + +static any Native_GetControllerCanBeSeen(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.CanBeSeen(GetNativeCell(2), GetNativeCell(3), GetNativeCell(4)); +} + +static any Native_GetControllerPlayerCanSee(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.PlayerCanSee(GetNativeCell(2), GetNativeCell(3), GetNativeCell(4), GetNativeCell(5)); +} + +static any Native_GetControllerIsAffectedBySight(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + return npc.IsAffectedBySight(); +} + +static any Native_SetControllerAffectedBySight(Handle plugin, int numParams) +{ + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", npc); + } + + npc.SetAffectedBySight(GetNativeCell(2)); + return 0; +} + +static any Native_GetChaserControllerProfileData(Handle plugin, int numParams) +{ + SF2NPC_Chaser npc = SF2NPC_Chaser(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid chaser boss index %d", npc); + } + + return npc.GetProfileData(); +} + +static any Native_GetChaserControllerDeathHealth(Handle plugin, int numParams) +{ + SF2NPC_Chaser npc = SF2NPC_Chaser(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid chaser boss index %d", npc); + } + + return npc.GetDeathHealth(GetNativeCell(2)); +} + +static any Native_SetChaserControllerDeathHealth(Handle plugin, int numParams) +{ + SF2NPC_Chaser npc = SF2NPC_Chaser(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid chaser boss index %d", npc); + } + + npc.SetDeathHealth(GetNativeCell(2), GetNativeCell(3)); + return 0; +} + +static any Native_GetChaserControllerAddStunHealth(Handle plugin, int numParams) +{ + SF2NPC_Chaser npc = SF2NPC_Chaser(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid chaser boss index %d", npc); + } + + return npc.GetAddStunHealth(); +} + +static any Native_SetChaserControllerAddStunHealth(Handle plugin, int numParams) +{ + SF2NPC_Chaser npc = SF2NPC_Chaser(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid chaser boss index %d", npc); + } + + npc.SetAddStunHealth(GetNativeCell(2)); + return 0; +} + +static any Native_GetChaserControllerChaseOnLookTargets(Handle plugin, int numParams) +{ + SF2NPC_Chaser npc = SF2NPC_Chaser(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid chaser boss index %d", npc); + } + + return npc.ChaseOnLookTargets; +} + +static any Native_GetStatueControllerProfileData(Handle plugin, int numParams) +{ + SF2NPC_Statue npc = SF2NPC_Statue(GetNativeCell(1)); + if (!npc.IsValid()) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid statue boss index %d", npc); + } + + return npc.GetProfileData(); } static any Native_GetClientUserID(Handle plugin, int numParams) @@ -2158,7 +2673,7 @@ static any Native_GetClientUsingFlashlight(Handle plugin, int numParams) return player.UsingFlashlight; } -static any Native_ClientHandleFlashlight(Handle plugin, int numParams) +static any Native_ClientToggleFlashlight(Handle plugin, int numParams) { int client = GetNativeCell(1); if (!IsValidClient(client)) @@ -2167,7 +2682,7 @@ static any Native_ClientHandleFlashlight(Handle plugin, int numParams) } SF2_BasePlayer player = SF2_BasePlayer(client); - player.HandleFlashlight(); + player.ToggleFlashlight(); return 0; } @@ -2584,19 +3099,6 @@ static any Native_SetClientLatcher(Handle plugin, int numParams) return 0; } -static any Native_ClientUpdateMusicSystem(Handle plugin, int numParams) -{ - int client = GetNativeCell(1); - if (!IsValidClient(client)) - { - return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", client); - } - - SF2_BasePlayer player = SF2_BasePlayer(client); - player.UpdateMusicSystem(GetNativeCell(2)); - return 0; -} - static any Native_GetClientHasConstantGlow(Handle plugin, int numParams) { int client = GetNativeCell(1); diff --git a/addons/sourcemod/scripting/sf2/npc.sp b/addons/sourcemod/scripting/sf2/npc.sp index f6b5f452..8058b4a6 100644 --- a/addons/sourcemod/scripting/sf2/npc.sp +++ b/addons/sourcemod/scripting/sf2/npc.sp @@ -4,107 +4,26 @@ #define _sf2_npc_included #pragma semicolon 1 +#pragma newdecls required #define SF2_BOSS_PAGE_CALCULATION 512.0 -#define SF2_BOSS_COPY_SPAWN_MIN_DISTANCE 800.0 // The default minimum distance boss copies can spawn from each other. - -#define SF2_BOSS_ATTACK_MELEE 0 static int g_NpcGlobalUniqueID = 0; -static SF2BossProfileData g_NpcProfileData[MAX_BOSSES]; - static int g_NpcUniqueID[MAX_BOSSES] = { -1, ... }; static char g_SlenderProfile[MAX_BOSSES][SF2_MAX_PROFILE_NAME_LENGTH]; -static int g_NpcProfileIndex[MAX_BOSSES] = { -1, ... }; -static int g_NpcUniqueProfileIndex[MAX_BOSSES] = { -1, ... }; -static int g_NpcType[MAX_BOSSES] = { SF2BossType_Unknown, ... }; static int g_NpcFlags[MAX_BOSSES] = { 0, ... }; -static float g_NpcModelScale[MAX_BOSSES] = { 1.0, ... }; -static int g_NpcModelSkin[MAX_BOSSES] = { 0, ... }; -static int g_NpcModelSkinDifficulty[MAX_BOSSES][Difficulty_Max]; -static int g_NpcModelSkinMax[MAX_BOSSES] = { 0, ... }; -static int g_NpcModelBodyGroups[MAX_BOSSES] = { 0, ... }; -static int g_NpcModelBodyGroupsDifficulty[MAX_BOSSES][Difficulty_Max]; -static int g_NpcModelBodyGroupsMax[MAX_BOSSES] = { 0, ... }; -bool g_NpcRaidHitbox[MAX_BOSSES] = { false, ... }; -static float g_NpcSoundMusicLoop[MAX_BOSSES][Difficulty_Max]; -static int g_NpcAllowMusicOnDifficulty[MAX_BOSSES]; -static char g_NpcName[MAX_BOSSES][Difficulty_Max][SF2_MAX_PROFILE_NAME_LENGTH]; -static bool g_NpcHasFakeCopiesEnabled[MAX_BOSSES][Difficulty_Max]; -static float g_NpcBlinkLookRate[MAX_BOSSES]; -static float g_NpcBlinkStaticRate[MAX_BOSSES]; -static float g_NpcStepSize[MAX_BOSSES]; static int g_NpcTeleporters[MAX_BOSSES][MAX_NPCTELEPORTER]; static int g_NpcModelMaster[2049]; - -static bool g_NpcHasDiscoMode[MAX_BOSSES]; -static float g_NpcDiscoRadiusMin[MAX_BOSSES]; -static float g_NpcDiscoRadiusMax[MAX_BOSSES]; -static float g_NpcDiscoModePos[MAX_BOSSES][3]; -static bool g_NpcHasFestiveLights[MAX_BOSSES]; -static int g_NpcFestiveLightBrightness[MAX_BOSSES]; -static float g_NpcFestiveLightDistance[MAX_BOSSES]; -static float g_NpcFestiveLightRadius[MAX_BOSSES]; -static float g_NpcFestiveLightPos[MAX_BOSSES][3]; -static float g_NpcFestiveLightAng[MAX_BOSSES][3]; - -static float g_NpcFieldOfView[MAX_BOSSES] = { 0.0, ... }; - -static bool g_NpcHasTeleportAllowed[MAX_BOSSES][Difficulty_Max]; -static float g_NpcTeleportTimeMin[MAX_BOSSES][Difficulty_Max]; -static float g_NpcTeleportTimeMax[MAX_BOSSES][Difficulty_Max]; -static float g_NpcTeleportRestPeriod[MAX_BOSSES][Difficulty_Max]; -static float g_NpcTeleportStressMin[MAX_BOSSES][Difficulty_Max]; -static float g_NpcTeleportStressMax[MAX_BOSSES][Difficulty_Max]; -static float g_NpcTeleportPersistencyPeriod[MAX_BOSSES][Difficulty_Max]; - -static float g_NpcJumpscareDistance[MAX_BOSSES][Difficulty_Max]; -static float g_NpcJumpscareDuration[MAX_BOSSES][Difficulty_Max]; -static float g_NpcJumpscareCooldown[MAX_BOSSES][Difficulty_Max]; -static bool g_NpcHasJumpscareOnScare[MAX_BOSSES]; +static float g_NpcHighestPagePercent[MAX_BOSSES] = { -1.0, ... }; int g_SlenderEnt[MAX_BOSSES] = { INVALID_ENT_REFERENCE, ... }; static float g_NpcAddSpeed[MAX_BOSSES]; +static float g_NpcAddSpeedPersistent[MAX_BOSSES]; +static float g_NpcAddWalkSpeedPersistent[MAX_BOSSES]; static float g_NpcAddAcceleration[MAX_BOSSES]; -static float g_NpcIdleLifetime[MAX_BOSSES][Difficulty_Max]; - -static float g_NpcScareRadius[MAX_BOSSES]; -static float g_NpcScareCooldown[MAX_BOSSES]; - -static bool g_NpcHasPlayerScareSpeedBoost[MAX_BOSSES]; -static float g_NpcPlayerSpeedBoostDuration[MAX_BOSSES]; - -static bool g_NpcHasPlayerScareReaction[MAX_BOSSES]; -static int g_NpcPlayerScareReactionType[MAX_BOSSES]; - -static bool g_NpcHasPlayerScareReplenishSprint[MAX_BOSSES]; -static float g_NpcPlayerScareReplenishSprintAmount[MAX_BOSSES]; - -static int g_NpcTeleportType[MAX_BOSSES] = { -1, ... }; - -static bool g_NpcHasDeathCamEnabled[MAX_BOSSES] = { false, ... }; - -static bool g_NpcHasProxyWeaponsEnabled[MAX_BOSSES] = { false, ... }; - -static float g_NpcProxySpawnChanceMin[MAX_BOSSES][Difficulty_Max]; -static float g_NpcProxySpawnChanceMax[MAX_BOSSES][Difficulty_Max]; -static float g_NpcProxySpawnChanceThreshold[MAX_BOSSES][Difficulty_Max]; -static int g_NpcProxySpawnNumMin[MAX_BOSSES][Difficulty_Max]; -static int g_NpcProxySpawnNumMax[MAX_BOSSES][Difficulty_Max]; -static float g_NpcProxySpawnCooldownMin[MAX_BOSSES][Difficulty_Max]; -static float g_NpcProxySpawnCooldownMax[MAX_BOSSES][Difficulty_Max]; - -static bool g_NpcHasIgnoreNavPrefer[MAX_BOSSES]; - -static int g_NpcDeathMessageDifficultyIndexes[MAX_BOSSES]; - -static bool g_NpcHasDrainCreditsState[MAX_BOSSES]; -static int g_NpcDrainCreditAmount[MAX_BOSSES][Difficulty_Max]; - -static bool g_NpcHasProxySpawnEffectEnabled[MAX_BOSSES]; -static float g_NpcProxySpawnEffectZOffset[MAX_BOSSES]; +static float g_NpcAddAccelerationPersistent[MAX_BOSSES]; static bool g_NpcShouldBeAffectedBySight[MAX_BOSSES]; @@ -112,93 +31,12 @@ static int g_NpcDefaultTeam[MAX_BOSSES]; static bool g_NpcWasKilled[MAX_BOSSES]; -Handle timerMusic = null; //Planning to add a bosses array on. - -bool NPCGetBossName(int npcIndex = -1, char[] buffer, int bufferLen, char profile[SF2_MAX_PROFILE_NAME_LENGTH] = "") -{ - if (npcIndex == -1 && profile[0] == '\0') - { - return false; - } - int difficulty = g_DifficultyConVar.IntValue; - if (npcIndex != -1) - { - switch (difficulty) - { - case Difficulty_Normal: - { - strcopy(buffer, bufferLen, g_NpcName[npcIndex][1]); - } - case Difficulty_Hard: - { - strcopy(buffer, bufferLen, g_NpcName[npcIndex][2]); - } - case Difficulty_Insane: - { - strcopy(buffer, bufferLen, g_NpcName[npcIndex][3]); - } - case Difficulty_Nightmare: - { - strcopy(buffer, bufferLen, g_NpcName[npcIndex][4]); - } - case Difficulty_Apollyon: - { - strcopy(buffer, bufferLen, g_NpcName[npcIndex][5]); - } - } - } - else - { - ArrayList arrayNames; - arrayNames = GetBossProfileNames(profile); - switch (difficulty) - { - case Difficulty_Normal: - { - arrayNames.GetString(Difficulty_Normal, buffer, bufferLen); - } - case Difficulty_Hard: - { - arrayNames.GetString(Difficulty_Hard, buffer, bufferLen); - } - case Difficulty_Insane: - { - arrayNames.GetString(Difficulty_Insane, buffer, bufferLen); - } - case Difficulty_Nightmare: - { - arrayNames.GetString(Difficulty_Nightmare, buffer, bufferLen); - } - case Difficulty_Apollyon: - { - arrayNames.GetString(Difficulty_Apollyon, buffer, bufferLen); - } - } - arrayNames = null; - } - return true; -} - -bool NPCHasProxyWeapons(int npcIndex) -{ - return g_NpcHasProxyWeaponsEnabled[npcIndex]; -} - -bool NPCHasDeathCamEnabled(int npcIndex) -{ - return g_NpcHasDeathCamEnabled[npcIndex]; -} - -void NPCSetDeathCamEnabled(int npcIndex, bool state) -{ - g_NpcHasDeathCamEnabled[npcIndex] = state; -} - void NPCInitialize() { g_OnEntityDestroyedPFwd.AddFunction(null, EntityDestroyed); g_OnEntityTeleportedPFwd.AddFunction(null, EntityTeleported); g_OnPlayerLookAtBossPFwd.AddFunction(null, OnPlayerLookAtBoss); + g_OnPageCountChangedPFwd.AddFunction(null, OnPageCountChanged); NPCChaserInitialize(); SetupNPCGlows(); @@ -259,6 +97,8 @@ void NPC_InitializeAPI() CreateNative("SF2_SpawnBossEffects", Native_SpawnBossEffects); CreateNative("SF2_CanBossBeSeen", Native_CanBossBeSeen); + + SetupNPCEffectsAPI(); } static void EntityDestroyed(CBaseEntity ent, const char[] classname) @@ -266,7 +106,7 @@ static void EntityDestroyed(CBaseEntity ent, const char[] classname) SF2NPC_BaseNPC controller = SF2NPC_BaseNPC.FromEntity(ent.index); if (controller.IsValid()) { - NPCOnDespawn(controller, ent); + NPCOnDespawn(controller, view_as(ent)); } } @@ -281,7 +121,7 @@ static void EntityTeleported(CBaseEntity teleporter, CBaseEntity activator) // A boss took a teleporter, remove it from our list if possible SF2_BaseBoss boss = SF2_BaseBoss(controller.EntIndex); ArrayList teleporters = boss.Teleporters; - int index = teleporters.FindValue(teleporter.index); + int index = teleporters.FindValue(EntIndexToEntRef(teleporter.index)); if (index != -1) { teleporters.Erase(index); @@ -306,7 +146,7 @@ static void EntityTeleported(CBaseEntity teleporter, CBaseEntity activator) if (boss.Target == activator) { // The boss target took a teleporter and the boss can follow, add the teleporter to the list of teleporters - boss.Teleporters.Push(teleporter.index); + boss.Teleporters.Push(EntIndexToEntRef(teleporter.index)); } } } @@ -339,7 +179,7 @@ static void EntityTeleported(CBaseEntity teleporter, CBaseEntity activator) controller.UnSpawn(); } - if (controller.Type == SF2BossType_Chaser) + if (controller.GetProfileData().Type == SF2BossType_Chaser) { if (view_as(controller).GetProfileData().ChasesEndlessly) { @@ -353,7 +193,7 @@ static void EntityTeleported(CBaseEntity teleporter, CBaseEntity activator) static void OnPlayerLookAtBoss(SF2_BasePlayer client, SF2NPC_BaseNPC boss) { - switch (boss.Type) + switch (boss.GetProfileData().Type) { case SF2BossType_Statue: { @@ -365,1200 +205,388 @@ static void OnPlayerLookAtBoss(SF2_BasePlayer client, SF2NPC_BaseNPC boss) } } -void NPCOnDespawn(SF2NPC_BaseNPC controller, CBaseEntity entity, bool killed = false) +static void OnPageCountChanged(int pageCount, int oldPageCount) { - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - controller.GetProfile(profile, sizeof(profile)); - int bossIndex = controller.Index; - SF2BossProfileData data; - data = controller.GetProfileData(); - Call_StartForward(g_OnBossDespawnFwd); - Call_PushCell(bossIndex); - Call_Finish(); - - //Turn off all slender's effects in order to prevent some bugs. - SlenderRemoveEffects(bossIndex, true); - - if (controller.Type == SF2BossType_Statue) - { - Despawn_Statue(SF2NPC_Statue(bossIndex), entity); - } - else + ArrayList indexes = null; + for (int i = 0; i < MAX_BOSSES; i++) { - Despawn_Chaser(bossIndex); - } + SF2NPC_BaseNPC controller = SF2NPC_BaseNPC(i); + if (!controller.IsValid()) + { + continue; + } - if (g_BossPathFollower[bossIndex] != view_as(0) && g_BossPathFollower[bossIndex].IsValid()) - { - g_BossPathFollower[bossIndex].Invalidate(); - } + int difficulty = controller.Difficulty; + BaseBossProfile profileData = controller.GetProfileData(); + if (profileData.IsPvEBoss) + { + continue; + } - if (data.DespawnEffects != null) - { - if (killed && data.HideDespawnEffectsOnDeath) + float old = g_NpcHighestPagePercent[controller.Index]; + float newValue = -1.0; + BossOnPageCountChangedData pageChangeData = profileData.GetPageCountChagedData(pageCount, old, newValue); + if (pageChangeData == null) + { + continue; + } + + int entity = NPCGetEntIndex(i); + if (entity && entity != INVALID_ENT_REFERENCE && pageChangeData.GetLocalSounds() != null) + { + pageChangeData.GetLocalSounds().EmitSound(_, entity); + } + if (pageChangeData.GetGlobalSounds() != null) { - // Do nothing + if (indexes == null) + { + indexes = new ArrayList(); + } + indexes.Push(NPCGetUniqueID(i)); } else { - StringMapSnapshot snapshot = data.DespawnEffects.Snapshot(); - char key[64]; - snapshot.GetKey(GetRandomInt(0, snapshot.Length - 1), key, sizeof(key)); - ArrayList effects; - data.DespawnEffects.GetValue(key, effects); - float pos[3], ang[3]; - CBaseEntity(NPCGetEntIndex(bossIndex)).GetAbsOrigin(pos); - CBaseEntity(NPCGetEntIndex(bossIndex)).GetAbsAngles(ang); - SlenderSpawnEffects(effects, bossIndex, false, pos, ang, _, _, true); - delete snapshot; + g_NpcHighestPagePercent[controller.Index] = newValue; } - } - if (data.EngineSound[0] != '\0') - { - StopSound(entity.index, SNDCHAN_STATIC, data.EngineSound); + controller.SetPersistentAddSpeed(pageChangeData.GetAddSpeed(difficulty)); + controller.SetPersistentAddWalkSpeed(pageChangeData.GetAddWalkSpeed(difficulty)); + controller.SetPersistentAddAcceleration(pageChangeData.GetAddAcceleration(difficulty)); } - if (NPCGetFlags(bossIndex) & SFF_HASSTATICLOOPLOCALSOUND) + if (indexes != null) { - char loopSound[PLATFORM_MAX_PATH]; - GetBossProfileStaticLocalSound(profile, loopSound, sizeof(loopSound)); - - if (loopSound[0] != '\0') + indexes.Sort(Sort_Random, Sort_Integer); + SF2NPC_BaseNPC controller = SF2_INVALID_NPC; + int index = 0; + while (!controller.IsValid()) { - StopSound(entity.index, SNDCHAN_STATIC, loopSound); + controller = SF2NPC_BaseNPC(NPCGetFromUniqueID(indexes.Get(index))); + index++; } - } - if (SF_IsRaidMap() || SF_BossesChaseEndlessly() || data.Healthbar) - { - controller.Flags = controller.Flags & ~SFF_NOTELEPORT; - } - g_SlenderEnt[bossIndex] = INVALID_ENT_REFERENCE; - if (data.Healthbar && g_HealthBar != -1) - { - int npcIndex = 0; - for (npcIndex = 0; npcIndex < MAX_BOSSES; npcIndex++) + BaseBossProfile profileData = controller.GetProfileData(); + BossOnPageCountChangedData pageChangeData = profileData.GetPageCountChagedData(pageCount, g_NpcHighestPagePercent[controller.Index], g_NpcHighestPagePercent[controller.Index]); + for (int i = 1; i <= MaxClients; i++) { - if (NPCGetUniqueID(npcIndex) == -1) + SF2_BasePlayer player = SF2_BasePlayer(i); + if (!player.IsValid) { continue; } - data = NPCGetProfileData(npcIndex); - if (data.Healthbar) + + if (player.IsEliminated && !player.IsInGhostMode && !player.IsProxy) { - int tempSlender = NPCGetEntIndex(npcIndex); + continue; + } - if (tempSlender && tempSlender != INVALID_ENT_REFERENCE) - { - UpdateHealthBar(npcIndex); - break; - } + if (!player.IsEliminated && player.HasEscaped) + { + continue; } + + pageChangeData.GetGlobalSounds().EmitSound(true, i); } - if (npcIndex == MAX_BOSSES) - { - UpdateHealthBar(bossIndex, 0); - } + delete indexes; } } -void NPCOnConfigsExecuted() -{ - g_NpcGlobalUniqueID = 0; -} - -bool NPCIsValid(int npcIndex) -{ - return npcIndex >= 0 && npcIndex < MAX_BOSSES && NPCGetUniqueID(npcIndex) != -1; -} - -int NPCGetUniqueID(int npcIndex) +void NPCOnDespawn(SF2NPC_BaseNPC controller, CBaseCombatCharacter entity) { - return g_NpcUniqueID[npcIndex]; -} + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + controller.GetProfile(profile, sizeof(profile)); + int bossIndex = controller.Index; + BaseBossProfile data = controller.GetProfileData(); + Call_StartForward(g_OnBossDespawnFwd); + Call_PushCell(bossIndex); + Call_Finish(); -int NPCGetFromUniqueID(int NPCUniqueID) -{ - if (NPCUniqueID == -1) - { - return -1; - } + //Turn off all slender's effects in order to prevent some bugs. + SlenderRemoveEffects(bossIndex, true); - for (int i = 0; i < MAX_BOSSES; i++) + if (data.Type == SF2BossType_Statue) { - if (NPCGetUniqueID(i) == NPCUniqueID) - { - return i; - } + Despawn_Statue(SF2NPC_Statue(bossIndex), entity); } - - return -1; -} - -int NPCGetEntRef(int npcIndex) -{ - return g_SlenderEnt[npcIndex]; -} - -int NPCGetEntIndex(int npcIndex) -{ - return EntRefToEntIndex(NPCGetEntRef(npcIndex)); -} - -int NPCGetFromEntIndex(int entity) -{ - if (!entity || !IsValidEntity(entity)) + else { - return -1; + Despawn_Chaser(bossIndex); } - for (int i = 0; i < MAX_BOSSES; i++) + if (g_BossPathFollower[bossIndex] != view_as(0) && g_BossPathFollower[bossIndex].IsValid()) { - if (NPCGetEntIndex(i) == entity) - { - return i; - } + g_BossPathFollower[bossIndex].Invalidate(); } - return -1; -} - -int NPCGetCount() -{ - int count; - for (int i = 0; i < MAX_BOSSES; i++) + if (data.GetDespawnEffects() != null) { - if (NPCGetUniqueID(i) == -1) - { - continue; - } - if (NPCGetFlags(i) & SFF_FAKE) + ProfileObject obj = view_as(data.GetDespawnEffects().GetSection(GetRandomInt(0, data.GetDespawnEffects().Length - 1))); + if (obj != null) { - continue; + if (data.GetBool("__was_killed") && obj.GetBool("hide_on_death", false)) + { + // Do nothing + } + else + { + obj = obj.GetSection("effects"); + if (obj != null) + { + float pos[3], ang[3]; + CBaseEntity(NPCGetEntIndex(bossIndex)).GetAbsOrigin(pos); + CBaseEntity(NPCGetEntIndex(bossIndex)).GetAbsAngles(ang); + SlenderSpawnEffects(view_as(obj), bossIndex, false, pos, ang, _, _, true); + } + } } - - count++; } - return count; -} - -int NPCGetProfileIndex(int npcIndex) -{ - return g_NpcProfileIndex[npcIndex]; -} - -int NPCGetUniqueProfileIndex(int npcIndex) -{ - return g_NpcUniqueProfileIndex[npcIndex]; -} - -bool NPCGetProfile(int npcIndex, char[] buffer, int bufferLen) -{ - strcopy(buffer, bufferLen, g_SlenderProfile[npcIndex]); - return true; -} - -SF2BossProfileData NPCGetProfileData(int npcIndex) -{ - return g_NpcProfileData[npcIndex]; -} - -void NPCSetProfileData(int npcIndex, SF2BossProfileData value) -{ - g_NpcProfileData[npcIndex] = value; -} - -void NPCSetProfile(int npcIndex, const char[] profile) -{ - strcopy(g_SlenderProfile[npcIndex], sizeof(g_SlenderProfile[]), profile); -} - -void NPCRemove(int npcIndex) -{ - if (!NPCIsValid(npcIndex)) + if (data.GetDespawnInputs() != null) { - return; + data.GetDespawnInputs().AcceptInputs(entity); } - RemoveProfile(npcIndex); -} - -void NPCStopMusic() -{ - //Stop the music timer - if (timerMusic != null) - { - delete timerMusic; - } - //Stop the music for all players. - for (int i = 1; i <= MaxClients; i++) + char sound[PLATFORM_MAX_PATH]; + data.GetConstantSound(sound, sizeof(sound)); + if (sound[0] != '\0') { - if (IsValidClient(i)) - { - if (currentMusicTrack[0] != '\0') - { - StopSound(i, MUSIC_CHAN, currentMusicTrack); - } - ClientUpdateMusicSystem(i); - } + StopSound(entity.index, SNDCHAN_STATIC, sound); } -} -void CheckIfMusicValid() -{ - int difficulty = g_DifficultyConVar.IntValue; - for (int i = 0; i < MAX_BOSSES; i++) - { - if (NPCGetUniqueID(i) == -1) - { - continue; - } - SF2BossProfileData data; - data = NPCGetProfileData(i); - if (data.IsPvEBoss) - { - continue; - } - if (g_NpcAllowMusicOnDifficulty[i] & difficulty) - { - currentMusicTrackNormal[0] = '\0'; - currentMusicTrackHard[0] = '\0'; - currentMusicTrackInsane[0] = '\0'; - currentMusicTrackNightmare[0] = '\0'; - currentMusicTrackApollyon[0] = '\0'; - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(i, profile, sizeof(profile)); - for(int client = 1;client <= MaxClients; client++) - { - if (IsValidClient(client) && (!g_PlayerEliminated[client] || IsClientInGhostMode(client))) - { - SF2BossProfileSoundInfo soundInfo; - GetBossProfileMusicSounds(profile, soundInfo, 1); - ArrayList soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNormal, sizeof(currentMusicTrackNormal)); - } - soundList = null; - - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackHard, sizeof(currentMusicTrackHard)); - } - if (currentMusicTrackHard[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackHard, sizeof(currentMusicTrackHard)); - } - } - soundList = null; - - GetBossProfileMusicSounds(profile, soundInfo, 3); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackInsane, sizeof(currentMusicTrackInsane)); - } - if (currentMusicTrackInsane[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackInsane, sizeof(currentMusicTrackInsane)); - } - if (currentMusicTrackInsane[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackInsane, sizeof(currentMusicTrackInsane)); - } - } - } - soundList = null; - - GetBossProfileMusicSounds(profile, soundInfo, 4); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNightmare, sizeof(currentMusicTrackNightmare)); - } - if (currentMusicTrackNightmare[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 3); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNightmare, sizeof(currentMusicTrackNightmare)); - } - if (currentMusicTrackNightmare[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNightmare, sizeof(currentMusicTrackNightmare)); - } - if (currentMusicTrackNightmare[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNightmare, sizeof(currentMusicTrackNightmare)); - } - } - } - } - soundList = null; - - GetBossProfileMusicSounds(profile, soundInfo, 5); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - if (currentMusicTrackApollyon[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 4); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - if (currentMusicTrackApollyon[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 3); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - if (currentMusicTrackApollyon[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - if (currentMusicTrackApollyon[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - } - } - } - } - soundList = null; - - switch (g_DifficultyConVar.IntValue) - { - case Difficulty_Normal: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackNormal); - } - case Difficulty_Hard: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackHard); - } - case Difficulty_Insane: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackInsane); - } - case Difficulty_Nightmare: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackNightmare); - } - case Difficulty_Apollyon: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackApollyon); - } - } - if (currentMusicTrack[0] != '\0') - { - timerMusic = CreateTimer(NPCGetSoundMusicLoop(i, difficulty), BossMusic, i, TIMER_FLAG_NO_MAPCHANGE); - StopSound(client, MUSIC_CHAN, currentMusicTrack); - ClientChaseMusicReset(client); - ClientChaseMusicSeeReset(client); - ClientAlertMusicReset(client); - ClientIdleMusicReset(client); - GetChaserProfileChaseMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); - GetChaserProfileChaseVisibleMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); - GetChaserProfileAlertMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); - GetChaserProfileIdleMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); - if (g_PlayerMusicString[client][0] != '\0') - { - EmitSoundToClient(client, g_PlayerMusicString[client], _, MUSIC_CHAN, SNDLEVEL_NONE, SND_CHANGEVOL, 0.0001); - } - ClientMusicStart(client, currentMusicTrack, _, MUSIC_PAGE_VOLUME,true); - ClientUpdateMusicSystem(client); - break; - } - } - } - break; - } - } -} - -bool MusicActive() -{ - if (timerMusic != null) - { - return true; - } - return false; -} - -bool BossHasMusic(char[] profile) -{ - int difficulty = g_DifficultyConVar.IntValue; - char temp[512]; - ArrayList soundList; - SF2BossProfileSoundInfo soundInfo; - switch (difficulty) - { - case Difficulty_Normal: - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - } - case Difficulty_Hard: - { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - } - } - case Difficulty_Insane: - { - GetBossProfileMusicSounds(profile, soundInfo, 3); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - } - } - } - case Difficulty_Nightmare: - { - GetBossProfileMusicSounds(profile, soundInfo, 4); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 3); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - } - } - } - } - case Difficulty_Apollyon: - { - GetBossProfileMusicSounds(profile, soundInfo, 5); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 4); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 3); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - else - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), temp, sizeof(temp)); - } - if (temp[0] != '\0') - { - return true; - } - } - } - } - } - } - } - return false; -} - -bool BossMatchesCurrentMusic(char[] profile) -{ - if (!IsProfileValid(profile)) - { - return false; - } - - char buffer[PLATFORM_MAX_PATH], currentMusic[PLATFORM_MAX_PATH]; - int difficulty = g_DifficultyConVar.IntValue; - - GetBossMusic(currentMusic, sizeof(currentMusic)); - - ArrayList soundList; - SF2BossProfileSoundInfo soundInfo; - switch (difficulty) - { - case Difficulty_Normal: - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - for (int i = 0; i < soundList.Length; i++) - { - soundList.GetString(i, buffer, sizeof(buffer)); - if (strcmp(buffer, currentMusic, false) == 0) - { - return true; - } - } - } - } - case Difficulty_Hard: - { - for (int section = 1; section <= Difficulty_Hard; section++) - { - GetBossProfileMusicSounds(profile, soundInfo, section); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - for (int i = 0; i < soundList.Length; i++) - { - soundList.GetString(i, buffer, sizeof(buffer)); - if (strcmp(buffer, currentMusic, false) == 0) - { - return true; - } - } - } - } - } - case Difficulty_Insane: - { - for (int section = 1; section <= Difficulty_Insane; section++) - { - GetBossProfileMusicSounds(profile, soundInfo, section); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - for (int i = 0; i < soundList.Length; i++) - { - soundList.GetString(i, buffer, sizeof(buffer)); - if (strcmp(buffer, currentMusic, false) == 0) - { - return true; - } - } - } - } - } - case Difficulty_Nightmare: - { - for (int section = 1; section <= Difficulty_Nightmare; section++) - { - GetBossProfileMusicSounds(profile, soundInfo, section); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - for (int i = 0; i < soundList.Length; i++) - { - soundList.GetString(i, buffer, sizeof(buffer)); - if (strcmp(buffer, currentMusic, false) == 0) - { - return true; - } - } - } - } - } - case Difficulty_Apollyon: - { - for (int section = 1; section <= Difficulty_Apollyon; section++) - { - GetBossProfileMusicSounds(profile, soundInfo, section); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - for (int i = 0; i < soundList.Length; i++) - { - soundList.GetString(i, buffer, sizeof(buffer)); - if (strcmp(buffer, currentMusic, false) == 0) - { - return true; - } - } - } - } - } - } - - return false; -} - -void GetBossMusic(char[] buffer,int bufferLen) -{ - int difficulty = g_DifficultyConVar.IntValue; - switch (difficulty) - { - case Difficulty_Normal: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackNormal); - strcopy(buffer, bufferLen, currentMusicTrackNormal); - } - case Difficulty_Hard: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackHard); - strcopy(buffer, bufferLen, currentMusicTrackHard); - } - case Difficulty_Insane: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackInsane); - strcopy(buffer, bufferLen, currentMusicTrackInsane); - } - case Difficulty_Nightmare: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackNightmare); - strcopy(buffer, bufferLen, currentMusicTrackNightmare); - } - case Difficulty_Apollyon: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackApollyon); - strcopy(buffer, bufferLen, currentMusicTrackApollyon); - } - } -} - -static Action BossMusic(Handle timer, any bossIndex) -{ - int difficulty = g_DifficultyConVar.IntValue; - float time = NPCGetSoundMusicLoop(bossIndex, difficulty); - SF2BossProfileData data; - data = NPCGetProfileData(bossIndex); - if (!data.IsPvEBoss && time > 0.0 && (g_NpcAllowMusicOnDifficulty[bossIndex] & difficulty)) - { - if (bossIndex > -1) - { - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(bossIndex, profile, sizeof(profile)); - for (int i = 1; i <= MaxClients; i++) - { - if (IsValidClient(i) && currentMusicTrack[0] != '\0') - { - StopSound(i, MUSIC_CHAN, currentMusicTrack); - } - } - timerMusic = CreateTimer(time, BossMusic, bossIndex, TIMER_FLAG_NO_MAPCHANGE); - return Plugin_Continue; - } - NPCStopMusic(); - } - timerMusic = null; - return Plugin_Continue; -} - -void NPCRemoveAll() -{ - for (int i = 0; i < MAX_BOSSES; i++) - { - NPCRemove(i); - } -} - -int NPCGetType(int npcIndex) -{ - return g_NpcType[npcIndex]; -} - -int NPCGetFlags(int npcIndex) -{ - return g_NpcFlags[npcIndex]; -} - -void NPCSetFlags(int npcIndex,int flags) -{ - g_NpcFlags[npcIndex] = flags; -} - -float NPCGetSoundMusicLoop(int npcIndex, int difficulty) -{ - return g_NpcSoundMusicLoop[npcIndex][difficulty]; -} - -float NPCGetModelScale(int npcIndex) -{ - return g_NpcModelScale[npcIndex]; -} - -int NPCGetModelSkin(int npcIndex) -{ - return g_NpcModelSkin[npcIndex]; -} - -int NPCGetModelSkinDifficulty(int npcIndex, int difficulty) -{ - return g_NpcModelSkinDifficulty[npcIndex][difficulty]; -} - -int NPCGetModelSkinMax(int npcIndex) -{ - return g_NpcModelSkinMax[npcIndex]; -} - -int NPCGetModelBodyGroups(int npcIndex) -{ - return g_NpcModelBodyGroups[npcIndex]; -} - -int NPCGetModelBodyGroupsDifficulty(int npcIndex, int difficulty) -{ - return g_NpcModelBodyGroupsDifficulty[npcIndex][difficulty]; -} - -int NPCGetModelBodyGroupsMax(int npcIndex) -{ - return g_NpcModelBodyGroupsMax[npcIndex]; -} - -bool NPCGetRaidHitbox(int npcIndex) -{ - return g_NpcRaidHitbox[npcIndex]; -} - -float NPCGetBlinkLookRate(int npcIndex) -{ - return g_NpcBlinkLookRate[npcIndex]; -} - -float NPCGetBlinkStaticRate(int npcIndex) -{ - return g_NpcBlinkStaticRate[npcIndex]; -} - -bool NPCGetDiscoModeState(int npcIndex) -{ - return g_NpcHasDiscoMode[npcIndex]; -} - -float NPCGetDiscoModeRadiusMin(int npcIndex) -{ - return g_NpcDiscoRadiusMin[npcIndex]; -} - -float NPCGetDiscoModeRadiusMax(int npcIndex) -{ - return g_NpcDiscoRadiusMax[npcIndex]; -} - -float[] NPCGetDiscoModePos(int npcIndex) -{ - return g_NpcDiscoModePos[npcIndex]; -} - -bool NPCGetFestiveLightState(int npcIndex) -{ - return g_NpcHasFestiveLights[npcIndex]; -} - -int NPCGetFestiveLightBrightness(int npcIndex) -{ - return g_NpcFestiveLightBrightness[npcIndex]; -} - -float NPCGetFestiveLightDistance(int npcIndex) -{ - return g_NpcFestiveLightDistance[npcIndex]; -} - -float NPCGetFestiveLightRadius(int npcIndex) -{ - return g_NpcFestiveLightRadius[npcIndex]; -} - -float[] NPCGetFestiveLightPosition(int npcIndex) -{ - return g_NpcFestiveLightPos[npcIndex]; -} - -float[] NPCGetFestiveLightAngle(int npcIndex) -{ - return g_NpcFestiveLightAng[npcIndex]; -} - -bool NPCGetCustomOutlinesState(int npcIndex) -{ - return g_SlenderUseCustomOutlines[npcIndex]; -} - -bool NPCGetRainbowOutlineState(int npcIndex) -{ - return g_SlenderUseRainbowOutline[npcIndex]; -} - -void NPCSetAddSpeed(int npcIndex, float amount) -{ - g_NpcAddSpeed[npcIndex] += amount; -} - -void NPCSetAddAcceleration(int npcIndex, float amount) -{ - g_NpcAddAcceleration[npcIndex] += amount; -} - -float NPCGetAddSpeed(int npcIndex) -{ - return g_NpcAddSpeed[npcIndex]; -} - -float NPCGetAddAcceleration(int npcIndex) -{ - return g_NpcAddAcceleration[npcIndex]; -} + if (NPCGetFlags(bossIndex) & SFF_HASSTATICLOOPLOCALSOUND) + { + char loopSound[PLATFORM_MAX_PATH]; + data.GetStaticLocalLoopSound(loopSound, sizeof(loopSound)); -bool NPCIsTeleportAllowed(int npcIndex, int difficulty) -{ - return g_NpcHasTeleportAllowed[npcIndex][difficulty]; -} + if (loopSound[0] != '\0') + { + StopSound(entity.index, SNDCHAN_STATIC, loopSound); + } + } + if (SF_IsRaidMap() || SF_BossesChaseEndlessly() || view_as(data).Healthbar) + { + controller.Flags = controller.Flags & ~SFF_NOTELEPORT; + } -float NPCGetTeleportTimeMin(int npcIndex, int difficulty) -{ - return g_NpcTeleportTimeMin[npcIndex][difficulty]; -} + g_SlenderEnt[bossIndex] = INVALID_ENT_REFERENCE; + if (view_as(data).Healthbar && g_HealthBar != -1) + { + int npcIndex = 0; + for (npcIndex = 0; npcIndex < MAX_BOSSES; npcIndex++) + { + if (NPCGetUniqueID(npcIndex) == -1) + { + continue; + } + data = SF2NPC_BaseNPC(npcIndex).GetProfileData(); + if (view_as(data).Healthbar) + { + int tempSlender = NPCGetEntIndex(npcIndex); -float NPCGetTeleportTimeMax(int npcIndex, int difficulty) -{ - return g_NpcTeleportTimeMax[npcIndex][difficulty]; -} + if (tempSlender && tempSlender != INVALID_ENT_REFERENCE) + { + UpdateHealthBar(npcIndex); + break; + } + } + } + if (npcIndex == MAX_BOSSES) + { + UpdateHealthBar(bossIndex, 0); + } + } -float NPCGetTeleportRestPeriod(int npcIndex, int difficulty) -{ - return g_NpcTeleportRestPeriod[npcIndex][difficulty]; + data.SetBool("__was_killed", false); } -float NPCGetTeleportStressMin(int npcIndex, int difficulty) +void NPCOnConfigsExecuted() { - return g_NpcTeleportStressMin[npcIndex][difficulty]; + g_NpcGlobalUniqueID = 0; } -float NPCGetTeleportStressMax(int npcIndex, int difficulty) +bool NPCIsValid(int npcIndex) { - return g_NpcTeleportStressMax[npcIndex][difficulty]; + return npcIndex >= 0 && npcIndex < MAX_BOSSES && NPCGetUniqueID(npcIndex) != -1; } -float NPCGetTeleportPersistencyPeriod(int npcIndex, int difficulty) +int NPCGetUniqueID(int npcIndex) { - return g_NpcTeleportPersistencyPeriod[npcIndex][difficulty]; + return g_NpcUniqueID[npcIndex]; } -void NPCSetTeleporter(int bossIndex, int teleporterNumber, int entity) +int NPCGetFromUniqueID(int NPCUniqueID) { - g_NpcTeleporters[bossIndex][teleporterNumber] = entity; -} + if (NPCUniqueID == -1) + { + return -1; + } -int NPCGetTeleporter(int bossIndex, int teleporterNumber) -{ - return g_NpcTeleporters[bossIndex][teleporterNumber]; -} + for (int i = 0; i < MAX_BOSSES; i++) + { + if (NPCGetUniqueID(i) == NPCUniqueID) + { + return i; + } + } -SF2NPC_BaseNPC NPCGetCopyMaster(SF2NPC_BaseNPC controller) -{ - return SF2NPC_BaseNPC(g_SlenderCopyMaster[controller.Index]); + return -1; } -SF2NPC_BaseNPC NPCGetCompanionMaster(SF2NPC_BaseNPC controller) +int NPCGetEntRef(int npcIndex) { - return SF2NPC_BaseNPC(g_SlenderCompanionMaster[controller.Index]); + return g_SlenderEnt[npcIndex]; } -float NPCGetJumpscareDistance(int npcIndex, int difficulty) +int NPCGetEntIndex(int npcIndex) { - return g_NpcJumpscareDistance[npcIndex][difficulty]; + return EntRefToEntIndex(NPCGetEntRef(npcIndex)); } -float NPCGetJumpscareDuration(int npcIndex, int difficulty) +int NPCGetFromEntIndex(int entity) { - return g_NpcJumpscareDuration[npcIndex][difficulty]; -} + if (!entity || !IsValidEntity(entity)) + { + return -1; + } -float NPCGetJumpscareCooldown(int npcIndex, int difficulty) -{ - return g_NpcJumpscareCooldown[npcIndex][difficulty]; -} + for (int i = 0; i < MAX_BOSSES; i++) + { + if (NPCGetEntIndex(i) == entity) + { + return i; + } + } -bool NPCGetJumpscareOnScare(int npcIndex) -{ - return g_NpcHasJumpscareOnScare[npcIndex]; + return -1; } -float NPCGetIdleLifetime(int npcIndex,int difficulty) +int NPCGetCount() { - return g_NpcIdleLifetime[npcIndex][difficulty]; -} + int count; + for (int i = 0; i < MAX_BOSSES; i++) + { + if (NPCGetUniqueID(i) == -1) + { + continue; + } + if (NPCGetFlags(i) & SFF_FAKE) + { + continue; + } -void NPCGetEyePositionOffset(int npcIndex, float buffer[3]) -{ - buffer[0] = g_SlenderEyePosOffset[npcIndex][0]; - buffer[1] = g_SlenderEyePosOffset[npcIndex][1]; - buffer[2] = g_SlenderEyePosOffset[npcIndex][2]; -} + count++; + } -float NPCGetScareRadius(int npcIndex) -{ - return g_NpcScareRadius[npcIndex]; + return count; } -float NPCGetScareCooldown(int npcIndex) +bool NPCGetProfile(int npcIndex, char[] buffer, int bufferLen) { - return g_NpcScareCooldown[npcIndex]; + strcopy(buffer, bufferLen, g_SlenderProfile[npcIndex]); + return true; } -bool NPCGetSpeedBoostOnScare(int npcIndex) +void NPCSetProfile(int npcIndex, const char[] profile) { - return g_NpcHasPlayerScareSpeedBoost[npcIndex]; + strcopy(g_SlenderProfile[npcIndex], sizeof(g_SlenderProfile[]), profile); } -float NPCGetScareSpeedBoostDuration(int npcIndex) +void NPCRemove(int npcIndex) { - return g_NpcPlayerSpeedBoostDuration[npcIndex]; + if (!NPCIsValid(npcIndex)) + { + return; + } + + RemoveProfile(npcIndex); } -bool NPCGetScareReactionState(int npcIndex) +void NPCRemoveAll() { - return g_NpcHasPlayerScareReaction[npcIndex]; + for (int i = 0; i < MAX_BOSSES; i++) + { + NPCRemove(i); + } } -int NPCGetScareReactionType(int npcIndex) +int NPCGetFlags(int npcIndex) { - return g_NpcPlayerScareReactionType[npcIndex]; + return g_NpcFlags[npcIndex]; } -bool NPCGetScareReplenishSprintState(int npcIndex) +void NPCSetFlags(int npcIndex,int flags) { - return g_NpcHasPlayerScareReplenishSprint[npcIndex]; + g_NpcFlags[npcIndex] = flags; } -float NPCGetScareReplenishSprintAmount(int npcIndex) +void NPCSetAddSpeed(int npcIndex, float amount) { - return g_NpcPlayerScareReplenishSprintAmount[npcIndex]; + g_NpcAddSpeed[npcIndex] += amount; } -int NPCGetTeleportType(int npcIndex) +void NPCSetPersistentAddSpeed(int npcIndex, float amount) { - return g_NpcTeleportType[npcIndex]; + g_NpcAddSpeedPersistent[npcIndex] += amount; } -bool NPCGetFakeCopyState(int npcIndex, int difficulty) +void NPCSetPersistentAddWalkSpeed(int npcIndex, float amount) { - return g_NpcHasFakeCopiesEnabled[npcIndex][difficulty]; + g_NpcAddWalkSpeedPersistent[npcIndex] += amount; } -#if defined _store_included -bool NPCGetDrainCreditState(int npcIndex) +void NPCSetAddAcceleration(int npcIndex, float amount) { - return g_NpcHasDrainCreditsState[npcIndex]; + g_NpcAddAcceleration[npcIndex] += amount; } -int NPCGetDrainCreditAmount(int npcIndex, int difficulty) +void NPCSetPersistentAddAcceleration(int npcIndex, float amount) { - return g_NpcDrainCreditAmount[npcIndex][difficulty]; + g_NpcAddAccelerationPersistent[npcIndex] += amount; } -#endif -bool NPCGetProxySpawnEffectState(int npcIndex) +float NPCGetAddSpeed(int npcIndex) { - return g_NpcHasProxySpawnEffectEnabled[npcIndex]; + return g_NpcAddSpeed[npcIndex]; } -float NPCGetProxySpawnEffectZOffset(int npcIndex) +float NPCGetPersistentAddSpeed(int npcIndex) { - return g_NpcProxySpawnEffectZOffset[npcIndex]; + return g_NpcAddSpeedPersistent[npcIndex]; } -float NPCGetProxySpawnChanceMin(int npcIndex, int difficulty) +float NPCGetPersistentAddWalkSpeed(int npcIndex) { - return g_NpcProxySpawnChanceMin[npcIndex][difficulty]; + return g_NpcAddWalkSpeedPersistent[npcIndex]; } -float NPCGetProxySpawnChanceMax(int npcIndex, int difficulty) +float NPCGetAddAcceleration(int npcIndex) { - return g_NpcProxySpawnChanceMax[npcIndex][difficulty]; + return g_NpcAddAcceleration[npcIndex]; } -float NPCGetProxySpawnChanceThreshold(int npcIndex, int difficulty) +float NPCGetPersistentAddAcceleration(int npcIndex) { - return g_NpcProxySpawnChanceThreshold[npcIndex][difficulty]; + return g_NpcAddAccelerationPersistent[npcIndex]; } -int NPCGetProxySpawnNumMin(int npcIndex, int difficulty) +void NPCSetTeleporter(int bossIndex, int teleporterNumber, int entity) { - return g_NpcProxySpawnNumMin[npcIndex][difficulty]; + g_NpcTeleporters[bossIndex][teleporterNumber] = entity; } -int NPCGetProxySpawnNumMax(int npcIndex, int difficulty) +int NPCGetTeleporter(int bossIndex, int teleporterNumber) { - return g_NpcProxySpawnNumMax[npcIndex][difficulty]; + return g_NpcTeleporters[bossIndex][teleporterNumber]; } -float NPCGetProxySpawnCooldownMin(int npcIndex, int difficulty) +SF2NPC_BaseNPC NPCGetCopyMaster(SF2NPC_BaseNPC controller) { - return g_NpcProxySpawnCooldownMin[npcIndex][difficulty]; + return SF2NPC_BaseNPC(g_SlenderCopyMaster[controller.Index]); } -float NPCGetProxySpawnCooldownMax(int npcIndex, int difficulty) +SF2NPC_BaseNPC NPCGetCompanionMaster(SF2NPC_BaseNPC controller) { - return g_NpcProxySpawnCooldownMax[npcIndex][difficulty]; + return SF2NPC_BaseNPC(g_SlenderCompanionMaster[controller.Index]); } bool NPCGetAffectedBySightState(int npcIndex) @@ -1619,7 +647,7 @@ bool NPCShouldHearEntity(int npcIndex, int entity, SoundType soundType) return false; } - if (NPCGetType(npcIndex) == SF2BossType_Statue) + if (SF2NPC_BaseNPC(npcIndex).GetProfileData().Type == SF2BossType_Statue) { return false; } @@ -1674,7 +702,7 @@ void NPCGetEyePosition(int npcIndex, float buffer[3], const float defaultValue[3 float pos[3], eyePosOffset[3]; CBaseEntity(npcEnt).GetAbsOrigin(pos); - NPCGetEyePositionOffset(npcIndex, eyePosOffset); + SF2NPC_BaseNPC(npcIndex).GetProfileData().GetEyes().GetOffsetPos(eyePosOffset); AddVectors(pos, eyePosOffset, buffer); } @@ -1689,11 +717,14 @@ bool NPCHasAttribute(int npcIndex, int attribute) char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(npcIndex, profile, sizeof(profile)); - SF2BossProfileAttributesInfo attributesInfo; - GetBossProfileAttributesInfo(profile, attributesInfo); + BossProfileAttributes attributesInfo = GetBossProfile(profile).GetAttributes(); + if (attributesInfo == null) + { + return false; + } bool returnValue = false; - if (attributesInfo.Value[attribute] >= 0.0) + if (attributesInfo.GetValue(attribute) >= 0.0) { returnValue = true; } @@ -1711,10 +742,14 @@ float NPCGetAttributeValue(int npcIndex, int attribute) char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(npcIndex, profile, sizeof(profile)); - SF2BossProfileAttributesInfo attributesInfo; - GetBossProfileAttributesInfo(profile, attributesInfo); + BossProfileAttributes attributesInfo = GetBossProfile(profile).GetAttributes(); - return attributesInfo.Value[attribute]; + if (attributesInfo == null) + { + return 0.0; + } + + return attributesInfo.GetValue(attribute); } bool SlenderCanRemove(int bossIndex) @@ -1731,8 +766,9 @@ bool SlenderCanRemove(int bossIndex) char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); + BaseBossProfile profileData = GetBossProfile(profile); - int teleportType = NPCGetTeleportType(bossIndex); + int teleportType = profileData.TeleportType; int difficulty = GetLocalGlobalDifficulty(bossIndex); @@ -1765,7 +801,7 @@ bool SlenderCanRemove(int bossIndex) } GetClientAbsOrigin(i, buffer); - if (GetVectorSquareMagnitude(buffer, slenderPos) <= SquareFloat(g_SlenderStaticRadius[bossIndex][difficulty])) + if (GetVectorSquareMagnitude(buffer, slenderPos) <= SquareFloat(profileData.GetStaticRadius(difficulty))) { return false; } @@ -1828,75 +864,21 @@ bool SelectProfile(SF2NPC_BaseNPC npc, const char[] profile, int additionalBossF npc.SetProfile(profile); - SF2BossProfileData profileData; - g_BossProfileData.GetArray(profile, profileData, sizeof(profileData)); + BaseBossProfile profileData = GetBossProfile(profile); - int bossType = GetBossProfileType(profile); + int bossType = profileData.Type; g_NpcUniqueID[npc.Index] = g_NpcGlobalUniqueID++; - g_NpcProfileData[npc.Index] = profileData; - g_NpcType[npc.Index] = bossType; - - g_NpcModelScale[npc.Index] = GetBossProfileModelScale(profile); - - g_NpcModelSkin[npc.Index] = GetBossProfileSkin(profile); - - g_NpcModelSkinMax[npc.Index] = GetBossProfileSkinMax(profile); - - g_NpcModelBodyGroups[npc.Index] = GetBossProfileBodyGroups(profile); - - g_NpcModelBodyGroupsMax[npc.Index] = GetBossProfileBodyGroupsMax(profile); - - g_NpcRaidHitbox[npc.Index] = GetBossProfileRaidHitbox(profile); - - g_NpcHasIgnoreNavPrefer[npc.Index] = GetBossProfileIgnoreNavPrefer(profile); - - g_NpcBlinkLookRate[npc.Index] = GetBossProfileBlinkLookRate(profile); - g_NpcBlinkStaticRate[npc.Index] = GetBossProfileBlinkStaticRate(profile); - - g_NpcStepSize[npc.Index] = GetBossProfileStepSize(profile); - - g_NpcHasDiscoMode[npc.Index] = GetBossProfileDiscoModeState(profile); - g_NpcDiscoRadiusMin[npc.Index] = GetBossProfileDiscoRadiusMin(profile); - g_NpcDiscoRadiusMax[npc.Index] = GetBossProfileDiscoRadiusMax(profile); - - g_NpcHasFestiveLights[npc.Index] = GetBossProfileFestiveLightState(profile); - g_NpcFestiveLightBrightness[npc.Index] = GetBossProfileFestiveLightBrightness(profile); - g_NpcFestiveLightDistance[npc.Index] = GetBossProfileFestiveLightDistance(profile); - g_NpcFestiveLightRadius[npc.Index] = GetBossProfileFestiveLightRadius(profile); - GetBossProfileDiscoPosition(profile, g_NpcDiscoModePos[npc.Index]); - GetBossProfileFestiveLightPosition(profile, g_NpcFestiveLightPos[npc.Index]); - GetBossProfileFestiveLightAngles(profile, g_NpcFestiveLightAng[npc.Index]); - - g_SlenderUseCustomOutlines[npc.Index] = GetBossProfileCustomOutlinesState(profile); - g_SlenderUseRainbowOutline[npc.Index] = GetBossProfileRainbowOutlineState(profile); - - g_NpcHasProxyWeaponsEnabled[npc.Index] = GetBossProfileProxyWeapons(profile); - - g_NpcHasDrainCreditsState[npc.Index] = GetBossProfileDrainCreditState(profile); - g_NpcHasProxySpawnEffectEnabled[npc.Index] = GetBossProfileProxySpawnEffectState(profile); - g_NpcProxySpawnEffectZOffset[npc.Index] = GetBossProfileProxySpawnEffectZOffset(profile); if (SF_IsSlaughterRunMap()) { - NPCSetFlags(npc.Index, GetBossProfileFlags(profile) | additionalBossFlags | SFF_NOTELEPORT); + NPCSetFlags(npc.Index, profileData.Flags | additionalBossFlags | SFF_NOTELEPORT); } else { - NPCSetFlags(npc.Index, GetBossProfileFlags(profile) | additionalBossFlags); + NPCSetFlags(npc.Index, profileData.Flags | additionalBossFlags); } - GetBossProfileEyePositionOffset(profile, g_SlenderEyePosOffset[npc.Index]); - GetBossProfileEyeAngleOffset(profile, g_SlenderEyeAngOffset[npc.Index]); - - GetBossProfileHullMins(profile, g_SlenderDetectMins[npc.Index]); - GetBossProfileHullMaxs(profile, g_SlenderDetectMaxs[npc.Index]); - - GetBossProfileRenderColor(profile, g_SlenderRenderColor[npc.Index]); - - g_SlenderRenderFX[npc.Index] = GetBossProfileRenderFX(profile); - g_SlenderRenderMode[npc.Index] = GetBossProfileRenderMode(profile); - npc.CopyMaster = SF2_INVALID_NPC; npc.CompanionMaster = SF2_INVALID_NPC; @@ -1904,106 +886,13 @@ bool SelectProfile(SF2NPC_BaseNPC npc, const char[] profile, int additionalBossF g_NpcDefaultTeam[npc.Index] = g_DefaultBossTeamConVar.IntValue; - for (int difficulty = 0; difficulty < Difficulty_Max; difficulty++) - { - g_NpcHasFakeCopiesEnabled[npc.Index][difficulty] = GetBossProfileFakeCopies(profile, difficulty); - g_NpcSoundMusicLoop[npc.Index][difficulty] = GetBossProfileSoundMusicLoop(profile, difficulty); - g_NpcIdleLifetime[npc.Index][difficulty] = GetBossProfileIdleLifetime(profile, difficulty); - g_SlenderStaticRadius[npc.Index][difficulty] = GetBossProfileStaticRadius(profile, difficulty); - g_SlenderStaticRate[npc.Index][difficulty] = GetBossProfileStaticRate(profile, difficulty); - g_SlenderStaticRateDecay[npc.Index][difficulty] = GetBossProfileStaticRateDecay(profile, difficulty); - g_SlenderStaticGraceTime[npc.Index][difficulty] = GetBossProfileStaticGraceTime(profile, difficulty); - g_SlenderTeleportMinRange[npc.Index][difficulty] = GetBossProfileTeleportRangeMin(profile, difficulty); - g_SlenderTeleportMaxRange[npc.Index][difficulty] = GetBossProfileTeleportRangeMax(profile, difficulty); - g_NpcHasTeleportAllowed[npc.Index][difficulty] = GetBossProfileTeleportAllowed(profile, difficulty); - g_NpcTeleportTimeMin[npc.Index][difficulty] = GetBossProfileTeleportTimeMin(profile, difficulty); - g_NpcTeleportTimeMax[npc.Index][difficulty] = GetBossProfileTeleportTimeMax(profile, difficulty); - g_NpcTeleportRestPeriod[npc.Index][difficulty] = GetBossProfileTeleportTargetRestPeriod(profile, difficulty); - g_NpcTeleportStressMin[npc.Index][difficulty] = GetBossProfileTeleportTargetStressMin(profile, difficulty); - g_NpcTeleportStressMax[npc.Index][difficulty] = GetBossProfileTeleportTargetStressMax(profile, difficulty); - g_NpcTeleportPersistencyPeriod[npc.Index][difficulty] = GetBossProfileTeleportTargetPersistencyPeriod(profile, difficulty); - g_NpcJumpscareDistance[npc.Index][difficulty] = GetBossProfileJumpscareDistance(profile, difficulty); - g_NpcJumpscareDuration[npc.Index][difficulty] = GetBossProfileJumpscareDuration(profile, difficulty); - g_NpcJumpscareCooldown[npc.Index][difficulty] = GetBossProfileJumpscareCooldown(profile, difficulty); - g_NpcModelSkinDifficulty[npc.Index][difficulty] = GetBossProfileSkinDifficulty(profile, difficulty); - g_NpcModelBodyGroupsDifficulty[npc.Index][difficulty] = GetBossProfileBodyGroupsDifficulty(profile, difficulty); - g_SlenderMaxCopies[npc.Index][difficulty] = GetBossProfileMaxCopies(profile, difficulty); - - g_SlenderProxyDamageVsEnemy[npc.Index][difficulty] = GetBossProfileProxyDamageVsEnemy(profile, difficulty); - g_SlenderProxyDamageVsBackstab[npc.Index][difficulty] = GetBossProfileProxyDamageVsBackstab(profile, difficulty); - g_SlenderProxyDamageVsSelf[npc.Index][difficulty] = GetBossProfileProxyDamageVsSelf(profile, difficulty); - g_SlenderProxyControlGainHitEnemy[npc.Index][difficulty] = GetBossProfileProxyControlGainHitEnemy(profile, difficulty); - g_SlenderProxyControlGainHitByEnemy[npc.Index][difficulty] = GetBossProfileProxyControlGainHitByEnemy(profile, difficulty); - g_SlenderProxyControlDrainRate[npc.Index][difficulty] = GetBossProfileProxyControlDrainRate(profile, difficulty); - g_SlenderMaxProxies[npc.Index][difficulty] = GetBossProfileMaxProxies(profile, difficulty); - g_NpcProxySpawnChanceMin[npc.Index][difficulty] = GetBossProfileProxySpawnChanceMin(profile, difficulty); - g_NpcProxySpawnChanceMax[npc.Index][difficulty] = GetBossProfileProxySpawnChanceMax(profile, difficulty); - g_NpcProxySpawnChanceThreshold[npc.Index][difficulty] = GetBossProfileProxySpawnChanceThreshold(profile, difficulty); - g_NpcProxySpawnNumMin[npc.Index][difficulty] = GetBossProfileProxySpawnNumberMin(profile, difficulty); - g_NpcProxySpawnNumMax[npc.Index][difficulty] = GetBossProfileProxySpawnNumberMax(profile, difficulty); - g_NpcProxySpawnCooldownMin[npc.Index][difficulty] = GetBossProfileProxySpawnCooldownMin(profile, difficulty); - g_NpcProxySpawnCooldownMax[npc.Index][difficulty] = GetBossProfileProxySpawnCooldownMax(profile, difficulty); - g_SlenderProxyTeleportMinRange[npc.Index][difficulty] = GetBossProfileProxyTeleportRangeMin(profile, difficulty); - g_SlenderProxyTeleportMaxRange[npc.Index][difficulty] = GetBossProfileProxyTeleportRangeMax(profile, difficulty); - g_NpcDrainCreditAmount[npc.Index][difficulty] = GetBossProfileDrainCreditAmount(profile, difficulty); - } - - ArrayList arrayNames; - - arrayNames = GetBossProfileNames(profile); - arrayNames.GetString(Difficulty_Normal, g_NpcName[npc.Index][1], sizeof(g_NpcName[][])); - - strcopy(g_NpcName[npc.Index][0], sizeof(g_NpcName[][]), g_NpcName[npc.Index][1]); - - arrayNames.GetString(Difficulty_Hard, g_NpcName[npc.Index][2], sizeof(g_NpcName[][])); - - arrayNames.GetString(Difficulty_Insane, g_NpcName[npc.Index][3], sizeof(g_NpcName[][])); - - arrayNames.GetString(Difficulty_Nightmare, g_NpcName[npc.Index][4], sizeof(g_NpcName[][])); - - arrayNames.GetString(Difficulty_Apollyon, g_NpcName[npc.Index][5], sizeof(g_NpcName[][])); - - arrayNames = null; - - g_NpcHasJumpscareOnScare[npc.Index] = GetBossProfileJumpscareOnScare(profile); - - g_NpcScareRadius[npc.Index] = GetBossProfileScareRadius(profile); - g_NpcScareCooldown[npc.Index] = GetBossProfileScareCooldown(profile); - - g_NpcHasPlayerScareSpeedBoost[npc.Index] = GetBossProfileSpeedBoostOnScare(profile); - g_NpcPlayerSpeedBoostDuration[npc.Index] = GetBossProfileScareSpeedBoostDuration(profile); - - g_NpcHasPlayerScareReaction[npc.Index] = GetBossProfileScareReactionState(profile); - g_NpcPlayerScareReactionType[npc.Index] = GetBossProfileScareReactionType(profile); - - g_NpcHasPlayerScareReplenishSprint[npc.Index] = GetBossProfileScareReplenishState(profile); - g_NpcPlayerScareReplenishSprintAmount[npc.Index] = GetBossProfileScareReplenishAmount(profile); - - g_NpcTeleportType[npc.Index] = GetBossProfileTeleportType(profile); - - g_SlenderTeleportIgnoreChases[npc.Index] = GetBossProfileTeleportIgnoreChases(profile); - - g_SlenderTeleportIgnoreVis[npc.Index] = GetBossProfileTeleportIgnoreVis(profile); - - g_SlenderProxiesAllowNormalVoices[npc.Index] = GetBossProfileProxyAllowNormalVoices(profile); - - g_NpcDeathMessageDifficultyIndexes[npc.Index] = GetBossProfileChatDeathMessageDifficultyIndexes(profile); + g_NpcHighestPagePercent[npc.Index] = -1.0; g_NpcAddSpeed[npc.Index] = 0.0; + g_NpcAddSpeedPersistent[npc.Index] = 0.0; + g_NpcAddWalkSpeedPersistent[npc.Index] = 0.0; g_NpcAddAcceleration[npc.Index] = 0.0; - - // Deathcam values. - npc.DeathCamEnabled = GetBossProfileDeathCamState(profile); - g_SlenderDeathCamScareSound[npc.Index] = GetBossProfileDeathCamScareSound(profile); - g_SlenderPublicDeathCam[npc.Index] = GetBossProfilePublicDeathCamState(profile); - g_SlenderPublicDeathCamSpeed[npc.Index] = GetBossProfilePublicDeathCamSpeed(profile); - g_SlenderPublicDeathCamAcceleration[npc.Index] = GetBossProfilePublicDeathCamAcceleration(profile); - g_SlenderPublicDeathCamDeceleration[npc.Index] = GetBossProfilePublicDeathCamDeceleration(profile); - g_SlenderPublicDeathCamBackwardOffset[npc.Index] = GetBossProfilePublicDeathCamBackwardOffset(profile); - g_SlenderPublicDeathCamDownwardOffset[npc.Index] = GetBossProfilePublicDeathCamDownwardOffset(profile); - g_SlenderDeathCamOverlay[npc.Index] = GetBossProfileDeathCamOverlayState(profile); - g_SlenderDeathCamOverlayTimeStart[npc.Index] = GetBossProfileDeathCamOverlayStartTime(profile); - g_SlenderDeathCamTime[npc.Index] = GetBossProfileDeathCamTime(profile); + g_NpcAddAccelerationPersistent[npc.Index] = 0.0; g_SlenderFakeTimer[npc.Index] = null; g_SlenderEntityThink[npc.Index] = null; @@ -2014,8 +903,6 @@ bool SelectProfile(SF2NPC_BaseNPC npc, const char[] profile, int additionalBossF g_SlenderNextJumpScare[npc.Index] = -1.0; g_SlenderTimeUntilNextProxy[npc.Index] = -1.0; - g_SlenderCustomOutroSong[npc.Index] = profileData.OutroMusic; - for (int i = 1; i <= MaxClients; i++) { g_SlenderTeleportPlayersRestTime[npc.Index][i] = -1.0; @@ -2068,19 +955,22 @@ bool SelectProfile(SF2NPC_BaseNPC npc, const char[] profile, int additionalBossF } else { - if (profileData.IsPvEBoss && showPvEMessage && profileData.PvESpawnMessagesArray != null && profileData.PvESpawnMessagesArray.Length > 0) + BossProfilePvEData pveData = profileData.GetPvEData(); + if (pveData.IsEnabled && showPvEMessage && pveData.GetSpawnMessages() != null && pveData.GetSpawnMessages().KeyLength > 0) { - char prefix[PLATFORM_MAX_PATH], message[PLATFORM_MAX_PATH]; - strcopy(prefix, sizeof(prefix), profileData.PvESpawnMessagePrefix); + char prefix[PLATFORM_MAX_PATH], message[PLATFORM_MAX_PATH], name[SF2_MAX_NAME_LENGTH], keyName[64]; + pveData.GetSpawnMessagePrefix(prefix, sizeof(prefix)); if (prefix[0] == '\0') { prefix = "[SF2]"; } - int messageIndex = GetRandomInt(0, profileData.PvESpawnMessagesArray.Length - 1); - profileData.PvESpawnMessagesArray.GetString(messageIndex, message, sizeof(message)); + int messageIndex = GetRandomInt(0, pveData.GetSpawnMessages().KeyLength - 1); + pveData.GetSpawnMessages().GetKeyNameFromIndex(messageIndex, keyName, sizeof(keyName)); + pveData.GetSpawnMessages().GetString(keyName, message, sizeof(message)); if (StrContains(message, "[BOSS]", true) != -1) { - ReplaceString(message, sizeof(message), "[BOSS]", g_NpcName[npc.Index][1]); + profileData.GetName(1, name, sizeof(name)); + ReplaceString(message, sizeof(message), "[BOSS]", name); } int chatLength = strlen(prefix) + strlen(message); if (chatLength > 255) @@ -2091,254 +981,44 @@ bool SelectProfile(SF2NPC_BaseNPC npc, const char[] profile, int additionalBossF { for (int i = 1; i <= MaxClients; i++) { - if (!IsValidClient(i) || !g_PlayerEliminated[i]) - { - continue; - } - CPrintToChat(i, "{royalblue}%s {default}%s", prefix, message); - } - } - } - if (playSpawnSound) - { - SF2BossProfileSoundInfo soundInfo; - GetBossProfileIntroSounds(profile, soundInfo); - for (int i = 1; i <= MaxClients; i++) - { - if (!IsValidClient(i)) - { - continue; - } - if (g_Enabled) - { - if (profileData.IsPvEBoss && !g_PlayerEliminated[i]) - { - continue; - } - } - soundInfo.EmitSound(true, i); - } - } - if (g_Enabled && !profileData.IsPvEBoss && timerMusic == null) - { - bool allowMusic = false; - float time; - for (int difficulty = 0; difficulty < Difficulty_Max; difficulty++) - { - if (g_NpcSoundMusicLoop[npc.Index][difficulty] > 0.0) - { - allowMusic = true; - g_NpcAllowMusicOnDifficulty[npc.Index] |= difficulty; - } - } - if (allowMusic) - { - time = g_NpcSoundMusicLoop[npc.Index][g_DifficultyConVar.IntValue]; - currentMusicTrackNormal[0] = '\0'; - currentMusicTrackHard[0] = '\0'; - currentMusicTrackInsane[0] = '\0'; - currentMusicTrackNightmare[0] = '\0'; - currentMusicTrackApollyon[0] = '\0'; - ArrayList soundList; - SF2BossProfileSoundInfo soundInfo; - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNormal, sizeof(currentMusicTrackNormal)); - } - soundList = null; - - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackHard, sizeof(currentMusicTrackHard)); - } - if (currentMusicTrackHard[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackHard, sizeof(currentMusicTrackHard)); - } - } - soundList = null; - - GetBossProfileMusicSounds(profile, soundInfo, 3); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackInsane, sizeof(currentMusicTrackInsane)); - } - if (currentMusicTrackInsane[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackInsane, sizeof(currentMusicTrackInsane)); - } - if (currentMusicTrackInsane[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackInsane, sizeof(currentMusicTrackInsane)); - } - } - } - soundList = null; - - GetBossProfileMusicSounds(profile, soundInfo, 4); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNightmare, sizeof(currentMusicTrackNightmare)); - } - if (currentMusicTrackNightmare[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 3); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNightmare, sizeof(currentMusicTrackNightmare)); - } - if (currentMusicTrackNightmare[0] == '\0') + if (!IsValidClient(i) || !g_PlayerEliminated[i]) { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNightmare, sizeof(currentMusicTrackNightmare)); - } - if (currentMusicTrackNightmare[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackNightmare, sizeof(currentMusicTrackNightmare)); - } - } + continue; } + CPrintToChat(i, "{royalblue}%s {default}%s", prefix, message); } - soundList = null; - - GetBossProfileMusicSounds(profile, soundInfo, 5); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - if (currentMusicTrackApollyon[0] == '\0') + } + } + if (playSpawnSound) + { + ProfileSound soundInfo = profileData.GetIntroSounds(); + for (int i = 1; i <= MaxClients; i++) + { + if (!IsValidClient(i)) { - GetBossProfileMusicSounds(profile, soundInfo, 4); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - if (currentMusicTrackApollyon[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 3); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - if (currentMusicTrackApollyon[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 2); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - if (currentMusicTrackApollyon[0] == '\0') - { - GetBossProfileMusicSounds(profile, soundInfo, 1); - soundList = soundInfo.Paths; - if (soundList != null && soundList.Length > 0) - { - soundList.GetString(GetRandomInt(0, soundList.Length - 1), currentMusicTrackApollyon, sizeof(currentMusicTrackApollyon)); - } - } - } - } + continue; } - soundList = null; - - if ((g_NpcAllowMusicOnDifficulty[npc.Index] & g_DifficultyConVar.IntValue) && time > 0.0) + if (g_Enabled) { - timerMusic = CreateTimer(time, BossMusic, npc.Index, TIMER_FLAG_NO_MAPCHANGE); - for(int client = 1; client <= MaxClients; client++) + if (profileData.IsPvEBoss && !g_PlayerEliminated[i]) { - if (IsValidClient(client) && (!g_PlayerEliminated[client] || IsClientInGhostMode(client))) - { - ClientChaseMusicReset(client); - ClientChaseMusicSeeReset(client); - ClientAlertMusicReset(client); - ClientIdleMusicReset(client); - GetChaserProfileChaseMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); - GetChaserProfileChaseVisibleMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); - GetChaserProfileAlertMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); - GetChaserProfileIdleMusics(profile, soundInfo); - soundInfo.StopAllSounds(client); - if (g_PlayerMusicString[client][0] != '\0') - { - EmitSoundToClient(client, g_PlayerMusicString[client], _, MUSIC_CHAN, SNDLEVEL_NONE, SND_CHANGEVOL, 0.0001); - } - switch (g_DifficultyConVar.IntValue) - { - case Difficulty_Normal: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackNormal); - } - case Difficulty_Hard: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackHard); - } - case Difficulty_Insane: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackInsane); - } - case Difficulty_Nightmare: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackNightmare); - } - case Difficulty_Apollyon: - { - strcopy(currentMusicTrack, sizeof(currentMusicTrack), currentMusicTrackApollyon); - } - } - if (currentMusicTrack[0] != '\0') - { - StopSound(client, MUSIC_CHAN, currentMusicTrack); - } - ClientMusicStart(client, currentMusicTrack, _, MUSIC_PAGE_VOLUME,true); - ClientUpdateMusicSystem(client); - } + continue; } } + soundInfo.EmitSound(true, i); } } if (spawnCompanions) { char compProfile[SF2_MAX_PROFILE_NAME_LENGTH]; - ArrayList companions = GetBossProfileCompanions(profile); + BossProfileCompanions companions = profileData.GetCompanions(); if (companions != null) { char spawnType[64]; - GetBossProfileCompanionsSpawnType(profile, spawnType, sizeof(spawnType)); + companions.GetSpawnType(spawnType, sizeof(spawnType)); if (spawnType[0] == '\0') // No random companions { - SF2BossProfileCompanionsInfo companionInfo; + /*SF2BossProfileCompanionsInfo companionInfo; companions.GetArray(0, companionInfo); if (companionInfo.Bosses != null && companionInfo.Bosses.Length > 0) { @@ -2358,7 +1038,7 @@ bool SelectProfile(SF2NPC_BaseNPC npc, const char[] profile, int additionalBossF LogSF2Message("Companion boss profile %s is invalid, skipping boss...", compProfile); } } - } + }*/ } else { @@ -2373,12 +1053,23 @@ bool SelectProfile(SF2NPC_BaseNPC npc, const char[] profile, int additionalBossF } } } + + if (profileData.GetRedCameraOverlays() != null) + { + char value[PLATFORM_MAX_PATH]; + profileData.GetRedCameraOverlays().GetString(GetRandomInt(0, profileData.GetRedCameraOverlays().Length - 1), value, sizeof(value)); + profileData.SetActiveRedCameraOverlay(value); + } } Call_StartForward(g_OnBossAddedFwd); Call_PushCell(npc.Index); Call_Finish(); + Call_StartForward(g_OnBossAddedPFwd); + Call_PushCell(npc); + Call_Finish(); + if (!npc.IsCopy) { LogSF2Message("Boss profile %s has been added to the game.", profile); @@ -2414,7 +1105,7 @@ void NPCAddCompanions(SF2NPC_BaseNPC npc) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; npc.GetProfile(profile, sizeof(profile)); - ArrayList companions = GetBossProfileCompanions(profile); + BossProfileCompanions companions = npc.GetProfileData().GetCompanions(); if (companions == null) { return; @@ -2423,24 +1114,20 @@ void NPCAddCompanions(SF2NPC_BaseNPC npc) ArrayList companionsToAdd = new ArrayList(SF2_MAX_PROFILE_NAME_LENGTH); char compProfile[SF2_MAX_PROFILE_NAME_LENGTH]; float maxWeight = 0.0; - for (int i = 0; i < companions.Length; i++) + for (int i = 0; i < companions.SectionLength; i++) { - SF2BossProfileCompanionsInfo companionInfo; - companions.GetArray(i, companionInfo, sizeof(companionInfo)); - maxWeight += companionInfo.Weight[npc.Difficulty]; + maxWeight += companions.GetWeightFromGroupEx(i, npc.Difficulty); } float randomWeight = GetRandomFloat(0.0, maxWeight); - for (int i = 0; i < companions.Length; i++) + for (int i = 0; i < companions.SectionLength; i++) { - SF2BossProfileCompanionsInfo companionInfo; - companions.GetArray(i, companionInfo, sizeof(companionInfo)); - if (companionInfo.Bosses == null) + if (companions.GetBossesFromGroupEx(i) == null) { continue; } - float weight = companionInfo.Weight[npc.Difficulty]; + float weight = companions.GetWeightFromGroupEx(i, npc.Difficulty); if (weight <= 0.0) { continue; @@ -2452,9 +1139,11 @@ void NPCAddCompanions(SF2NPC_BaseNPC npc) continue; } - for (int i2 = 0; i2 < companionInfo.Bosses.Length; i2++) + for (int i2 = 0; i2 < companions.GetBossesFromGroupEx(i).KeyLength; i2++) { - companionInfo.Bosses.GetString(i2, compProfile, sizeof(compProfile)); + char key[64]; + companions.GetBossesFromGroupEx(i).GetKeyNameFromIndex(i2, key, sizeof(key)); + companions.GetBossesFromGroupEx(i).GetString(key, compProfile, sizeof(compProfile)); companionsToAdd.PushString(compProfile); } break; @@ -2491,33 +1180,32 @@ bool GetSlenderModel(int bossIndex, int modelState = 0, char[] buffer, int buffe char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); - ArrayList modelsArray; + BaseBossProfile profileData = GetBossProfile(profile); switch (modelState) { case 0: { - modelsArray = GetBossProfileModels(profile); switch (difficulty) { case Difficulty_Normal: { - modelsArray.GetString(Difficulty_Normal, buffer, bufferLen); + profileData.GetModel(Difficulty_Normal, buffer, bufferLen); } case Difficulty_Hard: { - modelsArray.GetString(Difficulty_Hard, buffer, bufferLen); + profileData.GetModel(Difficulty_Hard, buffer, bufferLen); } case Difficulty_Insane: { - modelsArray.GetString(Difficulty_Insane, buffer, bufferLen); + profileData.GetModel(Difficulty_Insane, buffer, bufferLen); } case Difficulty_Nightmare: { - modelsArray.GetString(Difficulty_Nightmare, buffer, bufferLen); + profileData.GetModel(Difficulty_Nightmare, buffer, bufferLen); } case Difficulty_Apollyon: { - modelsArray.GetString(Difficulty_Apollyon, buffer, bufferLen); + profileData.GetModel(Difficulty_Apollyon, buffer, bufferLen); } } } @@ -2528,6 +1216,7 @@ bool GetSlenderModel(int bossIndex, int modelState = 0, char[] buffer, int buffe bool NPCFindUnstuckPosition(SF2_BaseBoss boss, float lastPos[3], float destination[3]) { SF2NPC_BaseNPC controller = boss.Controller; + BaseBossProfile data = controller.GetProfileData(); PathFollower path = controller.Path; CBaseNPC npc = TheNPCs.FindNPCByEntIndex(boss.index); CNavArea area = TheNavMesh.GetNearestNavArea(lastPos, _, _, _, false); @@ -2535,16 +1224,20 @@ bool NPCFindUnstuckPosition(SF2_BaseBoss boss, float lastPos[3], float destinati float tempMaxs[3]; npc.GetBodyMaxs(tempMaxs); float traceMins[3]; - traceMins[0] = g_SlenderDetectMins[controller.Index][0] - 5.0; - traceMins[1] = g_SlenderDetectMins[controller.Index][1] - 5.0; + data.GetHullMins(traceMins); + traceMins[0] -= 5.0; + traceMins[1] -= 5.0; traceMins[2] = 0.0; float traceMaxs[3]; - traceMaxs[0] = g_SlenderDetectMaxs[controller.Index][0] + 5.0; - traceMaxs[1] = g_SlenderDetectMaxs[controller.Index][1] + 5.0; + data.GetHullMaxs(traceMaxs); + traceMaxs[0] += 5.0; + traceMaxs[1] += 5.0; traceMaxs[2] = tempMaxs[2]; - TR_TraceHullFilter(destination, destination, traceMins, traceMaxs, MASK_NPCSOLID, TraceRayDontHitPlayersOrEntityEx); - if (GetVectorSquareMagnitude(destination, lastPos) <= SquareFloat(16.0) || TR_DidHit()) + Handle trace = TR_TraceHullFilterEx(destination, destination, traceMins, traceMaxs, MASK_NPCSOLID, TraceRayDontHitPlayersOrEntityEx); + bool hit = TR_DidHit(trace); + delete trace; + if (GetVectorSquareMagnitude(destination, lastPos) <= SquareFloat(16.0) || hit) { SurroundingAreasCollector collector = TheNavMesh.CollectSurroundingAreas(area, 400.0); int areaCount = collector.Count(); @@ -2566,8 +1259,10 @@ bool NPCFindUnstuckPosition(SF2_BaseBoss boss, float lastPos[3], float destinati area = collector.Get(randomArea); area.GetCenter(destination); - TR_TraceHullFilter(destination, destination, traceMins, traceMaxs, MASK_NPCSOLID, TraceRayDontHitPlayersOrEntityEx); - if (TR_DidHit()) + trace = TR_TraceHullFilterEx(destination, destination, traceMins, traceMaxs, MASK_NPCSOLID, TraceRayDontHitPlayersOrEntityEx); + hit = TR_DidHit(trace); + delete trace; + if (hit) { area = NULL_AREA; validAreaCount--; @@ -2617,12 +1312,9 @@ bool NPCFindUnstuckPosition(SF2_BaseBoss boss, float lastPos[3], float destinati } } - SF2BossProfileData data; - data = controller.GetProfileData(); - int ent = -1; char targetName[64]; - if (data.IsPvEBoss) + if (controller.GetProfileData().IsPvEBoss) { ArrayList spawnPointList = new ArrayList(); @@ -2700,6 +1392,7 @@ void ChangeAllSlenderModels() } char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(npcIndex, profile, sizeof(profile)); + BaseBossProfile data = GetBossProfile(profile); GetSlenderModel(npcIndex, _, buffer, sizeof(buffer)); SetEntityModel(slender, buffer); @@ -2708,59 +1401,46 @@ void ChangeAllSlenderModels() SF2_ChaserEntity(slender).UpdateMovementAnimation(); } SetGlowModel(slender, buffer); - if (NPCGetModelSkinMax(npcIndex) > 0) + if (data.SkinMax > 0) { - int randomSkin = GetRandomInt(0, NPCGetModelSkinMax(npcIndex)); + int randomSkin = GetRandomInt(0, data.SkinMax); SetEntProp(slender, Prop_Send, "m_nSkin", randomSkin); } else { - if (GetBossProfileSkinDifficultyState(profile)) - { - SetEntProp(slender, Prop_Send, "m_nSkin", NPCGetModelSkinDifficulty(npcIndex, difficulty)); - } - else - { - SetEntProp(slender, Prop_Send, "m_nSkin", NPCGetModelSkin(npcIndex)); - } + SetEntProp(slender, Prop_Send, "m_nSkin", data.GetSkin(difficulty)); } - if (NPCGetModelBodyGroupsMax(npcIndex) > 0) + if (data.BodyMax > 0) { - int randomBody = GetRandomInt(0, NPCGetModelBodyGroupsMax(npcIndex)); + int randomBody = GetRandomInt(0, data.BodyMax); SetEntProp(slender, Prop_Send, "m_nBody", randomBody); } else { - if (GetBossProfileBodyGroupsDifficultyState(profile)) - { - SetEntProp(slender, Prop_Send, "m_nBody", NPCGetModelBodyGroupsDifficulty(npcIndex, difficulty)); - } - else - { - SetEntProp(slender, Prop_Send, "m_nBody", NPCGetModelBodyGroups(npcIndex)); - } + SetEntProp(slender, Prop_Send, "m_nBody", data.GetBodyGroup(difficulty)); } - if (NPCGetType(npcIndex) == SF2BossType_Chaser) + if (data.Type == SF2BossType_Chaser) { - float tempHitboxMins[3]; - if (NPCGetRaidHitbox(npcIndex)) + float mins[3], maxs[3]; + if (data.RaidHitbox) { - CopyVector(g_SlenderDetectMins[npcIndex], tempHitboxMins); - tempHitboxMins[2] = 10.0; - SetEntPropVector(slender, Prop_Send, "m_vecMins", tempHitboxMins); - SetEntPropVector(slender, Prop_Send, "m_vecMaxs", g_SlenderDetectMaxs[npcIndex]); + data.GetHullMins(mins); + data.GetHullMaxs(maxs); + mins[2] = 10.0; + SetEntPropVector(slender, Prop_Send, "m_vecMins", mins); + SetEntPropVector(slender, Prop_Send, "m_vecMaxs", maxs); - SetEntPropVector(slender, Prop_Send, "m_vecMinsPreScaled", tempHitboxMins); - SetEntPropVector(slender, Prop_Send, "m_vecMaxsPreScaled", g_SlenderDetectMaxs[npcIndex]); + SetEntPropVector(slender, Prop_Send, "m_vecMinsPreScaled", mins); + SetEntPropVector(slender, Prop_Send, "m_vecMaxsPreScaled", maxs); } else { - CopyVector(HULL_HUMAN_MINS, tempHitboxMins); - tempHitboxMins[2] = 10.0; - SetEntPropVector(slender, Prop_Send, "m_vecMins", tempHitboxMins); + CopyVector(HULL_HUMAN_MINS, mins); + mins[2] = 10.0; + SetEntPropVector(slender, Prop_Send, "m_vecMins", mins); SetEntPropVector(slender, Prop_Send, "m_vecMaxs", HULL_HUMAN_MAXS); - SetEntPropVector(slender, Prop_Send, "m_vecMinsPreScaled", tempHitboxMins); + SetEntPropVector(slender, Prop_Send, "m_vecMinsPreScaled", mins); SetEntPropVector(slender, Prop_Send, "m_vecMaxsPreScaled", HULL_HUMAN_MAXS); } } @@ -2775,7 +1455,7 @@ void RemoveProfile(int bossIndex) KillPvEBoss(controller.EntIndex); } - if (SF_IsBoxingMap() && (GetRoundState() == SF2RoundState_Escape) && NPCChaserIsBoxingBoss(bossIndex)) + if (SF_IsBoxingMap() && (GetRoundState() == SF2RoundState_Escape) && view_as(controller).GetProfileData().BoxingBoss) { g_SlenderBoxingBossCount -= 1; } @@ -2783,11 +1463,6 @@ void RemoveProfile(int bossIndex) char profile[SF2_MAX_PROFILE_NAME_LENGTH]; controller.GetProfile(profile, sizeof(profile)); - if (!controller.IsCopy && MusicActive() && BossHasMusic(profile) && BossMatchesCurrentMusic(profile)) - { - NPCStopMusic(); - } - controller.UnSpawn(true); // Call our forward. @@ -2805,7 +1480,6 @@ void RemoveProfile(int bossIndex) g_SlenderTeleportPlayersRestTime[bossIndex][i] = -1.0; } - g_NpcTeleportType[bossIndex] = -1; g_SlenderTeleportTarget[bossIndex] = INVALID_ENT_REFERENCE; g_SlenderProxyTarget[bossIndex] = INVALID_ENT_REFERENCE; g_SlenderTeleportMaxTargetStress[bossIndex] = 9999.0; @@ -2836,28 +1510,10 @@ void RemoveProfile(int bossIndex) g_SlenderProxyTeleportMaxRange[bossIndex][difficulty] = 0.0; } - g_NpcType[bossIndex] = -1; - g_NpcProfileIndex[bossIndex] = -1; - g_NpcUniqueProfileIndex[bossIndex] = -1; - controller.Flags = 0; - g_NpcFieldOfView[bossIndex] = 0.0; - controller.SetAddSpeed(0.0); controller.SetAddAcceleration(0.0); - g_NpcStepSize[bossIndex] = 0.0; - - g_NpcHasDiscoMode[bossIndex] = false; - g_NpcDiscoRadiusMin[bossIndex] = 0.0; - g_NpcDiscoRadiusMax[bossIndex] = 0.0; - - g_NpcHasFestiveLights[bossIndex] = false; - g_NpcFestiveLightBrightness[bossIndex] = 0; - g_NpcFestiveLightDistance[bossIndex] = 0.0; - g_NpcFestiveLightRadius[bossIndex] = 0.0; - - NPCSetDeathCamEnabled(bossIndex, false); controller.CopyMaster = SF2_INVALID_NPC; controller.CompanionMaster = SF2_INVALID_NPC; @@ -2866,36 +1522,11 @@ void RemoveProfile(int bossIndex) g_SlenderDeathCamTarget[bossIndex] = INVALID_ENT_REFERENCE; g_SlenderThink[bossIndex] = null; g_SlenderEntityThink[bossIndex] = null; - g_SlenderCustomOutroSong[bossIndex] = false; g_SlenderFakeTimer[bossIndex] = null; g_SlenderModel[bossIndex] = INVALID_ENT_REFERENCE; g_SlenderBoxingBossIsKilled[bossIndex] = false; g_SlenderTimeUntilNextProxy[bossIndex] = -1.0; - g_NpcScareRadius[bossIndex] = 0.0; - g_NpcHasPlayerScareSpeedBoost[bossIndex] = false; - g_NpcPlayerSpeedBoostDuration[bossIndex] = 0.0; - g_NpcHasPlayerScareReaction[bossIndex] = false; - g_NpcPlayerScareReactionType[bossIndex] = 0; - g_NpcHasPlayerScareReplenishSprint[bossIndex] = false; - g_NpcPlayerScareReplenishSprintAmount[bossIndex] = 0.0; - g_SlenderRenderFX[bossIndex] = 0; - g_SlenderRenderMode[bossIndex] = 0; - - for (int i = 0; i < 3; i++) - { - g_SlenderDetectMins[bossIndex][i] = 0.0; - g_SlenderDetectMaxs[bossIndex][i] = 0.0; - g_SlenderEyePosOffset[bossIndex][i] = 0.0; - g_NpcDiscoModePos[bossIndex][i] = 0.0; - g_NpcFestiveLightPos[bossIndex][i] = 0.0; - g_NpcFestiveLightAng[bossIndex][i] = 0.0; - } - - for (int i = 0; i < 4; i++) - { - g_SlenderRenderColor[bossIndex][i] = 0; - } controller.SetProfile(""); } @@ -2914,8 +1545,9 @@ void SpawnSlender(SF2NPC_BaseNPC npc, const float pos[3]) char profile[SF2_MAX_PROFILE_NAME_LENGTH]; npc.UnSpawn(true); - npc.GetProfile(profile,sizeof(profile)); + npc.GetProfile(profile, sizeof(profile)); npc.WasKilled = false; + BaseBossProfile data = npc.GetProfileData(); float truePos[3], trueAng[3]; trueAng[1] = GetRandomFloat(0.0, 360.0); @@ -2923,13 +1555,9 @@ void SpawnSlender(SF2NPC_BaseNPC npc, const float pos[3]) int bossIndex = npc.Index; - char buffer[PLATFORM_MAX_PATH]; - - GetSlenderModel(bossIndex, _, buffer, sizeof(buffer)); - CBaseCombatCharacter entity; - switch (NPCGetType(bossIndex)) + switch (data.Type) { case SF2BossType_Statue: { @@ -2938,227 +1566,9 @@ void SpawnSlender(SF2NPC_BaseNPC npc, const float pos[3]) case SF2BossType_Chaser: { entity = view_as(Spawn_Chaser(npc, truePos, trueAng)); - /*CBaseNPC npcBoss = CBaseNPC(); - CBaseCombatCharacter npcEntity = CBaseCombatCharacter(npcBoss.GetEntity()); - CBaseNPC_Locomotion locomotion = npcBoss.GetLocomotion(); - npcEntity.Hook_HandleAnimEvent(CBaseAnimating_HandleAnimEvent); - - npcEntity.Teleport(truePos, trueAng); - npcEntity.SetModel(buffer); - npcEntity.SetRenderMode(view_as(g_SlenderRenderMode[bossIndex])); - npcEntity.SetRenderFx(view_as(g_SlenderRenderFX[bossIndex])); - npcEntity.SetRenderColor(g_SlenderRenderColor[bossIndex][0], g_SlenderRenderColor[bossIndex][1], g_SlenderRenderColor[bossIndex][2], g_SlenderRenderColor[bossIndex][3]); - if (SF_SpecialRound(SPECIALROUND_TINYBOSSES)) - { - float scaleModel = NPCGetModelScale(bossIndex) * 0.5; - npcEntity.SetPropFloat(Prop_Send, "m_flModelScale", scaleModel); - } - else - { - npcEntity.SetPropFloat(Prop_Send, "m_flModelScale", NPCGetModelScale(bossIndex)); - } - npcEntity.Spawn(); - npcEntity.Activate(); - - npcBoss.flStepSize = 18.0; - npcBoss.flGravity = g_Gravity; - npcBoss.flAcceleration = g_SlenderCalculatedAcceleration[bossIndex]; - npcBoss.flDeathDropHeight = 99999.0; - npcBoss.flJumpHeight = 512.0; - npcBoss.flWalkSpeed = g_SlenderCalculatedWalkSpeed[bossIndex]; - npcBoss.flRunSpeed = g_SlenderCalculatedSpeed[bossIndex]; - - if (!SF_IsBoxingMap()) - { - locomotion.SetCallback(LocomotionCallback_IsAbleToJumpAcrossGaps, CanJumpAcrossGaps); - locomotion.SetCallback(LocomotionCallback_IsAbleToClimb, CanJumpAcrossGaps); - locomotion.SetCallback(LocomotionCallback_JumpAcrossGap, JumpAcrossGapsCBase); - locomotion.SetCallback(LocomotionCallback_ClimbUpToLedge, ClimbUpCBase); - } - - if (NPCGetRaidHitbox(bossIndex)) - { - npcBoss.SetBodyMins(g_SlenderDetectMins[bossIndex]); - npcBoss.SetBodyMaxs(g_SlenderDetectMaxs[bossIndex]); - - npcEntity.SetPropVector(Prop_Send, "m_vecMins", g_SlenderDetectMins[bossIndex]); - npcEntity.SetPropVector(Prop_Send, "m_vecMaxs", g_SlenderDetectMaxs[bossIndex]); - - npcEntity.SetPropVector(Prop_Send, "m_vecMinsPreScaled", g_SlenderDetectMins[bossIndex]); - npcEntity.SetPropVector(Prop_Send, "m_vecMaxsPreScaled", g_SlenderDetectMaxs[bossIndex]); - } - else - { - npcBoss.SetBodyMins(HULL_HUMAN_MINS); - npcBoss.SetBodyMaxs(HULL_HUMAN_MAXS); - - npcEntity.SetPropVector(Prop_Send, "m_vecMins", HULL_HUMAN_MINS); - npcEntity.SetPropVector(Prop_Send, "m_vecMaxs", HULL_HUMAN_MAXS); - - npcEntity.SetPropVector(Prop_Send, "m_vecMinsPreScaled", HULL_HUMAN_MINS); - npcEntity.SetPropVector(Prop_Send, "m_vecMaxsPreScaled", HULL_HUMAN_MAXS); - } - - if (SF_IsBoxingMap()) - { - npcEntity.SetProp(Prop_Send, "m_CollisionGroup", COLLISION_GROUP_DEBRIS_TRIGGER); - } - - SDKHook(npcEntity.iEnt, SDKHook_OnTakeDamageAlive, Hook_SlenderOnTakeDamage); - - // Reset stats. - g_SlenderInBacon[bossIndex] = false; - g_SlenderTarget[bossIndex] = INVALID_ENT_REFERENCE; - g_SlenderTargetIsVisible[bossIndex] = false; - g_SlenderState[bossIndex] = STATE_IDLE; - g_IsSlenderAttacking[bossIndex] = false; - g_SlenderGiveUp[bossIndex] = false; - g_SlenderAttackTimer[bossIndex] = null; - g_SlenderLaserTimer[bossIndex] = null; - g_SlenderBackupAtkTimer[bossIndex] = null; - g_SlenderChaseInitialTimer[bossIndex] = null; - g_SlenderRage1Timer[bossIndex] = null; - g_SlenderRage2Timer[bossIndex] = null; - g_SlenderRage3Timer[bossIndex] = null; - g_SlenderHealTimer[bossIndex] = null; - g_SlenderHealDelayTimer[bossIndex] = null; - g_SlenderHealEventTimer[bossIndex] = null; - g_SlenderStartFleeTimer[bossIndex] = null; - g_SlenderTargetSoundLastTime[bossIndex] = -1.0; - g_SlenderTargetSoundDiscardMasterPosTime[bossIndex] = -1.0; - g_SlenderTargetSoundType[bossIndex] = SoundType_None; - g_SlenderInvestigatingSound[bossIndex] = false; - g_SlenderNextStunTime[bossIndex] = -1.0; - g_NpcHasCloaked[bossIndex] = false; - g_SlenderLastHeardFootstep[bossIndex] = 0.0; - g_SlenderLastHeardVoice[bossIndex] = 0.0; - g_SlenderLastHeardWeapon[bossIndex] = 0.0; - g_SlenderNextVoiceSound[bossIndex] = 0.0; - g_SlenderNextFootstepSound[bossIndex] = 0.0; - g_SlenderNextMoanSound[bossIndex] = 0.0; - g_SlenderTauntAlertCount[bossIndex] = 0; - - for (int difficulty2 = 0; difficulty2 < Difficulty_Max; difficulty2++) - { - g_SlenderTimeUntilKill[bossIndex] = GetGameTime() + NPCGetIdleLifetime(bossIndex, difficulty2); - } - - g_SlenderTimeUntilRecover[bossIndex] = -1.0; - g_SlenderTimeUntilAlert[bossIndex] = -1.0; - g_SlenderTimeUntilIdle[bossIndex] = -1.0; - g_SlenderTimeUntilChase[bossIndex] = -1.0; - g_SlenderTimeUntilNoPersistence[bossIndex] = -1.0; - g_SlenderTimeUntilAttackEnd[bossIndex] = -1.0; - g_SlenderNextPathTime[bossIndex] = 0.0; - g_SlenderLastCalculPathTime[bossIndex] = -1.0; - g_LastStuckTime[bossIndex] = -1.0; - g_SlenderInterruptConditions[bossIndex] = 0; - g_SlenderChaseDeathPositionBool[bossIndex] = false; - g_NpcPlayerScareVictin[bossIndex] = INVALID_ENT_REFERENCE; - g_NpcChasingScareVictin[bossIndex] = false; - g_NpcLostChasingScareVictim[bossIndex] = false; - g_NpcVelocityCancel[bossIndex] = false; - g_SlenderBurnTimer[bossIndex] = null; - g_SlenderBleedTimer[bossIndex] = null; - g_SlenderMarkedTimer[bossIndex] = null; - g_SlenderDeathCamTimer[bossIndex] = null; - g_SlenderDeathCamTarget[bossIndex] = INVALID_ENT_REFERENCE; - g_SlenderStopBurningTimer[bossIndex] = 0.0; - g_SlenderStopBleedingTimer[bossIndex] = 0.0; - g_SlenderIsBurning[bossIndex] = false; - g_SlenderIsMarked[bossIndex] = false; - g_NpcAddSpeed[bossIndex] = 0.0; - g_NpcAddAcceleration[bossIndex] = 0.0; - g_SlenderAutoChaseCount[bossIndex] = 0; - g_SlenderAutoChaseCooldown[bossIndex] = 0.0; - g_SlenderSoundTarget[bossIndex] = INVALID_ENT_REFERENCE; - g_SlenderSeeTarget[bossIndex] = INVALID_ENT_REFERENCE; - g_SlenderIsAutoChasingLoudPlayer[bossIndex] = false; - g_SlenderInDeathcam[bossIndex] = false; - - Spawn_Chaser(bossIndex); - - NPCChaserResetAnimationInfo(bossIndex, 0); - - SDKHook(npcEntity.iEnt, SDKHook_ThinkPost, SlenderSetNextThink); - - if (GetChaserProfileSpawnAnimationState(profile) && !SF_IsSlaughterRunMap()) - { - g_SlenderSpawning[bossIndex] = true; - NPCChaserUpdateBossAnimation(bossIndex, npcEntity.iEnt, STATE_IDLE, true); - g_SlenderEntityThink[bossIndex] = null; - g_SlenderSpawnTimer[bossIndex] = CreateTimer(g_SlenderAnimationDuration[bossIndex], Timer_SlenderSpawnTimer, EntIndexToEntRef(npcEntity.iEnt), TIMER_FLAG_NO_MAPCHANGE); - } - else - { - SDKHook(npcEntity.iEnt, SDKHook_Think, SlenderChaseBossProcessMovement); - g_SlenderSpawning[bossIndex] = false; - NPCChaserUpdateBossAnimation(bossIndex, npcEntity.iEnt, STATE_IDLE); - g_SlenderEntityThink[bossIndex] = CreateTimer(BOSS_THINKRATE, Timer_SlenderChaseBossThink, EntIndexToEntRef(npcEntity.iEnt), TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); - } - - for (int i = 0; i < 3; i++) - { - g_SlenderGoalPos[bossIndex][i] = 0.0; - g_SlenderTargetSoundTempPos[bossIndex][i] = 0.0; - g_SlenderTargetSoundMasterPos[bossIndex][i] = 0.0; - g_SlenderChaseDeathPosition[bossIndex][i] = 0.0; - } - - for (int i = 1; i <= MaxClients; i++) - { - g_SlenderLastFoundPlayer[bossIndex][i] = -1.0; - - for (int i2 = 0; i2 < 3; i2++) - { - g_SlenderLastFoundPlayerPos[bossIndex][i][i2] = 0.0; - } - } - - //(Experimental) - if (NPCGetHealthbarState(bossIndex)) - { - //The boss spawned for the 1st time, block now its teleportation ability to prevent healthbar conflict. - NPCSetFlags(bossIndex,NPCGetFlags(bossIndex)|SFF_NOTELEPORT); - UpdateHealthBar(bossIndex); - } - - //Stun Health - float maxHealth = NPCChaserGetStunInitialHealth(bossIndex); - float health = 0.0; - for(int client=1; client<=MaxClients; client++) - { - if (IsValidClient(client) && !g_PlayerEliminated[client] && IsPlayerAlive(client) && !DidClientEscape(client)) - { - int classToInt = view_as(TF2_GetPlayerClass(client)); - health = GetChaserProfileStunHealthPerClass(profile, classToInt); - if (health > 0.0) - { - maxHealth += health; - } - else - { - health = GetChaserProfileStunHealthPerPlayer(profile); - maxHealth += health; - } - if (SF_IsBoxingMap() && TF2_GetPlayerClass(client) == TFClass_Scout) - { - NPCSetAddSpeed(bossIndex, 10.0); - } - } - } - NPCChaserSetStunInitialHealth(bossIndex, maxHealth); - int entHealth = RoundToCeil(maxHealth + 1500000000.0); - npcEntity.SetProp(Prop_Data, "m_iHealth", entHealth); - npcEntity.SetProp(Prop_Data, "m_iMaxHealth", entHealth); - - entity = npcEntity;*/ } } - SF2BossProfileData data; - data = npc.GetProfileData(); - if (g_Enabled) { if (!data.IsPvEBoss && (npc.Flags & SFF_ATTACKWAITERS) == 0) @@ -3182,39 +1592,25 @@ void SpawnSlender(SF2NPC_BaseNPC npc, const float pos[3]) g_BossPathFollower[bossIndex] = PathFollower(_, TraceRayDontHitAnyEntity_Pathing, Path_FilterOnlyActors); } - g_BossPathFollower[bossIndex].SetMinLookAheadDistance(GetBossProfileNodeDistanceLookAhead(profile)); + g_BossPathFollower[bossIndex].SetMinLookAheadDistance(data.NodeDistanceLookAhead); - if (NPCGetModelSkinMax(bossIndex) > 0) + if (data.SkinMax > 0) { - int randomSkin = GetRandomInt(0, NPCGetModelSkinMax(bossIndex)); + int randomSkin = GetRandomInt(0, data.SkinMax); entity.SetProp(Prop_Send, "m_nSkin", randomSkin); } else { - if (GetBossProfileSkinDifficultyState(profile)) - { - entity.SetProp(Prop_Send, "m_nSkin", NPCGetModelSkinDifficulty(bossIndex, difficulty)); - } - else - { - entity.SetProp(Prop_Send, "m_nSkin", NPCGetModelSkin(bossIndex)); - } + entity.SetProp(Prop_Send, "m_nSkin", data.GetSkin(difficulty)); } - if (NPCGetModelBodyGroupsMax(bossIndex) > 0) + if (data.BodyMax > 0) { - int randomBody = GetRandomInt(0, NPCGetModelBodyGroupsMax(bossIndex)); + int randomBody = GetRandomInt(0, data.BodyMax); entity.SetProp(Prop_Send, "m_nBody", randomBody); } else { - if (GetBossProfileBodyGroupsDifficultyState(profile)) - { - entity.SetProp(Prop_Send, "m_nBody", NPCGetModelBodyGroupsDifficulty(bossIndex, difficulty)); - } - else - { - entity.SetProp(Prop_Send, "m_nBody", NPCGetModelBodyGroups(bossIndex)); - } + entity.SetProp(Prop_Send, "m_nBody", data.GetBodyGroup(difficulty)); } entity.AddFlag(FL_NPC); @@ -3233,27 +1629,38 @@ void SpawnSlender(SF2NPC_BaseNPC npc, const float pos[3]) g_SlenderEnt[bossIndex] = EntIndexToEntRef(entity.index); - if (data.EngineSound[0] != '\0') + char buffer[PLATFORM_MAX_PATH]; + data.GetConstantSound(buffer, sizeof(buffer)); + if (buffer[0] != '\0') + { + EmitSoundToAll(buffer, entity.index, SNDCHAN_STATIC, data.ConstantSoundLevel, + _, data.ConstantSoundVolume); + } + + if (data.GetSpawnEffects() != null) + { + ProfileObject obj = view_as(data.GetSpawnEffects().GetSection(GetRandomInt(0, data.GetSpawnEffects().Length - 1))); + obj = obj != null ? obj.GetSection("effects") : null; + if (obj != null) + { + SlenderSpawnEffects(view_as(obj), bossIndex, false); + } + } + + if (data.GetOutputs() != null) { - EmitSoundToAll(data.EngineSound, entity.index, SNDCHAN_STATIC, data.EngineSoundLevel, - _, data.EngineSoundVolume); + data.GetOutputs().AddOutputs(entity); } - if (data.SpawnEffects != null) + if (data.GetSpawnInputs() != null) { - StringMapSnapshot snapshot = data.SpawnEffects.Snapshot(); - char key[64]; - snapshot.GetKey(GetRandomInt(0, snapshot.Length - 1), key, sizeof(key)); - ArrayList effects; - data.SpawnEffects.GetValue(key, effects); - SlenderSpawnEffects(effects, bossIndex, false); - delete snapshot; + data.GetSpawnInputs().AcceptInputs(entity); } int master = g_SlenderCopyMaster[bossIndex]; int flags = NPCGetFlags(bossIndex); - if (MAX_BOSSES > master >= 0 && NPCGetFakeCopyState(bossIndex, difficulty)) + if (MAX_BOSSES > master >= 0 && data.GetCopies().GetFakes(difficulty)) { if (!SF_SpecialRound(SPECIALROUND_DREAMFAKEBOSSES)) { @@ -3261,16 +1668,16 @@ void SpawnSlender(SF2NPC_BaseNPC npc, const float pos[3]) } } - ArrayList effects = data.EffectsArray; - SlenderSpawnEffects(effects, bossIndex); + SlenderSpawnEffects(view_as(data.GetSection("effects")), bossIndex); if (entity.IsValid()) { - SF2BossProfileSoundInfo soundInfo; - GetBossProfileSpawnLocalSounds(profile, soundInfo); - soundInfo.EmitSound(_, entity.index); + data.GetLocalSpawnSounds().EmitSound(_, entity.index); } + float teleportTime = GetRandomFloat(data.GetMinTeleportTime(difficulty), data.GetMaxTeleportTime(difficulty)); + g_SlenderNextTeleportTime[bossIndex] = GetGameTime() + teleportTime; + // Call our forward. Call_StartForward(g_OnBossSpawnFwd); Call_PushCell(bossIndex); @@ -3281,17 +1688,6 @@ void SpawnSlender(SF2NPC_BaseNPC npc, const float pos[3]) Call_Finish(); } -bool ClimbUpCBase(CBaseNPC_Locomotion loco, const float goal[3], const float fwd[3], int entity) -{ - INextBot bot = loco.GetBot(); - SF2_BaseBoss boss = SF2_BaseBoss(bot.GetEntity()); - if (boss.IsValid()) - { - boss.IsJumping = true; - } - return loco.CallBaseFunction(goal, fwd, entity); -} - MRESReturn CBaseAnimating_HandleAnimEvent(int thisInt, DHookParam params) { int bossIndex = NPCGetFromEntIndex(thisInt); @@ -3303,7 +1699,6 @@ MRESReturn CBaseAnimating_HandleAnimEvent(int thisInt, DHookParam params) if (event > 0 && IsValidEntity(thisInt) && bossIndex != -1 && NPCGetUniqueID(bossIndex) != -1) { - SlenderCastFootstepAnimEvent(bossIndex, event, thisInt); SlenderCastAnimEvent(bossIndex, event, thisInt); } return MRES_Ignored; @@ -3314,6 +1709,10 @@ void UpdateHealthBar(int bossIndex, int optionalSetPercent = -1) SF2_ChaserEntity chaser = SF2_ChaserEntity(NPCGetEntIndex(bossIndex)); if (!chaser.IsValid()) { + if (g_HealthBar != -1) + { + SetEntProp(g_HealthBar, Prop_Send, "m_iBossHealthPercentageByte", 0); + } return; } float maxHealth = chaser.MaxHealth; @@ -3517,7 +1916,7 @@ Action Timer_BossMarked(Handle timer, any entref) bool SlenderUsesBlink(int bossIndex) { - if (NPCGetType(bossIndex) == SF2BossType_Statue) + if (SF2NPC_BaseNPC(bossIndex).GetProfileData().Type == SF2BossType_Statue) { return true; } @@ -3528,7 +1927,7 @@ void SlenderPrintChatMessage(int bossIndex, int player) { if (g_Enabled && GetClientTeam(player) != TFTeam_Red) { - return; + //return; } if (bossIndex == -1) @@ -3545,13 +1944,14 @@ void SlenderPrintChatMessage(int bossIndex, int player) char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); - ArrayList deathMessages = GetBossProfileChatDeathMessages(profile); - if (deathMessages == null) + BaseBossProfile profileData = GetBossProfile(profile); + ProfileObject deathMessages = profileData.GetSection("chat_message_upon_death"); + if (deathMessages == null || deathMessages.Size <= 0) { return; } - int difficultyIndex = g_NpcDeathMessageDifficultyIndexes[bossIndex]; + int difficultyIndex = profileData.GetInt("chat_message_upon_death_difficulty_indexes", 123456); char indexes[8], currentIndex[2]; FormatEx(indexes, sizeof(indexes), "%d", difficultyIndex); @@ -3567,19 +1967,16 @@ void SlenderPrintChatMessage(int bossIndex, int player) int currentAtkIndex = StringToInt(currentIndex); if (difficultyNumber == currentAtkIndex) //WHOA, legacy system actually won't be legacy. { - char buffer[PLATFORM_MAX_PATH], prefix[PLATFORM_MAX_PATH], name[SF2_MAX_NAME_LENGTH], time[PLATFORM_MAX_PATH]; + char buffer[PLATFORM_MAX_PATH], prefix[PLATFORM_MAX_PATH], name[SF2_MAX_NAME_LENGTH], time[PLATFORM_MAX_PATH], keyName[64]; int roundTime = RoundToNearest(g_RoundTimeMessage); - int randomMessage = GetRandomInt(0, deathMessages.Length - 1); - GetBossProfileChatDeathMessagePrefix(profile, prefix, sizeof(prefix)); - deathMessages.GetString(randomMessage, buffer, sizeof(buffer)); - NPCGetBossName(bossIndex, name, sizeof(name)); + int randomMessage = GetRandomInt(0, deathMessages.KeyLength - 1); + profileData.GetString("chat_message_upon_death_prefix", prefix, sizeof(prefix), "[SF2]"); + deathMessages.GetKeyNameFromIndex(randomMessage, keyName, sizeof(keyName)); + deathMessages.GetString(keyName, buffer, sizeof(buffer)); + profileData.GetName(GetLocalGlobalDifficulty(bossIndex), name, sizeof(name)); FormatEx(time, sizeof(time), "%d", roundTime); char playerName[32], replacePlayer[64]; FormatEx(playerName, sizeof(playerName), "%N", player); - if (prefix[0] == '\0') - { - prefix = "[SF2]"; - } if (buffer[0] != '\0') { if (StrContains(buffer, "[PLAYER]", true) != -1) @@ -3614,47 +2011,31 @@ void SlenderPrintChatMessage(int bossIndex, int player) } else { - CPrintToChatAll("{royalblue}%s{default} %s", prefix, buffer); + if (prefix[0] == '\0') + { + CPrintToChatAll("%s", buffer); + } + else + { + CPrintToChatAll("{royalblue}%s {default}%s", prefix, buffer); + } } } } } } -void SlenderCastFootstepAnimEvent(int bossIndex, int event, int slender) +void SlenderCastAnimEvent(int bossIndex, int index, int slender) { - if (bossIndex == -1) - { - return; - } - - if (!IsValidEntity(slender)) - { - return; - } - - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(bossIndex, profile, sizeof(profile)); - - ArrayList arraySounds = GetBossProfileFootstepEventSounds(profile); - ArrayList arrayIndexes = GetBossProfileFootstepEventIndexes(profile); - - if (arraySounds == null || arrayIndexes == null) - { - return; - } - - int foundIndex = arrayIndexes.FindValue(event); - if (foundIndex == -1) + if (bossIndex == -1 || !IsValidEntity(slender)) { return; } - SF2BossProfileSoundInfo soundInfo; - arraySounds.GetArray(foundIndex, soundInfo, sizeof(soundInfo)); + ChaserBossProfile data = SF2NPC_Chaser(bossIndex).GetProfileData(); - ArrayList soundPaths = soundInfo.Paths; - if (soundPaths == null) + BossProfileEventData event = data.GetEvents(index); + if (event == null) { return; } @@ -3662,55 +2043,22 @@ void SlenderCastFootstepAnimEvent(int bossIndex, int event, int slender) float myPos[3]; GetEntPropVector(slender, Prop_Data, "m_vecAbsOrigin", myPos); - soundInfo.EmitSound(_, slender); - SF2ChaserBossProfileData data; - data = SF2NPC_Chaser(bossIndex).GetProfileData(); - if (data.EarthquakeFootsteps) - { - UTIL_ScreenShake(myPos, data.EarthquakeFootstepAmplitude, - data.EarthquakeFootstepFrequency, data.EarthquakeFootstepDuration, - data.EarthquakeFootstepRadius, 0, data.EarthquakeFootstepAirShake); - } -} - -void SlenderCastAnimEvent(int bossIndex, int event, int slender) -{ - if (bossIndex == -1) - { - return; - } - - if (!IsValidEntity(slender)) - { - return; - } - - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(bossIndex, profile, sizeof(profile)); - - ArrayList arraySounds = GetBossProfileEventSounds(profile); - ArrayList arrayIndexes = GetBossProfileEventIndexes(profile); - - if (!arraySounds || arrayIndexes == null) + if (event.GetSounds() != null) { - return; + event.GetSounds().EmitSound(.entity = slender); } - int foundIndex = arrayIndexes.FindValue(event); - if (foundIndex == -1) + if (event.GetEffects() != null) { - return; + SlenderSpawnEffects(event.GetEffects(), bossIndex, false); } - SF2BossProfileSoundInfo soundInfo; - arraySounds.GetArray(foundIndex, soundInfo, sizeof(soundInfo)); - - ArrayList soundPaths = soundInfo.Paths; - if (soundPaths == null) + if (event.IsFootsteps && data.EarthquakeFootsteps) { - return; + UTIL_ScreenShake(myPos, data.EarthquakeFootstepAmplitude, + data.EarthquakeFootstepFrequency, data.EarthquakeFootstepDuration, + data.EarthquakeFootstepRadius, 0, data.EarthquakeFootstepAirShake); } - soundInfo.EmitSound(_, slender); } // As time passes on, we have to get more aggressive in order to successfully peak the target's @@ -3754,6 +2102,7 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) } float gameTime = GetGameTime(); + BaseBossProfile data = controller.GetProfileData(); int difficulty = GetLocalGlobalDifficulty(bossIndex); @@ -3772,16 +2121,16 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) } } - if (!NPCIsTeleportAllowed(bossIndex, difficulty) && (!bossEnt || bossEnt == INVALID_ENT_REFERENCE)) + if (!data.IsTeleportAllowed(difficulty) && (!bossEnt || bossEnt == INVALID_ENT_REFERENCE)) { return Plugin_Continue; } - if (controller.TeleportType == 2) + if (data.TeleportType == 2) { if (bossEnt && bossEnt != INVALID_ENT_REFERENCE) { - if (NPCGetType(bossIndex) == SF2BossType_Chaser) + if (data.Type == SF2BossType_Chaser) { SF2_ChaserEntity chaser = SF2_ChaserEntity(bossEnt); // Check to see if it's a good time to teleport away. @@ -3797,12 +2146,12 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) { if (gameTime >= g_SlenderTimeUntilKill[bossIndex]) { - g_SlenderTimeUntilKill[bossIndex] = gameTime + NPCGetIdleLifetime(bossIndex, difficulty); + g_SlenderTimeUntilKill[bossIndex] = gameTime + data.GetIdleLifeTime(difficulty); } return Plugin_Continue; } } - else if (NPCGetType(bossIndex) == SF2BossType_Statue) + else if (data.Type == SF2BossType_Statue) { if (g_SlenderStatueIdleLifeTime[bossIndex] > gameTime) { @@ -3819,14 +2168,14 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) { if (gameTime >= g_SlenderNextTeleportTime[bossIndex]) { - if (!NPCIsTeleportAllowed(bossIndex, difficulty) && bossEnt && bossEnt != INVALID_ENT_REFERENCE) + if (!data.IsTeleportAllowed(difficulty) && bossEnt && bossEnt != INVALID_ENT_REFERENCE) { controller.UnSpawn(); return Plugin_Continue; } - float teleportTime = GetRandomFloat(NPCGetTeleportTimeMin(bossIndex, difficulty), NPCGetTeleportTimeMax(bossIndex, difficulty)); + float teleportTime = GetRandomFloat(data.GetMinTeleportTime(difficulty), data.GetMaxTeleportTime(difficulty)); g_SlenderNextTeleportTime[bossIndex] = gameTime + teleportTime; - bool ignoreFuncNavPrefer = g_NpcHasIgnoreNavPrefer[bossIndex]; + bool ignoreFuncNavPrefer = data.IgnoreNavPrefer; int teleportTarget = EntRefToEntIndex(g_SlenderTeleportTarget[bossIndex]); @@ -3853,14 +2202,14 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) } // Let's start the persistency timer here, so that way we won't have infinite looping impossible to spawn bosses - float targetDuration = controller.GetTeleportPersistencyPeriod(difficulty); + float targetDuration = data.GetTeleportPersistencyPeriod(difficulty); float deviation = GetRandomFloat(0.92, 1.08); targetDuration = Pow(deviation * targetDuration, ((g_RoundDifficultyModifier) / 2.0)) + ((deviation * targetDuration) - 1.0); g_SlenderTeleportMaxTargetTime[controller.Index] = gameTime + targetDuration; - float teleportMinRange = g_SlenderTeleportMinRange[bossIndex][difficulty]; + float teleportMinRange = data.GetMinTeleportRange(difficulty); bool shouldBeBehindObstruction = false; - if (NPCGetTeleportType(bossIndex) == 2) + if (data.TeleportType == 2) { shouldBeBehindObstruction = true; } @@ -3875,7 +2224,7 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) CNavArea area = TheNavMesh.GetNearestNavArea(navPos, false, _, false, false); if (area != NULL_AREA) { - SurroundingAreasCollector collector = TheNavMesh.CollectSurroundingAreas(area, g_SlenderTeleportMaxRange[bossIndex][difficulty]); + SurroundingAreasCollector collector = TheNavMesh.CollectSurroundingAreas(area, data.GetMaxTeleportRange(difficulty)); int areaCount = collector.Count(); ArrayList areaArray = new ArrayList(1, areaCount); int validAreaCount = 0; @@ -3886,7 +2235,7 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) continue; } - if (view_as(collector.Get(i)).HasAttributeTF(NO_SPAWNING)) + if (area.HasAttributes(NAV_MESH_NO_HOSTAGES) || view_as(area).HasAttributeTF(NO_SPAWNING)) { continue; } @@ -3910,7 +2259,7 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) randomArea = areaArray.Get(randomCell); area = collector.Get(randomArea); area.GetCenter(spawnPos); - /*float cornerPosition[3]; // Will revisit later + float cornerPosition[3]; ArrayList corners = new ArrayList(); for (int i = 0; i < 4; i++) { @@ -3942,23 +2291,22 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) NavCornerType cornerB = corners.Get(GetRandomInt(0, corners.Length - 1)); area.GetCorner(cornerA, spawnPos); area.GetCorner(cornerB, cornerPosition); - LerpVectors(spawnPos, cornerPosition, spawnPos, GetRandomFloat(0.3, 0.7)); + LerpVectors(spawnPos, cornerPosition, spawnPos, GetRandomFloat(0.35, 0.65)); area.GetCorner(invert, cornerPosition); - LerpVectors(spawnPos, cornerPosition, spawnPos, GetRandomFloat(0.3, 0.7)); - delete corners;*/ + LerpVectors(spawnPos, cornerPosition, spawnPos, GetRandomFloat(0.35, 0.65)); + delete corners; float traceMins[3]; - traceMins[0] = g_SlenderDetectMins[bossIndex][0]; - traceMins[1] = g_SlenderDetectMins[bossIndex][1]; + data.GetHullMins(traceMins); traceMins[2] = 0.0; float traceMaxs[3]; - traceMaxs[0] = g_SlenderDetectMaxs[bossIndex][0]; - traceMaxs[1] = g_SlenderDetectMaxs[bossIndex][1]; - traceMaxs[2] = g_SlenderDetectMaxs[bossIndex][2]; + data.GetHullMaxs(traceMaxs); - TR_TraceHullFilter(spawnPos, spawnPos, traceMins, traceMaxs, MASK_NPCSOLID, TraceRayDontHitEntity); - if (TR_DidHit()) + Handle trace = TR_TraceHullFilterEx(spawnPos, spawnPos, traceMins, traceMaxs, MASK_NPCSOLID, TraceRayDontHitEntity); + bool hit = TR_DidHit(trace); + delete trace; + if (hit) { area = NULL_AREA; validAreaCount--; @@ -4006,7 +2354,7 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) } // Check visibility. - if (!g_SlenderTeleportIgnoreVis[bossIndex] && IsPointVisibleToAPlayer(spawnPos, !shouldBeBehindObstruction, false, _, true)) + if (!data.TeleportIgnoreVis && IsPointVisibleToAPlayer(spawnPos, !shouldBeBehindObstruction, false, _, true)) { area = NULL_AREA; validAreaCount--; @@ -4021,9 +2369,11 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) canSpawn = false; } - AddVectors(spawnPos, g_SlenderEyePosOffset[bossIndex], spawnPos); + float offset[3]; + data.GetEyes().GetOffsetPos(offset); + AddVectors(spawnPos, offset, spawnPos); - if (!g_SlenderTeleportIgnoreVis[bossIndex] && IsPointVisibleToAPlayer(spawnPos, !shouldBeBehindObstruction, false, _, true)) + if (!data.TeleportIgnoreVis && IsPointVisibleToAPlayer(spawnPos, !shouldBeBehindObstruction, false, _, true)) { area = NULL_AREA; validAreaCount--; @@ -4035,12 +2385,12 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) canSpawn = false; } - SubtractVectors(spawnPos, g_SlenderEyePosOffset[bossIndex], spawnPos); + SubtractVectors(spawnPos, offset, spawnPos); // Look for copies - if (controller.GetProfileData().CopiesInfo.Enabled[difficulty] && canSpawn) + if (controller.GetProfileData().GetCopies().IsEnabled(difficulty) && canSpawn) { - float minDistBetweenBosses = GetBossProfileTeleportCopyDistance(profile, difficulty); + float minDistBetweenBosses = data.GetCopies().GetTeleportDistanceSpacing(difficulty); for (int bossCheck = 0; bossCheck < MAX_BOSSES; bossCheck++) { @@ -4109,20 +2459,19 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) if (PlayerCanSeeSlender(i, bossIndex, false)) { - if ((NPCGetDistanceFromEntity(bossIndex, i) <= SquareFloat(NPCGetJumpscareDistance(bossIndex, difficulty)) && GetGameTime() >= g_SlenderNextJumpScare[bossIndex]) || - (PlayerCanSeeSlender(i, bossIndex) && !GetBossProfileJumpscareNoSight(profile))) + if ((NPCGetDistanceFromEntity(bossIndex, i) <= SquareFloat(data.GetJumpscareDistance(difficulty)) && GetGameTime() >= g_SlenderNextJumpScare[bossIndex]) || + (PlayerCanSeeSlender(i, bossIndex) && !data.JumpscareNoSight)) { didJumpScare = true; - float jumpScareDuration = NPCGetJumpscareDuration(bossIndex, difficulty); - ClientDoJumpScare(i, bossIndex, jumpScareDuration); + ClientDoJumpScare(i, bossIndex, data.GetJumpscareDuration(difficulty)); } } } if (didJumpScare) { - g_SlenderNextJumpScare[bossIndex] = GetGameTime() + NPCGetJumpscareCooldown(bossIndex, difficulty); + g_SlenderNextJumpScare[bossIndex] = GetGameTime() + data.GetJumpscareCooldown(difficulty); } } break; @@ -4155,7 +2504,7 @@ static Action Timer_SlenderTeleportThink(Handle timer, any id) } else { - float teleportTime = GetRandomFloat(NPCGetTeleportTimeMin(bossIndex, difficulty), NPCGetTeleportTimeMax(bossIndex, difficulty)); + float teleportTime = GetRandomFloat(data.GetMinTeleportTime(difficulty), data.GetMaxTeleportTime(difficulty)); g_SlenderNextTeleportTime[bossIndex] = gameTime + teleportTime; } @@ -4184,30 +2533,31 @@ static Action Timer_SlenderRespawnThink(Handle timer, any id) float gameTime = GetGameTime(); int difficulty = GetLocalGlobalDifficulty(bossIndex); + BaseBossProfile data = controller.GetProfileData(); bool cont = false; int bossEnt = controller.EntIndex; - if (bossEnt && bossEnt != INVALID_ENT_REFERENCE && controller.TeleportType == 2) + if (bossEnt && bossEnt != INVALID_ENT_REFERENCE && data.TeleportType == 2) { cont = true; } - if (!NPCIsTeleportAllowed(bossIndex, difficulty) && (!bossEnt || bossEnt == INVALID_ENT_REFERENCE)) + if (!data.IsTeleportAllowed(difficulty) && (!bossEnt || bossEnt == INVALID_ENT_REFERENCE)) { cont = true; } if (cont) { - float teleportTime = GetRandomFloat(NPCGetTeleportTimeMin(bossIndex, difficulty), NPCGetTeleportTimeMax(bossIndex, difficulty)); + float teleportTime = GetRandomFloat(data.GetMinTeleportTime(difficulty), data.GetMaxTeleportTime(difficulty)); g_SlenderNextTeleportTime[bossIndex] = gameTime + teleportTime; return Plugin_Continue; } if (gameTime >= g_SlenderNextTeleportTime[bossIndex]) { - float teleportTime = GetRandomFloat(NPCGetTeleportTimeMin(bossIndex, difficulty), NPCGetTeleportTimeMax(bossIndex, difficulty)); + float teleportTime = GetRandomFloat(data.GetMinTeleportTime(difficulty), data.GetMaxTeleportTime(difficulty)); g_SlenderNextTeleportTime[bossIndex] = gameTime + teleportTime; if (bossEnt && bossEnt != INVALID_ENT_REFERENCE) // For teleport type 0 @@ -4260,8 +2610,7 @@ bool SlenderMarkAsFake(int bossIndex) NPCSetFlags(bossIndex, bossFlags | SFF_MARKEDASFAKE); - SF2BossProfileData data; - data = NPCGetProfileData(bossIndex); + BaseBossProfile data = SF2NPC_BaseNPC(bossIndex).GetProfileData(); g_SlenderFakeTimer[bossIndex] = CreateTimer(3.0, Timer_SlenderMarkedAsFake, bossIndex, TIMER_FLAG_NO_MAPCHANGE); @@ -4280,9 +2629,11 @@ bool SlenderMarkAsFake(int bossIndex) } SetEntProp(slender, Prop_Send, "m_usSolidFlags", flags); - if (data.EngineSound[0] != '\0') + char sound[PLATFORM_MAX_PATH]; + data.GetConstantSound(sound, sizeof(sound)); + if (sound[0] != '\0') { - StopSound(slender, SNDCHAN_STATIC, data.EngineSound); + StopSound(slender, SNDCHAN_STATIC, sound); } SetEntPropFloat(slender, Prop_Send, "m_flPlaybackRate", 0.0); @@ -4315,6 +2666,8 @@ int SpawnSlenderModel(int bossIndex, const float pos[3], bool deathCam = false) char buffer[PLATFORM_MAX_PATH], profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(bossIndex, profile, sizeof(profile)); + BaseBossProfile profileData = GetBossProfile(profile); + float playbackRate, cycle; int difficulty = GetLocalGlobalDifficulty(bossIndex); @@ -4324,13 +2677,13 @@ int SpawnSlenderModel(int bossIndex, const float pos[3], bool deathCam = false) LogError("Could not spawn boss model: model is invalid!"); return -1; } - float modelScale = NPCGetModelScale(bossIndex); + float modelScale = profileData.ModelScale; if (modelScale <= 0.0) { LogError("Could not spawn boss model: model scale is less than or equal to 0.0!"); return -1; } - int modelSkin = NPCGetModelSkin(bossIndex); + int modelSkin = profileData.GetSkin(difficulty); if (modelSkin < 0) { LogError("Could not spawn boss model: model skin is less than 0!"); @@ -4345,18 +2698,29 @@ int SpawnSlenderModel(int bossIndex, const float pos[3], bool deathCam = false) slenderModel.Teleport(pos, NULL_VECTOR, NULL_VECTOR); slenderModel.Spawn(); slenderModel.Activate(); - SF2BossProfileMasterAnimationsData animData; - GetBossProfileAnimationsData(profile, animData); + ProfileMasterAnimations animData = profileData.GetAnimations(); + ProfileAnimation animSection = null; if (!deathCam) { - animData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_Idle], difficulty, buffer, sizeof(buffer), playbackRate, _, cycle); + animSection = animData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_Idle]); + if (animSection != null) + { + animSection.GetAnimationName(difficulty, buffer, sizeof(buffer)); + playbackRate = animSection.GetAnimationPlaybackRate(difficulty); + cycle = animSection.GetAnimationCycle(difficulty); + } } else { - bool animationFound = animData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_DeathCam], difficulty, buffer, sizeof(buffer), playbackRate, _, cycle); - if (!animationFound || strcmp(buffer,"") <= 0) + animSection = animData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_DeathCam]); + if (animSection == null) + { + animData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_Idle]); + } + if (animSection != null) { - animData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_Idle], difficulty, buffer, sizeof(buffer), playbackRate, _, cycle); + playbackRate = animSection.GetAnimationPlaybackRate(difficulty); + cycle = animSection.GetAnimationCycle(difficulty); } } if (buffer[0] != '\0') @@ -4381,45 +2745,31 @@ int SpawnSlenderModel(int bossIndex, const float pos[3], bool deathCam = false) { slenderModel.SetPropFloat(Prop_Send, "m_flModelScale", modelScale); } - if (NPCGetModelSkinMax(bossIndex) > 0) + if (profileData.SkinMax > 0) { - int randomSkin = GetRandomInt(0, NPCGetModelSkinMax(bossIndex)); + int randomSkin = GetRandomInt(0, profileData.SkinMax); slenderModel.SetProp(Prop_Send, "m_nSkin", randomSkin); } else { - if (GetBossProfileSkinDifficultyState(profile)) - { - slenderModel.SetProp(Prop_Send, "m_nSkin", NPCGetModelSkinDifficulty(bossIndex, difficulty)); - } - else - { - slenderModel.SetProp(Prop_Send, "m_nSkin", modelSkin); - } + slenderModel.SetProp(Prop_Send, "m_nSkin", modelSkin); } - if (NPCGetModelBodyGroupsMax(bossIndex) > 0) + if (profileData.BodyMax > 0) { - int randomBody = GetRandomInt(0, NPCGetModelBodyGroupsMax(bossIndex)); + int randomBody = GetRandomInt(0, profileData.BodyMax); slenderModel.SetProp(Prop_Send, "m_nBody", randomBody); } else { - if (GetBossProfileBodyGroupsDifficultyState(profile)) - { - slenderModel.SetProp(Prop_Send, "m_nBody", NPCGetModelBodyGroupsDifficulty(bossIndex, difficulty)); - } - else - { - slenderModel.SetProp(Prop_Send, "m_nBody", NPCGetModelBodyGroups(bossIndex)); - } + slenderModel.SetProp(Prop_Send, "m_nBody", profileData.GetBodyGroup(difficulty)); } - slenderModel.SetProp(Prop_Send, "m_nBody", GetBossProfileBodyGroups(profile)); - // Create special effects. - slenderModel.SetRenderMode(view_as(g_SlenderRenderMode[bossIndex])); - slenderModel.SetRenderFx(view_as(g_SlenderRenderFX[bossIndex])); - slenderModel.SetRenderColor(g_SlenderRenderColor[bossIndex][0], g_SlenderRenderColor[bossIndex][1], g_SlenderRenderColor[bossIndex][2], g_SlenderRenderColor[bossIndex][3]); + slenderModel.SetRenderMode(profileData.GetRenderMode(difficulty)); + slenderModel.SetRenderFx(profileData.GetRenderFx(difficulty)); + int color[4]; + profileData.GetRenderColor(difficulty, color); + slenderModel.SetRenderColor(color[0], color[1], color[2], color[3]); g_NpcModelMaster[slenderModel.index] = bossIndex; slenderModel.Hook_HandleAnimEvent(CBaseAnimating_HandleAnimEvent); @@ -4477,7 +2827,7 @@ float NPCGetDistanceFromPoint(int npcIndex, const float point[3]) float pos[3]; CBaseEntity(npcEnt).GetAbsOrigin(pos); - return GetVectorSquareMagnitude(pos, point); + return GetVectorDistance(pos, point, true); } return -1.0; @@ -4491,7 +2841,7 @@ float NPCGetDistanceFromEntity(int npcIndex, int ent) } float pos[3]; - GetEntPropVector(ent, Prop_Data, "m_vecAbsOrigin", pos); + CBaseEntity(ent).GetAbsOrigin(pos); return NPCGetDistanceFromPoint(npcIndex, pos); } @@ -4509,7 +2859,7 @@ bool TraceRayBossVisibility(int entity, int mask, any data) return false; } } - if (IsValidEntity(entity) && NPCGetFromEntIndex(entity) != -1) + if (SF2_ChaserEntity(entity).IsValid() || SF2_StatueEntity(entity).IsValid()) { return false; } @@ -4523,7 +2873,7 @@ bool TraceRayDontHitCharacters(int entity, int mask, any data) return false; } - if (IsValidEntity(entity) && NPCGetFromEntIndex(entity) != -1) + if (SF2_ChaserEntity(entity).IsValid() || SF2_StatueEntity(entity).IsValid()) { return false; } @@ -4544,7 +2894,7 @@ bool TraceRayDontHitAnyEntity(int entity, int mask, any data) return false; } } - if (IsValidEntity(entity) && NPCGetFromEntIndex(entity) != -1) + if (SF2_ChaserEntity(entity).IsValid() || SF2_StatueEntity(entity).IsValid()) { return false; } @@ -4560,7 +2910,7 @@ bool TraceRayDontHitAnyEntity_Pathing(int entity, int contentsMask, int desiredc return false; } } - if (IsValidEntity(entity) && NPCGetFromEntIndex(entity) != -1) + if (SF2_ChaserEntity(entity).IsValid() || SF2_StatueEntity(entity).IsValid()) { return false; } @@ -4579,7 +2929,7 @@ bool TraceRayDontHitCharactersOrEntity(int entity, int mask, any data) return false; } - if (IsValidEntity(entity) && NPCGetFromEntIndex(entity) != -1) + if (SF2_ChaserEntity(entity).IsValid() || SF2_StatueEntity(entity).IsValid()) { return false; } @@ -4701,17 +3051,17 @@ bool SpawnProxy(int client, int bossIndex, float teleportPos[3], int &spawnPoint for (int areaIndex = 0, maxCount = collector.Count(); areaIndex < maxCount; areaIndex++) { - CNavArea Area = collector.Get(areaIndex); + CNavArea area = collector.Get(areaIndex); // Check flags. - if (Area.GetAttributes() & NAV_MESH_NO_HOSTAGES) + if (area.HasAttributes(NAV_MESH_NO_HOSTAGES) || view_as(area).HasAttributeTF(NO_SPAWNING)) { // Don't spawn/teleport at areas marked with the "NO HOSTAGES" flag. continue; } - int index = areaArray.Push(Area); - areaArray.Set(index, Area.GetCostSoFar(), 1); + int index = areaArray.Push(area); + areaArray.Set(index, area.GetCostSoFar(), 1); poppedAreas++; } @@ -4989,7 +3339,7 @@ static any Native_GetBossName(Handle plugin, int numParams) static any Native_GetBossType(Handle plugin, int numParams) { - return NPCGetType(GetNativeCell(1)); + return SF2NPC_BaseNPC(GetNativeCell(1)).GetProfileData().Type; } static any Native_GetBossFlags(Handle plugin, int numParams) @@ -5047,7 +3397,7 @@ static any Native_GetBossMaster(Handle plugin, int numParams) static any Native_GetBossIdleLifetime(Handle plugin, int numParams) { - return NPCGetIdleLifetime(GetNativeCell(1), GetNativeCell(2)); + return SF2NPC_BaseNPC(GetNativeCell(1)).GetProfileData().GetIdleLifeTime(GetNativeCell(2)); } static any Native_GetBossState(Handle plugin, int numParams) @@ -5099,7 +3449,7 @@ static any Native_GetBossEyePositionOffset(Handle plugin, int numParams) } float eyePos[3]; - boss.GetEyePositionOffset(eyePos); + boss.GetProfileData().GetEyes().GetOffsetPos(eyePos); SetNativeArray(2, eyePos, 3); return 0; @@ -5191,24 +3541,14 @@ static any Native_GetProfileData(Handle plugin, int numParams) return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", controller.Index); } - SF2BossProfileData data; - data = controller.GetProfileData(); - SetNativeArray(2, data, sizeof(data)); - return 0; + return controller.GetProfileData(); } static any Native_GetProfileDataEx(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, sizeof(profile)); - SF2BossProfileData data; - if (!g_BossProfileData.GetArray(profile, data, sizeof(data))) - { - return false; - } - - SetNativeArray(2, data, sizeof(data)); - return true; + return GetBossProfile(profile); } static any Native_SpawnBossEffects(Handle plugin, int numParams) diff --git a/addons/sourcemod/scripting/sf2/npc/entities/base/actions/deathcam.sp b/addons/sourcemod/scripting/sf2/npc/entities/base/actions/deathcam.sp index 8cbc11ee..c8ad5f0c 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/base/actions/deathcam.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/base/actions/deathcam.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -9,6 +10,9 @@ methodmap SF2_DeathCamAction < NextBotAction if (g_Factory == null) { g_Factory = new NextBotActionFactory("SF2_Deathcam"); + g_Factory.BeginDataMapDesc() + .DefineFloatField("m_Duration") + .EndDataMapDesc(); g_Factory.SetCallback(NextBotActionCallbackType_InitialContainedAction, InitialContainedAction); g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); g_Factory.SetCallback(NextBotActionCallbackType_OnEnd, OnEnd); @@ -16,6 +20,19 @@ methodmap SF2_DeathCamAction < NextBotAction } return view_as(g_Factory.Create()); } + + property float Duration + { + public get() + { + return this.GetDataFloat("m_Duration"); + } + + public set(float value) + { + this.SetDataFloat("m_Duration", value); + } + } } static NextBotAction InitialContainedAction(SF2_DeathCamAction action, SF2_BaseBoss actor) @@ -26,23 +43,47 @@ static NextBotAction InitialContainedAction(SF2_DeathCamAction action, SF2_BaseB actor.IsKillingSomeone = true; float duration = 0.0, rate = 1.0, cycle = 0.0; SF2NPC_BaseNPC controller = actor.Controller; - SF2BossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileSoundInfo info; - info = data.LocalDeathCamSounds; - info.EmitSound(_, actor.index); + int difficulty = controller.Difficulty; + BaseBossProfile data = controller.GetProfileData(); + data.GetLocalDeathCamSounds().EmitSound(_, actor.index, .difficulty = controller.Difficulty); + BossProfileDeathCamData deathCamData = data.GetDeathCamData(); + DeathCamAnimation section = view_as(deathCamData.GetAnimations().GetAnimation("start")); + char animation[64]; + if (section == null) + { + return NULL_ACTION; + } - int sequence = actor.SelectProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_DeathCam], rate, duration, cycle); - if (sequence != -1) + section.GetAnimationName(difficulty, animation, sizeof(animation)); + rate = section.GetAnimationPlaybackRate(difficulty); + cycle = section.GetAnimationCycle(difficulty); + duration = section.GetDuration(difficulty); + int sequence = actor.LookupSequence(animation); + if (sequence == -1) { - return SF2_PlaySequenceAndWait(sequence, duration, rate, cycle); + return NULL_ACTION; } - return NULL_ACTION; + if (duration <= 0.0) + { + duration = actor.SequenceDuration(sequence) / rate; + duration *= (1.0 - cycle); + } + action.Duration = duration; + return SF2_PlaySequenceAndWait(sequence, duration, rate, cycle); } -static int Update(SF2_DeathCamAction action, SF2_BaseBoss actor, NextBotAction priorAction) +static int Update(SF2_DeathCamAction action, SF2_BaseBoss actor, float interval) { + if (actor.FullDeathCamDuration) + { + action.Duration -= interval; + if (action.Duration > 0.0) + { + return action.Continue(); + } + } + if (!actor.KillTarget.IsValid()) { return action.Done(); @@ -60,6 +101,18 @@ static int Update(SF2_DeathCamAction action, SF2_BaseBoss actor, NextBotAction p static void OnEnd(SF2_DeathCamAction action, SF2_BaseBoss actor) { actor.IsKillingSomeone = false; + actor.FullDeathCamDuration = false; + if (!actor.Controller.IsValid()) + { + return; + } + SF2NPC_BaseNPC controller = actor.Controller; + BaseBossProfile data = controller.GetProfileData(); + BossProfileDeathCamData deathCamData = data.GetDeathCamData(); + if (deathCamData.StopSounds) + { + data.GetLocalDeathCamSounds().StopAllSounds(actor.index); + } } static void OnOtherKilled(SF2_DeathCamAction action, SF2_BaseBoss actor, CBaseCombatCharacter victim, CBaseEntity attacker, CBaseEntity inflictor, float damage, int damagetype) diff --git a/addons/sourcemod/scripting/sf2/npc/entities/base/actions/playsequenceandwait.sp b/addons/sourcemod/scripting/sf2/npc/entities/base/actions/playsequenceandwait.sp index f4b96d1c..040ded21 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/base/actions/playsequenceandwait.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/base/actions/playsequenceandwait.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -162,4 +163,4 @@ static any Native_Create(Handle plugin, int numParams) float cycle = GetNativeCell(4); return SF2_PlaySequenceAndWait(sequence, duration, rate, cycle); -} +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/base/actions/playsequenceandwait_ex.sp b/addons/sourcemod/scripting/sf2/npc/entities/base/actions/playsequenceandwait_ex.sp new file mode 100644 index 00000000..d645b38a --- /dev/null +++ b/addons/sourcemod/scripting/sf2/npc/entities/base/actions/playsequenceandwait_ex.sp @@ -0,0 +1,132 @@ +#pragma semicolon 1 +#pragma newdecls required + +static NextBotActionFactory g_Factory; + +methodmap SF2_PlaySequenceAndWaitEx < NextBotAction +{ + public SF2_PlaySequenceAndWaitEx(const char[] section, const char[] preDefinedName = "", ProfileMasterAnimations animations = null) + { + if (g_Factory == null) + { + g_Factory = new NextBotActionFactory("SF2_PlaySequenceAndWaitEx"); + g_Factory.SetCallback(NextBotActionCallbackType_OnStart, OnStart); + g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); + g_Factory.SetCallback(NextBotActionCallbackType_OnSuspend, OnSuspend); + g_Factory.SetCallback(NextBotActionCallbackType_OnEnd, OnEnd); + g_Factory.BeginDataMapDesc() + .DefineIntField("m_Animations") + .DefineStringField("m_Section") + .DefineStringField("m_PreDefinedName") + .DefineFloatField("m_EndTime") + .EndDataMapDesc(); + } + + SF2_PlaySequenceAndWaitEx action = view_as(g_Factory.Create()); + + action.Animations = animations; + action.SetSection(section); + action.SetPreDefinedName(preDefinedName); + + return action; + } + + property ProfileMasterAnimations Animations + { + public get() + { + return this.GetData("m_Animations"); + } + + public set(ProfileMasterAnimations value) + { + this.SetData("m_Animations", value); + } + } + + public char[] GetSection() + { + char name[128]; + this.GetDataString("m_Section", name, sizeof(name)); + return name; + } + + public void SetSection(const char[] name) + { + this.SetDataString("m_Section", name); + } + + public char[] GetPreDefinedName() + { + char name[128]; + this.GetDataString("m_PreDefinedName", name, sizeof(name)); + return name; + } + + public void SetPreDefinedName(const char[] name) + { + this.SetDataString("m_PreDefinedName", name); + } + + property float EndTime + { + public get() + { + return this.GetDataFloat("m_EndTime"); + } + + public set(float value) + { + this.SetDataFloat("m_EndTime", value); + } + } +} + +static int OnStart(SF2_PlaySequenceAndWaitEx action, SF2_BaseBoss actor, NextBotAction priorAction) +{ + float duration = 0.0, cycle = 0.0, rate = 1.0; + int sequence = -1; + ProfileMasterAnimations animations = action.Animations; + if (!actor.ResetProfileAnimation(action.GetSection(), .preDefinedName = action.GetPreDefinedName(), .sequence = sequence, .duration = duration, .rate = rate, .cycle = cycle, .animations = animations)) + { + return action.Done("Invalid section"); + } + + if (duration <= 0.0) + { + duration = actor.SequenceDuration(sequence) / rate; + duration *= (1.0 - cycle); + } + + if (SF2_ChaserEntity(actor.index).IsValid()) + { + SF2_ChaserEntity(actor.index).GroundSpeedOverride = true; + } + + action.EndTime = GetGameTime() + duration; + + return action.Continue(); +} + +static int Update(SF2_PlaySequenceAndWaitEx action, SF2_BaseBoss actor, float interval) +{ + if (GetGameTime() > action.EndTime) + { + return action.Done(); + } + + return action.Continue(); +} + +static int OnSuspend(SF2_PlaySequenceAndWaitEx action, SF2_BaseBoss actor, NextBotAction interruptingAction) +{ + return action.Done(); +} + +static void OnEnd(SF2_PlaySequenceAndWaitEx action, SF2_ChaserEntity actor) +{ + if (SF2_ChaserEntity(actor.index).IsValid()) + { + SF2_ChaserEntity(actor.index).GroundSpeedOverride = false; + } +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/base/entity.sp b/addons/sourcemod/scripting/sf2/npc/entities/base/entity.sp index 21f1e8a5..98dc97f3 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/base/entity.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/base/entity.sp @@ -1,6 +1,8 @@ #pragma semicolon 1 +#pragma newdecls required #include "actions/playsequenceandwait.sp" +#include "actions/playsequenceandwait_ex.sp" #include "actions/deathcam.sp" static CEntityFactory g_Factory; @@ -42,6 +44,8 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter .DefineEntityField("m_OldTarget") .DefineIntField("m_InterruptConditions") .DefineBoolField("m_IsJumping") + .DefineFloatField("m_AirTime") + .DefineBoolField("m_CanLand") .DefineBoolField("m_IsEntityVisible", 2049) .DefineBoolField("m_IsEntityInFOV", 2049) .DefineBoolField("m_IsEntityNear", 2049) @@ -62,10 +66,14 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter .DefineVectorField("m_ForceWanderPos") .DefineBoolField("m_IsKillingSomeone") .DefineEntityField("m_KillTarget") + .DefineBoolField("m_FullDeathCamDuration") .DefineBoolField("m_IsAttemptingToMove") .DefineIntField("m_EyeBoneIndex") .DefineBoolField("m_VelocityCancel") .DefineIntField("m_Teleporters") + .DefineBoolField("m_ShouldAnimationSyncWithGround") + .DefineFloatField("m_GroundSyncSpeed") + .DefineBoolField("m_LockAnimations") .EndDataMapDesc(); g_Factory.Install(); @@ -125,7 +133,7 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter } } - public SF2BossProfileData GetProfileData() + public BaseBossProfile GetProfileData() { return this.Controller.GetProfileData(); } @@ -182,6 +190,32 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter } } + property float AirTime + { + public get() + { + return this.GetPropFloat(Prop_Data, "m_AirTime"); + } + + public set(float value) + { + this.SetPropFloat(Prop_Data, "m_AirTime", value); + } + } + + property bool CanLand + { + public get() + { + return this.GetProp(Prop_Data, "m_CanLand") != 0; + } + + public set(bool value) + { + this.SetProp(Prop_Data, "m_CanLand", value); + } + } + public bool GetIsVisible(CBaseEntity entity) { return this.GetProp(Prop_Data, "m_IsEntityVisible", entity.index) != 0; @@ -430,6 +464,19 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter } } + property bool FullDeathCamDuration + { + public get() + { + return this.GetProp(Prop_Data, "m_FullDeathCamDuration") != 0; + } + + public set(bool value) + { + this.SetProp(Prop_Data, "m_FullDeathCamDuration", value); + } + } + property bool IsAttemptingToMove { public get() @@ -482,6 +529,45 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter } } + property bool ShouldAnimationSyncWithGround + { + public get() + { + return this.GetProp(Prop_Data, "m_ShouldAnimationSyncWithGround") != 0; + } + + public set(bool value) + { + this.SetProp(Prop_Data, "m_ShouldAnimationSyncWithGround", value); + } + } + + property float GroundSyncSpeed + { + public get() + { + return this.GetPropFloat(Prop_Data, "m_GroundSyncSpeed"); + } + + public set(float value) + { + this.SetPropFloat(Prop_Data, "m_GroundSyncSpeed", value); + } + } + + property bool LockAnimations + { + public get() + { + return this.GetProp(Prop_Data, "m_LockAnimations") != 0; + } + + public set(bool value) + { + this.SetProp(Prop_Data, "m_LockAnimations", value); + } + } + public void EyePosition(float buffer[3], const float defaultValue[3] = { 0.0, 0.0, 0.0 }) { this.Controller.GetEyePosition(buffer, defaultValue); @@ -504,6 +590,7 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter { CreateNative("SF2_BaseBossEntity.IsValid.get", Native_GetIsValid); CreateNative("SF2_BaseBossEntity.Controller.get", Native_GetController); + CreateNative("SF2_BaseBossEntity.ControllerEx.get", Native_GetController); CreateNative("SF2_BaseBossEntity.Target.get", Native_GetTarget); CreateNative("SF2_BaseBossEntity.State.get", Native_GetState); CreateNative("SF2_BaseBossEntity.CurrentChaseDuration.get", Native_GetCurrentChaseDuration); @@ -514,12 +601,13 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter CreateNative("SF2_BaseBossEntity.EyePosition", Native_EyePosition); CreateNative("SF2_BaseBossEntity.GetProfileName", Native_GetProfileName); CreateNative("SF2_BaseBossEntity.GetName", Native_GetName); + CreateNative("SF2_BaseBossEntity.LockAnimations.get", Native_GetLockAnimations); + CreateNative("SF2_BaseBossEntity.LockAnimations.set", Native_SetLockAnimations); CreateNative("SF2_BaseBossEntity.ProfileData", Native_GetProfileData); CreateNative("SF2_BaseBossEntity.ResetProfileAnimation", Native_ResetProfileAnimation); } - public int SelectProfileAnimation(const char[] animType, float &rate = 1.0, float &duration = 0.0, float &cycle = 0.0, float &footstepInterval = 0.0, - int &index = 0, int preDefinedIndex = -1, const char[] preDefinedName = "", const char[] posture = NULL_STRING, bool &overrideLoop = false, bool &loop = false, char[] returnAnimation = "", int rtnAnimationLength = 0) + public int SelectProfileAnimation(const char[] animType, float &rate = 1.0, float &duration = 0.0, float &cycle = 0.0, float &footstepInterval = 0.0, int &index = 0, int preDefinedIndex = -1, const char[] preDefinedName = "", const char[] posture = NULL_STRING, bool &overrideLoop = false, bool &loop = false, char[] returnAnimation = "", int rtnAnimationLength = 0, bool &sync = false, float &syncSpeed = -1.0, ProfileMasterAnimations animations = null) { SF2NPC_BaseNPC controller = this.Controller; int difficulty = controller.Difficulty; @@ -527,31 +615,48 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter char animation[64]; bool found = false; + ProfileAnimation section = null; - if (controller.Type == SF2BossType_Chaser && !IsNullString(posture) && strcmp(posture, SF2_PROFILE_CHASER_DEFAULT_POSTURE) != 0) + if (controller.GetProfileData().Type == SF2BossType_Chaser && !IsNullString(posture) && strcmp(posture, SF2_PROFILE_CHASER_DEFAULT_POSTURE) != 0) { - SF2NPC_Chaser chaserController = view_as(controller); - SF2ChaserBossProfileData data; - data = chaserController.GetProfileData(); - SF2ChaserBossProfilePostureInfo postureInfo; - found = data.GetPosture(posture, postureInfo); + ChaserBossProfile data = view_as(this.GetProfileData()); + found = data.GetPosture(posture) != null; if (found) { - found = postureInfo.Animations.GetAnimation(animType, difficulty, animation, sizeof(animation), - rate, duration, cycle, footstepInterval, index, preDefinedIndex, preDefinedName, overrideLoop, loop); - if (found) + section = data.GetPostureAnimations(posture).GetAnimation(animType, preDefinedIndex, preDefinedName, index); + if (section != null) { + section.GetAnimationName(difficulty, animation, sizeof(animation)); + section.GetAnimationName(difficulty, returnAnimation, rtnAnimationLength); + rate = section.GetAnimationPlaybackRate(difficulty); + duration = section.GetDuration(difficulty); + cycle = section.GetAnimationCycle(difficulty); + footstepInterval = section.GetFootstepInterval(difficulty); + overrideLoop = section.CanOverrideLoop(difficulty); + loop = section.GetLoopState(difficulty); + sync = section.ShouldSyncWithGround(difficulty); + syncSpeed = section.GetGroundSyncSpeed(difficulty); this.AnimationPlaybackRate = rate; - return LookupProfileAnimation(this.index, animation); } } } - found = controller.GetProfileData().AnimationData.GetAnimation(animType, difficulty, animation, sizeof(animation), - rate, duration, cycle, footstepInterval, index, preDefinedIndex, preDefinedName, overrideLoop, loop); + if (animations == null) + { + animations = controller.GetProfileData().GetAnimations(); + } + + section = animations.GetAnimation(animType, preDefinedIndex, preDefinedName, index); - if (!found) + if (section == null) + { + return -1; + } + + section.GetAnimationName(difficulty, animation, sizeof(animation)); + + if (animation[0] == '\0') { return -1; } @@ -559,44 +664,64 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter int sequence = LookupProfileAnimation(this.index, animation); strcopy(returnAnimation, rtnAnimationLength, animation); + rate = section.GetAnimationPlaybackRate(difficulty); + duration = section.GetDuration(difficulty); + cycle = section.GetAnimationCycle(difficulty); + footstepInterval = section.GetFootstepInterval(difficulty); + overrideLoop = section.CanOverrideLoop(difficulty); + loop = section.GetLoopState(difficulty); + sync = section.ShouldSyncWithGround(difficulty); + syncSpeed = section.GetGroundSyncSpeed(difficulty); this.AnimationPlaybackRate = rate; return sequence; } - public int SelectProfileGesture(int definedIndex = -1, const char[] definedName = "", const char[] animType, float &rate = 1.0, float &cycle = 0.0) + public int SelectProfileGesture(int definedIndex = -1, const char[] definedName = "", const char[] animType, float &rate = 1.0, float &cycle = 0.0, ProfileMasterAnimations animations = null) { SF2NPC_BaseNPC controller = view_as(this.Controller); int difficulty = GetLocalGlobalDifficulty(controller.Index); char gesture[64]; - bool found = controller.GetProfileData().AnimationData.GetGesture(definedIndex, definedName, animType, difficulty, gesture, sizeof(gesture), - rate, cycle); + if (animations == null) + { + animations = controller.GetProfileData().GetAnimations(); + } + ProfileAnimation section = animations.GetAnimation(animType, definedIndex, definedName); + + if (section == null) + { + return -1; + } + + section.GetGestureName(difficulty, gesture, sizeof(gesture)); - if (!found) + if (gesture[0] == '\0') { return -1; } int sequence = LookupProfileAnimation(this.index, gesture); + rate = section.GetGesturePlaybackRate(difficulty); + cycle = section.GetGestureCycle(difficulty); return sequence; } - public bool ResetProfileAnimation(const char[] animType, int preDefinedIndex = -1, const char[] preDefinedName = "", float &duration = 0.0, const char[] posture = NULL_STRING) + public bool ResetProfileAnimation(const char[] animType, int preDefinedIndex = -1, const char[] preDefinedName = "", float &duration = 0.0, const char[] posture = NULL_STRING, int& sequence = -1, float &rate = 1.0, float &cycle = 0.0, ProfileMasterAnimations animations = null) { if (this.Controller.IsValid() && (this.Controller.Flags & SFF_MARKEDASFAKE) != 0) { return false; } - float rate = 1.0, cycle = 0.0, footstepInterval = 0.0; - bool overrideLoop, loop; + float footstepInterval = 0.0, syncSpeed = -1.0; + bool overrideLoop, loop, sync; int index = 0; char animation[64]; - int sequence = this.SelectProfileAnimation(animType, rate, duration, cycle, footstepInterval, index, preDefinedIndex, preDefinedName, posture, overrideLoop, loop, animation, sizeof(animation)); + sequence = this.SelectProfileAnimation(animType, rate, duration, cycle, footstepInterval, index, preDefinedIndex, preDefinedName, posture, overrideLoop, loop, animation, sizeof(animation), sync, syncSpeed, animations); if (sequence == -1) { return false; @@ -617,14 +742,17 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter Call_PushString(animation); Call_Finish(result); - if (result != Plugin_Handled) + if (result != Plugin_Handled && !this.LockAnimations) { bool isMovement = strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_Walk]) == 0 || strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_Run]) == 0; - bool shouldLoop = isMovement || strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_Idle]) == 0; - this.ResetSequence(sequence); - this.SetPropFloat(Prop_Send, "m_flCycle", cycle); + bool shouldLoop = isMovement || strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_Idle]) == 0 || strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_Air]) == 0; + if (sequence != this.GetProp(Prop_Send, "m_nSequence")) + { + this.ResetSequence(sequence); + this.SetPropFloat(Prop_Send, "m_flCycle", cycle); + } if (strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_Attack]) == 0 && this.MovementType == SF2NPCMoveType_Attack) { shouldLoop = true; @@ -638,17 +766,19 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter this.SetProp(Prop_Data, "m_bSequenceLoops", shouldLoop); } this.SetPropFloat(Prop_Send, "m_flPlaybackRate", rate); - + this.LegacyFootstepTime = 0.0; this.LegacyFootstepInterval = footstepInterval; + this.ShouldAnimationSyncWithGround = sync; + this.GroundSyncSpeed = syncSpeed; } return true; } - public bool AddGesture(const char[] animType, int definedIndex = -1, const char[] definedName = "") + public bool AddGesture(const char[] animType, int definedIndex = -1, const char[] definedName = "", ProfileMasterAnimations animations = null) { float rate = 1.0, duration = 0.0, cycle = 0.0; - int sequence = this.SelectProfileGesture(definedIndex, definedName, animType, rate, cycle); + int sequence = this.SelectProfileGesture(definedIndex, definedName, animType, rate, cycle, animations); if (sequence == -1) { return false; @@ -720,12 +850,11 @@ methodmap SF2_BaseBoss < CBaseCombatCharacter public void ProcessRainbowOutline() { SF2NPC_BaseNPC controller = this.Controller; - SF2BossProfileData data; - data = controller.GetProfileData(); + BossProfileOutlineData data = controller.GetProfileData().GetOutlineData(); int color[4]; - color[0] = RoundToNearest(Cosine((GetGameTime() * data.RainbowOutlineCycle) + controller.Index + 0) * 127.5 + 127.5); - color[1] = RoundToNearest(Cosine((GetGameTime() * data.RainbowOutlineCycle) + controller.Index + 2) * 127.5 + 127.5); - color[2] = RoundToNearest(Cosine((GetGameTime() * data.RainbowOutlineCycle) + controller.Index + 4) * 127.5 + 127.5); + color[0] = RoundToNearest(Cosine((GetGameTime() * data.GetRainbowCycle(controller.Difficulty)) + controller.Index + 0) * 127.5 + 127.5); + color[1] = RoundToNearest(Cosine((GetGameTime() * data.GetRainbowCycle(controller.Difficulty)) + controller.Index + 2) * 127.5 + 127.5); + color[2] = RoundToNearest(Cosine((GetGameTime() * data.GetRainbowCycle(controller.Difficulty)) + controller.Index + 4) * 127.5 + 127.5); color[3] = 255; SetGlowColor(this.index, color); } @@ -1011,12 +1140,37 @@ static any Native_GetName(Handle plugin, int numParams) } char buffer[SF2_MAX_PROFILE_NAME_LENGTH]; - controller.GetName(buffer, sizeof(buffer)); + controller.GetProfileData().GetName(controller.Difficulty, buffer, sizeof(buffer)); SetNativeString(2, buffer, sizeof(buffer)); return 0; } +static any Native_GetLockAnimations(Handle plugin, int numParams) +{ + int entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid entity index %d", entity); + } + + SF2_BaseBoss bossEntity = SF2_BaseBoss(entity); + return bossEntity.LockAnimations; +} + +static any Native_SetLockAnimations(Handle plugin, int numParams) +{ + int entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid entity index %d", entity); + } + + SF2_BaseBoss bossEntity = SF2_BaseBoss(entity); + bossEntity.LockAnimations = GetNativeCell(2); + return 0; +} + static any Native_GetProfileData(Handle plugin, int numParams) { int entity = GetNativeCell(1); @@ -1032,10 +1186,7 @@ static any Native_GetProfileData(Handle plugin, int numParams) return 0; } - SF2BossProfileData data; - data = bossEntity.Controller.GetProfileData(); - SetNativeArray(2, data, sizeof(data)); - return 0; + return bossEntity.Controller.GetProfileData(); } static any Native_ResetProfileAnimation(Handle plugin, int numParams) diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/alert.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/alert.sp index c16faa3d..a617166f 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/alert.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/alert.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -172,8 +173,8 @@ methodmap SF2_ChaserAlertAction < NextBotAction static int OnStart(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, NextBotAction priorAction) { SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileAlertData alertData = data.GetAlertBehavior(); int difficulty = controller.Difficulty; float gameTime = GetGameTime(); if (!action.IsRunning) @@ -185,11 +186,11 @@ static int OnStart(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, NextBot actor.MovementType = SF2NPCMoveType_Run; } - action.EndTime = gameTime + data.AlertData.Duration[difficulty]; - action.TimeUntilChase = gameTime + data.AlertData.GraceTime[difficulty]; + action.EndTime = gameTime + alertData.GetDuration(difficulty); + action.TimeUntilChase = gameTime + alertData.GetGraceTime(difficulty); action.InitialState = true; - if (data.AlertOnAlertInfo.OnChangeState[difficulty]) + if (alertData.GetAlertSyncData().IsEnabled(difficulty) && alertData.GetAlertSyncData().ShouldStartOnStateChange(difficulty)) { actor.ForceAlertOtherBosses(); } @@ -221,12 +222,10 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in return action.Continue(); } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileAlertData alertData = data.GetAlertBehavior(); - if (!originalData.IsPvEBoss && IsBeatBoxBeating(2)) + if (!data.IsPvEBoss && IsBeatBoxBeating(2)) { return action.SuspendFor(SF2_ChaserBeatBoxFreezeAction(actor.IsAttemptingToMove)); } @@ -239,7 +238,7 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in PathFollower path = controller.Path; - if (data.ChaseOnLookData.Enabled[difficulty] && controller.ChaseOnLookTargets.Length > 0) + if (data.GetChaseOnLookData().IsEnabled(difficulty) && controller.ChaseOnLookTargets.Length > 0) { SF2_BasePlayer lookTarget = controller.ChaseOnLookTargets.Get(0); if (lookTarget.IsValid && !lookTarget.IsEliminated && lookTarget.IsAlive && !lookTarget.IsInGhostMode && !lookTarget.HasEscaped) @@ -265,7 +264,7 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in } if (actor.FollowedCompanionAlert && actor.Controller.IsValid()) { - actor.FollowCooldownAlert = GetGameTime() + data.AlertOnAlertInfo.FollowCooldown[difficulty]; + actor.FollowCooldownAlert = GetGameTime() + alertData.GetAlertSyncData().GetFollowCooldown(difficulty); actor.FollowedCompanionAlert = false; } actor.AlertWithBoss = false; @@ -274,6 +273,18 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in if (target.IsValid()) { + if (SF_IsRaidMap() || SF_BossesChaseEndlessly() || SF_IsProxyMap() || SF_IsBoxingMap() || SF_IsSlaughterRunMap() || data.ChasesEndlessly || + data.IsPvEBoss) + { + actor.State = STATE_CHASE; + path.Invalidate(); + if (data.NormalSoundHook) + { + actor.NextVoiceTime = 0.0; + } + return action.ChangeTo(SF2_ChaserChaseAction(), "We must endless chase, GET THEM!"); + } + if ((interruptConditions & COND_ENEMYRECHASE) != 0) { actor.State = STATE_CHASE; @@ -317,7 +328,7 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in float pos[3]; actor.GetAlertTriggerPosition(alertTarget, pos); - UpdateAlertPosition(action, actor, pos, data.AlertData.RunOnSuspect[difficulty]); + UpdateAlertPosition(action, actor, pos, alertData.ShouldRunOnSuspect(difficulty)); } } else if ((interruptConditions & COND_ALERT_TRIGGER_POS) != 0 || actor.QueueForAlertState) @@ -325,7 +336,7 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in float pos[3]; actor.GetAlertTriggerPositionEx(pos); - UpdateAlertPosition(action, actor, pos, data.AlertData.RunOnSuspect[difficulty]); + UpdateAlertPosition(action, actor, pos, alertData.ShouldRunOnSuspect(difficulty)); actor.QueueForAlertState = false; } @@ -334,10 +345,10 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in float pos[3]; actor.GetForceWanderPosition(pos); - UpdateAlertPosition(action, actor, pos, data.AlertData.RunOnWander[difficulty]); + UpdateAlertPosition(action, actor, pos, alertData.ShouldRunOnWander(difficulty)); } - bool isAbleToWander = action.HasReachedAlertPosition && data.CanWander[difficulty]; + bool isAbleToWander = action.HasReachedAlertPosition && data.CanWander(difficulty); if (controller.HasAttribute(SF2Attribute_BlockWalkSpeedUnderDifficulty)) { int value = RoundToNearest(controller.GetAttributeValue(SF2Attribute_BlockWalkSpeedUnderDifficulty)); @@ -351,13 +362,13 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in { if (gameTime >= action.NextWanderTime) { - float min = data.WanderTimeMin[difficulty]; - float max = data.WanderTimeMax[difficulty]; + float min = data.GetWanderMinTime(difficulty); + float max = data.GetWanderMaxTime(difficulty); action.NextWanderTime = gameTime + GetRandomFloat(min, max); - float rangeMin = data.WanderRangeMin[difficulty]; - float rangeMax = data.WanderRangeMax[difficulty]; + float rangeMin = data.GetWanderMinRange(difficulty); + float rangeMax = data.GetWanderMaxRange(difficulty); float range = GetRandomFloat(rangeMin, rangeMax); CNavArea area = actor.GetLastKnownArea(); @@ -388,7 +399,7 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in { wanderArea.GetCenter(wanderPos); - UpdateAlertPosition(action, actor, wanderPos, data.AlertData.RunOnWander[difficulty]); + UpdateAlertPosition(action, actor, wanderPos, alertData.ShouldRunOnWander(difficulty)); } } @@ -414,7 +425,7 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in } else { - if (data.AlertData.TurnEnabled[difficulty]) + if (alertData.IsTurnEnabled(difficulty)) { if (!action.InitialState) { @@ -424,22 +435,22 @@ static int Update(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, float in } if (action.NextTurnTime <= 0.0) { - action.NextTurnTime = gameTime + GetRandomFloat(data.AlertData.TurnMinTime[difficulty], data.AlertData.TurnMaxTime[difficulty]); + action.NextTurnTime = gameTime + GetRandomFloat(alertData.GetTurnMinCooldown(difficulty), alertData.GetTurnMaxCooldown(difficulty)); } } } - if (data.AlertData.TurnEnabled[difficulty] && action.NextTurnTime > 0.0 && action.NextTurnTime <= gameTime) + if (action.NextTurnTime > 0.0 && action.NextTurnTime <= gameTime && alertData.IsTurnEnabled(difficulty)) { float myPos[3], myAng[3]; actor.GetAbsOrigin(myPos); actor.GetAbsAngles(myAng); - myAng[1] += GetRandomFloat(-data.AlertData.TurnAngle[difficulty], data.AlertData.TurnAngle[difficulty]); + myAng[1] += GetRandomFloat(-alertData.GetTurnAngle(difficulty), alertData.GetTurnAngle(difficulty)); float lookAt[3]; lookAt[0] = 50.0; VectorTransform(lookAt, myPos, myAng, lookAt); action.SetLookPosition(lookAt); - action.NextTurnTime = gameTime + GetRandomFloat(data.AlertData.TurnMinTime[difficulty], data.AlertData.TurnMaxTime[difficulty]); + action.NextTurnTime = gameTime + GetRandomFloat(alertData.GetTurnMinCooldown(difficulty), alertData.GetTurnMaxCooldown(difficulty)); action.InitialState = false; } @@ -514,13 +525,13 @@ static void OnResume(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, NextB static void OnReachedAlertPosition(SF2_ChaserAlertAction action, SF2_ChaserEntity actor) { SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileAlertData alertData = data.GetAlertBehavior(); int difficulty = controller.Difficulty; float gameTime = GetGameTime(); - action.NextTurnTime = gameTime + GetRandomFloat(data.AlertData.TurnMinTime[difficulty], data.AlertData.TurnMaxTime[difficulty]); - action.NextWanderTime = gameTime + GetRandomFloat(data.WanderEnterTimeMin[difficulty], data.WanderEnterTimeMax[difficulty]); + action.NextTurnTime = gameTime + GetRandomFloat(alertData.GetTurnMinCooldown(difficulty), alertData.GetTurnMaxCooldown(difficulty)); + action.NextWanderTime = gameTime + GetRandomFloat(data.GetWanderEnterMinTime(difficulty), data.GetWanderEnterMaxTime(difficulty)); } static void OnMoveToSuccess(SF2_ChaserAlertAction action, SF2_ChaserEntity actor, Path path) diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attack.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attack.sp index 0b5e620f..6f6ba87f 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attack.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attack.sp @@ -1,5 +1,1274 @@ #pragma semicolon 1 -// This manages the end time and getting a attack +#pragma newdecls required + +methodmap ChaserBossProfileBaseAttack < ProfileObject +{ + property int Type + { + public get() + { + return this.GetInt("type", view_as(SF2BossAttackType_Melee)); + } + } + + property int Index + { + public get() + { + int val = 0; + this.GetValue("__index", val); + return val; + } + + public set(int value) + { + this.SetValue("__index", value); + } + } + + public bool IsEnabled(int difficulty) + { + return this.GetDifficultyBool("enabled", difficulty, true); + } + + public bool CanUseWithPosture(const char[] posture) + { + bool ret = true; + + ProfileObject arr = this.GetSection("use_with_posture"); + if (arr != null) + { + ret = false; + + if (posture[0] == '\0') + { + ret = arr.ContainsString(SF2_PROFILE_CHASER_DEFAULT_POSTURE); + } + else + { + ret = arr.ContainsString(posture); + } + } + + return ret; + } + + property bool CanUseAgainstProps + { + public get() + { + bool def = false; + def = this.GetBool("props", def); + def = this.GetBool("attack_props", def); + return def; + } + } + + public bool ShouldAutoAim(int difficulty) + { + return this.GetDifficultyBool("autoaim", difficulty, false); + } + + property int UseOnDifficulty + { + public get() + { + int def = 0; + def = this.GetInt("use_on_difficulty", def); + def = this.GetInt("attack_use_on_difficulty", def); + return def; + } + } + + property int BlockOnDifficulty + { + public get() + { + int def = 6; + def = this.GetInt("block_on_difficulty", def); + def = this.GetInt("attack_block_on_difficulty", def); + return def; + } + } + + public float CanUseOnHealth(int difficulty) + { + float def = -1.0; + def = this.GetDifficultyFloat("use_on_health", difficulty, def); + def = this.GetDifficultyFloat("attack_use_on_health", difficulty, def); + return def; + } + + public float CanBlockOnHealth(int difficulty) + { + float def = -1.0; + def = this.GetDifficultyFloat("block_on_health", difficulty, def); + def = this.GetDifficultyFloat("attack_block_on_health", difficulty, def); + return def; + } + + public float GetDamage(int difficulty) + { + float defaultValue; + + switch (this.Type) + { + case SF2BossAttackType_Melee: + { + defaultValue = 50.0; + } + + case SF2BossAttackType_Ranged: + { + defaultValue = 8.0; + defaultValue = this.GetDifficultyFloat("bullet_damage", difficulty, defaultValue); + defaultValue = this.GetDifficultyFloat("attack_bullet_damage", difficulty, defaultValue); + } + + case SF2BossAttackType_Projectile: + { + defaultValue = 60.0; + defaultValue = this.GetDifficultyFloat("projectile_damage", difficulty, defaultValue); + defaultValue = this.GetDifficultyFloat("attack_projectile_damage", difficulty, defaultValue); + } + + case SF2BossAttackType_LaserBeam: + { + defaultValue = 25.0; + defaultValue = this.GetDifficultyFloat("laser_damage", difficulty, defaultValue); + defaultValue = this.GetDifficultyFloat("attack_laser_damage", difficulty, defaultValue); + } + + case SF2BossAttackType_ExplosiveDance: + { + defaultValue = 25.0; + } + + case SF2BossAttackType_Tongue: + { + defaultValue = 10.0; + } + } + + defaultValue = this.GetDifficultyFloat("damage", difficulty, defaultValue); + defaultValue = this.GetDifficultyFloat("attack_damage", difficulty, defaultValue); + + return defaultValue; + } + + public float GetDamagePercent(int difficulty) + { + return this.GetDifficultyFloat("damage_percent", difficulty); + } + + public float GetDamageFallOff(int difficulty) + { + float def = 1.0; + ProfileObject obj = this.GetSection("fall_off"); + if (obj != null) + { + return obj.GetDifficultyFloat("percent", difficulty, def); + } + return def; + } + + public float GetDamageFallOffRange(int difficulty) + { + float def = 1024.0; + ProfileObject obj = this.GetSection("fall_off"); + if (obj != null) + { + return obj.GetDifficultyFloat("range", difficulty, def); + } + return def; + } + + public float GetDamageRampUp(int difficulty) + { + float def = 1.0; + ProfileObject obj = this.GetSection("ramp_up"); + if (obj != null) + { + return obj.GetDifficultyFloat("percent", difficulty, def); + } + return def; + } + + public float GetDamageRampUpRange(int difficulty) + { + float def = 512.0; + ProfileObject obj = this.GetSection("ramp_up"); + if (obj != null) + { + return obj.GetDifficultyFloat("range", difficulty, def); + } + return def; + } + + public int GetDamageType(int difficulty) + { + int defaultValue; + + switch (this.Type) + { + case SF2BossAttackType_Melee: + { + defaultValue = DMG_CLUB; + } + + case SF2BossAttackType_Ranged: + { + defaultValue = DMG_BULLET; + } + + case SF2BossAttackType_Projectile, SF2BossAttackType_ExplosiveDance: + { + defaultValue = DMG_BLAST; + } + + case SF2BossAttackType_LaserBeam: + { + defaultValue = DMG_SHOCK | DMG_ALWAYSGIB; + } + + case SF2BossAttackType_Tongue: + { + defaultValue = DMG_BULLET | DMG_PREVENT_PHYSICS_FORCE; + } + } + defaultValue = this.GetDifficultyInt("damagetype", difficulty, defaultValue); + defaultValue = this.GetDifficultyInt("attack_damagetype", difficulty, defaultValue); + + return defaultValue; + } + + public float GetDelay(int difficulty) + { + float def = 0.0; + def = this.GetDifficultyFloat("delay", difficulty, def); + def = this.GetDifficultyFloat("attack_delay", difficulty, def); + return def; + } + + public float GetDuration(int difficulty) + { + float value = this.GetDifficultyFloat("duration", difficulty); + float endAfter = this.GetDifficultyFloat("endafter", difficulty); + value = this.GetDifficultyFloat("attack_duration", difficulty, value); + endAfter = this.GetDifficultyFloat("attack_endafter", difficulty, endAfter); + + if (value <= 0.0) + { + value = this.GetDelay(difficulty) + endAfter; + } + + return value; + } + + public float GetBeginRange(int difficulty) + { + float def = 80.0; + def = this.GetDifficultyFloat("begin_range", difficulty, def); + def = this.GetDifficultyFloat("attack_begin_range", difficulty, def); + return def; + } + + public float GetBeginFOV(int difficulty) + { + float def = this.GetFOV(difficulty); + def = this.GetDifficultyFloat("begin_fov", difficulty, def); + def = this.GetDifficultyFloat("attack_begin_fov", difficulty, def); + return def; + } + + public float GetFOV(int difficulty) + { + float def = 45.0; + def = this.GetDifficultyFloat("spread", difficulty, def); + def = this.GetDifficultyFloat("attack_spread", difficulty, def); + return def; + } + + public float GetRange(int difficulty) + { + float def = 180.0; + def = this.GetDifficultyFloat("range", difficulty, def); + def = this.GetDifficultyFloat("attack_range", difficulty, def); + return def; + } + + public float GetCooldown(int difficulty) + { + float def = 0.0; + def = this.GetDifficultyFloat("cooldown", difficulty, def); + def = this.GetDifficultyFloat("attack_cooldown", difficulty, def); + return def; + } + + public bool GetDisappearAfterAttack(int difficulty) + { + bool def = false; + def = this.GetDifficultyBool("disappear", difficulty, def); + def = this.GetDifficultyBool("attack_disappear_upon_damaging", difficulty, def); + return def; + } + + public bool GetDisappearAfterHit(int difficulty) + { + return this.GetDifficultyBool("disappear_upon_damaging", difficulty); + } + + public void GetViewPunchAngles(int difficulty, float viewPunch[3]) + { + this.GetDifficultyVector("punchvel", difficulty, viewPunch, viewPunch); + this.GetDifficultyVector("attack_punchvel", difficulty, viewPunch, viewPunch); + } + + public float GetDamageForce(int difficulty) + { + float def = 0.0; + def = this.GetDifficultyFloat("damageforce", difficulty); + def = this.GetDifficultyFloat("attack_damageforce", difficulty); + return def; + } + + public int GetEventNumber(int difficulty) + { + return this.GetDifficultyInt("event", difficulty, -1); + } + + public bool GetStartThroughWalls(int difficulty) + { + return this.GetDifficultyBool("start_through_walls", difficulty); + } + + public bool GetHitThroughWalls(int difficulty) + { + return this.GetDifficultyBool("hit_through_walls", difficulty); + } + + public int GetRepeatState(int difficulty) + { + int def = 0; + def = this.GetDifficultyInt("repeat", difficulty, def); + def = this.GetDifficultyInt("attack_repeat", difficulty, def); + return def; + } + + public float GetRepeatTimer(int difficulty, int index) + { + float def = -1.0; + char key[64]; + FormatEx(key, sizeof(key), "repeat_%i_delay", index); + def = this.GetDifficultyFloat(key, difficulty, def); + FormatEx(key, sizeof(key), "attack_repeat_%i_delay", index); + def = this.GetDifficultyFloat(key, difficulty, def); + return def; + } + + public float GetRunSpeed(int difficulty) + { + float def = 0.0; + def = this.GetDifficultyFloat("run_speed", difficulty, def); + def = this.GetDifficultyFloat("attack_run_speed", difficulty, def); + return def; + } + + public float GetAcceleration(int difficulty) + { + return this.GetDifficultyFloat("run_acceleration", difficulty, 4000.0); + } + + public float GetRunDuration(int difficulty) + { + float def = 0.0; + def = this.GetDifficultyFloat("run_duration", difficulty, def); + def = this.GetDifficultyFloat("attack_run_duration", difficulty, def); + return def; + } + + public float GetRunDelay(int difficulty) + { + float def = 0.0; + def = this.GetDifficultyFloat("run_delay", difficulty, def); + def = this.GetDifficultyFloat("attack_run_delay", difficulty, def); + return def; + } + + public bool GetGroundSpeedOverride(int difficulty) + { + return this.GetDifficultyBool("run_ground_speed", difficulty); + } + + public float GetMinCancelDistance(int difficulty) + { + return this.GetDifficultyFloat("cancel_distance_min", difficulty, -1.0); + } + + public float GetMaxCancelDistance(int difficulty) + { + return this.GetDifficultyFloat("cancel_distance_max", difficulty, -1.0); + } + + public bool GetCancelLOS(int difficulty) + { + return this.GetDifficultyBool("cancel_los", difficulty, false); + } + + property bool DeathCamLowHealth + { + public get() + { + bool def = false; + def = this.GetBool("deathcam_on_low_health", def); + def = this.GetBool("attack_deathcam_on_low_health", def); + return def; + } + } + + public bool IsImmuneToDamage(int difficulty) + { + return this.GetDifficultyBool("invulnerable", difficulty, false); + } + + public bool ShouldNotInterruptChaseInitial(int difficulty) + { + return this.GetDifficultyBool("dont_interrupt_chaseinitial", difficulty, false); + } + + public ProfileSound GetBeginSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj == null) + { + return null; + } + + ProfileSound sound = view_as(obj.GetSection("begin")); + if (sound == null) + { + return null; + } + + return sound; + } + + public ProfileSound GetStartSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj == null) + { + return null; + } + + ProfileSound sound = view_as(obj.GetSection("attack")); + if (sound == null) + { + return null; + } + + return sound; + } + + public ProfileSound GetHitSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj == null) + { + return null; + } + + ProfileSound sound = view_as(obj.GetSection("hit")); + if (sound == null) + { + return null; + } + + return sound; + } + + public ProfileSound GetMissSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj == null) + { + return null; + } + + ProfileSound sound = view_as(obj.GetSection("miss")); + if (sound == null) + { + return null; + } + + return sound; + } + + public ProfileSound GetLoopSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj == null) + { + return null; + } + + ProfileSound sound = view_as(obj.GetSection("loop")); + if (sound == null) + { + return null; + } + + return sound; + } + + public ProfileSound GetEndSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj == null) + { + return null; + } + + ProfileSound sound = view_as(obj.GetSection("end")); + if (sound == null) + { + return null; + } + + return sound; + } + + public ProfileMasterAnimations GetAnimations() + { + return view_as(this.GetSection("animations")); + } + + public void GetWeaponString(char[] buffer, int bufferSize) + { + this.GetString("weapontype", buffer, bufferSize, buffer); + this.GetString("attack_weapontype", buffer, bufferSize, buffer); + } + + property int WeaponInt + { + public get() + { + int def = 0; + def = this.GetInt("weapontypeint", def); + def = this.GetInt("attack_weapontypeint", def); + return def; + } + } + + public ProfileEffectMaster GetStartEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("start") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileInputsList GetStartInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) + { + return view_as(obj.GetSection("start")); + } + return null; + } + + public ProfileEffectMaster GetHitEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("hit") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileInputsList GetHitInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) + { + return view_as(obj.GetSection("hit")); + } + return null; + } + + public ProfileEffectMaster GetMissEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("miss") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileInputsList GetMissInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) + { + return view_as(obj.GetSection("miss")); + } + return null; + } + + public ProfileEffectMaster GetOnKillEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("on_kill") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileInputsList GetOnKillInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) + { + return view_as(obj.GetSection("on_kill")); + } + return null; + } + + public KeyMap_Array GetDamageEffects() + { + return this.GetArray("apply_conditions"); + } + + public void ApplyDamageEffects(CBaseCombatCharacter player, int difficulty, SF2_ChaserEntity chaser = view_as(-1)) + { + if (this.GetDamageEffects() == null) + { + return; + } + + if (!player.IsValid()) + { + return; + } + + for (int i = 0; i < this.GetDamageEffects().Length; i++) + { + KeyMap obj = this.GetDamageEffects().GetSection(i); + if (obj == null) + { + continue; + } + BossProfileDamageEffect effect = view_as(obj); + effect.Apply(player, difficulty, chaser); + } + } + + public BossProfileShockwave GetShockwave() + { + return view_as(this.GetSection("shockwave")); + } + + public void Precache() + { + this.ConvertSectionsSectionToArray("apply_conditions"); + + if (this.GetBeginSounds() != null) + { + this.GetBeginSounds().Precache(); + } + + if (this.GetStartSounds() != null) + { + this.GetStartSounds().Precache(); + } + + if (this.GetHitSounds() != null) + { + this.GetHitSounds().Precache(); + } + + if (this.GetMissSounds() != null) + { + this.GetMissSounds().Precache(); + } + + if (this.GetEndSounds() != null) + { + this.GetEndSounds().Precache(); + } + + if (this.GetLoopSounds() != null) + { + this.GetLoopSounds().Precache(); + } + + if (this.GetStartEffects() != null) + { + this.GetStartEffects().Precache(); + } + + if (this.GetHitEffects() != null) + { + this.GetHitEffects().Precache(); + } + + if (this.GetMissEffects() != null) + { + this.GetMissEffects().Precache(); + } + + if (this.GetOnKillEffects() != null) + { + this.GetOnKillEffects().Precache(); + } + + if (this.GetShockwave() != null) + { + this.GetShockwave().Precache(); + } + + if (this.GetDamageEffects() != null) + { + for (int i = 0; i < this.GetDamageEffects().Length; i++) + { + BossProfileDamageEffect effect = view_as(this.GetDamageEffects().GetSection(i)); + if (effect != null) + { + effect.Precache(); + } + } + } + + switch (this.Type) + { + case SF2BossAttackType_Ranged: + { + view_as(this).Precache(); + } + + case SF2BossAttackType_Projectile: + { + view_as(this).Precache(); + } + + case SF2BossAttackType_Tongue: + { + view_as(this).Precache(); + } + + case SF2BossAttackType_Combo: + { + view_as(this).Precache(); + } + } + } +} + +methodmap BossProfileDamageEffect < ProfileObject +{ + property int Type + { + public get() + { + char section[64]; + this.GetSectionName(section, sizeof(section)); + if (strcmp(section, "ignite", false) == 0) + { + return SF2DamageType_Ignite; + } + else if (strcmp(section, "gas", false) == 0) + { + return SF2DamageType_Gas; + } + else if (strcmp(section, "bleed", false) == 0) + { + return SF2DamageType_Bleed; + } + else if (strcmp(section, "mark", false) == 0) + { + return SF2DamageType_Mark; + } + else if (strcmp(section, "jarate", false) == 0) + { + return SF2DamageType_Jarate; + } + else if (strcmp(section, "milk", false) == 0) + { + return SF2DamageType_Milk; + } + else if (strcmp(section, "stun", false) == 0) + { + return SF2DamageType_Stun; + } + else if (strcmp(section, "smite", false) == 0) + { + return SF2DamageType_Smite; + } + else if (strcmp(section, "random", false) == 0) + { + return SF2DamageType_Random; + } + + return SF2DamageType_Invalid; + } + } + + public bool IsEnabled(int difficulty) + { + if (this.Type == SF2DamageType_Invalid) + { + return false; + } + return this.GetDifficultyBool("enabled", difficulty, true); + } + + public float GetDuration(int difficulty) + { + float def = 8.0; + if (this.Type == SF2DamageType_Invalid) + { + return def; + } + return this.GetDifficultyFloat("duration", difficulty, def); + } + + public ProfileSound GetSounds() + { + return view_as(this.GetSection("sounds")); + } + + public ProfileObject GetParticles() + { + return this.GetSection("particles"); + } + + public void Apply(CBaseCombatCharacter player, int difficulty, CBaseAnimating attacker = view_as(-1), SF2NPC_Chaser parentController = SF2_INVALID_NPC_CHASER) + { + if (!this.IsEnabled(difficulty)) + { + return; + } + this.GetSounds().EmitSound(_, attacker.index); + if (this.GetParticles() != null) + { + char name[64]; + this.GetParticles().GetSectionNameFromIndex(GetRandomInt(0, this.GetParticles().SectionLength - 1), name, sizeof(name)); + ProfileObject obj = this.GetParticles().GetSection(name); + if (obj != null) + { + view_as(obj).Apply(player, attacker); + } + } + int type = this.Type; + if (type == SF2DamageType_Random) + { + type = view_as(this).GetRandomType(); + } + + switch (type) + { + case SF2DamageType_Ignite: + { + TF2_IgnitePlayer(player.index, player.index, this.GetDuration(difficulty)); + } + case SF2DamageType_Gas: + { + TF2_AddCondition(player.index, TFCond_Gas, this.GetDuration(difficulty), player.index); + } + case SF2DamageType_Mark: + { + TF2_AddCondition(player.index, view_as(this).GetSilentMark(difficulty) ? TFCond_MarkedForDeathSilent : TFCond_MarkedForDeath, this.GetDuration(difficulty), player.index); + } + case SF2DamageType_Bleed: + { + TF2_MakeBleed(player.index, player.index, this.GetDuration(difficulty)); + } + case SF2DamageType_Milk: + { + TF2_AddCondition(player.index, TFCond_Milked, this.GetDuration(difficulty), player.index); + } + case SF2DamageType_Jarate: + { + TF2_AddCondition(player.index, TFCond_Jarated, this.GetDuration(difficulty), player.index); + } + case SF2DamageType_Stun: + { + TF2_StunPlayer(player.index, this.GetDuration(difficulty), view_as(this).GetSlowdown(difficulty), view_as(this).GetFlags(difficulty)); + } + case SF2DamageType_Smite: + { + BossProfileDamageEffect_Smite smiteData = view_as(this); + float targetPos[3]; + player.GetAbsOrigin(targetPos); + targetPos[2] -= 26.0; + + int randomX = GetRandomInt(-500, 500); + int randomY = GetRandomInt(-500, 500); + + float startPos[3]; + startPos[0] = targetPos[0] + randomX; + startPos[1] = targetPos[1] + randomY; + startPos[2] = targetPos[2] + 800.0; + + float origin[3]; + int color[4]; + smiteData.GetSmiteColor(color); + TE_SetupBeamPoints(startPos, targetPos, smiteData.SmiteSprite, 0, 0, 0, 0.2, 20.0, 10.0, 0, 1.0, color, 3); + TE_SendToAll(); + + TE_SetupSparks(targetPos, origin, 5000, 1000); + TE_SendToAll(); + + TE_SetupEnergySplash(targetPos, origin, false); + TE_SendToAll(); + + TE_SetupSmoke(targetPos, smiteData.SmiteSmokeSprite, 5.0, 10); + TE_SendToAll(); + + char sound[PLATFORM_MAX_PATH]; + smiteData.GetHitSound(sound, sizeof(sound)); + EmitAmbientSound(sound, startPos, player.index, SNDLEVEL_SCREAMING); + SDKHooks_TakeDamage(player.index, attacker.index, attacker.index, smiteData.GetDamage(difficulty), smiteData.GetDamageType(difficulty), .bypassHooks = false); + + SF2NPC_Chaser controller = parentController; + if (!controller.IsValid() && SF2_ChaserEntity(attacker.index).IsValid()) + { + controller = SF2_ChaserEntity(attacker.index).Controller; + if (smiteData.SmiteMessage && controller.IsValid() && GetClientTeam(player.index) == 2) + { + char playerName[32], bossName[SF2_MAX_NAME_LENGTH]; + GetClientName(player.index, playerName, sizeof(playerName)); + controller.GetProfileData().GetName(controller.Difficulty, bossName, sizeof(bossName)); + CPrintToChatAll("{royalblue}%t {default}%t", "SF2 Prefix", "SF2 Smote target", bossName, playerName); + } + } + } + } + } + + public void Precache() + { + if (this.GetSounds() != null) + { + this.GetSounds().Precache(); + } + } +} + +methodmap BossProfileDamageEffect_Mark < BossProfileDamageEffect +{ + public bool GetSilentMark(int difficulty) + { + bool def = false; + if (this.Type == SF2DamageType_Invalid) + { + return def; + } + return this.GetDifficultyBool("silent", difficulty, def); + } +} + +methodmap BossProfileDamageEffect_Stun < BossProfileDamageEffect +{ + public float GetSlowdown(int difficulty) + { + float def = 0.5; + if (this.Type == SF2DamageType_Invalid) + { + return def; + } + return this.GetDifficultyFloat("slow_multiplier", difficulty, def); + } + + public int GetFlags(int difficulty) + { + char flag[256]; + this.GetDifficultyString("flags", difficulty, flag, sizeof(flag), "slow"); + char flags[32][64]; + int nums = ExplodeString(flag, " ", flags, sizeof(flags), sizeof(flags[])); + int value = 0; + + for (int i = 0; i < nums; i++) + { + if (strcmp(flags[i], "slow", false) == 0) + { + value |= (1 << 0); + } + else if (strcmp(flags[i], "stuck", false) == 0) + { + value |= (1 << 1); + } + else if (strcmp(flags[i], "cheer_fx", false) == 0) + { + value |= (1 << 3); + } + else if (strcmp(flags[i], "no_fx", false) == 0) + { + value |= (1 << 5); + } + else if (strcmp(flags[i], "thirdperson", false) == 0) + { + value |= (1 << 6); + } + else if (strcmp(flags[i], "ghost_fx", false) == 0) + { + value |= (1 << 7); + } + else if (strcmp(flags[i], "loser", false) == 0) + { + value |= (1 << 0) | (1 << 5) | (1 << 6); + } + else if (strcmp(flags[i], "boo", false) == 0) + { + value |= (1 << 7) | (1 << 6); + } + } + + return value; + } +} + +methodmap BossProfileDamageEffect_Smite < BossProfileDamageEffect +{ + property int SmiteSprite + { + public get() + { + int val = 0; + this.GetValue("__smite_sprite", val); + return val; + } + + public set(int value) + { + this.SetValue("__smite_sprite", value); + } + } + + property int SmiteSmokeSprite + { + public get() + { + int val = 0; + this.GetValue("__smite_smoke_sprite", val); + return val; + } + + public set(int value) + { + this.SetValue("__smite_smoke_sprite", value); + } + } + + public float GetDamage(int difficulty) + { + float def = 9001.0; + if (this.Type == SF2DamageType_Invalid) + { + return def; + } + return this.GetDifficultyFloat("damage", difficulty, def); + } + + public int GetDamageType(int difficulty) + { + int def = (1 << 20); + if (this.Type == SF2DamageType_Invalid) + { + return def; + } + return this.GetDifficultyInt("damagetype", difficulty, def); + } + + public void GetSmiteColor(int buffer[4]) + { + this.GetColor("color", buffer, { 255, 255, 255, 255 }); + } + + property bool SmiteMessage + { + public get() + { + bool def = false; + if (this.Type == SF2DamageType_Invalid) + { + return def; + } + return this.GetBool("message", def); + } + } + + public void GetHitSound(char[] buffer, int bufferSize) + { + if (this.Type == SF2DamageType_Invalid) + { + return; + } + this.GetString("hit_sound", buffer, bufferSize, SOUND_THUNDER); + } +} + +methodmap BossProfileDamageEffect_Random < BossProfileDamageEffect +{ + public int GetRandomType() + { + if (this.Type == SF2DamageType_Invalid) + { + return SF2DamageType_Invalid; + } + char type[256]; + this.GetString("random_types", type, sizeof(type)); + ArrayList random = new ArrayList(); + char types[32][32]; + int nums = ExplodeString(type, " ", types, sizeof(types), sizeof(types[])); + for (int i = 0; i < nums; i++) + { + if (strcmp(types[i], "ignite", false) == 0) + { + random.Push(SF2DamageType_Ignite); + } + else if (strcmp(types[i], "gas", false) == 0) + { + random.Push(SF2DamageType_Gas); + } + else if (strcmp(types[i], "bleed", false) == 0) + { + random.Push(SF2DamageType_Bleed); + } + else if (strcmp(types[i], "mark", false) == 0) + { + random.Push(SF2DamageType_Mark); + } + else if (strcmp(types[i], "jarate", false) == 0) + { + random.Push(SF2DamageType_Jarate); + } + else if (strcmp(types[i], "milk", false) == 0) + { + random.Push(SF2DamageType_Milk); + } + else if (strcmp(types[i], "stun", false) == 0) + { + random.Push(SF2DamageType_Stun); + } + else if (strcmp(types[i], "smite", false) == 0) + { + random.Push(SF2DamageType_Smite); + } + } + int val = random.Get(GetRandomInt(0, random.Length - 1)); + delete random; + return val; + } +} + +methodmap BossProfileShockwave < ProfileObject +{ + public bool IsEnabled(int difficulty) + { + if (this == null) + { + return false; + } + + return this.GetDifficultyBool("enabled", difficulty, true); + } + + public float GetHeight(int difficulty) + { + return this.GetDifficultyFloat("height", difficulty, 80.0); + } + + public float GetRadius(int difficulty) + { + return this.GetDifficultyFloat("radius", difficulty, 200.0); + } + + public float GetForce(int difficulty) + { + return this.GetDifficultyFloat("force", difficulty, 600.0); + } + + public float GetBatteryDrainPercent(int difficulty) + { + return this.GetDifficultyFloat("battery_drain", difficulty, 0.0); + } + + public float GetStaminaDrainPercent(int difficulty) + { + return this.GetDifficultyFloat("stamina_drain", difficulty, 0.0); + } + + public ProfileEffectMaster GetEffects() + { + return view_as(this.GetSection("effects")); + } + + public KeyMap_Array GetDamageEffects() + { + return this.GetArray("apply_conditions"); + } + + public void ApplyDamageEffects(CBaseCombatCharacter player, int difficulty, SF2_ChaserEntity chaser = view_as(-1)) + { + if (this.GetDamageEffects() == null) + { + return; + } + + if (!player.IsValid()) + { + return; + } + + for (int i = 0; i < this.GetDamageEffects().Length; i++) + { + KeyMap obj = this.GetDamageEffects().GetSection(i); + if (obj == null) + { + continue; + } + BossProfileDamageEffect effect = view_as(obj); + effect.Apply(player, difficulty, chaser); + } + } + + public void Precache() + { + this.ConvertSectionsSectionToArray("apply_conditions"); + if (this.GetDamageEffects() != null) + { + for (int i = 0; i < this.GetDamageEffects().Length; i++) + { + BossProfileDamageEffect obj = view_as(this.GetDamageEffects().GetSection(i)); + if (obj == null) + { + continue; + } + obj.Precache(); + } + } + + if (this.GetEffects() != null) + { + this.GetEffects().Precache(); + } + } +} #include "attacks/melee.sp" #include "attacks/bullet.sp" @@ -8,13 +1277,14 @@ #include "attacks/explosive_dance.sp" #include "attacks/custom.sp" #include "attacks/tongue.sp" +#include "attacks/combo.sp" #include "attacks/forward_based.sp" static NextBotActionFactory g_Factory; methodmap SF2_ChaserAttackAction < NextBotAction { - public SF2_ChaserAttackAction(const char[] attackName, int attackIndex, float endTime) + public SF2_ChaserAttackAction(ChaserBossProfile profileData, const char[] attackName, int attackIndex, float endTime) { if (g_Factory == null) { @@ -27,6 +1297,7 @@ methodmap SF2_ChaserAttackAction < NextBotAction g_Factory.SetCallback(NextBotActionCallbackType_OnEnd, OnEnd); g_Factory.SetEventCallback(EventResponderType_OnOtherKilled, OnOtherKilled); g_Factory.BeginDataMapDesc() + .DefineIntField("m_ProfileData") .DefineIntField("m_OldState") .DefineIntField("m_OldMovementType") .DefineStringField("m_AttackName") @@ -41,7 +1312,9 @@ methodmap SF2_ChaserAttackAction < NextBotAction SF2_ChaserAttackAction action = view_as(g_Factory.Create()); action.SetAttackName(attackName); + action.ProfileData = profileData.GetAttack(attackName); action.EndTime = endTime; + action.AttackIndex = attackIndex; return action; } @@ -55,9 +1328,45 @@ methodmap SF2_ChaserAttackAction < NextBotAction SF2_ChaserAttackAction_Laser.Initialize(); SF2_ChaserAttackAction_Custom.Initialize(); SF2_ChaserAttackAction_Tongue.Initialize(); + SF2_ChaserAttackAction_Combo.Initialize(); SF2_ChaserAttackAction_ForwardBased.Initialize(); } + public static void SetupAPI() + { + CreateNative("SF2_ChaserBossProfileBaseAttack.Type.get", Native_GetType); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetDamageDelay", Native_GetDamageDelay); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetRange", Native_GetRange); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetDamage", Native_GetDamage); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetDamageType", Native_GetDamageType); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetDamageForce", Native_GetDamageForce); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetViewPunchAngles", Native_GetViewPunchAngles); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetDuration", Native_GetDuration); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetFOV", Native_GetFOV); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetBeginRange", Native_GetBeginRange); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetBeginFOV", Native_GetBeginFOV); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetCooldown", Native_GetCooldown); + CreateNative("SF2_ChaserBossProfileBaseAttack.UseOnDifficulty.get", Native_GetUseOnDifficulty); + CreateNative("SF2_ChaserBossProfileBaseAttack.BlockOnDifficulty.get", Native_GetBlockOnDifficulty); + CreateNative("SF2_ChaserBossProfileBaseAttack.CanUseOnHealth", Native_GetCanUseOnHealth); + CreateNative("SF2_ChaserBossProfileBaseAttack.CanBlockOnHealth", Native_GetCanBlockOnHealth); + CreateNative("SF2_ChaserBossProfileBaseAttack.GetEventNumber", Native_GetEventNumber); + ChaserBossProfileCustomAttack.SetupAPI(); + } + + property ChaserBossProfileBaseAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileBaseAttack value) + { + this.SetData("m_ProfileData", value); + } + } + property int OldState { public get() @@ -176,9 +1485,6 @@ methodmap SF2_ChaserAttackAction < NextBotAction static NextBotAction InitialContainedAction(SF2_ChaserAttackAction action, SF2_ChaserEntity actor) { - actor.MyNextBotPointer().GetLocomotionInterface().Stop(); - actor.Controller.Path.Invalidate(); - NextBotAction attackAction = NULL_ACTION; Action result = Plugin_Continue; @@ -213,42 +1519,51 @@ static int OnStart(SF2_ChaserAttackAction action, SF2_ChaserEntity actor, NextBo actor.SetAttackName(action.GetAttackName()); actor.AttackIndex = action.AttackIndex; actor.IsAttacking = true; + actor.EndCloak(); actor.RemoveAllGestures(); CBaseNPC_RemoveAllLayers(actor.index); int difficulty = controller.Difficulty; float gameTime = GetGameTime(); - SF2ChaserBossProfileData data; - SF2ChaserBossProfileAttackData attackData; - data = controller.GetProfileData(); - data.GetAttack(actor.GetAttackName(), attackData); - actor.AttackRunDelay = gameTime + attackData.RunDelay[difficulty]; - actor.AttackRunDuration = gameTime + attackData.RunDuration[difficulty]; - actor.MyNextBotPointer().GetLocomotionInterface().Stop(); - controller.Path.Invalidate(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileChaseData chaseData = data.GetChaseBehavior(); + ChaserBossProfileBaseAttack attackData = action.ProfileData; + actor.AttackRunDelay = gameTime + attackData.GetRunDelay(difficulty); + actor.AttackRunDuration = gameTime + attackData.GetRunDuration(difficulty); if (actor.State == STATE_CHASE) { - actor.CurrentChaseDuration += data.ChaseDurationAddOnAttack[difficulty]; - if (actor.CurrentChaseDuration > data.ChaseDuration[difficulty]) + actor.CurrentChaseDuration += chaseData.GetDurationAddOnAttack(difficulty); + if (actor.CurrentChaseDuration > chaseData.GetMaxChaseDuration(difficulty)) { - actor.CurrentChaseDuration = data.ChaseDuration[difficulty]; + actor.CurrentChaseDuration = chaseData.GetMaxChaseDuration(difficulty); } } action.OldState = actor.State; - action.OldMovementType = actor.MovementType; actor.State = STATE_ATTACK; + action.OldMovementType = actor.MovementType; actor.InvokeOnStartAttack(action.GetAttackName()); - if (attackData.StartEffects != null) + if (attackData.GetStartEffects() != null) + { + SlenderSpawnEffects(attackData.GetStartEffects(), controller.Index, false); + } + + if (attackData.GetStartInputs() != null) { - SlenderSpawnEffects(attackData.StartEffects, controller.Index, false); + attackData.GetStartInputs().AcceptInputs(actor); } - if (attackData.RunSpeed[difficulty] > 0.0) + if (attackData.GetRunSpeed(difficulty) <= 0.0) + { + actor.MyNextBotPointer().GetLocomotionInterface().Stop(); + controller.Path.Invalidate(); + actor.IsAttemptingToMove = false; + } + else { actor.MovementType = SF2NPCMoveType_Attack; } @@ -258,26 +1573,39 @@ static int OnStart(SF2_ChaserAttackAction action, SF2_ChaserEntity actor, NextBo return action.Continue(); } - actor.ResetProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Attack], _, action.GetAttackName()); - actor.AddGesture(g_SlenderAnimationsList[SF2BossAnimation_Attack], _, action.GetAttackName()); - - SF2BossProfileSoundInfo info; - if (actor.SearchSoundsWithSectionName(data.AttackBeginSounds, action.GetAttackName(), info)) + ProfileSound info; + if (attackData.GetStartSounds() == null && attackData.GetBeginSounds() == null) { - action.PlayedBeginVoice = actor.PerformVoice(SF2BossSound_AttackBegin, action.GetAttackName()); + actor.SearchSoundsWithSectionName(data.GetAttackBeginSounds(), action.GetAttackName(), info, "attack_begin"); + if (info != null) + { + action.PlayedBeginVoice = actor.PerformVoice(SF2BossSound_AttackBegin, action.GetAttackName()); + } + else + { + actor.PerformVoice(SF2BossSound_Attack, action.GetAttackName()); + action.PlayedBeginVoice = true; + } } else { - actor.PerformVoice(SF2BossSound_Attack, action.GetAttackName()); - action.PlayedBeginVoice = true; + if (attackData.GetBeginSounds() != null) + { + action.PlayedBeginVoice = actor.PerformVoiceCooldown(attackData.GetBeginSounds(), attackData.GetBeginSounds().Paths); + } + else + { + actor.PerformVoiceCooldown(attackData.GetStartSounds(), attackData.GetStartSounds().Paths); + action.PlayedBeginVoice = true; + } } - float duration = 0.0; - NextBotAction newAction = actor.IsAttackTransitionPossible(action.GetAttackName(), _, duration); + NextBotAction newAction = actor.IsAttackTransitionPossible(action.GetAttackName()); action.ShouldReplayAnimation = newAction != NULL_ACTION; - if (newAction != NULL_ACTION) + if (newAction == NULL_ACTION) { - action.EndTime += duration; + actor.ResetProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Attack], .preDefinedName = attackData.GetAnimations() == null ? action.GetAttackName() : "", .animations = attackData.GetAnimations()); + actor.AddGesture(g_SlenderAnimationsList[SF2BossAnimation_Attack], .definedName = attackData.GetAnimations() == null ? action.GetAttackName() : "", .animations = attackData.GetAnimations()); } return newAction != NULL_ACTION ? action.SuspendFor(newAction) : action.Continue(); } @@ -296,11 +1624,9 @@ static int Update(SF2_ChaserAttackAction action, SF2_ChaserEntity actor, float i return action.Done(); } - SF2ChaserBossProfileData data; - SF2ChaserBossProfileAttackData attackData; + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileBaseAttack attackData = action.ProfileData; int interrputConditions = actor.InterruptConditions; - data = controller.GetProfileData(); - data.GetAttack(actor.GetAttackName(), attackData); bool end = false; INextBot bot = actor.MyNextBotPointer(); PathFollower path = controller.Path; @@ -315,34 +1641,33 @@ static int Update(SF2_ChaserAttackAction action, SF2_ChaserEntity actor, float i actor.GetAbsOrigin(myPos); target.GetAbsOrigin(targetPos); float distance = GetVectorSquareMagnitude(myPos, targetPos); - if (attackData.CancelDistance[difficulty] >= 0.0 && distance > Pow(attackData.CancelDistance[difficulty], 2.0)) + if (attackData.GetMaxCancelDistance(difficulty) > 0.0 && distance > Pow(attackData.GetMaxCancelDistance(difficulty), 2.0)) { end = true; } - if (attackData.MinCancelDistance[difficulty] >= 0.0 && distance < Pow(attackData.MinCancelDistance[difficulty], 2.0)) + if (attackData.GetMinCancelDistance(difficulty) > 0.0 && distance < Pow(attackData.GetMinCancelDistance(difficulty), 2.0)) { end = true; } - if (attackData.CancelLos[difficulty] && (actor.InterruptConditions & COND_ENEMYVISIBLE) == 0) + if (attackData.GetCancelLOS(difficulty) && (actor.InterruptConditions & COND_ENEMYVISIBLE_NOGLASS) == 0) { end = true; } if (actor.MovementType == SF2NPCMoveType_Attack) { + float pos[3]; + target.GetAbsOrigin(pos); if (actor.Teleporters.Length > 0) { - CBaseEntity(actor.Teleporters.Get(0)).GetAbsOrigin(targetPos); + CBaseEntity(EntRefToEntIndex(actor.Teleporters.Get(0))).GetAbsOrigin(pos); } - if (!bot.IsRangeLessThanEx(targetPos, 8.0)) + if ((interrputConditions & COND_NEWENEMY) != 0 || path.GetAge() > 0.3 || (path.IsValid() && (path.GetLength() - path.GetCursorPosition()) < 256.0)) { - if ((interrputConditions & COND_NEWENEMY) != 0 || path.GetAge() > 0.3 || (path.IsValid() && (path.GetLength() - path.GetCursorPosition()) < 256.0)) - { - path.ComputeToPos(bot, targetPos); - } + path.ComputeToPos(bot, pos); } } } @@ -367,12 +1692,16 @@ static int Update(SF2_ChaserAttackAction action, SF2_ChaserEntity actor, float i return action.Continue(); } - SF2BossProfileSoundInfo info; + ProfileSound info = attackData.GetLoopSounds(); char value[PLATFORM_MAX_PATH]; value = action.GetLoopSound(); if (value[0] == '\0') { - if (actor.SearchSoundsWithSectionName(data.AttackLoopSounds, action.GetAttackName(), info)) + if (info == null) + { + actor.SearchSoundsWithSectionName(data.GetAttackLoopSounds(), action.GetAttackName(), info, "attack_loop"); + } + if (info != null) { char sound[PLATFORM_MAX_PATH]; info.EmitSound(_, actor.index, _, _, _, sound); @@ -384,12 +1713,21 @@ static int Update(SF2_ChaserAttackAction action, SF2_ChaserEntity actor, float i } } - if (action.ActiveChild == NULL_ACTION || GetGameTime() > action.EndTime || end) + action.EndTime -= interval; + + if (action.ActiveChild == NULL_ACTION || (action.EndTime > -1.0 && action.EndTime <= 0.0) || end) { NextBotAction newAction = actor.IsAttackTransitionPossible(action.GetAttackName(), true); if (!action.DidEndAnimation && newAction != NULL_ACTION) { - actor.PerformVoice(SF2BossSound_AttackEnd, action.GetAttackName()); + if (attackData.GetEndSounds() == null) + { + actor.PerformVoice(SF2BossSound_AttackEnd, action.GetAttackName()); + } + else + { + actor.PerformVoiceCooldown(attackData.GetEndSounds(), attackData.GetEndSounds().Paths); + } action.DidEndAnimation = true; if (newAction != NULL_ACTION) { @@ -423,15 +1761,24 @@ static int OnSuspend(SF2_ChaserAttackAction action, SF2_ChaserEntity actor, Next static int OnResume(SF2_ChaserAttackAction action, SF2_ChaserEntity actor, NextBotAction priorAction) { + ChaserBossProfileBaseAttack attackData = action.ProfileData; + ProfileMasterAnimations animations = attackData.GetAnimations(); if (!action.PlayedBeginVoice) { - actor.PerformVoice(SF2BossSound_Attack, action.GetAttackName()); + if (attackData.GetStartSounds() == null) + { + actor.PerformVoice(SF2BossSound_Attack, action.GetAttackName()); + } + else + { + actor.PerformVoiceCooldown(attackData.GetStartSounds(), attackData.GetStartSounds().Paths); + } action.PlayedBeginVoice = true; } if (action.ShouldReplayAnimation) { - actor.ResetProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Attack], _, action.GetAttackName()); - actor.AddGesture(g_SlenderAnimationsList[SF2BossAnimation_Attack], _, action.GetAttackName()); + actor.ResetProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Attack], .preDefinedName = animations == null ? action.GetAttackName() : "", .animations = animations); + actor.AddGesture(g_SlenderAnimationsList[SF2BossAnimation_Attack], .definedName = animations == null ? action.GetAttackName() : "", .animations = animations); action.ShouldReplayAnimation = false; } @@ -444,6 +1791,8 @@ static int OnResume(SF2_ChaserAttackAction action, SF2_ChaserEntity actor, NextB static void OnEnd(SF2_ChaserAttackAction action, SF2_ChaserEntity actor) { + CBaseNPC_RemoveAllLayers(actor.index, false); + actor.IsAttacking = false; actor.IsAttemptingToMove = false; actor.CancelAttack = false; @@ -461,10 +1810,8 @@ static void OnEnd(SF2_ChaserAttackAction action, SF2_ChaserEntity actor) if (controller.IsValid()) { float gameTime = GetGameTime(); - SF2ChaserBossProfileData data; - SF2ChaserBossProfileAttackData attackData; - data = controller.GetProfileData(); - data.GetAttack(actor.GetAttackName(), attackData); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileBaseAttack attackData = action.ProfileData; int difficulty = controller.Difficulty; if (data.NormalSoundHook) @@ -472,7 +1819,7 @@ static void OnEnd(SF2_ChaserAttackAction action, SF2_ChaserEntity actor) actor.NextVoiceTime = 0.0; } - actor.SetNextAttackTime(actor.GetAttackName(), gameTime + attackData.Cooldown[difficulty]); + actor.SetNextAttackTime(actor.GetAttackName(), gameTime + attackData.GetCooldown(difficulty)); actor.InvokeOnEndAttack(actor.GetAttackName()); } @@ -486,4 +1833,221 @@ static void OnEnd(SF2_ChaserAttackAction action, SF2_ChaserEntity actor) static void OnOtherKilled(SF2_ChaserMainAction action, SF2_ChaserEntity actor, CBaseCombatCharacter victim, CBaseEntity attacker, CBaseEntity inflictor, float damage, int damagetype) { actor.CheckTauntKill(SF2_BasePlayer(victim.index)); -} \ No newline at end of file +} + +static any Native_GetType(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + return attackData.Type; +} + +static any Native_GetDamageDelay(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetDelay(difficulty); +} + +static any Native_GetRange(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetRange(difficulty); +} + +static any Native_GetDamage(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetDamage(difficulty); +} + +static any Native_GetDamageType(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetDamageType(difficulty); +} + +static any Native_GetDamageForce(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetDamageForce(difficulty); +} + +static any Native_GetViewPunchAngles(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + float vec[3]; + attackData.GetViewPunchAngles(difficulty, vec); + SetNativeArray(3, vec, 3); + return 0; +} + +static any Native_GetDuration(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetDuration(difficulty); +} + +static any Native_GetFOV(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetFOV(difficulty); +} + +static any Native_GetBeginRange(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetBeginRange(difficulty); +} + +static any Native_GetBeginFOV(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetBeginFOV(difficulty); +} + +static any Native_GetCooldown(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetCooldown(difficulty); +} + +static any Native_GetUseOnDifficulty(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + return attackData.UseOnDifficulty; +} + +static any Native_GetBlockOnDifficulty(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + return attackData.BlockOnDifficulty; +} + +static any Native_GetCanUseOnHealth(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.CanUseOnHealth(difficulty); +} + +static any Native_GetCanBlockOnHealth(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.CanBlockOnHealth(difficulty); +} + +static any Native_GetEventNumber(Handle plugin, int numParams) +{ + ChaserBossProfileBaseAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int difficulty = GetNativeCell(2); + + return attackData.GetEventNumber(difficulty); +} diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacklayer.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacklayer.sp index d038b3d6..99a83ec4 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacklayer.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacklayer.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required // This is for detecting attacks static NextBotActionFactory g_Factory; @@ -32,7 +33,6 @@ static int Update(SF2_ChaserAttackLayerAction action, SF2_ChaserEntity actor) NextBotAction attackAction = actor.GetAttackAction(target); if (attackAction != NULL_ACTION) { - actor.EndCloak(); return action.SuspendFor(attackAction, "Time to die!"); } } diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/bullet.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/bullet.sp index b48d9ad4..3b20cb06 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/bullet.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/bullet.sp @@ -1,10 +1,68 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap ChaserBossProfileBulletAttack < ChaserBossProfileBaseAttack +{ + public int GetBulletCount(int difficulty) + { + int def = 4; + def = this.GetDifficultyInt("attack_bullet_count", difficulty, def); + def = this.GetDifficultyInt("bullet_count", difficulty, def); + return def; + } + + public float GetBulletSpread(int difficulty) + { + float def = 0.1; + def = this.GetDifficultyFloat("bullet_spread", difficulty, def); + def = this.GetDifficultyFloat("attack_bullet_spread", difficulty, def); + return def; + } + + public void GetBulletTrace(char[] buffer, int bufferSize) + { + FormatEx(buffer, bufferSize, "bullet_tracer02_blue"); + this.GetString("bullet_tracer", buffer, bufferSize, buffer); + this.GetString("attack_bullet_tracer", buffer, bufferSize, buffer); + } + + public void GetBulletOffset(float buffer[3]) + { + this.GetVector("bullet_offset", buffer, buffer); + this.GetVector("attack_bullet_offset", buffer, buffer); + } + + public ProfileSound GetShootSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj == null) + { + return null; + } + + ProfileSound sound = view_as(obj.GetSection("shoot")); + if (sound == null) + { + return null; + } + + return sound; + } + + public void Precache() + { + if (this.GetShootSounds() != null) + { + this.GetShootSounds().Precache(); + } + } +} static NextBotActionFactory g_Factory; methodmap SF2_ChaserAttackAction_Bullet < NextBotAction { - public SF2_ChaserAttackAction_Bullet(int attackIndex, const char[] attackName, float fireDelay) + public SF2_ChaserAttackAction_Bullet(const char[] attackName, ChaserBossProfileBulletAttack data, float fireDelay) { if (g_Factory == null) { @@ -13,8 +71,8 @@ methodmap SF2_ChaserAttackAction_Bullet < NextBotAction g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); g_Factory.SetEventCallback(EventResponderType_OnAnimationEvent, OnAnimationEvent); g_Factory.BeginDataMapDesc() - .DefineIntField("m_AttackIndex") .DefineStringField("m_AttackName") + .DefineIntField("m_ProfileData") .DefineFloatField("m_NextFireTime") .DefineIntField("m_RepeatIndex") .EndDataMapDesc(); @@ -22,25 +80,12 @@ methodmap SF2_ChaserAttackAction_Bullet < NextBotAction SF2_ChaserAttackAction_Bullet action = view_as(g_Factory.Create()); action.NextFireTime = fireDelay; - action.AttackIndex = attackIndex; action.SetAttackName(attackName); + action.ProfileData = data; return action; } - property int AttackIndex - { - public get() - { - return this.GetData("m_AttackIndex"); - } - - public set(int value) - { - this.SetData("m_AttackIndex", value); - } - } - public static void Initialize() { g_OnChaserGetAttackActionPFwd.AddFunction(null, OnChaserGetAttackAction); @@ -58,6 +103,19 @@ methodmap SF2_ChaserAttackAction_Bullet < NextBotAction this.SetDataString("m_AttackName", name); } + property ChaserBossProfileBulletAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileBulletAttack value) + { + this.SetData("m_ProfileData", value); + } + } + property float NextFireTime { public get() @@ -92,10 +150,8 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - SF2ChaserBossProfileData data; - data = chaser.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileBulletAttack attackData = view_as(data.GetAttack(attackName)); int difficulty = chaser.Controller.Difficulty; if (attackData.Type != SF2BossAttackType_Ranged) @@ -103,7 +159,7 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - result = SF2_ChaserAttackAction_Bullet(attackData.Index, attackData.Name, attackData.DamageDelay[difficulty] + GetGameTime()); + result = SF2_ChaserAttackAction_Bullet(attackName, attackData, attackData.GetDelay(difficulty) + GetGameTime()); return Plugin_Changed; } @@ -119,42 +175,38 @@ static int Update(SF2_ChaserAttackAction_Bullet action, SF2_ChaserEntity actor, return action.Done("No longer firing bullets"); } - if (actor.CancelAttack) + if (actor.CancelAttack || actor.ClearCurrentAttack) { return action.Done(); } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileBulletAttack attackData = action.ProfileData; float gameTime = GetGameTime(); int difficulty = controller.Difficulty; - if (action.NextFireTime >= 0.0 && gameTime > action.NextFireTime && attackData.EventNumber == -1) + if (action.NextFireTime >= 0.0 && gameTime > action.NextFireTime && attackData.GetEventNumber(difficulty) == -1) { - DoBulletAttack(actor, action.GetAttackName()); + DoBulletAttack(action, actor, action.GetAttackName()); - int repeatState = attackData.Repeat; + int repeatState = attackData.GetRepeatState(difficulty); if (repeatState > 0) { switch (repeatState) { case 1: { - action.NextFireTime = gameTime + attackData.DamageDelay[difficulty]; + action.NextFireTime = gameTime + attackData.GetDelay(difficulty); } case 2: { - if (action.RepeatIndex >= attackData.RepeatTimers.Length) + if (attackData.GetRepeatTimer(difficulty, action.RepeatIndex) < 0.0) { action.NextFireTime = -1.0; } else { - float next = attackData.RepeatTimers.Get(action.RepeatIndex); - action.NextFireTime = next + gameTime; + action.NextFireTime = attackData.GetRepeatTimer(difficulty, action.RepeatIndex) + gameTime; action.RepeatIndex++; } } @@ -168,22 +220,23 @@ static int Update(SF2_ChaserAttackAction_Bullet action, SF2_ChaserEntity actor, return action.Continue(); } -static void DoBulletAttack(SF2_ChaserEntity actor, const char[] attackName) +static void DoBulletAttack(SF2_ChaserAttackAction_Bullet action, SF2_ChaserEntity actor, const char[] attackName) { SF2NPC_Chaser controller = actor.Controller; CBaseEntity target = actor.Target; int difficulty = controller.Difficulty; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileBulletAttack attackData = action.ProfileData; - float spread = attackData.BulletSpread[difficulty]; - ArrayList bulletSounds = data.BulletShootSounds; - SF2BossProfileSoundInfo soundInfo; - if (actor.SearchSoundsWithSectionName(bulletSounds, attackName, soundInfo)) + float spread = attackData.GetBulletSpread(difficulty); + ProfileSound soundInfo = attackData.GetShootSounds(); + if (soundInfo == null) + { + actor.SearchSoundsWithSectionName(data.GetBulletShootSounds(), attackName, soundInfo, "bulletshoot"); + } + if (soundInfo != null) { soundInfo.EmitSound(_, actor.index); } @@ -206,7 +259,7 @@ static void DoBulletAttack(SF2_ChaserEntity actor, const char[] attackName) targetPos[1] = eyePos[1] + fwd[1] * 9001.0; targetPos[2] = eyePos[2] + fwd[2] * 9001.0; } - effectPos = attackData.BulletOffset; + attackData.GetBulletOffset(effectPos); VectorTransform(effectPos, basePos, baseAng, effectPos); AddVectors(effectAng, baseAng, effectAng); @@ -225,7 +278,8 @@ static void DoBulletAttack(SF2_ChaserEntity actor, const char[] attackName) float dir[3], end[3]; float x, y; - for (int i = 0; i < attackData.BulletCount[difficulty]; i++) + ArrayList hitTargets = null; + for (int i = 0; i < attackData.GetBulletCount(difficulty); i++) { x = GetRandomFloat(-0.5, 0.5) + GetRandomFloat(-0.5, 0.5); y = GetRandomFloat(-0.5, 0.5) + GetRandomFloat(-0.5, 0.5); @@ -256,21 +310,29 @@ static void DoBulletAttack(SF2_ChaserEntity actor, const char[] attackName) TE_SetupEffectDispatch(GetEffectDispatchStringTableIndex("Impact"), .origin = endPos, .start = effectPos, - .damageType = DMG_BULLET, + .damageType = attackData.GetDamageType(difficulty), .surfaceProp = TR_GetSurfaceProps(trace), .hitbox = TR_GetHitGroup(trace), .entindex = TR_GetEntityIndex(trace)); TE_SendToAll(); + delete trace; - SDKHooks_TakeDamage(hitTarget, actor.index, actor.index, attackData.BulletDamage[difficulty], DMG_BULLET, _, CalculateBulletDamageForce(dir, 1.0), endPos); - if (SF2_BasePlayer(hitTarget).IsValid) + if (hitTargets == null) + { + hitTargets = new ArrayList(2); + } + int hitIndex = hitTargets.FindValue(hitTarget); + if (hitIndex == -1) { - attackData.ApplyDamageEffects(SF2_BasePlayer(hitTarget), difficulty, SF2_ChaserBossEntity(actor.index)); + hitIndex = hitTargets.Push(hitTarget); + hitTargets.Set(hitIndex, 0, 1); } + hitTargets.Set(hitIndex, hitTargets.Get(hitIndex, 1) + 1, 1); - if (attackData.BulletTrace[0] == '\0') + char traceParticle[64]; + attackData.GetBulletTrace(traceParticle, sizeof(traceParticle)); + if (traceParticle[0] == '\0') { - delete trace; continue; } @@ -281,7 +343,7 @@ static void DoBulletAttack(SF2_ChaserEntity actor, const char[] attackName) for (int i2 = 0; i2 < count; i2++) { ReadStringTable(table, i2, tmp, sizeof(tmp)); - if (strcmp(tmp, attackData.BulletTrace, false) == 0) + if (strcmp(tmp, traceParticle, false) == 0) { index = i2; break; @@ -290,8 +352,7 @@ static void DoBulletAttack(SF2_ChaserEntity actor, const char[] attackName) if (index == INVALID_STRING_INDEX) { - LogError("Could not find particle: %s", attackData.BulletTrace); - delete trace; + LogError("Could not find particle: %s", traceParticle); continue; } TE_Particle(index, effectPos, effectPos, dir, actor.index, view_as(PATTACH_CUSTOMORIGIN), _, _, true, view_as(PATTACH_CUSTOMORIGIN), endPos); @@ -299,6 +360,26 @@ static void DoBulletAttack(SF2_ChaserEntity actor, const char[] attackName) } delete trace; } + + if (hitTargets != null) + { + for (int i = 0; i < hitTargets.Length; i++) + { + int hitTarget = hitTargets.Get(i); + float damage = attackData.GetDamage(difficulty) * float(hitTargets.Get(i, 1)); + CBaseEntity(hitTargets.Get(i)).GetAbsOrigin(targetPos); + damage = GetDamageDistance(basePos, targetPos, damage, + attackData.GetDamageRampUpRange(difficulty), attackData.GetDamageFallOffRange(difficulty), + attackData.GetDamageRampUp(difficulty), attackData.GetDamageFallOff(difficulty)); + + SDKHooks_TakeDamage(hitTarget, actor.index, actor.index, damage, attackData.GetDamageType(difficulty), _, CalculateBulletDamageForce(dir, 1.0), basePos, .bypassHooks = false); + if (SF2_BasePlayer(hitTarget).IsValid) + { + attackData.ApplyDamageEffects(SF2_BasePlayer(hitTarget), difficulty, actor); + } + } + delete hitTargets; + } } static float[] CalculateBulletDamageForce(const float bulletDir[3], float scale) @@ -319,13 +400,11 @@ static void OnAnimationEvent(SF2_ChaserAttackAction_Bullet action, SF2_ChaserEnt } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + int difficulty = controller.Difficulty; + ChaserBossProfileBulletAttack attackData = action.ProfileData; - if (event == attackData.EventNumber) + if (event == attackData.GetEventNumber(difficulty)) { - DoBulletAttack(actor, action.GetAttackName()); + DoBulletAttack(action, actor, action.GetAttackName()); } } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/combo.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/combo.sp new file mode 100644 index 00000000..fd6af5ae --- /dev/null +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/combo.sp @@ -0,0 +1,300 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include "combolayer.sp" + +methodmap ChaserBossProfileComboAttack < ChaserBossProfileBaseAttack +{ + public ProfileObject GetAttacks() + { + return this.GetSection("attacks"); + } + + public KeyMap_Array GetPossibleCombos(int difficulty) + { + return this.GetDifficultyArray("combos", difficulty); + } + + public ChaserBossProfileBaseAttack GetAttack(const char[] name) + { + ProfileObject array = this.GetAttacks(); + if (array != null) + { + return view_as(array.GetSection(name)); + } + return null; + } + + public ChaserBossProfileBaseAttack GetAttackFromIndex(int index) + { + ProfileObject array = this.GetAttacks(); + if (array != null) + { + if (index >= array.SectionLength) + { + return null; + } + char name[64]; + array.GetSectionNameFromIndex(index, name, sizeof(name)); + return view_as(array.GetSection(name)); + } + return null; + } + + public bool GetAttackFromComboList(int difficulty, int index, int subIndex, char[] buffer, int bufferSize) + { + KeyMap_Array combos = this.GetPossibleCombos(difficulty); + if (index >= combos.Length) + { + return false; + } + + char value[2048]; + combos.GetString(index, value, sizeof(value)); + char attacks[128][64]; + int length = ExplodeString(value, " ", attacks, sizeof(attacks), sizeof(attacks[])); + if (subIndex >= length) + { + return false; + } + + strcopy(buffer, bufferSize, attacks[subIndex]); + return true; + } + + public void Precache() + { + this.ConvertDifficultyValuesSectionToArray("combos"); + + if (this.GetAttacks() != null) + { + for (int i = 0; i < this.GetAttacks().SectionLength; i++) + { + ChaserBossProfileBaseAttack attack = this.GetAttackFromIndex(i); + if (attack != null) + { + attack.Precache(); + } + } + } + + } +} + +static NextBotActionFactory g_Factory; + +methodmap SF2_ChaserAttackAction_Combo < NextBotAction +{ + public SF2_ChaserAttackAction_Combo(const char[] attackName, ChaserBossProfileComboAttack data) + { + if (g_Factory == null) + { + g_Factory = new NextBotActionFactory("Chaser_AttackCombo"); + g_Factory.SetCallback(NextBotActionCallbackType_InitialContainedAction, InitialContainedAction); + g_Factory.SetCallback(NextBotActionCallbackType_OnStart, OnStart); + g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); + g_Factory.BeginDataMapDesc() + .DefineStringField("m_AttackName") + .DefineIntField("m_ProfileData") + .DefineIntField("m_SelectedData") + .DefineIntField("m_Index") + .DefineIntField("m_PrimaryIndex") + .DefineIntField("m_CurrentDifficulty") + .DefineFloatField("m_EndTime") + .EndDataMapDesc(); + } + SF2_ChaserAttackAction_Combo action = view_as(g_Factory.Create()); + + action.SetAttackName(attackName); + action.ProfileData = data; + action.Index = -1; + + return action; + } + + public static void Initialize() + { + g_OnChaserGetAttackActionPFwd.AddFunction(null, OnChaserGetAttackAction); + } + + public char[] GetAttackName() + { + char name[128]; + this.GetDataString("m_AttackName", name, sizeof(name)); + return name; + } + + public void SetAttackName(const char[] name) + { + this.SetDataString("m_AttackName", name); + } + + property ChaserBossProfileComboAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileComboAttack value) + { + this.SetData("m_ProfileData", value); + } + } + + property ChaserBossProfileBaseAttack SelectedData + { + public get() + { + return this.GetData("m_SelectedData"); + } + + public set(ChaserBossProfileBaseAttack value) + { + this.SetData("m_SelectedData", value); + } + } + + property int Index + { + public get() + { + return this.GetData("m_Index"); + } + + public set(int value) + { + this.SetData("m_Index", value); + } + } + + property int PrimaryIndex + { + public get() + { + return this.GetData("m_PrimaryIndex"); + } + + public set(int value) + { + this.SetData("m_PrimaryIndex", value); + } + } + + property int CurrentDifficulty + { + public get() + { + return this.GetData("m_CurrentDifficulty"); + } + + public set(int value) + { + this.SetData("m_CurrentDifficulty", value); + } + } + + property float EndTime + { + public get() + { + return this.GetDataFloat("m_EndTime"); + } + + public set(float value) + { + this.SetDataFloat("m_EndTime", value); + } + } +} + +static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] attackName, NextBotAction &result) +{ + if (result != NULL_ACTION) + { + return Plugin_Continue; + } + + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileComboAttack attackData = view_as(data.GetAttack(attackName)); + + if (attackData.Type != SF2BossAttackType_Combo) + { + return Plugin_Continue; + } + + result = SF2_ChaserAttackAction_Combo(attackName, attackData); + return Plugin_Changed; +} + +static NextBotAction InitialContainedAction(SF2_ChaserAttackAction_Combo action, SF2_ChaserEntity actor) +{ + return SF2_ChaserAttackAction_ComboLayer(action.GetAttackName(), action.ProfileData); +} + +static int OnStart(SF2_ChaserAttackAction_Combo action, SF2_ChaserEntity actor, NextBotAction priorAction) +{ + SF2_ChaserAttackAction attackAction = view_as(action.Parent); + attackAction.EndTime = -1.0; + action.PrimaryIndex = -1; + action.CurrentDifficulty = actor.Controller.Difficulty; + if (action.ProfileData.GetPossibleCombos(action.CurrentDifficulty) != null) + { + action.PrimaryIndex = GetRandomInt(0, action.ProfileData.GetPossibleCombos(action.CurrentDifficulty).Length - 1); + } + return action.Continue(); +} + +static int Update(SF2_ChaserAttackAction_Combo action, SF2_ChaserEntity actor, float interval) +{ + if (action.Parent == NULL_ACTION) + { + return action.Done("No more combo attacks"); + } + + if (action.ActiveChild == NULL_ACTION) + { + return action.Done("No more combo attacks"); + } + + if (actor.CancelAttack) + { + return action.Done(); + } + + float gameTime = GetGameTime(); + if (action.EndTime <= gameTime) + { + action.Index++; + ChaserBossProfileBaseAttack attackData = null; + if (action.ProfileData.GetPossibleCombos(action.CurrentDifficulty) != null) + { + char name[64]; + action.ProfileData.GetAttackFromComboList(action.CurrentDifficulty, action.PrimaryIndex, action.Index, name, sizeof(name)); + attackData = action.ProfileData.GetAttack(name); + } + else + { + attackData = action.ProfileData.GetAttackFromIndex(action.Index); + } + if (attackData == null) + { + return action.Done(); + } + int difficulty = actor.Controller.Difficulty; + action.EndTime = gameTime + attackData.GetDuration(difficulty); + if (attackData.GetStartSounds() != null) + { + actor.PerformVoiceCooldown(attackData.GetStartSounds(), attackData.GetStartSounds().Paths); + } + + if (attackData.GetAnimations() != null) + { + actor.ResetProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Attack], .animations = attackData.GetAnimations()); + } + action.SelectedData = attackData; + actor.ClearCurrentAttack = true; + } + + return action.Continue(); +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/combolayer.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/combolayer.sp new file mode 100644 index 00000000..245bb229 --- /dev/null +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/combolayer.sp @@ -0,0 +1,130 @@ +#pragma semicolon 1 +#pragma newdecls required + +static NextBotActionFactory g_Factory; + +methodmap SF2_ChaserAttackAction_ComboLayer < NextBotAction +{ + public SF2_ChaserAttackAction_ComboLayer(const char[] attackName, ChaserBossProfileComboAttack data) + { + if (g_Factory == null) + { + g_Factory = new NextBotActionFactory("Chaser_AttackComboLayer"); + g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); + g_Factory.BeginDataMapDesc() + .DefineStringField("m_AttackName") + .DefineIntField("m_ProfileData") + .EndDataMapDesc(); + } + SF2_ChaserAttackAction_ComboLayer action = view_as(g_Factory.Create()); + + action.SetAttackName(attackName); + action.ProfileData = data; + + return action; + } + + public char[] GetAttackName() + { + char name[128]; + this.GetDataString("m_AttackName", name, sizeof(name)); + return name; + } + + public void SetAttackName(const char[] name) + { + this.SetDataString("m_AttackName", name); + } + + property ChaserBossProfileComboAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileComboAttack value) + { + this.SetData("m_ProfileData", value); + } + } +} + +static int Update(SF2_ChaserAttackAction_ComboLayer action, SF2_ChaserEntity actor, float interval) +{ + if (action.Parent == NULL_ACTION) + { + return action.Done("No more combo attacks"); + } + + if (actor.CancelAttack) + { + return action.Done(); + } + + if (actor.ClearCurrentAttack) + { + actor.ClearCurrentAttack = false; + SF2_ChaserAttackAction_Combo parent = view_as(action.Parent); + ChaserBossProfileBaseAttack attackData = parent.SelectedData; + if (attackData == null) + { + return action.Done(); + } + float gameTime = GetGameTime(); + int difficulty = actor.Controller.Difficulty; + + switch (attackData.Type) + { + case SF2BossAttackType_Melee: + { + return action.SuspendFor(SF2_ChaserAttackAction_Melee(action.GetAttackName(), attackData, attackData.GetDelay(difficulty) + gameTime)); + } + + case SF2BossAttackType_Ranged: + { + return action.SuspendFor(SF2_ChaserAttackAction_Bullet(action.GetAttackName(), view_as(attackData), attackData.GetDelay(difficulty) + gameTime)); + } + + case SF2BossAttackType_Projectile: + { + return action.SuspendFor(SF2_ChaserAttackAction_Projectile(action.GetAttackName(), view_as(attackData), attackData.GetDelay(difficulty) + gameTime)); + } + + case SF2BossAttackType_ExplosiveDance: + { + return action.SuspendFor(SF2_ChaserAttackAction_ExplosiveDance(action.GetAttackName(), view_as(attackData), attackData.GetDelay(difficulty) + gameTime)); + } + + case SF2BossAttackType_LaserBeam: + { + return action.SuspendFor(SF2_ChaserAttackAction_Laser(action.GetAttackName(), view_as(attackData), attackData.GetDelay(difficulty) + gameTime, view_as(attackData).GetLaserDuration(difficulty) + gameTime + attackData.GetDelay(difficulty))); + } + + case SF2BossAttackType_Custom: + { + NextBotAction customAction = NULL_ACTION; + Action retAction = Plugin_Continue; + Call_StartForward(g_OnBossGetCustomAttackActionFwd); + Call_PushCell(actor); + Call_PushString(action.GetAttackName()); + Call_PushCell(attackData); + Call_PushCell(actor.Target); + Call_PushCellRef(customAction); + Call_Finish(retAction); + + if (retAction != Plugin_Continue) + { + return action.SuspendFor(customAction); + } + } + + case SF2BossAttackType_Tongue: + { + return action.SuspendFor(SF2_ChaserAttackAction_Tongue(action.GetAttackName(), view_as(attackData), attackData.GetDelay(difficulty) + gameTime)); + } + } + } + + return action.Continue(); +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/custom.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/custom.sp index 4c5a0aba..cbc08fbd 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/custom.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/custom.sp @@ -1,4 +1,19 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap ChaserBossProfileCustomAttack < ChaserBossProfileBaseAttack +{ + public void GetSubType(char[] buffer, int bufferSize) + { + this.GetString("subtype", buffer, bufferSize); + } + + public static void SetupAPI() + { + CreateNative("SF2_ChaserBossProfileCustomAttack.GetSubType", Native_GetSubType); + CreateNative("SF2_ChaserBossProfileCustomAttack.IsSubType", Native_GetIsSubType); + } +} methodmap SF2_ChaserAttackAction_Custom < NextBotAction { @@ -16,10 +31,8 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - SF2ChaserBossProfileData data; - data = chaser.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileCustomAttack attackData = view_as(data.GetAttack(attackName)); if (attackData.Type != SF2BossAttackType_Custom) { @@ -38,4 +51,39 @@ static Action OnChaserGetCustomAttackPossibleState(SF2_ChaserEntity chaser, cons } return Plugin_Handled; -} \ No newline at end of file +} + +static any Native_GetSubType(Handle plugin, int numParams) +{ + ChaserBossProfileCustomAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int bufferSize = GetNativeCell(3); + char[] buffer = new char[bufferSize]; + attackData.GetSubType(buffer, bufferSize); + SetNativeString(2, buffer, bufferSize); + return 0; +} + +static any Native_GetIsSubType(Handle plugin, int numParams) +{ + ChaserBossProfileCustomAttack attackData = GetNativeCell(1); + if (attackData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", attackData); + } + + int subTypeSize; + GetNativeStringLength(2, subTypeSize); + subTypeSize++; + char[] subType = new char[subTypeSize]; + GetNativeString(2, subType, subTypeSize); + + char attackSubType[128]; + attackData.GetSubType(attackSubType, sizeof(attackSubType)); + + return strcmp(subType, attackSubType) == 0; +} diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/explosive_dance.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/explosive_dance.sp index 8bd5d9c5..c7e7d885 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/explosive_dance.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/explosive_dance.sp @@ -1,4 +1,21 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap ChaserBossProfileExplosiveDanceAttack < ChaserBossProfileBaseAttack +{ + public float GetBlastRadius(int difficulty) + { + float def = 350.0; + def = this.GetDifficultyFloat("explosivedance_radius", difficulty, def); + def = this.GetDifficultyFloat("attack_explosivedance_radius", difficulty, def); + return def; + } + + public float GetBlastSpread(int difficulty) + { + return this.GetDifficultyFloat("explosivedance_spread", difficulty, 350.0); + } +} static char g_ExplosionSounds[][] = { @@ -11,7 +28,7 @@ static NextBotActionFactory g_Factory; methodmap SF2_ChaserAttackAction_ExplosiveDance < NextBotAction { - public SF2_ChaserAttackAction_ExplosiveDance(int attackIndex, const char[] attackName, float damageDelay) + public SF2_ChaserAttackAction_ExplosiveDance(const char[] attackName, ChaserBossProfileExplosiveDanceAttack data, float damageDelay) { if (g_Factory == null) { @@ -20,8 +37,8 @@ methodmap SF2_ChaserAttackAction_ExplosiveDance < NextBotAction g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); g_Factory.SetEventCallback(EventResponderType_OnAnimationEvent, OnAnimationEvent); g_Factory.BeginDataMapDesc() - .DefineIntField("m_AttackIndex") .DefineStringField("m_AttackName") + .DefineIntField("m_ProfileData") .DefineFloatField("m_NextDamageTime") .DefineIntField("m_ExplosionCount") .EndDataMapDesc(); @@ -29,9 +46,9 @@ methodmap SF2_ChaserAttackAction_ExplosiveDance < NextBotAction SF2_ChaserAttackAction_ExplosiveDance action = view_as(g_Factory.Create()); action.NextDamageTime = damageDelay; - action.AttackIndex = attackIndex; action.SetAttackName(attackName); action.ExplosionCount = 0; + action.ProfileData = data; return action; } @@ -42,19 +59,6 @@ methodmap SF2_ChaserAttackAction_ExplosiveDance < NextBotAction g_OnGamemodeStartPFwd.AddFunction(null, OnGamemodeStart); } - property int AttackIndex - { - public get() - { - return this.GetData("m_AttackIndex"); - } - - public set(int value) - { - this.SetData("m_AttackIndex", value); - } - } - public char[] GetAttackName() { char name[128]; @@ -67,6 +71,19 @@ methodmap SF2_ChaserAttackAction_ExplosiveDance < NextBotAction this.SetDataString("m_AttackName", name); } + property ChaserBossProfileExplosiveDanceAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileExplosiveDanceAttack value) + { + this.SetData("m_ProfileData", value); + } + } + property float NextDamageTime { public get() @@ -109,10 +126,8 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - SF2ChaserBossProfileData data; - data = chaser.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileExplosiveDanceAttack attackData = view_as(data.GetAttack(attackName)); int difficulty = chaser.Controller.Difficulty; if (attackData.Type != SF2BossAttackType_ExplosiveDance) @@ -120,7 +135,7 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - result = SF2_ChaserAttackAction_ExplosiveDance(attackData.Index, attackData.Name, attackData.DamageDelay[difficulty] + GetGameTime()); + result = SF2_ChaserAttackAction_ExplosiveDance(attackName, attackData, attackData.GetDelay(difficulty) + GetGameTime()); return Plugin_Changed; } @@ -136,20 +151,17 @@ static int Update(SF2_ChaserAttackAction_ExplosiveDance action, SF2_ChaserEntity return action.Done("No longer explosion dancing"); } - if (actor.CancelAttack) + if (actor.CancelAttack || actor.ClearCurrentAttack) { return action.Done(); } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileExplosiveDanceAttack attackData = action.ProfileData; float gameTime = GetGameTime(); - if (action.NextDamageTime >= 0.0 && gameTime > action.NextDamageTime && attackData.EventNumber == -1) + if (action.NextDamageTime >= 0.0 && gameTime > action.NextDamageTime && attackData.GetEventNumber(controller.Difficulty) == -1) { DoExplosion(action, actor); } @@ -162,12 +174,9 @@ static void DoExplosion(SF2_ChaserAttackAction_ExplosiveDance action, SF2_Chaser int difficulty = controller.Difficulty; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); - float damage = attackData.Damage[difficulty]; - float radius = attackData.ExplosiveDanceRadius[difficulty]; + ChaserBossProfileExplosiveDanceAttack attackData = action.ProfileData; + float damage = attackData.GetDamage(difficulty); + float radius = attackData.GetBlastRadius(difficulty); if (SF_SpecialRound(SPECIALROUND_TINYBOSSES)) { damage *= 0.5; @@ -187,8 +196,8 @@ static void DoExplosion(SF2_ChaserAttackAction_ExplosiveDance action, SF2_Chaser float explosionPos[3]; explosionPos = worldPos; - explosionPos[0] = worldPos[0] + GetRandomFloat(-350.0, 350.0); - explosionPos[1] = worldPos[1] + GetRandomFloat(-350.0, 350.0); + explosionPos[0] = worldPos[0] + GetRandomFloat(-attackData.GetBlastSpread(difficulty), attackData.GetBlastSpread(difficulty)); + explosionPos[1] = worldPos[1] + GetRandomFloat(-attackData.GetBlastSpread(difficulty), attackData.GetBlastSpread(difficulty)); Explode(explosionPos, damage, radius, actor.index); EmitSoundToAll(g_ExplosionSounds[GetRandomInt(0, sizeof(g_ExplosionSounds) - 1)], actor.index, SNDCHAN_AUTO, SNDLEVEL_SCREAMING); @@ -203,12 +212,9 @@ static void OnAnimationEvent(SF2_ChaserAttackAction_ExplosiveDance action, SF2_C } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileExplosiveDanceAttack attackData = action.ProfileData; - if (event == attackData.EventNumber) + if (event == attackData.GetEventNumber(controller.Difficulty)) { DoExplosion(action, actor); } diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/forward_based.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/forward_based.sp index cc78288e..020b3264 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/forward_based.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/forward_based.sp @@ -1,10 +1,11 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; methodmap SF2_ChaserAttackAction_ForwardBased < NextBotAction { - public SF2_ChaserAttackAction_ForwardBased(int attackIndex, const char[] attackName, float damageDelay) + public SF2_ChaserAttackAction_ForwardBased(const char[] attackName, ChaserBossProfileBaseAttack data, float damageDelay) { if (g_Factory == null) { @@ -12,8 +13,8 @@ methodmap SF2_ChaserAttackAction_ForwardBased < NextBotAction g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); g_Factory.SetEventCallback(EventResponderType_OnAnimationEvent, OnAnimationEvent); g_Factory.BeginDataMapDesc() - .DefineIntField("m_AttackIndex") .DefineStringField("m_AttackName") + .DefineIntField("m_ProfileData") .DefineFloatField("m_NextDamageTime") .DefineIntField("m_RepeatIndex") .EndDataMapDesc(); @@ -21,25 +22,12 @@ methodmap SF2_ChaserAttackAction_ForwardBased < NextBotAction SF2_ChaserAttackAction_ForwardBased action = view_as(g_Factory.Create()); action.NextDamageTime = damageDelay; - action.AttackIndex = attackIndex; action.SetAttackName(attackName); + action.ProfileData = data; return action; } - property int AttackIndex - { - public get() - { - return this.GetData("m_AttackIndex"); - } - - public set(int value) - { - this.SetData("m_AttackIndex", value); - } - } - public static void Initialize() { g_OnChaserGetAttackActionPFwd.AddFunction(null, OnChaserGetAttackAction); @@ -57,6 +45,19 @@ methodmap SF2_ChaserAttackAction_ForwardBased < NextBotAction this.SetDataString("m_AttackName", name); } + property ChaserBossProfileBaseAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileBaseAttack value) + { + this.SetData("m_ProfileData", value); + } + } + property float NextDamageTime { public get() @@ -91,18 +92,16 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - SF2ChaserBossProfileData data; - data = chaser.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileBaseAttack attackData = data.GetAttack(attackName); int difficulty = chaser.Controller.Difficulty; - if (attackData.Type >= SF2BossAttackType_Melee && attackData.Type <= SF2BossAttackType_Tongue) + if (attackData.Type >= SF2BossAttackType_Melee && attackData.Type <= SF2BossAttackType_Combo) { return Plugin_Continue; } - result = SF2_ChaserAttackAction_ForwardBased(attackData.Index, attackData.Name, attackData.DamageDelay[difficulty] + GetGameTime()); + result = SF2_ChaserAttackAction_ForwardBased(attackName, attackData, attackData.GetDelay(difficulty) + GetGameTime()); return Plugin_Changed; } @@ -113,42 +112,38 @@ static int Update(SF2_ChaserAttackAction_ForwardBased action, SF2_ChaserEntity a return action.Done("No longer forward attacking"); } - if (actor.CancelAttack) + if (actor.CancelAttack || actor.ClearCurrentAttack) { return action.Done(); } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileBaseAttack attackData = action.ProfileData; int difficulty = controller.Difficulty; float gameTime = GetGameTime(); - if (action.NextDamageTime >= 0.0 && gameTime > action.NextDamageTime && attackData.EventNumber == -1) + if (action.NextDamageTime >= 0.0 && gameTime > action.NextDamageTime && attackData.GetEventNumber(difficulty) == -1) { - RegisterForward(action, actor); + RegisterForward(actor); - int repeatState = attackData.Repeat; + int repeatState = attackData.GetRepeatState(difficulty); if (repeatState > 0) { switch (repeatState) { case 1: { - action.NextDamageTime = gameTime + attackData.DamageDelay[difficulty]; + action.NextDamageTime = gameTime + attackData.GetDelay(difficulty); } case 2: { - if (action.RepeatIndex >= attackData.RepeatTimers.Length) + if (attackData.GetRepeatTimer(difficulty, action.RepeatIndex) < 0.0) { action.NextDamageTime = -1.0; } else { - float next = attackData.RepeatTimers.Get(action.RepeatIndex); - action.NextDamageTime = next + gameTime; + action.NextDamageTime = attackData.GetRepeatTimer(difficulty, action.RepeatIndex) + gameTime; action.RepeatIndex++; } } @@ -162,11 +157,11 @@ static int Update(SF2_ChaserAttackAction_ForwardBased action, SF2_ChaserEntity a return action.Continue(); } -static void RegisterForward(SF2_ChaserAttackAction_ForwardBased action, SF2_ChaserEntity actor) +static void RegisterForward(SF2_ChaserEntity actor) { Call_StartForward(g_OnBossAttackedFwd); Call_PushCell(actor.Controller.Index); - Call_PushCell(action.AttackIndex); + Call_PushCell(actor.AttackIndex); Call_Finish(); } @@ -178,13 +173,10 @@ static void OnAnimationEvent(SF2_ChaserAttackAction_ForwardBased action, SF2_Cha } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileBaseAttack attackData = action.ProfileData; - if (event == attackData.EventNumber) + if (event == attackData.GetEventNumber(controller.Difficulty)) { - RegisterForward(action, actor); + RegisterForward(actor); } } diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/laser_beam.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/laser_beam.sp index 82504be0..eecdab44 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/laser_beam.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/laser_beam.sp @@ -1,10 +1,73 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap ChaserBossProfileLaserAttack < ChaserBossProfileBaseAttack +{ + property float LaserSize + { + public get() + { + float def = 12.0; + def = this.GetFloat("laser_size", def); + def = this.GetFloat("attack_laser_size", def); + return def; + } + } + + public void GetLaserColor(int buffer[3]) + { + buffer[0] = this.GetInt("laser_color_r"); + buffer[0] = this.GetInt("attack_laser_color_r"); + buffer[1] = this.GetInt("laser_color_g"); + buffer[1] = this.GetInt("attack_laser_color_g"); + buffer[2] = this.GetInt("laser_color_b"); + buffer[2] = this.GetInt("attack_laser_color_b"); + } + + public void GetAttachment(char[] buffer, int bufferSize) + { + this.GetString("laser_attachment_name", buffer, bufferSize, buffer); + this.GetString("attack_laser_attachment_name", buffer, bufferSize, buffer); + } + + public float GetLaserDuration(int difficulty) + { + float def = this.GetDuration(difficulty); + def = this.GetDifficultyFloat("laser_duration", difficulty, def); + def = this.GetDifficultyFloat("attack_laser_duration", difficulty, def); + return def; + } + + property float LaserNoise + { + public get() + { + float def = 1.0; + def = this.GetFloat("laser_noise", def); + def = this.GetFloat("attack_laser_noise", def); + return def; + } + } + + public void GetOffset(float buffer[3]) + { + this.GetVector("laser_offset", buffer, buffer); + this.GetVector("attack_laser_offset", buffer, buffer); + } + + public float GetLaserTickRate(int difficulty) + { + float def = 0.1; + def = this.GetDifficultyFloat("laser_tick_delay", difficulty, def); + return def; + } +} static NextBotActionFactory g_Factory; methodmap SF2_ChaserAttackAction_Laser < NextBotAction { - public SF2_ChaserAttackAction_Laser(int attackIndex, const char[] attackName, float fireDelay, float duration) + public SF2_ChaserAttackAction_Laser(const char[] attackName, ChaserBossProfileLaserAttack data, float fireDelay, float duration) { if (g_Factory == null) { @@ -13,8 +76,8 @@ methodmap SF2_ChaserAttackAction_Laser < NextBotAction g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); g_Factory.SetEventCallback(EventResponderType_OnAnimationEvent, OnAnimationEvent); g_Factory.BeginDataMapDesc() - .DefineIntField("m_AttackIndex") .DefineStringField("m_AttackName") + .DefineIntField("m_ProfileData") .DefineFloatField("m_NextFireTime") .DefineFloatField("m_LaserDuration") .DefineFloatField("m_LaserCooldown") @@ -23,26 +86,13 @@ methodmap SF2_ChaserAttackAction_Laser < NextBotAction SF2_ChaserAttackAction_Laser action = view_as(g_Factory.Create()); action.NextFireTime = fireDelay; - action.AttackIndex = attackIndex; action.LaserDuration = duration; action.SetAttackName(attackName); + action.ProfileData = data; return action; } - property int AttackIndex - { - public get() - { - return this.GetData("m_AttackIndex"); - } - - public set(int value) - { - this.SetData("m_AttackIndex", value); - } - } - public static void Initialize() { g_OnChaserGetAttackActionPFwd.AddFunction(null, OnChaserGetAttackAction); @@ -60,6 +110,19 @@ methodmap SF2_ChaserAttackAction_Laser < NextBotAction this.SetDataString("m_AttackName", name); } + property ChaserBossProfileLaserAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileLaserAttack value) + { + this.SetData("m_ProfileData", value); + } + } + property float NextFireTime { public get() @@ -108,10 +171,8 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - SF2ChaserBossProfileData data; - data = chaser.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileLaserAttack attackData = view_as(data.GetAttack(attackName)); int difficulty = chaser.Controller.Difficulty; if (attackData.Type != SF2BossAttackType_LaserBeam) @@ -119,7 +180,7 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - result = SF2_ChaserAttackAction_Laser(attackData.Index, attackData.Name, attackData.DamageDelay[difficulty] + GetGameTime(), attackData.LaserDuration[difficulty] + GetGameTime() + attackData.DamageDelay[difficulty]); + result = SF2_ChaserAttackAction_Laser(attackName, attackData, attackData.GetDelay(difficulty) + GetGameTime(), attackData.GetLaserDuration(difficulty) + GetGameTime() + attackData.GetDelay(difficulty)); return Plugin_Changed; } @@ -135,26 +196,23 @@ static int Update(SF2_ChaserAttackAction_Laser action, SF2_ChaserEntity actor, f return action.Done("No longer firing my super laser beam"); } - if (actor.CancelAttack) + if (actor.CancelAttack || actor.ClearCurrentAttack) { return action.Done(); } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileLaserAttack attackData = action.ProfileData; int difficulty = controller.Difficulty; float gameTime = GetGameTime(); - if (action.NextFireTime >= 0.0 && gameTime > action.NextFireTime && attackData.EventNumber == -1) + if (action.NextFireTime >= 0.0 && gameTime > action.NextFireTime && attackData.GetEventNumber(difficulty) == -1) { if (action.LaserCooldown < gameTime) { - action.LaserCooldown = gameTime + attackData.LaserTicks[difficulty]; - FireLaser(action, actor, attackData.LaserTicks[difficulty]); + action.LaserCooldown = gameTime + attackData.GetLaserTickRate(difficulty); + FireLaser(action, actor, attackData.GetLaserTickRate(difficulty)); } if (action.LaserDuration < gameTime) @@ -181,10 +239,7 @@ static void FireLaser(SF2_ChaserAttackAction_Laser action, SF2_ChaserEntity acto int difficulty = controller.Difficulty; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileLaserAttack attackData = action.ProfileData; if (!target.IsValid()) { @@ -197,7 +252,7 @@ static void FireLaser(SF2_ChaserAttackAction_Laser action, SF2_ChaserEntity acto float basePos[3], baseAng[3], effectAng[3], effectPos[3]; actor.GetAbsOrigin(basePos); actor.GetAbsAngles(baseAng); - effectPos = attackData.LaserOffset; + attackData.GetOffset(effectPos); VectorTransform(effectPos, basePos, baseAng, effectPos); AddVectors(effectAng, baseAng, effectAng); @@ -232,16 +287,18 @@ static void FireLaser(SF2_ChaserAttackAction_Laser action, SF2_ChaserEntity acto TR_GetEndPosition(endPos, trace); int color[3]; - color = attackData.LaserColor; + attackData.GetLaserColor(color); int actualColor[4]; actualColor[0] = color[0]; actualColor[1] = color[1]; actualColor[2] = color[2]; actualColor[3] = 255; - if (attackData.LaserAttachment) + char attachment[64]; + attackData.GetAttachment(attachment, sizeof(attachment)); + if (attachment[0] != '\0') { - int attachmentIndex = actor.LookupAttachment(attackData.LaserAttachmentName); + int attachmentIndex = actor.LookupAttachment(attachment); float targetEntPos[3], tempAng[3]; actor.GetAttachment(attachmentIndex, targetEntPos, tempAng); TE_SetupBeamPoints(targetEntPos, endPos, g_ShockwaveBeam, g_ShockwaveHalo, 0, 30, interval, attackData.LaserSize, attackData.LaserSize, 5, attackData.LaserNoise, actualColor, 1); @@ -253,10 +310,11 @@ static void FireLaser(SF2_ChaserAttackAction_Laser action, SF2_ChaserEntity acto TE_SendToAll(); } - SDKHooks_TakeDamage(hitTarget, actor.index, actor.index, attackData.LaserDamage[difficulty], DMG_SHOCK | DMG_ALWAYSGIB, _, _, endPos); + SDKHooks_TakeDamage(hitTarget, actor.index, actor.index, attackData.GetDamage(difficulty), attackData.GetDamageType(difficulty), _, _, endPos, .bypassHooks = false); - attackData.ApplyDamageEffects(SF2_BasePlayer(hitTarget), difficulty, SF2_ChaserBossEntity(actor.index)); + attackData.ApplyDamageEffects(SF2_BasePlayer(hitTarget), difficulty, actor); } + delete trace; } static void OnAnimationEvent(SF2_ChaserAttackAction_Laser action, SF2_ChaserEntity actor, int event) @@ -267,12 +325,9 @@ static void OnAnimationEvent(SF2_ChaserAttackAction_Laser action, SF2_ChaserEnti } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileLaserAttack attackData = action.ProfileData; - if (event == attackData.EventNumber) + if (event == attackData.GetEventNumber(controller.Difficulty)) { FireLaser(action, actor, 0.1); } diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/melee.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/melee.sp index a5da6338..89eeab4a 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/melee.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/melee.sp @@ -1,10 +1,11 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; methodmap SF2_ChaserAttackAction_Melee < NextBotAction { - public SF2_ChaserAttackAction_Melee(int attackIndex, const char[] attackName, float damageDelay) + public SF2_ChaserAttackAction_Melee(const char[] attackName, ChaserBossProfileBaseAttack data, float damageDelay) { if (g_Factory == null) { @@ -13,8 +14,8 @@ methodmap SF2_ChaserAttackAction_Melee < NextBotAction g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); g_Factory.SetEventCallback(EventResponderType_OnAnimationEvent, OnAnimationEvent); g_Factory.BeginDataMapDesc() - .DefineIntField("m_AttackIndex") .DefineStringField("m_AttackName") + .DefineIntField("m_ProfileData") .DefineFloatField("m_NextDamageTime") .DefineIntField("m_RepeatIndex") .EndDataMapDesc(); @@ -22,25 +23,12 @@ methodmap SF2_ChaserAttackAction_Melee < NextBotAction SF2_ChaserAttackAction_Melee action = view_as(g_Factory.Create()); action.NextDamageTime = damageDelay; - action.AttackIndex = attackIndex; action.SetAttackName(attackName); + action.ProfileData = data; return action; } - property int AttackIndex - { - public get() - { - return this.GetData("m_AttackIndex"); - } - - public set(int value) - { - this.SetData("m_AttackIndex", value); - } - } - public static void Initialize() { g_OnChaserGetAttackActionPFwd.AddFunction(null, OnChaserGetAttackAction); @@ -58,6 +46,19 @@ methodmap SF2_ChaserAttackAction_Melee < NextBotAction this.SetDataString("m_AttackName", name); } + property ChaserBossProfileBaseAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileBaseAttack value) + { + this.SetData("m_ProfileData", value); + } + } + property float NextDamageTime { public get() @@ -92,10 +93,8 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - SF2ChaserBossProfileData data; - data = chaser.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileBaseAttack attackData = data.GetAttack(attackName); int difficulty = chaser.Controller.Difficulty; if (attackData.Type != SF2BossAttackType_Melee) @@ -103,7 +102,7 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - result = SF2_ChaserAttackAction_Melee(attackData.Index, attackData.Name, attackData.DamageDelay[difficulty] + GetGameTime()); + result = SF2_ChaserAttackAction_Melee(attackName, attackData, attackData.GetDelay(difficulty) + GetGameTime()); return Plugin_Changed; } @@ -119,42 +118,38 @@ static int Update(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity actor, f return action.Done("No longer melee attacking"); } - if (actor.CancelAttack) + if (actor.CancelAttack || actor.ClearCurrentAttack) { return action.Done(); } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileBaseAttack attackData = action.ProfileData; int difficulty = controller.Difficulty; float gameTime = GetGameTime(); - if (action.NextDamageTime >= 0.0 && gameTime > action.NextDamageTime && attackData.EventNumber == -1) + if (action.NextDamageTime >= 0.0 && gameTime >= action.NextDamageTime && attackData.GetEventNumber(difficulty) == -1) { DoMeleeAttack(action, actor); - int repeatState = attackData.Repeat; + int repeatState = attackData.GetRepeatState(difficulty); if (repeatState > 0) { switch (repeatState) { case 1: { - action.NextDamageTime = gameTime + attackData.DamageDelay[difficulty]; + action.NextDamageTime = gameTime + attackData.GetDelay(difficulty); } case 2: { - if (action.RepeatIndex >= attackData.RepeatTimers.Length) + if (attackData.GetRepeatTimer(difficulty, action.RepeatIndex) < 0.0) { action.NextDamageTime = -1.0; } else { - float next = attackData.RepeatTimers.Get(action.RepeatIndex); - action.NextDamageTime = next + gameTime; + action.NextDamageTime = attackData.GetRepeatTimer(difficulty, action.RepeatIndex) + gameTime; action.RepeatIndex++; } } @@ -175,36 +170,33 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity int difficulty = controller.Difficulty; bool attackEliminated = (controller.Flags & SFF_ATTACKWAITERS) != 0; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); - if (originalData.IsPvEBoss) + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileBaseAttack attackData = action.ProfileData; + if (data.IsPvEBoss) { attackEliminated = true; } - float damage = attackData.Damage[difficulty]; + float damage = attackData.GetDamage(difficulty); if (SF_SpecialRound(SPECIALROUND_TINYBOSSES)) { damage *= 0.5; } - int damageType = attackData.DamageType[difficulty]; + int damageType = attackData.GetDamageType(difficulty); - float myPos[3], myEyePos[3], myEyeAng[3]; + float myPos[3], myEyePos[3], myEyeAng[3], offset[3]; actor.GetAbsOrigin(myPos); controller.GetEyePosition(myEyePos); actor.GetAbsAngles(myEyeAng); + data.GetEyes().GetOffsetPos(offset); - AddVectors(g_SlenderEyePosOffset[controller.Index], myEyeAng, myEyeAng); + AddVectors(offset, myEyeAng, myEyeAng); float viewPunch[3]; - viewPunch = attackData.PunchVelocity; + attackData.GetViewPunchAngles(difficulty, viewPunch); - float range = attackData.Range[difficulty]; - float spread = attackData.Spread[difficulty]; - float force = attackData.DamageForce[difficulty]; + float range = attackData.GetRange(difficulty); + float spread = attackData.GetFOV(difficulty); + float force = attackData.GetDamageForce(difficulty); bool hit = false; @@ -219,12 +211,12 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity continue; } - if (!originalData.IsPvEBoss && !IsTargetValidForSlender(actor, building, attackEliminated)) + if (!data.IsPvEBoss && !IsTargetValidForSlender(actor, building, attackEliminated)) { continue; } - if (originalData.IsPvEBoss && !IsPvETargetValid(building)) + if (data.IsPvEBoss && !IsPvETargetValid(building)) { continue; } @@ -245,12 +237,12 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity continue; } - if (!originalData.IsPvEBoss && !IsTargetValidForSlender(actor, building, attackEliminated)) + if (!data.IsPvEBoss && !IsTargetValidForSlender(actor, building, attackEliminated)) { continue; } - if (originalData.IsPvEBoss && !IsPvETargetValid(building)) + if (data.IsPvEBoss && !IsPvETargetValid(building)) { continue; } @@ -271,12 +263,12 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity continue; } - if (!originalData.IsPvEBoss && !IsTargetValidForSlender(actor, building, attackEliminated)) + if (!data.IsPvEBoss && !IsTargetValidForSlender(actor, building, attackEliminated)) { continue; } - if (originalData.IsPvEBoss && !IsPvETargetValid(building)) + if (data.IsPvEBoss && !IsPvETargetValid(building)) { continue; } @@ -303,12 +295,37 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity prop.GetClassname(class, sizeof(class)); float realDamage = damage; - if (attackData.DamagePercent[difficulty] > 0.0) + if (attackData.GetDamagePercent(difficulty) > 0.0) { - realDamage = strcmp(class, "tank_boss", false) != 0 ? float(prop.GetProp(Prop_Send, "m_iMaxHealth")) : float(prop.GetProp(Prop_Data, "m_iMaxHealth")); - realDamage *= attackData.DamagePercent[difficulty]; + if (SF2_BasePlayer(targets.Get(i)).IsValid) + { + realDamage = float(SF2_BasePlayer(targets.Get(i)).ModifiedMaxHealth); + realDamage *= attackData.GetDamagePercent(difficulty); + } + else + { + if (strcmp(class, "prop_dynamic", false) != 0) + { + realDamage = (strcmp(class, "tank_boss", false) != 0 && strcmp(class, "func_breakable", false) != 0) ? float(prop.GetProp(Prop_Send, "m_iMaxHealth")) : float(prop.GetProp(Prop_Data, "m_iMaxHealth")); + } + else + { + realDamage = float(prop.GetProp(Prop_Data, "m_iMaxHealth")); + } + realDamage *= attackData.GetDamagePercent(difficulty); + } + } + SDKHooks_TakeDamage(prop.index, actor.index, actor.index, realDamage, 64, _, _, myEyePos, .bypassHooks = false); + + if (attackData.GetHitEffects() != null) + { + SlenderSpawnEffects(attackData.GetHitEffects(), controller.Index, false, _, _, _, prop.index); + } + + if (attackData.GetHitInputs() != null) + { + attackData.GetHitInputs().AcceptInputs(actor, prop, prop); } - SDKHooks_TakeDamage(prop.index, actor.index, actor.index, realDamage, 64, _, _, myEyePos, false); } delete targets; @@ -317,12 +334,12 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity { SF2_BasePlayer player = SF2_BasePlayer(i); - if (!originalData.IsPvEBoss && !IsTargetValidForSlender(actor, player, attackEliminated)) + if (!data.IsPvEBoss && !IsTargetValidForSlender(actor, player, attackEliminated)) { continue; } - if (originalData.IsPvEBoss && !IsPvETargetValid(player)) + if (data.IsPvEBoss && !IsPvETargetValid(player)) { continue; } @@ -343,9 +360,9 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity ScaleVector(direction, force); float realDamage = damage; - if (attackData.DamagePercent[difficulty] > 0.0) + if (attackData.GetDamagePercent(difficulty) > 0.0) { - realDamage = float(player.GetProp(Prop_Data, "m_iMaxHealth")) * attackData.DamagePercent[difficulty]; + realDamage = float(player.ModifiedMaxHealth) * attackData.GetDamagePercent(difficulty); } if (controller.HasAttribute(SF2Attribute_DeathCamOnLowHealth) || attackData.DeathCamLowHealth) @@ -385,7 +402,7 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity TF2_SetPlayerPowerPlay(player.index, false); } - attackData.ApplyDamageEffects(player, difficulty, SF2_ChaserBossEntity(actor.index)); + attackData.ApplyDamageEffects(player, difficulty, actor); // Add stress float stressScalar = damage / 125.0; @@ -396,9 +413,13 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity ClientAddStress(player.index, 0.33 * stressScalar); } - ArrayList hitSounds = hit ? data.HitSounds : data.MissSounds; - SF2BossProfileSoundInfo info; - if (actor.SearchSoundsWithSectionName(hitSounds, action.GetAttackName(), info)) + ProfileObject hitSounds = hit ? data.GetHitSounds() : data.GetMissSounds(); + ProfileSound info = hit ? attackData.GetHitSounds() : attackData.GetMissSounds(); + if (info == null) + { + actor.SearchSoundsWithSectionName(hitSounds, action.GetAttackName(), info, hit ? "hitenemy" : "missenemy"); + } + if (info != null) { info.EmitSound(_, actor.index); } @@ -424,21 +445,26 @@ static void DoMeleeAttack(SF2_ChaserAttackAction_Melee action, SF2_ChaserEntity } else { - if (attackData.MissEffects != null) + if (attackData.GetMissEffects() != null) { - SlenderSpawnEffects(attackData.MissEffects, controller.Index, false); + SlenderSpawnEffects(attackData.GetMissEffects(), controller.Index, false); + } + + if (attackData.GetMissInputs() != null) + { + attackData.GetMissInputs().AcceptInputs(actor); } } if (!SF_IsSlaughterRunMap()) { - if (attackData.Disappear[difficulty]) + if (attackData.GetDisappearAfterAttack(difficulty)) { controller.UnSpawn(true); } else { - if (hit && attackData.DisappearOnHit[difficulty]) + if (hit && attackData.GetDisappearAfterHit(difficulty)) { controller.UnSpawn(true); } @@ -454,30 +480,28 @@ static void OnAnimationEvent(SF2_ChaserAttackAction_Melee action, SF2_ChaserEnti } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + int difficulty = controller.Difficulty; - if (event == attackData.EventNumber) + if (event == action.ProfileData.GetEventNumber(difficulty)) { DoMeleeAttack(action, actor); } } -static bool IsTargetInMeleeChecks(SF2_ChaserEntity actor, SF2ChaserBossProfileAttackData data, CBaseEntity target, float range, float fov) +static bool IsTargetInMeleeChecks(SF2_ChaserEntity actor, ChaserBossProfileBaseAttack attack, CBaseEntity target, float range, float fov) { if (!target.IsValid()) { return false; } - float myPos[3], myEyePos[3], myEyeAng[3]; + float myPos[3], myEyePos[3], myEyeAng[3], offset[3]; actor.GetAbsOrigin(myPos); actor.Controller.GetEyePosition(myEyePos); actor.GetAbsAngles(myEyeAng); + actor.Controller.GetProfileData().GetEyes().GetOffsetPos(offset); - AddVectors(g_SlenderEyePosOffset[actor.Controller.Index], myEyeAng, myEyeAng); + AddVectors(offset, myEyeAng, myEyeAng); float targetPos[3]; target.WorldSpaceCenter(targetPos); @@ -497,16 +521,18 @@ static bool IsTargetInMeleeChecks(SF2_ChaserEntity actor, SF2ChaserBossProfileAt return false; } - if (!data.HitThroughWalls[actor.Controller.Difficulty]) + if (!attack.GetHitThroughWalls(actor.Controller.Difficulty)) { - TR_TraceRayFilter(myEyePos, targetPos, + Handle trace = TR_TraceRayFilterEx(myEyePos, targetPos, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP | CONTENTS_GRATE | CONTENTS_WINDOW, RayType_EndPoint, TraceRayDontHitAnyEntity, actor.index); - if (TR_DidHit() && TR_GetEntityIndex() != target.index) + if (TR_DidHit(trace) && TR_GetEntityIndex(trace) != target.index) { + delete trace; return false; } + delete trace; } return true; diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/projectile.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/projectile.sp index cf079605..67174da8 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/projectile.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/projectile.sp @@ -1,10 +1,127 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap ChaserBossProfileProjectileAttack < ChaserBossProfileBaseAttack +{ + public float GetSpeed(int difficulty) + { + float def = 1100.0; + def = this.GetDifficultyFloat("projectile_speed", difficulty, def); + def = this.GetDifficultyFloat("attack_projectile_speed", difficulty, def); + return def; + } + + public float GetBlastRadius(int difficulty) + { + float def = 128.0; + def = this.GetDifficultyFloat("projectile_radius", difficulty, def); + def = this.GetDifficultyFloat("attack_projectile_radius", difficulty, def); + return def; + } + + public float GetDeviation(int difficulty) + { + float def = 0.0; + def = this.GetDifficultyFloat("projectile_deviation", difficulty, def); + def = this.GetDifficultyFloat("attack_projectile_deviation", difficulty, def); + return def; + } + + public int GetProjectileCount(int difficulty) + { + int def = 1; + def = this.GetDifficultyInt("projectile_count", difficulty, def); + def = this.GetDifficultyInt("attack_projectile_count", difficulty, def); + return def; + } + + public bool GetCrits(int difficulty) + { + bool def = false; + def = this.GetDifficultyBool("projectile_crits", difficulty, def); + def = this.GetDifficultyBool("attack_projectile_crits", difficulty, def); + return def; + } + + property int ProjectileType + { + public get() + { + int def = SF2BossProjectileType_Fireball; + def = this.GetInt("projectiletype", def); + def = this.GetInt("attack_projectiletype", def); + return def; + } + } + + public void GetOffset(float buffer[3]) + { + this.GetVector("projectile_offset", buffer, buffer); + this.GetVector("attack_projectile_offset", buffer, buffer); + } + + public void GetFireballTrail(char[] buffer, int bufferSize) + { + this.GetString("fire_trail", buffer, bufferSize, FIREBALL_TRAIL); + } + + public void GetRocketModel(char[] buffer, int bufferSize) + { + this.GetString("rocket_model", buffer, bufferSize, ROCKET_MODEL); + } + + public float GetIceballSlowPercent(int difficulty) + { + float def = 0.55; + def = this.GetDifficultyFloat("projectile_iceslow_percent", difficulty, def); + def = this.GetDifficultyFloat("attack_projectile_iceslow_percent", difficulty, def); + return def; + } + + public float GetIceballSlowDuration(int difficulty) + { + float def = 2.0; + def = this.GetDifficultyFloat("projectile_iceslow_duration", difficulty, def); + def = this.GetDifficultyFloat("attack_projectile_iceslow_duration", difficulty, def); + return def; + } + + public void GetIceballTrail(char[] buffer, int bufferSize) + { + this.GetString("fire_iceball_trail", buffer, bufferSize, ICEBALL_TRAIL); + } + + public ProfileSound GetShootSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj == null) + { + return null; + } + + ProfileSound sound = view_as(obj.GetSection("shoot")); + if (sound == null) + { + return null; + } + + return sound; + } + + public void Precache() + { + if (this.GetShootSounds() != null) + { + this.GetShootSounds().Precache(); + } + } +} static NextBotActionFactory g_Factory; methodmap SF2_ChaserAttackAction_Projectile < NextBotAction { - public SF2_ChaserAttackAction_Projectile(int attackIndex, const char[] attackName, float fireDelay) + public SF2_ChaserAttackAction_Projectile(const char[] attackName, ChaserBossProfileProjectileAttack data, float fireDelay) { if (g_Factory == null) { @@ -13,8 +130,8 @@ methodmap SF2_ChaserAttackAction_Projectile < NextBotAction g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); g_Factory.SetEventCallback(EventResponderType_OnAnimationEvent, OnAnimationEvent); g_Factory.BeginDataMapDesc() - .DefineIntField("m_AttackIndex") .DefineStringField("m_AttackName") + .DefineIntField("m_ProfileData") .DefineFloatField("m_NextFireTime") .DefineIntField("m_RepeatIndex") .EndDataMapDesc(); @@ -22,25 +139,12 @@ methodmap SF2_ChaserAttackAction_Projectile < NextBotAction SF2_ChaserAttackAction_Projectile action = view_as(g_Factory.Create()); action.NextFireTime = fireDelay; - action.AttackIndex = attackIndex; action.SetAttackName(attackName); + action.ProfileData = data; return action; } - property int AttackIndex - { - public get() - { - return this.GetData("m_AttackIndex"); - } - - public set(int value) - { - this.SetData("m_AttackIndex", value); - } - } - public static void Initialize() { g_OnChaserGetAttackActionPFwd.AddFunction(null, OnChaserGetAttackAction); @@ -58,6 +162,19 @@ methodmap SF2_ChaserAttackAction_Projectile < NextBotAction this.SetDataString("m_AttackName", name); } + property ChaserBossProfileProjectileAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileProjectileAttack value) + { + this.SetData("m_ProfileData", value); + } + } + property float NextFireTime { public get() @@ -92,10 +209,8 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - SF2ChaserBossProfileData data; - data = chaser.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileProjectileAttack attackData = view_as(data.GetAttack(attackName)); int difficulty = chaser.Controller.Difficulty; if (attackData.Type != SF2BossAttackType_Projectile) @@ -103,7 +218,7 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - result = SF2_ChaserAttackAction_Projectile(attackData.Index, attackData.Name, attackData.DamageDelay[difficulty] + GetGameTime()); + result = SF2_ChaserAttackAction_Projectile(attackName, attackData, attackData.GetDelay(difficulty) + GetGameTime()); return Plugin_Changed; } @@ -119,44 +234,41 @@ static int Update(SF2_ChaserAttackAction_Projectile action, SF2_ChaserEntity act return action.Done("No longer firing projectiles"); } - if (actor.CancelAttack) + if (actor.CancelAttack || actor.ClearCurrentAttack) { return action.Done(); } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileProjectileAttack attackData = action.ProfileData; + ProfileMasterAnimations animations = attackData.GetAnimations(); int difficulty = controller.Difficulty; float gameTime = GetGameTime(); - if (action.NextFireTime >= 0.0 && gameTime > action.NextFireTime && attackData.EventNumber == -1) + if (action.NextFireTime >= 0.0 && gameTime > action.NextFireTime && attackData.GetEventNumber(difficulty) == -1) { - FireProjectile(actor, action.GetAttackName()); - actor.ResetProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_ProjectileShoot], _, action.GetAttackName()); + FireProjectile(actor, action, action.GetAttackName()); + actor.ResetProfileAnimation(animations == null ? g_SlenderAnimationsList[SF2BossAnimation_ProjectileShoot] : "shoot", .preDefinedName = animations == null ? action.GetAttackName() : "", .animations = animations); - int repeatState = attackData.Repeat; + int repeatState = attackData.GetRepeatState(difficulty); if (repeatState > 0) { switch (repeatState) { case 1: { - action.NextFireTime = gameTime + attackData.DamageDelay[difficulty]; + action.NextFireTime = gameTime + attackData.GetDelay(difficulty); } case 2: { - if (action.RepeatIndex >= attackData.RepeatTimers.Length) + if (attackData.GetRepeatTimer(difficulty, action.RepeatIndex) < 0.0) { action.NextFireTime = -1.0; } else { - float next = attackData.RepeatTimers.Get(action.RepeatIndex); - action.NextFireTime = next + gameTime; + action.NextFireTime = attackData.GetRepeatTimer(difficulty, action.RepeatIndex) + gameTime; action.RepeatIndex++; } } @@ -170,7 +282,7 @@ static int Update(SF2_ChaserAttackAction_Projectile action, SF2_ChaserEntity act return action.Continue(); } -static void FireProjectile(SF2_ChaserEntity actor, const char[] attackName) +static void FireProjectile(SF2_ChaserEntity actor, SF2_ChaserAttackAction_Projectile action, const char[] attackName) { SF2NPC_Chaser controller = actor.Controller; float targetPos[3], myPos[3], myAng[3]; @@ -191,86 +303,98 @@ static void FireProjectile(SF2_ChaserEntity actor, const char[] attackName) targetPos[2] = eyePos[2] + fwd[2] * 9001.0; } int difficulty = controller.Difficulty; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileProjectileAttack attackData = action.ProfileData; + ChaserBossProjectileData projectileData = data.GetProjectiles(); bool attackWaiters = (controller.Flags & SFF_ATTACKWAITERS) != 0; - if (originalData.IsPvEBoss) + if (data.IsPvEBoss) { attackWaiters = true; } - ArrayList projectileSounds = data.ProjectileShootSounds; - SF2BossProfileSoundInfo soundInfo; - if (actor.SearchSoundsWithSectionName(projectileSounds, attackName, soundInfo)) + ProfileSound soundInfo = attackData.GetShootSounds(); + if (soundInfo == null) + { + actor.SearchSoundsWithSectionName(data.GetProjectileShootSounds(), attackName, soundInfo, "attackshootprojectile"); + } + if (soundInfo != null) { soundInfo.EmitSound(_, actor.index); } float effectPos[3]; - effectPos = attackData.ProjectileOffset; + attackData.GetOffset(effectPos); VectorTransform(effectPos, myPos, myAng, effectPos); - for (int i = 0; i < attackData.ProjectileCount[difficulty]; i++) + for (int i = 0; i < attackData.GetProjectileCount(difficulty); i++) { float direction[3], angle[3]; SubtractVectors(targetPos, effectPos, direction); - float deviation = attackData.ProjectileDeviation[difficulty]; + float deviation = attackData.GetDeviation(difficulty) / 10.0; - if (deviation != 0) - { - direction[0] += GetRandomFloat(-deviation, deviation); - direction[1] += GetRandomFloat(-deviation, deviation); - direction[2] += GetRandomFloat(-deviation, deviation); - } NormalizeVector(direction, direction); GetVectorAngles(direction, angle); + if (deviation != 0.0) + { + angle[0] += GetRandomFloat(-deviation, deviation); + angle[1] += GetRandomFloat(-deviation, deviation); + angle[2] += GetRandomFloat(-deviation, deviation); + } switch (attackData.ProjectileType) { case SF2BossProjectileType_Fireball: { - SF2_ProjectileFireball.Create(actor, effectPos, angle, attackData.ProjectileSpeed[difficulty], attackData.ProjectileDamage[difficulty], - attackData.ProjectileRadius[difficulty], data.FireballExplodeSound, attackData.FireballTrail, attackWaiters); + char trail[PLATFORM_MAX_PATH], explode[PLATFORM_MAX_PATH]; + attackData.GetFireballTrail(trail, sizeof(trail)); + projectileData.GetFireballExplodeSound(explode, sizeof(explode)); + SF2_ProjectileFireball.Create(actor, effectPos, angle, attackData.GetSpeed(difficulty), attackData.GetDamage(difficulty), + attackData.GetBlastRadius(difficulty), explode, trail, attackWaiters); } case SF2BossProjectileType_Iceball: { - SF2_ProjectileIceball.Create(actor, effectPos, angle, attackData.ProjectileSpeed[difficulty], attackData.ProjectileDamage[difficulty], - attackData.ProjectileRadius[difficulty], data.FireballExplodeSound, attackData.IceballTrail, attackData.IceballSlowdownDuration[difficulty], attackData.IceballSlowdownPercent[difficulty], data.IceballSlowSound, attackWaiters); + char trail[PLATFORM_MAX_PATH], explode[PLATFORM_MAX_PATH], slow[PLATFORM_MAX_PATH]; + attackData.GetIceballTrail(trail, sizeof(trail)); + projectileData.GetFireballExplodeSound(explode, sizeof(explode)); + projectileData.GetIceballSlowSound(slow, sizeof(slow)); + SF2_ProjectileIceball.Create(actor, effectPos, angle, attackData.GetSpeed(difficulty), attackData.GetDamage(difficulty), + attackData.GetBlastRadius(difficulty), explode, trail, attackData.GetIceballSlowDuration(difficulty), attackData.GetIceballSlowPercent(difficulty), slow, attackWaiters); } case SF2BossProjectileType_Rocket: { - SF2_ProjectileRocket.Create(actor, effectPos, angle, attackData.ProjectileSpeed[difficulty], attackData.ProjectileDamage[difficulty], - attackData.ProjectileRadius[difficulty], attackData.CritProjectiles[difficulty], data.RocketTrail, data.RocketExplodeParticle, data.RocketExplodeSound, attackData.RocketModel, attackWaiters); + char model[PLATFORM_MAX_PATH], trail[PLATFORM_MAX_PATH], explodeSound[PLATFORM_MAX_PATH], explodeParticle[64]; + attackData.GetRocketModel(model, sizeof(model)); + projectileData.GetRocketTrail(trail, sizeof(trail)); + projectileData.GetRocketExplodeSound(explodeSound, sizeof(explodeSound)); + projectileData.GetRocketExplodeParticle(explodeParticle, sizeof(explodeParticle)); + SF2_ProjectileRocket.Create(actor, effectPos, angle, attackData.GetSpeed(difficulty), attackData.GetDamage(difficulty), + attackData.GetBlastRadius(difficulty), attackData.GetCrits(difficulty), trail, explodeParticle, explodeSound, model, attackWaiters); } case SF2BossProjectileType_SentryRocket: { - SF2_ProjectileSentryRocket.Create(actor, effectPos, angle, attackData.ProjectileSpeed[difficulty], attackData.ProjectileDamage[difficulty], - attackData.ProjectileRadius[difficulty], attackData.CritProjectiles[difficulty], attackWaiters); + SF2_ProjectileSentryRocket.Create(actor, effectPos, angle, attackData.GetSpeed(difficulty), attackData.GetDamage(difficulty), + attackData.GetBlastRadius(difficulty), attackData.GetCrits(difficulty), attackWaiters); } case SF2BossProjectileType_Mangler: { - SF2_ProjectileCowMangler.Create(actor, effectPos, angle, attackData.ProjectileSpeed[difficulty], attackData.ProjectileDamage[difficulty], - attackData.ProjectileRadius[difficulty], attackWaiters); + SF2_ProjectileCowMangler.Create(actor, effectPos, angle, attackData.GetSpeed(difficulty), attackData.GetDamage(difficulty), + attackData.GetBlastRadius(difficulty), attackWaiters); } case SF2BossProjectileType_Grenade: { - SF2_ProjectileGrenade.Create(actor, effectPos, angle, attackData.ProjectileSpeed[difficulty], attackData.ProjectileDamage[difficulty], - attackData.ProjectileRadius[difficulty], attackData.CritProjectiles[difficulty], "pipebombtrail_blue", ROCKET_EXPLODE_PARTICLE, ROCKET_IMPACT, "models/weapons/w_models/w_grenade_grenadelauncher.mdl", attackWaiters); + SF2_ProjectileGrenade.Create(actor, effectPos, angle, attackData.GetSpeed(difficulty), attackData.GetDamage(difficulty), + attackData.GetBlastRadius(difficulty), attackData.GetCrits(difficulty), "pipebombtrail_blue", ROCKET_EXPLODE_PARTICLE, ROCKET_IMPACT, "models/weapons/w_models/w_grenade_grenadelauncher.mdl", attackWaiters); } case SF2BossProjectileType_Arrow: { - SF2_ProjectileArrow.Create(actor, effectPos, angle, attackData.ProjectileSpeed[difficulty], attackData.ProjectileDamage[difficulty], - attackData.CritProjectiles[difficulty], "pipebombtrail_blue", "weapons/fx/rics/arrow_impact_flesh2.wav", "models/weapons/w_models/w_arrow.mdl", attackWaiters); + SF2_ProjectileArrow.Create(actor, effectPos, angle, attackData.GetSpeed(difficulty), attackData.GetDamage(difficulty), + attackData.GetCrits(difficulty), "effects/arrowtrail_red.vmt", "weapons/fx/rics/arrow_impact_flesh2.wav", "models/weapons/w_models/w_arrow.mdl", attackWaiters); } case SF2BossProjectileType_Baseball: { - SF2_ProjectileBaseball.Create(actor, effectPos, angle, attackData.ProjectileSpeed[difficulty], attackData.ProjectileDamage[difficulty], - attackData.CritProjectiles[difficulty], "models/weapons/w_models/w_baseball.mdl", attackWaiters); + SF2_ProjectileBaseball.Create(actor, effectPos, angle, attackData.GetSpeed(difficulty), attackData.GetDamage(difficulty), + attackData.GetCrits(difficulty), "models/weapons/w_models/w_baseball.mdl", attackWaiters); } } } @@ -284,13 +408,10 @@ static void OnAnimationEvent(SF2_ChaserAttackAction_Projectile action, SF2_Chase } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileProjectileAttack attackData = action.ProfileData; - if (event == attackData.EventNumber) + if (event == attackData.GetEventNumber(controller.Difficulty)) { - FireProjectile(actor, action.GetAttackName()); + FireProjectile(actor, action, action.GetAttackName()); } } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/tongue.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/tongue.sp index a4286682..fe19b4ae 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/tongue.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/attacks/tongue.sp @@ -1,10 +1,142 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap ChaserBossProfileTongueAttack < ChaserBossProfileBaseAttack +{ + public float GetSpeed(int difficulty) + { + float def = 900.0; + ProfileObject obj = this.GetSection("tongue"); + if (obj != null) + { + def = obj.GetDifficultyFloat("speed", difficulty, def); + } + return def; + } + + public float GetPullScale(int difficulty) + { + float def = 6.0; + ProfileObject obj = this.GetSection("tongue"); + if (obj != null) + { + def = obj.GetDifficultyFloat("pull_scale", difficulty, def); + } + return def; + } + + public bool CanEscape(int difficulty) + { + bool def = true; + ProfileObject obj = this.GetSection("tongue"); + if (obj != null) + { + def = obj.GetDifficultyBool("can_escape", difficulty, def); + } + return def; + } + + public void GetMaterial(char[] buffer, int bufferSize) + { + ProfileObject obj = this.GetSection("tongue"); + if (obj != null) + { + obj.GetString("material", buffer, bufferSize); + } + } + + public void SetMaterial(char[] buffer) + { + ProfileObject obj = this.GetSection("tongue"); + if (obj != null) + { + obj.SetString("material", buffer); + } + } + + public void GetAttachment(char[] buffer, int bufferSize) + { + ProfileObject obj = this.GetSection("tongue"); + if (obj != null) + { + obj.GetString("attachment", buffer, bufferSize); + } + } + + public void GetOffset(float buffer[3]) + { + ProfileObject obj = this.GetSection("tongue"); + if (obj != null) + { + obj.GetVector("offset", buffer); + } + } + + public ProfileSound GetLaunchSounds() + { + ProfileSound def = null; + ProfileObject obj = this.GetSection("tongue"); + obj = obj != null ? obj.GetSection("sounds") : null; + if (obj != null) + { + def = view_as(obj.GetSection("launch")); + } + return def; + } + + public ProfileSound GetTongueHitSounds() + { + ProfileSound def = null; + ProfileObject obj = this.GetSection("tongue"); + obj = obj != null ? obj.GetSection("sounds") : null; + if (obj != null) + { + def = view_as(obj.GetSection("hit")); + } + return def; + } + + public ProfileSound GetTiedSounds() + { + ProfileSound def = null; + ProfileObject obj = this.GetSection("tongue"); + obj = obj != null ? obj.GetSection("sounds") : null; + if (obj != null) + { + def = view_as(obj.GetSection("tied")); + } + return def; + } + + public ProfileMasterAnimations GetTongueAnimations() + { + ProfileMasterAnimations def = null; + ProfileObject obj = this.GetSection("tongue"); + if (obj != null) + { + def = view_as(obj.GetSection("animations")); + } + return def; + } + + public void Precache() + { + char asset[PLATFORM_MAX_PATH]; + this.GetMaterial(asset, sizeof(asset)); + if (asset[0] != '\0') + { + PrecacheMaterial2(asset, g_FileCheckConVar.BoolValue); + StripMaterialsFolder(asset, sizeof(asset)); + this.SetMaterial(asset); + } + } +} static NextBotActionFactory g_Factory; methodmap SF2_ChaserAttackAction_Tongue < NextBotAction { - public SF2_ChaserAttackAction_Tongue(int attackIndex, const char[] attackName, float fireDelay) + public SF2_ChaserAttackAction_Tongue(const char[] attackName, ChaserBossProfileTongueAttack data, float fireDelay) { if (g_Factory == null) { @@ -15,8 +147,8 @@ methodmap SF2_ChaserAttackAction_Tongue < NextBotAction g_Factory.SetEventCallback(EventResponderType_OnCommandString, OnCommandString); g_Factory.SetEventCallback(EventResponderType_OnAnimationEvent, OnAnimationEvent); g_Factory.BeginDataMapDesc() - .DefineIntField("m_AttackIndex") .DefineStringField("m_AttackName") + .DefineIntField("m_ProfileData") .DefineFloatField("m_NextFireTime") .DefineEntityField("m_TongueEntity") .DefineEntityField("m_TongueEntityEnd") @@ -30,27 +162,15 @@ methodmap SF2_ChaserAttackAction_Tongue < NextBotAction SF2_ChaserAttackAction_Tongue action = view_as(g_Factory.Create()); action.NextFireTime = fireDelay; - action.AttackIndex = attackIndex; action.SetAttackName(attackName); + action.ProfileData = data; return action; } - property int AttackIndex - { - public get() - { - return this.GetData("m_AttackIndex"); - } - - public set(int value) - { - this.SetData("m_AttackIndex", value); - } - } - public static void Initialize() { + g_OnPlayerSpawnPFwd.AddFunction(null, OnPlayerSpawn); g_OnPlayerJumpPFwd.AddFunction(null, OnJump); g_OnChaserGetAttackActionPFwd.AddFunction(null, OnChaserGetAttackAction); } @@ -67,6 +187,19 @@ methodmap SF2_ChaserAttackAction_Tongue < NextBotAction this.SetDataString("m_AttackName", name); } + property ChaserBossProfileTongueAttack ProfileData + { + public get() + { + return this.GetData("m_ProfileData"); + } + + public set(ChaserBossProfileTongueAttack value) + { + this.SetData("m_ProfileData", value); + } + } + property float NextFireTime { public get() @@ -179,10 +312,8 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - SF2ChaserBossProfileData data; - data = chaser.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileTongueAttack attackData = view_as(data.GetAttack(attackName)); int difficulty = chaser.Controller.Difficulty; if (attackData.Type != SF2BossAttackType_Tongue) @@ -190,45 +321,58 @@ static Action OnChaserGetAttackAction(SF2_ChaserEntity chaser, const char[] atta return Plugin_Continue; } - result = SF2_ChaserAttackAction_Tongue(attackData.Index, attackData.Name, attackData.DamageDelay[difficulty] + GetGameTime()); + result = SF2_ChaserAttackAction_Tongue(attackName, attackData, attackData.GetDelay(difficulty) + GetGameTime()); return Plugin_Changed; } +static void OnPlayerSpawn(SF2_BasePlayer client) +{ + if (IsClientParticipating(client.index) && client.IsLatched) + { + TF2Attrib_RemoveByName(client.index, "increased jump height"); + TF2Attrib_RemoveByName(client.index, "move speed bonus"); + client.UpdateSpeed(); + } + client.IsLatched = false; + client.LatchCount = 0; + client.Latcher = -1; +} + static void OnJump(SF2_BasePlayer client) { - if (client.IsEliminated || IsRoundEnding() || IsRoundInWarmup() || client.HasEscaped) + if (!client.IsLatched) { return; } - if (client.IsLatched) + client.LatchCount--; + if (client.LatchCount <= 0) { - client.LatchCount--; - if (client.LatchCount <= 0) + client.IsLatched = false; + client.LatchCount = 0; + TF2Attrib_RemoveByName(client.index, "increased jump height"); + TF2Attrib_RemoveByName(client.index, "move speed bonus"); + client.UpdateSpeed(); + for (int i = 0; i < MAX_BOSSES; i++) { - client.IsLatched = false; - client.LatchCount = 0; - for (int i = 0; i < MAX_BOSSES; i++) + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(i); + if (!npc.IsValid()) { - SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(i); - if (!npc.IsValid()) - { - continue; - } - - if (client.Latcher != npc.Index) - { - continue; - } + continue; + } - SF2_ChaserEntity chaser = SF2_ChaserEntity(npc.EntIndex); - if (!chaser.IsValid()) - { - continue; - } + if (client.Latcher != npc.Index) + { + continue; + } - chaser.MyNextBotPointer().GetIntentionInterface().OnCommandString("break tongue"); + SF2_ChaserEntity chaser = SF2_ChaserEntity(npc.EntIndex); + if (!chaser.IsValid()) + { + continue; } + + chaser.MyNextBotPointer().GetIntentionInterface().OnCommandString("break tongue"); } } } @@ -245,7 +389,7 @@ static int Update(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity actor, return action.Done("No longer firing a tongue"); } - if (actor.CancelAttack) + if (actor.CancelAttack || actor.ClearCurrentAttack) { return action.Done(); } @@ -262,13 +406,9 @@ static int Update(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity actor, SF2NPC_Chaser controller = actor.Controller; bool attackEliminated = (controller.Flags & SFF_ATTACKWAITERS) != 0; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); - if (originalData.IsPvEBoss) + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileTongueAttack attackData = action.ProfileData; + if (data.IsPvEBoss) { attackEliminated = true; } @@ -276,7 +416,7 @@ static int Update(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity actor, float gameTime = GetGameTime(); - if (action.NextFireTime >= 0.0 && gameTime > action.NextFireTime && attackData.EventNumber == -1) + if (action.NextFireTime >= 0.0 && gameTime > action.NextFireTime && attackData.GetEventNumber(difficulty) == -1) { FireTongue(actor, action); @@ -310,16 +450,19 @@ static int Update(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity actor, float worldSpace[3]; player.WorldSpaceCenter(worldSpace); - TR_TraceRayFilter(tonguePos, worldSpace, + Handle trace = TR_TraceRayFilterEx(tonguePos, worldSpace, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP | CONTENTS_GRATE | CONTENTS_WINDOW, RayType_EndPoint, TraceRayDontHitAnyEntity, tongueEnd.index); - if (TR_DidHit() && TR_GetEntityIndex() != player.index) + if (TR_DidHit(trace) && TR_GetEntityIndex(trace) != player.index) { + delete trace; continue; } - if (!attackData.TongueCanEscape[difficulty]) + delete trace; + + if (!attackData.CanEscape(difficulty)) { player.Stun(1.0, 1.0, TF_STUNFLAG_NOSOUNDOREFFECT | TF_STUNFLAG_BONKSTUCK); } @@ -327,7 +470,7 @@ static int Update(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity actor, { player.Stun(2.0, 1.0, TF_STUNFLAG_SLOWDOWN); } - player.TakeDamage(_, actor.index, actor.index, attackData.Damage[difficulty], DMG_BULLET | DMG_PREVENT_PHYSICS_FORCE); + player.TakeDamage(_, actor.index, actor.index, attackData.GetDamage(difficulty), attackData.GetDamageType(difficulty)); tongueEnd.AcceptInput("ClearParent"); tongueEnd.Teleport(worldSpace, NULL_VECTOR, NULL_VECTOR); @@ -337,11 +480,14 @@ static int Update(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity actor, action.TongueTime = 0.5 + gameTime; action.LatchedTarget = player; action.CaughtTarget = true; - attackData.TongueTiedSound.EmitSound(_, tongueEnd.index); - attackData.TongueHitSound.EmitSound(_, tongueEnd.index); + attackData.GetTiedSounds().EmitSound(_, tongueEnd.index); + attackData.GetTongueHitSounds().EmitSound(_, tongueEnd.index); player.IsLatched = true; + TF2Attrib_SetByName(player.index, "increased jump height", 0.0); + TF2Attrib_SetByName(player.index, "move speed bonus", 0.0001); + player.UpdateSpeed(); player.LatchCount = GetRandomInt(6, 10); - if (!attackData.TongueCanEscape[difficulty]) + if (!attackData.CanEscape(difficulty)) { player.LatchCount = 999999; // I'm lazy } @@ -377,10 +523,7 @@ static void TongueThink(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity a float gameTime = GetGameTime(); SF2NPC_Chaser controller = actor.Controller; int difficulty = controller.Difficulty; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileTongueAttack attackData = action.ProfileData; SF2_BasePlayer player = action.LatchedTarget; if (!player.IsValid || !player.IsAlive) @@ -394,16 +537,19 @@ static void TongueThink(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity a tongueStart.GetAbsOrigin(tonguePos); player.WorldSpaceCenter(worldSpace); - TR_TraceRayFilter(tonguePos, worldSpace, + Handle trace = TR_TraceRayFilterEx(tonguePos, worldSpace, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP | CONTENTS_GRATE | CONTENTS_WINDOW, RayType_EndPoint, TraceRayDontHitAnyEntity, tongueStart.index); - if (TR_DidHit() && TR_GetEntityIndex() != player.index) + if (TR_DidHit(trace) && TR_GetEntityIndex(trace) != player.index) { Unlatch(action, true); + delete trace; return; } + delete trace; + INextBot bot = actor.MyNextBotPointer(); ILocomotion loco = bot.GetLocomotionInterface(); float myPos[3], targetPos[3]; @@ -413,7 +559,7 @@ static void TongueThink(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity a if (action.TongueTime > -1.0 && action.TongueTime <= gameTime) { SF2_BasePlayer latched = action.LatchedTarget; - if (!attackData.TongueCanEscape[difficulty]) + if (!attackData.CanEscape(difficulty)) { latched.Stun(1.0, 1.0, TF_STUNFLAG_BONKSTUCK|TF_STUNFLAG_NOSOUNDOREFFECT); } @@ -427,10 +573,10 @@ static void TongueThink(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntity a SubtractVectors(myPos, targetPos, velocity); velocity[2] = 0.0; - ScaleVector(velocity, attackData.TonguePullScale[difficulty]); + ScaleVector(velocity, attackData.GetPullScale(difficulty)); player.SetPropVector(Prop_Data, "m_vecBaseVelocity", velocity); - player.TakeDamage(_, actor.index, actor.index, attackData.Damage[difficulty], DMG_BULLET | DMG_PREVENT_PHYSICS_FORCE); + player.TakeDamage(_, actor.index, actor.index, attackData.GetDamage(difficulty), attackData.GetDamageType(difficulty)); } loco.FaceTowards(targetPos); @@ -452,6 +598,9 @@ static void Unlatch(SF2_ChaserAttackAction_Tongue action, bool removeSlow = fals { action.LatchedTarget.IsLatched = false; action.LatchedTarget.LatchCount = 0; + TF2Attrib_RemoveByName(action.LatchedTarget.index, "increased jump height"); + TF2Attrib_RemoveByName(action.LatchedTarget.index, "move speed bonus"); + action.LatchedTarget.UpdateSpeed(); if (removeSlow) { action.LatchedTarget.ChangeCondition(TFCond_Dazed, true); @@ -479,12 +628,9 @@ static void FireTongue(SF2_ChaserEntity actor, SF2_ChaserAttackAction_Tongue act actor.GetAbsOrigin(myPos); actor.GetAbsAngles(myAng); int difficulty = controller.Difficulty; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileTongueAttack attackData = action.ProfileData; - effectPos = attackData.TongueOffset; + attackData.GetOffset(effectPos); VectorTransform(effectPos, myPos, myAng, effectPos); CBaseEntity tongue = CBaseEntity(CreateEntityByName("move_rope")); @@ -502,14 +648,16 @@ static void FireTongue(SF2_ChaserEntity actor, SF2_ChaserAttackAction_Tongue act char buffer[PLATFORM_MAX_PATH]; tongue.KeyValue("Slack", "50"); - tongue.KeyValue("RopeMaterial", attackData.TongueMaterial); + attackData.GetMaterial(buffer, sizeof(buffer)); + tongue.KeyValue("RopeMaterial", buffer); FormatEx(buffer, sizeof(buffer), "rope_%i", controller.Index); tongue.KeyValue("NextKey", buffer); CBaseEntity tongueEnd = CBaseEntity(CreateEntityByName("move_rope")); tongueEnd.KeyValue("targetname", buffer); tongueEnd.KeyValue("Slack", "50"); - tongueEnd.KeyValue("RopeMaterial", attackData.TongueMaterial); + attackData.GetMaterial(buffer, sizeof(buffer)); + tongueEnd.KeyValue("RopeMaterial", buffer); tongueEnd.Teleport(effectPos, myAng, NULL_VECTOR); tongueEnd.SetMoveType(MOVETYPE_FLY); @@ -527,9 +675,9 @@ static void FireTongue(SF2_ChaserEntity actor, SF2_ChaserAttackAction_Tongue act NormalizeVector(shootAng, shootAng); GetVectorAngles(shootAng, shootAng); GetAngleVectors(shootAng, velocity, NULL_VECTOR, NULL_VECTOR); - ScaleVector(velocity, attackData.TongueSpeed[difficulty]); + ScaleVector(velocity, attackData.GetSpeed(difficulty)); - attackData.TongueLaunchSound.EmitSound(_, actor.index); + attackData.GetLaunchSounds().EmitSound(_, actor.index); CBaseEntity trail = CBaseEntity(CreateEntityByName("tf_projectile_grapplinghook")); trail.SetModel("models/roller.mdl"); @@ -567,18 +715,21 @@ static Action Timer_Think(Handle timer, any ref) } float pos[3], mins[3] = {-8.0, ... }, maxs[3] = {8.0, ... }; trail.GetAbsOrigin(pos); - TR_TraceHullFilter(pos, pos, mins, maxs, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP, TraceRayGrenade, trail.index); + Handle trace = TR_TraceHullFilterEx(pos, pos, mins, maxs, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP, TraceRayGrenade, trail.index); - if (TR_GetEntityIndex() == 0) + if (TR_GetEntityIndex(trace) == 0) { if (IsValidEntity(owner)) { RemoveEntity(owner); } RemoveEntity(trail.index); + delete trace; return Plugin_Stop; } + delete trace; + return Plugin_Continue; } @@ -588,15 +739,18 @@ static int OnCommandString(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntit int difficulty = controller.Difficulty; char animName[64]; float rate = 1.0, duration = 0.0, cycle = 0.0; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileTongueAttack attackData = action.ProfileData; + ProfileAnimation section = null; if (strcmp(command, "break tongue") == 0) { Unlatch(action, true); - if (attackData.Animations.GetAnimation("break", difficulty, animName, sizeof(animName), rate, duration, cycle)) + section = attackData.GetTongueAnimations().GetAnimation("break"); + if (section != null) { + section.GetAnimationName(difficulty, animName, sizeof(animName)); + rate = section.GetAnimationPlaybackRate(difficulty); + duration = section.GetDuration(difficulty); + cycle = section.GetAnimationCycle(difficulty); int sequence = LookupProfileAnimation(actor.index, animName); if (sequence != -1) { @@ -607,8 +761,13 @@ static int OnCommandString(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEntit if (strcmp(command, "latch tongue") == 0 && !action.InPullAnimation) { - if (attackData.Animations.GetAnimation("pull", difficulty, animName, sizeof(animName), rate, duration, cycle)) + section = attackData.GetTongueAnimations().GetAnimation("pull"); + if (section != null) { + section.GetAnimationName(difficulty, animName, sizeof(animName)); + rate = section.GetAnimationPlaybackRate(difficulty); + duration = section.GetDuration(difficulty); + cycle = section.GetAnimationCycle(difficulty); int sequence = LookupProfileAnimation(actor.index, animName); if (sequence != -1) { @@ -631,12 +790,9 @@ static void OnAnimationEvent(SF2_ChaserAttackAction_Tongue action, SF2_ChaserEnt } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(action.GetAttackName(), attackData); + ChaserBossProfileTongueAttack attackData = action.ProfileData; - if (event == attackData.EventNumber) + if (event == attackData.GetEventNumber(controller.Difficulty)) { FireTongue(actor, action); } diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/beatbox_freeze.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/beatbox_freeze.sp index 8ec201b0..9ff1ade0 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/beatbox_freeze.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/beatbox_freeze.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chase.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chase.sp index c4b697e4..2c19873a 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chase.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chase.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -38,10 +39,10 @@ static int OnStart(SF2_ChaserChaseAction action, SF2_ChaserEntity actor, NextBot return action.ChangeTo(SF2_ChaserIdleAction(), "My target is no longer valid!"); } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileChaseData chaseData = data.GetChaseBehavior(); - actor.CurrentChaseDuration = data.ChaseDuration[difficulty]; + actor.CurrentChaseDuration = chaseData.GetMaxChaseDuration(difficulty); if (actor.InitialChaseDuration > 0.0) { @@ -49,7 +50,7 @@ static int OnStart(SF2_ChaserChaseAction action, SF2_ChaserEntity actor, NextBot actor.InitialChaseDuration = 0.0; } - if (data.AlertOnChaseInfo.OnChangeState[difficulty]) + if (chaseData.GetChaseTogetherData().IsEnabled(difficulty) && chaseData.GetChaseTogetherData().ShouldStartOnStateChange(difficulty)) { actor.ForceChaseOtherBosses(); } @@ -67,12 +68,11 @@ static int Update(SF2_ChaserChaseAction action, SF2_ChaserEntity actor) return action.Continue(); } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileAlertData alertData = data.GetAlertBehavior(); + ChaserBossProfileChaseData chaseData = data.GetChaseBehavior(); - if (!originalData.IsPvEBoss && IsBeatBoxBeating(2) && actor.State != STATE_ATTACK && !actor.IsInChaseInitial) + if (!data.IsPvEBoss && IsBeatBoxBeating(2) && actor.State != STATE_ATTACK && !actor.IsInChaseInitial) { return action.SuspendFor(SF2_ChaserBeatBoxFreezeAction(actor.IsAttemptingToMove)); } @@ -81,11 +81,7 @@ static int Update(SF2_ChaserChaseAction action, SF2_ChaserEntity actor) float gameTime = GetGameTime(); int difficulty = controller.Difficulty; INextBot bot = actor.MyNextBotPointer(); - bool attackEliminated = (controller.Flags & SFF_ATTACKWAITERS) != 0; - if (originalData.IsPvEBoss) - { - attackEliminated = true; - } + int interruptConditions = actor.InterruptConditions; if (target.IsValid()) { @@ -105,33 +101,33 @@ static int Update(SF2_ChaserChaseAction action, SF2_ChaserEntity actor) if (actor.CurrentChaseDuration <= 0.0) { actor.State = STATE_ALERT; - return action.ChangeTo(SF2_ChaserAlertAction(_, data.AlertData.RunOnSuspect[difficulty]), "Oh he got away..."); + return action.ChangeTo(SF2_ChaserAlertAction(_, alertData.ShouldRunOnSuspect(difficulty)), "Oh he got away..."); } } actor.RegisterProjectiles(); SF2_BasePlayer player = SF2_BasePlayer(target.index); - if (!originalData.IsPvEBoss && player.IsValid && player.HasEscaped) + if (!data.IsPvEBoss && player.IsValid && player.HasEscaped) { actor.State = STATE_IDLE; return action.ChangeTo(SF2_ChaserIdleAction(), "Our target escaped, that is no good!"); } - if (player.IsValid && player.CanSeeSlender(controller.Index, false, _, !attackEliminated)) + if ((interruptConditions & COND_SAWENEMY) != 0 || (interruptConditions & COND_ENEMYVISIBLE) != 0) { - float maxRange = data.ChaseDurationAddMaxRange[difficulty]; + float maxRange = chaseData.GetDurationTargetRange(difficulty); float distanceRatio = bot.GetRangeTo(player.index) / maxRange; if (maxRange > 0.0 && distanceRatio < 1.0) { - float durationTimeAddMin = data.ChaseDurationAddVisibleMin[difficulty]; - float durationTimeAddMax = data.ChaseDurationAddVisibleMax[difficulty]; - float durationAdd = durationTimeAddMin + ((durationTimeAddMax - durationTimeAddMin) * distanceRatio); + float addNear = chaseData.GetDurationAddVisibleTargetNear(difficulty); + float addFar = chaseData.GetDurationAddVisibleTargetFar(difficulty); + float durationAdd = addNear + ((addFar - addNear) * distanceRatio); actor.CurrentChaseDuration += durationAdd * GetGameFrameTime(); - if (actor.CurrentChaseDuration > data.ChaseDuration[difficulty]) + if (actor.CurrentChaseDuration > chaseData.GetMaxChaseDuration(difficulty)) { - actor.CurrentChaseDuration = data.ChaseDuration[difficulty]; + actor.CurrentChaseDuration = chaseData.GetMaxChaseDuration(difficulty); } } } @@ -153,9 +149,9 @@ static int Update(SF2_ChaserChaseAction action, SF2_ChaserEntity actor) } } - if (data.ChaseOnLookData.Enabled[difficulty] && controller.ChaseOnLookTargets.FindValue(target.index) != -1) + if (data.GetChaseOnLookData().IsEnabled(difficulty) && controller.ChaseOnLookTargets.FindValue(target.index) != -1) { - actor.CurrentChaseDuration = data.ChaseDuration[difficulty]; + actor.CurrentChaseDuration = chaseData.GetMaxChaseDuration(difficulty); } if (gameTime >= actor.NextVoiceTime && !actor.IsAttacking) @@ -177,7 +173,7 @@ static void OnResume(SF2_ChaserChaseAction action, SF2_ChaserEntity actor) SF2NPC_Chaser controller = actor.Controller; if (controller.IsValid()) { - if (controller.GetProfileData().StunData.ChaseInitialOnEnd[controller.Difficulty]) + if (controller.GetProfileData().GetStunBehavior().CanChaseInitialOnEnd(controller.Difficulty)) { actor.PerformVoice(SF2BossSound_ChaseInitial); } @@ -192,6 +188,7 @@ static void OnEnd(SF2_ChaserChaseAction action, SF2_ChaserEntity actor) return; } SF2NPC_Chaser controller = actor.Controller; + ChaserBossProfile data = controller.GetProfileData(); actor.EndCloak(); PathFollower path = actor.Controller.Path; float gameTime = GetGameTime(); @@ -204,18 +201,14 @@ static void OnEnd(SF2_ChaserChaseAction action, SF2_ChaserEntity actor) int difficulty = actor.Controller.Difficulty; if (actor.FollowedCompanionChase) { - SF2ChaserBossProfileData data; - data = actor.Controller.GetProfileData(); - actor.FollowCooldownChase = GetGameTime() + data.AlertOnChaseInfo.FollowCooldown[difficulty]; + actor.FollowCooldownChase = GetGameTime() + data.GetChaseBehavior().GetChaseTogetherData().GetFollowCooldown(difficulty); } if (actor.Teleporters != null) { actor.Teleporters.Clear(); } - SF2ChaserBossProfileData chaserData; - chaserData = actor.Controller.GetProfileData(); - float cooldown = chaserData.AutoChaseAfterChaseCooldown[difficulty]; + float cooldown = data.GetAutoChaseData().GetCooldownAfterChase(difficulty); if (cooldown > 0.0) { float nextTime = gameTime + cooldown; diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chaseinitial.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chaseinitial.sp index 2f249f60..adfc1aa9 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chaseinitial.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chaseinitial.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -25,10 +26,8 @@ methodmap SF2_ChaserChaseInitialAction < NextBotAction return false; } - SF2NPC_BaseNPC baseController = view_as(actor.Controller); - SF2BossProfileData data; - data = baseController.GetProfileData(); - if (!data.AnimationData.HasAnimationSection(g_SlenderAnimationsList[SF2BossAnimation_ChaseInitial])) + ChaserBossProfile data = actor.Controller.GetProfileData(); + if (!data.GetAnimations().HasAnimationSection(g_SlenderAnimationsList[SF2BossAnimation_ChaseInitial])) { return false; } @@ -39,21 +38,14 @@ methodmap SF2_ChaserChaseInitialAction < NextBotAction static NextBotAction InitialContainedAction(SF2_ChaserChaseInitialAction action, SF2_ChaserEntity actor) { - SF2NPC_BaseNPC baseController = view_as(actor.Controller); - SF2BossProfileData data; - data = baseController.GetProfileData(); - char animName[64]; - float rate = 1.0, duration = 0.0, cycle = 0.0; - int difficulty = baseController.Difficulty; + SF2NPC_Chaser controller = actor.Controller; + ChaserBossProfile data = controller.GetProfileData(); actor.IsInChaseInitial = true; - if (data.AnimationData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_ChaseInitial], difficulty, animName, sizeof(animName), rate, duration, cycle)) + ProfileAnimation section = data.GetAnimations().GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_ChaseInitial]); + if (section != null) { - int sequence = LookupProfileAnimation(actor.index, animName); - if (sequence != -1) - { - return SF2_PlaySequenceAndWait(sequence, duration, rate, cycle); - } + return SF2_PlaySequenceAndWaitEx(g_SlenderAnimationsList[SF2BossAnimation_ChaseInitial]); } return NULL_ACTION; diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chaselayer.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chaselayer.sp index f6086962..0c436158 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chaselayer.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/chaselayer.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -47,8 +48,8 @@ static int Update(SF2_ChaserChaseLayerAction action, SF2_ChaserEntity actor, flo ILocomotion loco = bot.GetLocomotionInterface(); bool tooClose = target.IsValid() && - (interrputConditions & COND_ENEMYVISIBLE) != 0 && - bot.IsRangeLessThan(target.index, 8.0); + (interrputConditions & COND_ENEMYVISIBLE_NOGLASS) != 0 && + bot.GetRangeSquaredTo(target.index) <= 32.0; if (tooClose && path.IsValid()) { @@ -60,15 +61,12 @@ static int Update(SF2_ChaserChaseLayerAction action, SF2_ChaserEntity actor, flo target.GetAbsOrigin(pos); if (actor.Teleporters.Length > 0) { - CBaseEntity(actor.Teleporters.Get(0)).GetAbsOrigin(pos); + CBaseEntity(EntRefToEntIndex(actor.Teleporters.Get(0))).GetAbsOrigin(pos); } - if (!bot.IsRangeLessThanEx(pos, 8.0)) + if ((interrputConditions & COND_NEWENEMY) != 0 || path.GetAge() > 0.3 || (path.IsValid() && (path.GetLength() - path.GetCursorPosition()) < 256.0)) { - if ((interrputConditions & COND_NEWENEMY) != 0 || path.GetAge() > 0.3 || (path.IsValid() && (path.GetLength() - path.GetCursorPosition()) < 256.0)) - { - path.ComputeToPos(bot, pos); - } + path.ComputeToPos(bot, pos); } } @@ -83,6 +81,32 @@ static int Update(SF2_ChaserChaseLayerAction action, SF2_ChaserEntity actor, flo actor.IsAttemptingToMove = true; } + if (target.IsValid()) + { + bool lookAt = false; + if (tooClose || controller.HasAttribute(SF2Attribute_AlwaysLookAtTarget) || controller.HasAttribute(SF2Attribute_AlwaysLookAtTargetWhileChasing)) + { + lookAt = true; + } + + if ((interrputConditions & COND_ENEMYVISIBLE) == 0) + { + lookAt = false; + } + + if (actor.IsKillingSomeone) + { + lookAt = false; + } + + if (lookAt) + { + float pos[3]; + target.GetAbsOrigin(pos); + loco.FaceTowards(pos); + } + } + return action.Continue(); } @@ -101,7 +125,7 @@ static int OnResume(SF2_ChaserChaseLayerAction action, SF2_ChaserEntity actor, N SF2NPC_Chaser controller = actor.Controller; if (controller.IsValid()) { - if (controller.GetProfileData().StunData.ChaseInitialOnEnd[controller.Difficulty] && SF2_ChaserChaseInitialAction.IsPossible(actor)) + if (controller.GetProfileData().GetStunBehavior().CanChaseInitialOnEnd(controller.Difficulty) && SF2_ChaserChaseInitialAction.IsPossible(actor)) { return action.SuspendFor(SF2_ChaserChaseInitialAction()); } diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/death.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/death.sp index 1d0942d1..329399cf 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/death.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/death.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -13,27 +14,26 @@ methodmap SF2_ChaserDeathAction < NextBotAction g_Factory.SetCallback(NextBotActionCallbackType_OnStart, OnStart); g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); g_Factory.SetCallback(NextBotActionCallbackType_OnEnd, OnEnd); - g_Factory.SetEventCallback(EventResponderType_OnAnimationEvent, OnAnimationEvent); g_Factory.BeginDataMapDesc() .DefineEntityField("m_Attacker") .EndDataMapDesc(); } SF2_ChaserDeathAction action = view_as(g_Factory.Create()); - action.Attacker = attacker.index; + action.Attacker = attacker; return action; } - property int Attacker + property CBaseEntity Attacker { public get() { - return this.GetDataEnt("m_Attacker"); + return CBaseEntity(EntRefToEntIndex(this.GetDataEnt("m_Attacker"))); } - public set(int value) + public set(CBaseEntity value) { - this.SetDataEnt("m_Attacker", value); + this.SetDataEnt("m_Attacker", EnsureEntRef(value.index)); } } } @@ -41,9 +41,7 @@ methodmap SF2_ChaserDeathAction < NextBotAction static NextBotAction InitialContainedAction(SF2_ChaserDeathAction action, SF2_ChaserEntity actor) { SF2NPC_Chaser controller = actor.Controller; - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); - int difficulty = controller.Difficulty; + ChaserBossProfile data = controller.GetProfileData(); INextBot bot = actor.MyNextBotPointer(); ILocomotion loco = bot.GetLocomotionInterface(); @@ -54,24 +52,18 @@ static NextBotAction InitialContainedAction(SF2_ChaserDeathAction action, SF2_Ch actor.EndCloak(); - char animName[64]; - float rate = 1.0, duration = 0.0, cycle = 0.0; actor.PerformVoice(SF2BossSound_Death); actor.State = STATE_DEATH; - if (originalData.IsPvEBoss) + if (data.IsPvEBoss) { KillPvEBoss(actor.index); } - if (originalData.AnimationData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_Death], difficulty, animName, sizeof(animName), rate, duration, cycle)) + if (data.GetAnimations().HasAnimationSection(g_SlenderAnimationsList[SF2BossAnimation_Death])) { - int sequence = actor.SelectProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Death], rate, duration, cycle); - if (sequence != -1) - { - return SF2_PlaySequenceAndWait(sequence, duration, rate, cycle); - } + return SF2_PlaySequenceAndWaitEx(g_SlenderAnimationsList[SF2BossAnimation_Death]); } return NULL_ACTION; @@ -80,33 +72,41 @@ static NextBotAction InitialContainedAction(SF2_ChaserDeathAction action, SF2_Ch static int OnStart(SF2_ChaserDeathAction action, SF2_ChaserEntity actor, NextBotAction priorAction) { SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileDeathData deathData = data.GetDeathBehavior(); actor.GroundSpeedOverride = true; int difficulty = controller.Difficulty; - if (data.DeathData.StartEffects != null) + if (deathData.GetOnStartEffects() != null) { float pos[3], ang[3]; actor.GetAbsOrigin(pos); actor.GetAbsAngles(ang); - SlenderSpawnEffects(data.DeathData.StartEffects, controller.Index, false, pos, ang, _, _, true); + SlenderSpawnEffects(deathData.GetOnStartEffects(), controller.Index, false, pos, ang, _, _, false); + } + + if (deathData.GetOnStartInputs() != null) + { + deathData.GetOnStartInputs().AcceptInputs(actor, action.Attacker, action.Attacker); } - if (data.DeathData.KeyDrop) + if (deathData.KeyDrop) { - if (SF_IsBoxingMap() && data.BoxingBoss && !g_SlenderBoxingBossIsKilled[controller.Index] && !view_as(controller).GetProfileData().IsPvEBoss) + char model[PLATFORM_MAX_PATH], trigger[64]; + deathData.GetKeyModel(model, sizeof(model)); + deathData.GetKeyTrigger(trigger, sizeof(trigger)); + if (SF_IsBoxingMap() && data.BoxingBoss && !g_SlenderBoxingBossIsKilled[controller.Index] && data.IsPvEBoss) { g_SlenderBoxingBossKilled++; if ((g_SlenderBoxingBossKilled == g_SlenderBoxingBossCount)) { - NPC_DropKey(controller.Index, data.DeathData.KeyModel, data.DeathData.KeyTrigger); + NPC_DropKey(controller.Index, model, trigger); } g_SlenderBoxingBossIsKilled[controller.Index] = true; } else { - NPC_DropKey(controller.Index, data.DeathData.KeyModel, data.DeathData.KeyTrigger); + NPC_DropKey(controller.Index, model, trigger); } } @@ -115,11 +115,14 @@ static int OnStart(SF2_ChaserDeathAction action, SF2_ChaserEntity actor, NextBot actor.RemoveAllGestures(); CBaseNPC_RemoveAllLayers(actor.index); - if (data.DeathData.AddHealthPerDeath[difficulty] > 0.0) + if (deathData.GetAddHealthPerDeath(difficulty) > 0.0) { - controller.SetDeathHealth(difficulty, controller.GetDeathHealth(difficulty) + data.DeathData.AddHealthPerDeath[difficulty]); + controller.SetDeathHealth(difficulty, controller.GetDeathHealth(difficulty) + deathData.GetAddHealthPerDeath(difficulty)); } + controller.SetAddSpeed(deathData.GetAddRunSpeed(difficulty)); + controller.SetAddAcceleration(deathData.GetAddAcceleration(difficulty)); + return action.Continue(); } @@ -142,21 +145,28 @@ static void OnEnd(SF2_ChaserDeathAction action, SF2_ChaserEntity actor) return; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileDeathData deathData = data.GetDeathBehavior(); - if (data.DeathData.EndEffects != null) + if (deathData.GetOnEndEffects() != null) { float pos[3], ang[3]; actor.GetAbsOrigin(pos); actor.GetAbsAngles(ang); - SlenderSpawnEffects(data.DeathData.EndEffects, controller.Index, false, pos, ang, _, _, true); + SlenderSpawnEffects(deathData.GetOnEndEffects(), controller.Index, false, pos, ang, _, _, false); + } + + if (deathData.GetOnEndInputs() != null) + { + deathData.GetOnEndInputs().AcceptInputs(actor, action.Attacker, action.Attacker); } - if (data.DeathData.RemoveOnDeath) + data.SetBool("__was_killed", true); + + if (deathData.RemoveOnDeath) { SpawnGibs(actor); - if (data.DeathData.RagdollOnDeath) + if (deathData.RagdollOnDeath) { actor.AcceptInput("BecomeRagdoll"); } @@ -224,34 +234,23 @@ static void OnEnd(SF2_ChaserDeathAction action, SF2_ChaserEntity actor) controller.Remove(); } } - else if (data.DeathData.DisappearOnDeath) + else if (deathData.DisappearOnDeath) { SpawnGibs(actor); controller.UnSpawn(true); } - else if (data.DeathData.RagdollOnDeath) + else if (deathData.RagdollOnDeath) { actor.AcceptInput("BecomeRagdoll"); } } -static void OnAnimationEvent(SF2_ChaserDeathAction action, SF2_ChaserEntity actor, int event) -{ - if (event == 0) - { - return; - } - - actor.CastAnimEvent(event); - actor.CastAnimEvent(event, true); -} - static void SpawnGibs(SF2_ChaserEntity actor) { SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - if (data.DeathData.Gibs == null) + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileDeathData deathData = data.GetDeathBehavior(); + if (deathData.GetGibs() == null) { return; } @@ -259,7 +258,7 @@ static void SpawnGibs(SF2_ChaserEntity actor) char model[PLATFORM_MAX_PATH]; actor.WorldSpaceCenter(pos); actor.GetAbsAngles(ang); - for (int i = 0; i < data.DeathData.Gibs.Length; i++) + for (int i = 0; i < deathData.GetGibs().Size; i++) { ang[1] = GetRandomFloat(-180.0, 180.0); @@ -269,7 +268,13 @@ static void SpawnGibs(SF2_ChaserEntity actor) } vel[2] = GetRandomFloat(-300.0, 300.0); - data.DeathData.Gibs.GetString(i, model, sizeof(model)); + char name[64]; + data.GetRages().GetKeyNameFromIndex(i, name, sizeof(name)); + if (strcmp(name, "skin") == 0) + { + continue; + } + deathData.GetGibs().GetString(name, model, sizeof(model)); if (strlen(model) > 0) { @@ -284,7 +289,7 @@ static void SpawnGibs(SF2_ChaserEntity actor) SetEntityCollisionGroup(gib.index, 1); gib.SetProp(Prop_Send, "m_usSolidFlags", 0); gib.SetProp(Prop_Send, "m_nSolidType", 2); - gib.SetProp(Prop_Send, "m_nSkin", data.DeathData.GibSkin); + gib.SetProp(Prop_Send, "m_nSkin", deathData.GibSkin); int effects = 16 | 64; gib.SetProp(Prop_Send, "m_fEffects", effects); diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/despawn.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/despawn.sp index 85ba7791..cd4c37e6 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/despawn.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/despawn.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/flee_to_heal.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/flee_to_heal.sp index 40bc5c64..fbf072c0 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/flee_to_heal.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/flee_to_heal.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -135,20 +136,25 @@ methodmap SF2_ChaserFleeToHealAction < NextBotAction static NextBotAction InitialContainedAction(SF2_ChaserFleeToHealAction action, SF2_ChaserEntity actor) { SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserRageInfo rageInfo; - data.Rages.GetArray(actor.RageIndex, rageInfo, sizeof(rageInfo)); - action.HealDuration = rageInfo.HealDuration; - action.HealTime = rageInfo.HealDelay; + ChaserBossProfile data = controller.GetProfileData(); + char name[64]; + data.GetRages().GetSectionNameFromIndex(actor.RageIndex, name, sizeof(name)); + ChaserBossProfileRageData rageData = view_as(data.GetRages().GetSection(name)); + action.HealDuration = rageData.HealDuration; + action.HealTime = rageData.HealDelay; char animName[64]; float rate = 1.0, duration = 0.0, cycle = 0.0; int difficulty = controller.Difficulty; action.IsHealing = false; action.HasPath = false; action.FleeTime = GetRandomFloat(5.0, 10.0); - if (rageInfo.Animations.GetAnimation("start", difficulty, animName, sizeof(animName), rate, duration, cycle)) + ProfileAnimation section = rageData.GetAnimations().GetAnimation("start"); + if (section != null) { + section.GetAnimationName(difficulty, animName, sizeof(animName)); + rate = section.GetAnimationPlaybackRate(difficulty); + duration = section.GetDuration(difficulty); + cycle = section.GetAnimationCycle(difficulty); int sequence = LookupProfileAnimation(actor.index, animName); if (sequence != -1) { @@ -169,12 +175,10 @@ static int Update(SF2_ChaserFleeToHealAction action, SF2_ChaserEntity actor, flo INextBot bot = actor.MyNextBotPointer(); ILocomotion loco = bot.GetLocomotionInterface(); SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); - SF2ChaserRageInfo rageInfo; - data.Rages.GetArray(actor.RageIndex, rageInfo, sizeof(rageInfo)); + ChaserBossProfile data = controller.GetProfileData(); + char name[64]; + data.GetRages().GetSectionNameFromIndex(actor.RageIndex, name, sizeof(name)); + ChaserBossProfileRageData rageData = view_as(data.GetRages().GetSection(name)); PathFollower path = controller.Path; int difficulty = controller.Difficulty; float gameTime = GetGameTime(); @@ -182,12 +186,12 @@ static int Update(SF2_ChaserFleeToHealAction action, SF2_ChaserEntity actor, flo if (!action.IsHealing && !action.HasPath) { actor.IsRunningAway = true; - if (rageInfo.HealCloak) + if (rageData.HealCloak) { actor.StartCloak(); } CNavArea area = actor.GetLastKnownArea(); - SurroundingAreasCollector collector = TheNavMesh.CollectSurroundingAreas(area, GetRandomFloat(rageInfo.FleeRange[0], rageInfo.FleeRange[1])); + SurroundingAreasCollector collector = TheNavMesh.CollectSurroundingAreas(area, GetRandomFloat(rageData.FleeMinRange, rageData.FleeMaxRange)); int areaCount = collector.Count(); ArrayList areaArray = new ArrayList(1, areaCount); int validAreaCount = 0; @@ -239,10 +243,14 @@ static int Update(SF2_ChaserFleeToHealAction action, SF2_ChaserEntity actor, flo if (!action.PlayedAnimation) { char animName[64]; - float rate = 1.0, duration = 0.0, cycle = 0.0; - if (rageInfo.Animations.GetAnimation("healing", difficulty, animName, sizeof(animName), rate, duration, cycle)) + float rate = 1.0, cycle = 0.0; + ProfileAnimation section = rageData.GetAnimations().GetAnimation("healing"); + if (section != null) { - action.HealAnimationDuration = duration; + section.GetAnimationName(difficulty, animName, sizeof(animName)); + action.HealAnimationDuration = section.GetDuration(difficulty); + rate = section.GetAnimationPlaybackRate(difficulty); + cycle = section.GetAnimationCycle(difficulty); int sequence = LookupProfileAnimation(actor.index, animName); if (sequence != -1) { @@ -262,16 +270,16 @@ static int Update(SF2_ChaserFleeToHealAction action, SF2_ChaserEntity actor, flo { if (!action.PlayedSound) { - actor.PerformVoiceEx(_, _, rageInfo.HealSounds, true); + actor.PerformVoiceEx(_, _, rageData.GetHealSounds(), true); action.PlayedSound = true; } - float amount = actor.MaxHealth * rageInfo.HealAmount; - if (!data.DeathData.Enabled[difficulty]) + float amount = actor.MaxHealth * rageData.HealAmount; + if (!data.GetDeathBehavior().IsEnabled(difficulty)) { - amount = actor.MaxStunHealth * rageInfo.HealAmount; + amount = actor.MaxStunHealth * rageData.HealAmount; } - float increase = LerpFloats(0.0, amount, GetGameFrameTime() * (1.0 / rageInfo.HealDuration)); - if (data.DeathData.Enabled[difficulty]) + float increase = LerpFloats(0.0, amount, GetGameFrameTime() * (1.0 / rageData.HealDuration)); + if (data.GetDeathBehavior().IsEnabled(difficulty)) { actor.SetProp(Prop_Data, "m_iHealth", RoundToFloor(increase + actor.GetProp(Prop_Data, "m_iHealth"))); if (float(actor.GetProp(Prop_Data, "m_iHealth")) > actor.MaxHealth) @@ -287,7 +295,7 @@ static int Update(SF2_ChaserFleeToHealAction action, SF2_ChaserEntity actor, flo actor.StunHealth = actor.MaxStunHealth; } } - if (originalData.Healthbar) + if (data.Healthbar) { UpdateHealthBar(controller.Index); SetHealthBarColor(true); diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/idle.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/idle.sp index b8c2d8b9..caf50d0b 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/idle.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/idle.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -75,13 +76,14 @@ static int OnStart(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, NextBotA { INextBot bot = actor.MyNextBotPointer(); SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileIdleData idleData = data.GetIdleBehavior(); + ChaserBossProfileSmellData smellData = data.GetSmellData(); int difficulty = controller.Difficulty; float gameTime = GetGameTime(); - action.NextTurnTime = gameTime + GetRandomFloat(data.IdleData.TurnMinTime[difficulty], data.IdleData.TurnMaxTime[difficulty]); - action.NextWanderTime = gameTime + GetRandomFloat(data.WanderEnterTimeMin[difficulty], data.WanderEnterTimeMax[difficulty]); + action.NextTurnTime = gameTime + GetRandomFloat(idleData.GetTurnMinCooldown(difficulty), idleData.GetTurnMaxCooldown(difficulty)); + action.NextWanderTime = gameTime + GetRandomFloat(data.GetWanderEnterMinTime(difficulty), data.GetWanderEnterMaxTime(difficulty)); action.InitialState = true; bot.GetLocomotionInterface().Stop(); @@ -98,16 +100,21 @@ static int OnStart(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, NextBotA } } - if (!actor.HasSmelled) + if (data.GetSmellData() != null) { - actor.SmellCooldown = gameTime + GetRandomFloat(data.SmellData.CooldownMin[difficulty], data.SmellData.CooldownMax[difficulty]); - } - else - { - actor.SmellCooldown = gameTime + GetRandomFloat(data.SmellData.CooldownAfterStateMin[difficulty], data.SmellData.CooldownAfterStateMax[difficulty]); + if (!actor.HasSmelled) + { + actor.SmellCooldown = gameTime + GetRandomFloat(smellData.GetMinCooldown(difficulty), smellData.GetMaxCooldown(difficulty)); + } + else + { + actor.SmellCooldown = gameTime + GetRandomFloat(smellData.GetMinCooldownAfterState(difficulty), smellData.GetMaxCooldownAfterState(difficulty)); + } } - g_SlenderTimeUntilKill[controller.Index] = GetGameTime() + NPCGetIdleLifetime(controller.Index, difficulty); + g_SlenderTimeUntilKill[controller.Index] = GetGameTime() + data.GetIdleLifeTime(difficulty); + + actor.IsSpawning = false; return action.Continue(); } @@ -120,12 +127,12 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int return action.Continue(); } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileIdleData idleData = data.GetIdleBehavior(); + ChaserBossProfileAlertData alertData = data.GetAlertBehavior(); + ChaserBossProfileSmellData smellData = data.GetSmellData(); - if (!originalData.IsPvEBoss && IsBeatBoxBeating(2)) + if (!data.IsPvEBoss && IsBeatBoxBeating(2)) { return action.SuspendFor(SF2_ChaserBeatBoxFreezeAction(actor.IsAttemptingToMove)); } @@ -138,7 +145,7 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int PathFollower path = controller.Path; - if (data.ChaseOnLookData.Enabled[difficulty] && controller.ChaseOnLookTargets.Length > 0) + if (data.GetChaseOnLookData().IsEnabled(difficulty) && controller.ChaseOnLookTargets.Length > 0) { SF2_BasePlayer lookTarget = SF2_BasePlayer(controller.ChaseOnLookTargets.Get(0)); if (lookTarget.IsValid && !lookTarget.IsEliminated && lookTarget.IsAlive && !lookTarget.IsInGhostMode && !lookTarget.HasEscaped) @@ -158,7 +165,7 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int if (target.IsValid()) { if (SF_IsRaidMap() || SF_BossesChaseEndlessly() || SF_IsProxyMap() || SF_IsBoxingMap() || SF_IsSlaughterRunMap() || data.ChasesEndlessly || - view_as(controller).GetProfileData().IsPvEBoss) + data.IsPvEBoss) { actor.State = STATE_CHASE; path.Invalidate(); @@ -199,7 +206,7 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int { actor.NextVoiceTime = 0.0; } - return action.ChangeTo(SF2_ChaserAlertAction(pos, data.AlertData.RunOnSuspect[difficulty]), "I saw someone!"); + return action.ChangeTo(SF2_ChaserAlertAction(pos, alertData.ShouldRunOnSuspect(difficulty)), "I saw someone!"); } } @@ -215,7 +222,7 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int { actor.NextVoiceTime = 0.0; } - return action.ChangeTo(SF2_ChaserAlertAction(pos, data.AlertData.RunOnSuspect[difficulty]), "Someone made noise, let's go!"); + return action.ChangeTo(SF2_ChaserAlertAction(pos, alertData.ShouldRunOnSuspect(difficulty)), "Someone made noise, let's go!"); } else if ((interruptConditions & COND_ALERT_TRIGGER_POS) != 0 || actor.QueueForAlertState) { @@ -229,7 +236,7 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int actor.NextVoiceTime = 0.0; } actor.QueueForAlertState = false; - return action.ChangeTo(SF2_ChaserAlertAction(pos, data.AlertData.RunOnSuspect[difficulty]), "We got a sound hint, let's go!"); + return action.ChangeTo(SF2_ChaserAlertAction(pos, alertData.ShouldRunOnSuspect(difficulty)), "We got a sound hint, let's go!"); } if ((interruptConditions & COND_DEBUG) != 0) @@ -243,7 +250,7 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int { actor.NextVoiceTime = 0.0; } - return action.ChangeTo(SF2_ChaserAlertAction(pos, data.AlertData.RunOnWander[difficulty]), "An admin told me to go here!"); + return action.ChangeTo(SF2_ChaserAlertAction(pos, alertData.ShouldRunOnWander(difficulty)), "An admin told me to go here!"); } if (actor.FollowedCompanionAlert || actor.AlertWithBoss) @@ -262,13 +269,13 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int { actor.NextVoiceTime = 0.0; } - return action.ChangeTo(SF2_ChaserAlertAction(pos, data.AlertData.RunOnWander[difficulty]), "One of my mates found something!"); + return action.ChangeTo(SF2_ChaserAlertAction(pos, alertData.ShouldRunOnWander(difficulty)), "One of my mates found something!"); } } - if (data.SmellData.Enabled[difficulty] && actor.SmellCooldown <= gameTime && actor.SmellPlayerList != null) + if (smellData.IsEnabled(difficulty) && actor.SmellCooldown <= gameTime && actor.SmellPlayerList != null) { - if (actor.SmellPlayerList.Length >= data.SmellData.PlayerCount[difficulty]) + if (actor.SmellPlayerList.Length >= smellData.GetRequiredPlayers(difficulty)) { actor.State = STATE_ALERT; path.Invalidate(); @@ -276,7 +283,7 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int } } - bool isAbleToWander = data.CanWander[difficulty]; + bool isAbleToWander = data.CanWander(difficulty); bool canWalk = true; if (controller.HasAttribute(SF2Attribute_BlockWalkSpeedUnderDifficulty)) { @@ -293,8 +300,8 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int float debugPos[3]; actor.GetForceWanderPosition(debugPos); path.Invalidate(); - float min = data.WanderTimeMin[difficulty]; - float max = data.WanderTimeMax[difficulty]; + float min = data.GetWanderMinTime(difficulty); + float max = data.GetWanderMaxTime(difficulty); action.NextWanderTime = gameTime + GetRandomFloat(min, max); path.ComputeToPos(bot, debugPos); @@ -315,7 +322,7 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int actor.NextVoiceTime = 0.0; } actor.WasInBacon = true; - return action.ChangeTo(SF2_ChaserAlertAction(pos, data.AlertData.RunOnSuspect[difficulty]), "What is this smell of bacon?"); + return action.ChangeTo(SF2_ChaserAlertAction(pos, alertData.ShouldRunOnSuspect(difficulty)), "What is this smell of bacon?"); } } @@ -323,13 +330,13 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int { if (gameTime >= action.NextWanderTime && GetRandomFloat(0.0, 1.0) <= 0.25) { - float min = data.WanderTimeMin[difficulty]; - float max = data.WanderTimeMax[difficulty]; + float min = data.GetWanderMinTime(difficulty); + float max = data.GetWanderMaxTime(difficulty); action.NextWanderTime = gameTime + GetRandomFloat(min, max); - float rangeMin = data.WanderRangeMin[difficulty]; - float rangeMax = data.WanderRangeMax[difficulty]; + float rangeMin = data.GetWanderMinRange(difficulty); + float rangeMax = data.GetWanderMaxRange(difficulty); float range = GetRandomFloat(rangeMin, rangeMax); CNavArea area = actor.GetLastKnownArea(); @@ -386,7 +393,7 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int } else { - if (data.IdleData.TurnEnabled[difficulty]) + if (idleData.IsTurnEnabled(difficulty)) { if (!action.InitialState) { @@ -396,22 +403,22 @@ static int Update(SF2_ChaserIdleAction action, SF2_ChaserEntity actor, float int } if (action.NextTurnTime <= 0.0) { - action.NextTurnTime = gameTime + GetRandomFloat(data.IdleData.TurnMinTime[difficulty], data.IdleData.TurnMaxTime[difficulty]); + action.NextTurnTime = gameTime + GetRandomFloat(idleData.GetTurnMinCooldown(difficulty), idleData.GetTurnMaxCooldown(difficulty)); } } } - if (data.IdleData.TurnEnabled[difficulty] && action.NextTurnTime > 0.0 && action.NextTurnTime <= gameTime) + if (action.NextTurnTime > 0.0 && action.NextTurnTime <= gameTime && idleData.IsTurnEnabled(difficulty)) { float myPos[3], myAng[3]; actor.GetAbsOrigin(myPos); actor.GetAbsAngles(myAng); - myAng[1] += GetRandomFloat(-data.IdleData.TurnAngle[difficulty], data.IdleData.TurnAngle[difficulty]); + myAng[1] += GetRandomFloat(-idleData.GetTurnAngle(difficulty), idleData.GetTurnAngle(difficulty)); float lookAt[3]; lookAt[0] = 50.0; VectorTransform(lookAt, myPos, myAng, lookAt); action.SetLookPosition(lookAt); - action.NextTurnTime = gameTime + GetRandomFloat(data.IdleData.TurnMinTime[difficulty], data.IdleData.TurnMaxTime[difficulty]); + action.NextTurnTime = gameTime + GetRandomFloat(idleData.GetTurnMinCooldown(difficulty), idleData.GetTurnMaxCooldown(difficulty)); action.InitialState = false; } diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/mainlayer.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/mainlayer.sp index bf7dca70..41b8266d 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/mainlayer.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/mainlayer.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required // The purpose of this action is to be the master action static NextBotActionFactory g_Factory; @@ -20,10 +21,10 @@ methodmap SF2_ChaserMainAction < NextBotAction g_Factory.SetCallback(NextBotActionCallbackType_Update, Update); g_Factory.SetCallback(NextBotActionCallbackType_OnResume, OnResume); g_Factory.SetEventCallback(EventResponderType_OnInjured, OnInjured); - g_Factory.SetEventCallback(EventResponderType_OnAnimationEvent, OnAnimationEvent); g_Factory.SetEventCallback(EventResponderType_OnContact, OnContact); g_Factory.SetEventCallback(EventResponderType_OnKilled, OnKilled); g_Factory.SetEventCallback(EventResponderType_OnLeaveGround, OnLeaveGround); + g_Factory.SetEventCallback(EventResponderType_OnLandOnGround, OnLandOnGround); g_Factory.SetEventCallback(EventResponderType_OnCommandString, OnCommandString); g_Factory.BeginDataMapDesc() .DefineFloatField("m_LastStuckTime") @@ -112,11 +113,9 @@ static int Update(SF2_ChaserMainAction action, SF2_ChaserEntity actor) actor.DoAttackMiscConditions(actor.GetAttackName()); } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); - if (data.StunData.FlashlightStun[difficulty] && actor.CanBeStunned() && actor.CanTakeDamage() && actor.FlashlightTick < gameTime && + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileStunData stunData = data.GetStunBehavior(); + if (stunData != null && stunData.CanFlashlightStun(difficulty) && actor.CanBeStunned() && actor.CanTakeDamage() && actor.FlashlightTick < gameTime && !IsNightVisionEnabled()) { bool inFlashlight = false; @@ -189,7 +188,7 @@ static int Update(SF2_ChaserMainAction action, SF2_ChaserEntity actor) if (inFlashlight) { - actor.StunHealth -= data.StunData.FlashlightStunDamage[difficulty] * customDamage; + actor.StunHealth -= stunData.GetFlashlightDamage(difficulty) * customDamage; actor.FlashlightTick = gameTime + 0.1; Event event = CreateEvent("npc_hurt"); @@ -197,7 +196,7 @@ static int Update(SF2_ChaserMainAction action, SF2_ChaserEntity actor) { event.SetInt("entindex", actor.index); event.SetInt("health", actor.GetProp(Prop_Data, "m_iHealth")); - event.SetInt("damageamount", RoundToFloor(data.StunData.FlashlightStunDamage[difficulty] * customDamage)); + event.SetInt("damageamount", RoundToFloor(stunData.GetFlashlightDamage(difficulty) * customDamage)); event.SetBool("crit", false); if (IsValidClient(attacker)) @@ -221,7 +220,7 @@ static int Update(SF2_ChaserMainAction action, SF2_ChaserEntity actor) } } - if (originalData.InstantKillRadius > 0.0) + if (data.GetInstantKillRadius(difficulty) > 0.0) { if (gameTime >= actor.LastKillTime && !actor.IsKillingSomeone) { @@ -229,7 +228,7 @@ static int Update(SF2_ChaserMainAction action, SF2_ChaserEntity actor) actor.WorldSpaceCenter(worldSpace); actor.GetAbsOrigin(myPos); bool attackEliminated = (controller.Flags & SFF_ATTACKWAITERS) != 0; - if (view_as(controller).GetProfileData().IsPvEBoss) + if (controller.GetProfileData().IsPvEBoss) { attackEliminated = true; } @@ -246,13 +245,13 @@ static int Update(SF2_ChaserMainAction action, SF2_ChaserEntity actor) continue; } - if (actor.MyNextBotPointer().GetRangeSquaredTo(client.index) > Pow(originalData.InstantKillRadius, 2.0) || + if (actor.MyNextBotPointer().GetRangeSquaredTo(client.index) > Pow(data.GetInstantKillRadius(difficulty), 2.0) || !client.CanSeeSlender(controller.Index, false, _, !attackEliminated)) { continue; } - actor.LastKillTime = gameTime + originalData.InstantKillCooldown[difficulty]; + actor.LastKillTime = gameTime + data.GetInstantKillCooldown(difficulty); client.StartDeathCam(controller.Index, myPos); actor.CheckTauntKill(SF2_BasePlayer(client.index)); } @@ -261,6 +260,7 @@ static int Update(SF2_ChaserMainAction action, SF2_ChaserEntity actor) if (actor.IsKillingSomeone) { + actor.EndCloak(); return action.SuspendFor(SF2_DeathCamAction()); } @@ -387,17 +387,17 @@ static int OnInjured(SF2_ChaserMainAction action, SF2_ChaserEntity actor, CBaseE } SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); int search = actor.RageIndex + 1; - if ((data.BoxingBoss || view_as(controller).GetProfileData().IsPvEBoss) && - actor.CanTakeDamage(attacker, inflictor, damage) && data.Rages != null && search < data.Rages.Length && !actor.IsRaging) + if ((data.BoxingBoss || data.IsPvEBoss) && + actor.CanTakeDamage(attacker, inflictor, damage) && data.GetRages() != null && search < data.GetRages().Size && !actor.IsRaging) { - SF2ChaserRageInfo rageInfo; - data.Rages.GetArray(search, rageInfo, sizeof(rageInfo)); + char name[64]; + data.GetRages().GetSectionNameFromIndex(search, name, sizeof(name)); + ChaserBossProfileRageData rageInfo = view_as(data.GetRages().GetSection(name)); float maxHealth = actor.MaxHealth; float health = float(actor.GetProp(Prop_Data, "m_iHealth")); - if (!data.DeathData.Enabled[controller.Difficulty]) + if (!data.GetDeathBehavior().IsEnabled(controller.Difficulty)) { maxHealth = actor.MaxStunHealth; health = actor.StunHealth; @@ -411,17 +411,6 @@ static int OnInjured(SF2_ChaserMainAction action, SF2_ChaserEntity actor, CBaseE return action.TryContinue(); } -static void OnAnimationEvent(SF2_ChaserMainAction action, SF2_ChaserEntity actor, int event) -{ - if (event == 0) - { - return; - } - - actor.CastAnimEvent(event); - actor.CastAnimEvent(event, true); -} - static void UnstuckCheck(SF2_ChaserMainAction action, SF2_ChaserEntity actor) { INextBot bot = actor.MyNextBotPointer(); @@ -492,20 +481,20 @@ static void OnContact(SF2_ChaserMainAction action, SF2_ChaserEntity actor, CBase if (strcmp(classname, "obj_dispenser", false) == 0 || strcmp(classname, "obj_teleporter", false) == 0 || strcmp(classname, "func_breakable", false) == 0) { - SDKHooks_TakeDamage(other.index, actor.index, actor.index, other.GetProp(Prop_Data, "m_iHealth") * 4.0); + SDKHooks_TakeDamage(other.index, actor.index, actor.index, other.GetProp(Prop_Data, "m_iHealth") * 4.0, .bypassHooks = false); } // Destroy mini sentires, not non-mini sentries if (strcmp(classname, "obj_sentrygun", false) == 0 && other.GetProp(Prop_Send, "m_bMiniBuilding")) { - SDKHooks_TakeDamage(other.index, actor.index, actor.index, other.GetProp(Prop_Data, "m_iHealth") * 4.0); + SDKHooks_TakeDamage(other.index, actor.index, actor.index, other.GetProp(Prop_Data, "m_iHealth") * 4.0, .bypassHooks = false); } if (strcmp(classname, "prop_physics") == 0 || strcmp(classname, "prop_dynamic") == 0) { if (other.GetProp(Prop_Data, "m_iHealth") > 0) { - SDKHooks_TakeDamage(other.index, actor.index, actor.index, other.GetProp(Prop_Data, "m_iHealth") * 4.0); + SDKHooks_TakeDamage(other.index, actor.index, actor.index, other.GetProp(Prop_Data, "m_iHealth") * 4.0, .bypassHooks = false); } } } @@ -522,6 +511,16 @@ static int OnKilled(SF2_ChaserMainAction action, SF2_ChaserEntity actor, CBaseEn static void OnLeaveGround(SF2_ChaserMainAction action, SF2_ChaserEntity actor, CBaseEntity ground) { + if (actor.State != STATE_IDLE && actor.State != STATE_ALERT && actor.State != STATE_CHASE) + { + return; + } + + if (actor.IsSpawning) + { + return; + } + if (!actor.IsJumping) { return; @@ -532,12 +531,72 @@ static void OnLeaveGround(SF2_ChaserMainAction action, SF2_ChaserEntity actor, C float velocity[3]; actor.MyNextBotPointer().GetLocomotionInterface().GetVelocity(velocity); velocity[2] += loco.GetStepHeight() * 4.0; - loco.SetVelocity(velocity); - actor.IsJumping = false; + //loco.SetVelocity(velocity); + + float duration = 0.0, rate = 1.0, cycle = 0.0; + int sequence = -1; + if (actor.ResetProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Jump], .sequence = sequence, .duration = duration, .rate = rate, .cycle = cycle)) + { + if (duration <= 0.0) + { + duration = actor.SequenceDuration(sequence) / rate; + duration *= (1.0 - cycle); + } + actor.AirTime = duration; + actor.IsInAirAnimation = true; + } + + actor.PerformVoice(SF2BossSound_Jump); +} + +static int OnLandOnGround(SF2_ChaserMainAction action, SF2_ChaserEntity actor, CBaseEntity ground) +{ + if (!actor.CanLand) + { + actor.CanLand = true; + return action.TryContinue(); + } + + if (actor.State != STATE_IDLE && actor.State != STATE_ALERT && actor.State != STATE_CHASE) + { + return action.TryContinue(); + } + + if (actor.IsSpawning) + { + return action.TryContinue(); + } + + if (actor.IsJumping) + { + actor.IsJumping = false; + } + + if (actor.IsInAirAnimation) + { + actor.IsInAirAnimation = false; + actor.UpdateMovementAnimation(); + } + + actor.AirTime = 0.0; + + SF2NPC_Chaser controller = actor.Controller; + if (controller.GetProfileData().GetAnimations().HasAnimationSection(g_SlenderAnimationsList[SF2BossAnimation_Land])) + { + actor.AddGesture(g_SlenderAnimationsList[SF2BossAnimation_Land]); + return action.TrySuspendFor(SF2_PlaySequenceAndWaitEx(g_SlenderAnimationsList[SF2BossAnimation_Land]), RESULT_CRITICAL); + } + + return action.TryContinue(); } static int OnCommandString(SF2_ChaserMainAction action, SF2_ChaserEntity actor, const char[] command) { + if (actor.IsSpawning) + { + return action.TryContinue(); + } + if (StrContains(command, "debug attack ") == 0 && !actor.IsAttacking) { SF2NPC_Chaser controller = actor.Controller; @@ -545,11 +604,9 @@ static int OnCommandString(SF2_ChaserMainAction action, SF2_ChaserEntity actor, char attack[128]; strcopy(attack, sizeof(attack), command); ReplaceString(attack, sizeof(attack), "debug attack ", ""); - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attack, attackData); - return action.TrySuspendFor(SF2_ChaserAttackAction(attack, attackData.Index, attackData.Duration[difficulty] + GetGameTime()), RESULT_IMPORTANT); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileBaseAttack attackData = data.GetAttack(attack); + return action.TrySuspendFor(SF2_ChaserAttackAction(data, attack, data.IndexOfSection(attackData), attackData.GetDuration(difficulty)), RESULT_IMPORTANT); } if (strcmp(command, "suspend for action") == 0) diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/rage.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/rage.sp index 558ff469..275701da 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/rage.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/rage.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -51,12 +52,12 @@ static NextBotAction InitialContainedAction(SF2_ChaserRageAction action, SF2_Cha { actor.RageIndex++; SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; + ChaserBossProfile data = controller.GetProfileData(); + char name[64]; + data.GetRages().GetSectionNameFromIndex(actor.RageIndex, name, sizeof(name)); + ChaserBossProfileRageData rageData = view_as(data.GetRages().GetSection(name)); INextBot bot = actor.MyNextBotPointer(); ILocomotion loco = bot.GetLocomotionInterface(); - data = controller.GetProfileData(); - SF2ChaserRageInfo rageInfo; - data.Rages.GetArray(actor.RageIndex, rageInfo, sizeof(rageInfo)); char animName[64]; float rate = 1.0, duration = 0.0, cycle = 0.0; int difficulty = controller.Difficulty; @@ -66,9 +67,9 @@ static NextBotAction InitialContainedAction(SF2_ChaserRageAction action, SF2_Cha actor.IsRaging = true; - actor.OverrideInvincible = rageInfo.Invincible; + actor.OverrideInvincible = rageData.Invincible; - actor.PerformVoiceEx(_, _, rageInfo.StartSounds, true); + actor.PerformVoiceEx(_, _, rageData.GetStartSounds(), true); actor.EndCloak(); @@ -77,7 +78,7 @@ static NextBotAction InitialContainedAction(SF2_ChaserRageAction action, SF2_Cha actor.CancelAttack = true; } - if (rageInfo.IsHealing) + if (rageData.IsHealing) { SetHealthBarColor(true); action.IsHealing = true; @@ -85,8 +86,13 @@ static NextBotAction InitialContainedAction(SF2_ChaserRageAction action, SF2_Cha return SF2_ChaserFleeToHealAction(); } - if (rageInfo.Animations.GetAnimation("start", difficulty, animName, sizeof(animName), rate, duration, cycle)) + ProfileAnimation section = rageData.GetAnimations().GetAnimation("start"); + if (section != null) { + section.GetAnimationName(difficulty, animName, sizeof(animName)); + duration = section.GetDuration(difficulty); + rate = section.GetAnimationPlaybackRate(difficulty); + cycle = section.GetAnimationCycle(difficulty); int sequence = LookupProfileAnimation(actor.index, animName); if (sequence != -1) { @@ -124,11 +130,11 @@ static void OnEnd(SF2_ChaserRageAction action, SF2_ChaserEntity actor) actor.IsGoingToHeal = false; if (actor.Controller.IsValid()) { - SF2ChaserBossProfileData data; - data = actor.Controller.GetProfileData(); - SF2ChaserRageInfo rageInfo; - data.Rages.GetArray(actor.RageIndex, rageInfo, sizeof(rageInfo)); - if (rageInfo.IncreaseDifficulty) + ChaserBossProfile data = actor.Controller.GetProfileData(); + char name[64]; + data.GetRages().GetSectionNameFromIndex(actor.RageIndex, name, sizeof(name)); + ChaserBossProfileRageData rageData = view_as(data.GetRages().GetSection(name)); + if (rageData.IncreaseDifficulty) { actor.Controller.Difficulty += 1; if (actor.Controller.Difficulty > Difficulty_Apollyon) diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/smell.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/smell.sp index 1501333f..32e37137 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/smell.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/smell.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -20,10 +21,8 @@ methodmap SF2_ChaserSmellAction < NextBotAction public static bool IsPossible(SF2_ChaserEntity actor) { - SF2NPC_BaseNPC baseController = view_as(actor.Controller); - SF2BossProfileData data; - data = baseController.GetProfileData(); - if (!data.AnimationData.HasAnimationSection(g_SlenderAnimationsList[SF2BossAnimation_Smell])) + ChaserBossProfile data = actor.Controller.GetProfileData(); + if (!data.GetAnimations().HasAnimationSection(g_SlenderAnimationsList[SF2BossAnimation_Smell])) { return false; } @@ -34,23 +33,15 @@ methodmap SF2_ChaserSmellAction < NextBotAction static NextBotAction InitialContainedAction(SF2_ChaserSmellAction action, SF2_ChaserEntity actor) { - SF2NPC_BaseNPC baseController = view_as(actor.Controller); - SF2BossProfileData data; - data = baseController.GetProfileData(); - char animName[64]; float rate = 1.0, duration = 0.0, cycle = 0.0; - int difficulty = baseController.Difficulty; actor.IsAttemptingToMove = false; actor.PerformVoice(SF2BossSound_Smell); - if (data.AnimationData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_Smell], difficulty, animName, sizeof(animName), rate, duration, cycle)) + int sequence = actor.SelectProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Smell], rate, duration, cycle); + if (sequence != -1) { - int sequence = actor.SelectProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Smell], rate, duration, cycle); - if (sequence != -1) - { - return SF2_PlaySequenceAndWait(sequence, duration, rate, cycle); - } + return SF2_PlaySequenceAndWait(sequence, duration, rate, cycle); } return NULL_ACTION; @@ -67,13 +58,14 @@ static int Update(SF2_ChaserSmellAction action, SF2_ChaserEntity actor, NextBotA { SF2NPC_Chaser controller = actor.Controller; int difficulty = controller.Difficulty; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileSmellData smellData = data.GetSmellData(); + ChaserBossProfileAlertData alertData = data.GetAlertBehavior(); PathFollower path = controller.Path; - g_SlenderTimeUntilKill[controller.Index] = GetGameTime() + NPCGetIdleLifetime(controller.Index, difficulty); + g_SlenderTimeUntilKill[controller.Index] = GetGameTime() + data.GetIdleLifeTime(difficulty); if (action.ActiveChild == NULL_ACTION) { - float bestDistance = Pow(data.SmellData.SmellRange[difficulty], 2.0); + float bestDistance = Pow(smellData.GetSmellRange(difficulty), 2.0); bool found = false; float pos[3], myPos[3], bestPos[3]; actor.GetAbsOrigin(myPos); @@ -98,7 +90,7 @@ static int Update(SF2_ChaserSmellAction action, SF2_ChaserEntity actor, NextBotA } if (found) { - if (!data.SmellData.ShouldChase[difficulty]) + if (!smellData.GetShouldChaseState(difficulty)) { actor.State = STATE_ALERT; path.Invalidate(); @@ -106,7 +98,7 @@ static int Update(SF2_ChaserSmellAction action, SF2_ChaserEntity actor, NextBotA { actor.NextVoiceTime = 0.0; } - return action.ChangeTo(SF2_ChaserAlertAction(bestPos, data.AlertData.RunOnSuspect[difficulty]), "I smelled someone, what is it?"); + return action.ChangeTo(SF2_ChaserAlertAction(bestPos, alertData.ShouldRunOnSuspect(difficulty)), "I smelled someone, what is it?"); } else { @@ -144,7 +136,7 @@ static int Update(SF2_ChaserSmellAction action, SF2_ChaserEntity actor, NextBotA { actor.NextVoiceTime = 0.0; } - return action.ChangeTo(SF2_ChaserAlertAction(pos, data.AlertData.RunOnSuspect[difficulty]), "Abort abort, I saw someone!"); + return action.ChangeTo(SF2_ChaserAlertAction(pos, alertData.ShouldRunOnSuspect(difficulty)), "Abort abort, I saw someone!"); } } @@ -160,7 +152,7 @@ static int Update(SF2_ChaserSmellAction action, SF2_ChaserEntity actor, NextBotA { actor.NextVoiceTime = 0.0; } - return action.ChangeTo(SF2_ChaserAlertAction(pos, data.AlertData.RunOnSuspect[difficulty]), "Stop! I heard someone!"); + return action.ChangeTo(SF2_ChaserAlertAction(pos, alertData.ShouldRunOnSuspect(difficulty)), "Stop! I heard someone!"); } else if ((interruptConditions & COND_ALERT_TRIGGER_POS) != 0) { @@ -173,7 +165,7 @@ static int Update(SF2_ChaserSmellAction action, SF2_ChaserEntity actor, NextBotA { actor.NextVoiceTime = 0.0; } - return action.ChangeTo(SF2_ChaserAlertAction(pos, data.AlertData.RunOnSuspect[difficulty]), "Stop! I got a sound hint!"); + return action.ChangeTo(SF2_ChaserAlertAction(pos, alertData.ShouldRunOnSuspect(difficulty)), "Stop! I got a sound hint!"); } return action.Continue(); diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/spawn.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/spawn.sp index 48bcc11d..0a449538 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/spawn.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/spawn.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -20,6 +21,7 @@ methodmap SF2_ChaserSpawnAction < NextBotAction static int OnStart(SF2_ChaserSpawnAction action, SF2_ChaserEntity actor, NextBotAction priorAction) { actor.State = STATE_IDLE; + actor.IsSpawning = true; float rate = 1.0, duration = 0.0, cycle = 0.0; if (actor.GetOverrideSpawnAnimation()[0] != '\0') diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/stun.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/stun.sp index 27ff1590..e0630eeb 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/stun.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/stun.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -54,14 +55,8 @@ methodmap SF2_ChaserStunnedAction < NextBotAction static NextBotAction InitialContainedAction(SF2_ChaserStunnedAction action, SF2_ChaserEntity actor) { - SF2NPC_BaseNPC baseController = view_as(actor.Controller); - SF2BossProfileData data; INextBot bot = actor.MyNextBotPointer(); ILocomotion loco = bot.GetLocomotionInterface(); - data = baseController.GetProfileData(); - char animName[64]; - float rate = 1.0, duration = 0.0, cycle = 0.0; - int difficulty = baseController.Difficulty; actor.IsAttemptingToMove = false; loco.Stop(); @@ -78,14 +73,13 @@ static NextBotAction InitialContainedAction(SF2_ChaserStunnedAction action, SF2_ actor.PerformVoice(SF2BossSound_Stun); actor.EndCloak(); + char posture[64]; + actor.GetPosture(posture, sizeof(posture)); - if (data.AnimationData.GetAnimation(g_SlenderAnimationsList[SF2BossAnimation_Stun], difficulty, animName, sizeof(animName), rate, duration, cycle)) + ChaserBossProfile data = actor.Controller.GetProfileData(); + if (data.GetAnimations().HasAnimationSection(g_SlenderAnimationsList[SF2BossAnimation_Stun])) { - int sequence = actor.SelectProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Stun], rate, duration, cycle); - if (sequence != -1) - { - return SF2_PlaySequenceAndWait(sequence, duration, rate, cycle); - } + return SF2_PlaySequenceAndWaitEx(g_SlenderAnimationsList[SF2BossAnimation_Stun]); } return NULL_ACTION; @@ -94,32 +88,41 @@ static NextBotAction InitialContainedAction(SF2_ChaserStunnedAction action, SF2_ static int OnStart(SF2_ChaserStunnedAction action, SF2_ChaserEntity actor, NextBotAction priorAction) { SF2NPC_Chaser controller = actor.Controller; - SF2ChaserBossProfileData data; + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileChaseData chaseData = data.GetChaseBehavior(); + ChaserBossProfileStunData stunData = data.GetStunBehavior(); int difficulty = controller.Difficulty; - data = controller.GetProfileData(); - if (data.StunData.StartEffects != null) + if (stunData.GetOnStartEffects() != null) { float pos[3], ang[3]; actor.GetAbsOrigin(pos); actor.GetAbsAngles(ang); - SlenderSpawnEffects(data.StunData.StartEffects, controller.Index, false, pos, ang, _, _, true); + SlenderSpawnEffects(stunData.GetOnStartEffects(), controller.Index, false, pos, ang, _, _, false); + } + + if (stunData.GetOnStartInputs() != null) + { + stunData.GetOnStartInputs().AcceptInputs(actor, action.Attacker, action.Attacker); } - if (data.KeyDrop) + if (stunData.KeyDrop) { - if (SF_IsBoxingMap() && data.StunData.Disappear[difficulty] && data.BoxingBoss && !g_SlenderBoxingBossIsKilled[controller.Index] && !view_as(controller).GetProfileData().IsPvEBoss) + char model[PLATFORM_MAX_PATH], trigger[64]; + stunData.GetKeyModel(model, sizeof(model)); + stunData.GetKeyTrigger(trigger, sizeof(trigger)); + if (SF_IsBoxingMap() && stunData.ShouldDisappear(difficulty) && data.BoxingBoss && !g_SlenderBoxingBossIsKilled[controller.Index] && data.IsPvEBoss) { g_SlenderBoxingBossKilled++; if ((g_SlenderBoxingBossKilled == g_SlenderBoxingBossCount)) { - NPC_DropKey(controller.Index, data.KeyModel, data.KeyTrigger); + NPC_DropKey(controller.Index, model, trigger); } g_SlenderBoxingBossIsKilled[controller.Index] = true; } else { - NPC_DropKey(controller.Index, data.KeyModel, data.KeyTrigger); + NPC_DropKey(controller.Index, model, trigger); } } @@ -130,10 +133,10 @@ static int OnStart(SF2_ChaserStunnedAction action, SF2_ChaserEntity actor, NextB if (actor.State == STATE_CHASE) { - actor.CurrentChaseDuration += data.ChaseDurationAddOnStun[difficulty]; - if (actor.CurrentChaseDuration > data.ChaseDuration[difficulty]) + actor.CurrentChaseDuration += chaseData.GetDurationAddOnStunned(difficulty); + if (actor.CurrentChaseDuration > chaseData.GetMaxChaseDuration(difficulty)) { - actor.CurrentChaseDuration = data.ChaseDuration[difficulty]; + actor.CurrentChaseDuration = chaseData.GetMaxChaseDuration(difficulty); } } @@ -145,7 +148,7 @@ static int OnStart(SF2_ChaserStunnedAction action, SF2_ChaserEntity actor, NextB } else { - actor.MaxStunHealth += data.StunData.AddHealthPerStun[difficulty]; + actor.MaxStunHealth += stunData.GetAddHealthPerStun(difficulty); } if (controller.HasAttribute(SF2Attribute_AddSpeedOnStun)) @@ -154,7 +157,7 @@ static int OnStart(SF2_ChaserStunnedAction action, SF2_ChaserEntity actor, NextB } else { - controller.SetAddSpeed(data.StunData.AddSpeedPerStun[difficulty]); + controller.SetAddSpeed(stunData.GetAddRunSpeed(difficulty)); } if (controller.HasAttribute(SF2Attribute_AddAccelerationOnStun)) @@ -163,7 +166,7 @@ static int OnStart(SF2_ChaserStunnedAction action, SF2_ChaserEntity actor, NextB } else { - controller.SetAddAcceleration(data.StunData.AddAccelerationPerStun[difficulty]); + controller.SetAddAcceleration(stunData.GetAddAcceleration(difficulty)); } actor.GroundSpeedOverride = true; @@ -183,7 +186,7 @@ static int Update(SF2_ChaserStunnedAction action, SF2_ChaserEntity actor, float return action.Continue(); } - if (actor.Controller.GetProfileData().StunData.Disappear[actor.Controller.Difficulty]) + if (actor.Controller.GetProfileData().GetStunBehavior().ShouldDisappear(actor.Controller.Difficulty)) { actor.Controller.UnSpawn(true); } @@ -204,22 +207,27 @@ static void OnEnd(SF2_ChaserStunnedAction action, SF2_ChaserEntity actor) return; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileStunData stunData = data.GetStunBehavior(); - if (data.StunData.EndEffects != null) + if (stunData.GetOnEndEffects() != null) { float pos[3], ang[3]; actor.GetAbsOrigin(pos); actor.GetAbsAngles(ang); - SlenderSpawnEffects(data.StunData.EndEffects, controller.Index, false, pos, ang, _, _, true); + SlenderSpawnEffects(stunData.GetOnEndEffects(), controller.Index, false, pos, ang, _, _, false); + } + + if (stunData.GetOnEndInputs() != null) + { + stunData.GetOnEndInputs().AcceptInputs(actor, action.Attacker, action.Attacker); } actor.IsStunned = false; actor.GroundSpeedOverride = false; actor.StunHealth = actor.MaxStunHealth; - actor.NextStunTime = GetGameTime() + data.StunData.Cooldown[actor.Controller.Difficulty]; + actor.NextStunTime = GetGameTime() + stunData.GetCooldown(actor.Controller.Difficulty); actor.UpdateMovementAnimation(); diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/tauntkill.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/tauntkill.sp index f4a2b7aa..bfd32b61 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/tauntkill.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/actions/tauntkill.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -17,10 +18,8 @@ methodmap SF2_ChaserTauntKillAction < NextBotAction public static bool IsPossible(SF2_ChaserEntity actor) { - SF2NPC_BaseNPC baseController = view_as(actor.Controller); - SF2BossProfileData data; - data = baseController.GetProfileData(); - if (!data.AnimationData.HasAnimationSection(g_SlenderAnimationsList[SF2BossAnimation_TauntKill])) + ChaserBossProfile data = actor.Controller.GetProfileData(); + if (!data.GetAnimations().HasAnimationSection(g_SlenderAnimationsList[SF2BossAnimation_TauntKill])) { return false; } @@ -46,8 +45,7 @@ static int OnStart(SF2_ChaserTauntKillAction action, SF2_ChaserEntity actor, Nex SF2NPC_Chaser controller = actor.Controller; if (controller.IsValid()) { - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); if (data.NormalSoundHook) { actor.NextVoiceTime = 0.0; @@ -62,8 +60,7 @@ static int OnResume(SF2_ChaserTauntKillAction action, SF2_ChaserEntity actor, Ne SF2NPC_Chaser controller = actor.Controller; if (controller.IsValid()) { - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); if (data.NormalSoundHook) { actor.NextVoiceTime = 0.0; diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/entity.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/entity.sp index 6bc4c68b..1c58fa2d 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/entity.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/entity.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required #include "actions/mainlayer.sp" #include "actions/idle.sp" @@ -52,6 +53,8 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss g_Factory.SetInitialActionFactory(SF2_ChaserMainAction.GetFactory()); g_Factory.BeginDataMapDesc() .DefineBoolField("m_IsAllowedToDespawn") + .DefineBoolField("m_IsSpawning") + .DefineBoolField("m_IsInAirAnimation") .DefineStringField("m_OverrideSpawnAnimation") .DefineFloatField("m_OverrideSpawnAnimationRate") .DefineFloatField("m_OverrideSpawnAnimationDuration") @@ -67,19 +70,23 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss .DefineStringField("m_DefaultPosture") .DefineBoolField("m_IsAttacking") .DefineBoolField("m_CancelAttack") + .DefineBoolField("m_ClearCurrentAttack") .DefineStringField("m_AttackName") .DefineIntField("m_AttackIndex") .DefineIntField("m_NextAttackTime") .DefineFloatField("m_AttackRunDuration") .DefineFloatField("m_AttackRunDelay") .DefineFloatField("m_NextVoiceTime") + .DefineFloatField("m_NextHurtVoiceTime") .DefineIntField("m_MovementType") + .DefineBoolField("m_LockMovementType") .DefineIntField("m_AlertTriggerCount", MAXTF2PLAYERS) .DefineVectorField("m_AlertTriggerPosition", MAXTF2PLAYERS) .DefineEntityField("m_AlertTriggerTarget") .DefineFloatField("m_AlertSoundTriggerCooldown", MAXTF2PLAYERS) .DefineVectorField("m_AlertTriggerPositionEx") .DefineFloatField("m_AlertChangePositionCooldown") + .DefineIntField("m_TauntAlertStrikes", MAXTF2PLAYERS) .DefineIntField("m_AutoChaseCount", MAXTF2PLAYERS) .DefineFloatField("m_AutoChaseAddCooldown", MAXTF2PLAYERS) .DefineFloatField("m_AutoChaseCooldown", MAXTF2PLAYERS) @@ -119,9 +126,10 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss g_OnEntityCreatedPFwd.AddFunction(null, EntityCreated); g_OnPlayerSpawnPFwd.AddFunction(null, OnPlayerSpawn); - g_OnPlayerTakeDamagePFwd.AddFunction(null, OnPlayerTakeDamage); + g_OnPlayerTakeDamagePostPFwd.AddFunction(null, OnPlayerTakeDamagePost); g_OnPlayerDeathPrePFwd.AddFunction(null, OnPlayerDeathPre); g_OnPlayerDeathPFwd.AddFunction(null, OnPlayerDeath); + g_OnBuildingDestroyedPFwd.AddFunction(null, OnBuildingDestroyed); SF2_ChaserAttackAction.Initialize(); InitializePostureRagePhase(); @@ -159,6 +167,32 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } + property bool IsSpawning + { + public get() + { + return this.GetProp(Prop_Data, "m_IsSpawning") != 0; + } + + public set(bool value) + { + this.SetProp(Prop_Data, "m_IsSpawning", value); + } + } + + property bool IsInAirAnimation + { + public get() + { + return this.GetProp(Prop_Data, "m_IsInAirAnimation") != 0; + } + + public set(bool value) + { + this.SetProp(Prop_Data, "m_IsInAirAnimation", value); + } + } + public char[] GetOverrideSpawnAnimation() { char buffer[128]; @@ -175,7 +209,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss public bool CanBeStunned() { - if (SF_IsSlaughterRunMap() && !view_as(this.Controller).GetProfileData().IsPvEBoss) + if (SF_IsSlaughterRunMap() && !this.Controller.GetProfileData().IsPvEBoss) { return false; } @@ -185,7 +219,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss return false; } - if (!this.Controller.GetProfileData().StunData.Enabled[this.Controller.Difficulty]) + if (!this.Controller.GetProfileData().GetStunBehavior().IsEnabled(this.Controller.Difficulty)) { return false; } @@ -220,7 +254,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss public bool CanTakeDamage(CBaseEntity attacker = view_as(-1), CBaseEntity inflictor = view_as(-1), float damage = 0.0) { - if (SF_IsSlaughterRunMap() && !view_as(this.Controller).GetProfileData().IsPvEBoss) + if (SF_IsSlaughterRunMap() && !this.Controller.GetProfileData().IsPvEBoss) { return false; } @@ -251,12 +285,8 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } int difficulty = this.Controller.Difficulty; - SF2ChaserBossProfileData data; - data = this.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(this.GetAttackName(), attackData); - return !attackData.ImmuneToDamage[difficulty]; + return !this.Controller.GetProfileData().GetAttack(this.GetAttackName()).IsImmuneToDamage(difficulty); } property bool IsStunned @@ -367,7 +397,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss this.SetPropString(Prop_Data, "m_Posture", posture); - if (this.Controller.IsValid() && strcmp(currentPosture, posture) != 0 && this.IsAttemptingToMove) + if (this.Controller.IsValid() && strcmp(currentPosture, posture) != 0) { this.UpdateMovementAnimation(); } @@ -378,7 +408,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss return this.GetPropString(Prop_Data, "m_DefaultPosture", buffer, bufferSize); } - public void SetDefaultPosture(const char[] posture) + public void SetDefaultPosture(const char[] posture, bool update = true) { if (posture[0] == '\0') { @@ -392,17 +422,23 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss return; } - SF2ChaserBossProfileData data; - data = this.Controller.GetProfileData(); - SF2ChaserBossProfilePostureInfo postureInfo; + ChaserBossProfile data = this.Controller.GetProfileData(); - if (!data.GetPosture(posture, postureInfo)) + if (data.GetPosture(posture) == null) { return; } } + char currentPosture[64]; + this.GetDefaultPosture(currentPosture, sizeof(currentPosture)); + this.SetPropString(Prop_Data, "m_DefaultPosture", posture); + + if (this.Controller.IsValid() && strcmp(currentPosture, posture) != 0 && update) + { + this.SetPosture(posture); + } } public bool CanUpdatePosture() @@ -456,6 +492,19 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } + property bool ClearCurrentAttack + { + public get() + { + return this.GetProp(Prop_Data, "m_ClearCurrentAttack") != 0; + } + + public set(bool value) + { + this.SetProp(Prop_Data, "m_ClearCurrentAttack", value); + } + } + public char[] GetAttackName() { char buffer[128]; @@ -559,6 +608,19 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } + property float NextHurtVoiceTime + { + public get() + { + return this.GetPropFloat(Prop_Data, "m_NextHurtVoiceTime"); + } + + public set(float value) + { + this.SetPropFloat(Prop_Data, "m_NextHurtVoiceTime", value); + } + } + property bool IsAttemptingToMove { public get() @@ -587,6 +649,11 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss public set(SF2NPCMoveTypes value) { + if (this.LockMovementType) + { + return; + } + SF2NPCMoveTypes oldType = this.MovementType; this.SetProp(Prop_Data, "m_MovementType", value); @@ -597,6 +664,19 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } + property bool LockMovementType + { + public get() + { + return this.GetProp(Prop_Data, "m_LockMovementType") != 0; + } + + public set(bool value) + { + this.SetProp(Prop_Data, "m_LockMovementType", value); + } + } + public int GetAlertTriggerCount(SF2_BasePlayer player) { return this.GetProp(Prop_Data, "m_AlertTriggerCount", _, player.index); @@ -650,6 +730,16 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } + public int GetTauntAlertStrikes(SF2_BasePlayer player) + { + return this.GetProp(Prop_Data, "m_TauntAlertStrikes", _, player.index); + } + + public void SetTauntAlertStrikes(SF2_BasePlayer player, int value) + { + this.SetProp(Prop_Data, "m_TauntAlertStrikes", value, _, player.index); + } + public int GetAutoChaseCount(SF2_BasePlayer player) { return this.GetProp(Prop_Data, "m_AutoChaseCount", _, player.index); @@ -1094,11 +1184,10 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss int difficulty = controller.Difficulty; float myPos[3]; this.GetAbsOrigin(myPos); - SF2BossProfileData data; - data = view_as(controller).GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); SF2_BasePlayer closest = SF2_INVALID_PLAYER; - float range = Pow(data.SearchRange[difficulty], 2.0); + float range = Pow(data.GetSearchRange(difficulty), 2.0); for (int i = 1; i <= MaxClients; i++) { @@ -1140,6 +1229,11 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss return; } + if (this.MovementType == SF2NPCMoveType_Attack) + { + return; + } + char animation[64]; strcopy(animation, sizeof(animation), g_SlenderAnimationsList[SF2BossAnimation_Idle]); @@ -1155,10 +1249,6 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss { strcopy(animation, sizeof(animation), g_SlenderAnimationsList[SF2BossAnimation_Run]); } - case SF2NPCMoveType_Attack: - { - return; - } } } if (this.IsKillingSomeone) @@ -1168,99 +1258,112 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss char posture[64]; this.GetPosture(posture, sizeof(posture)); - this.ResetProfileAnimation(animation, _, _, _, posture); + this.ResetProfileAnimation(animation, .posture = posture); } public bool PerformVoice(int soundType = -1, const char[] attackName = "") { - SF2BossProfileSoundInfo soundInfo; + ProfileSound soundInfo; return this.PerformVoiceEx(soundType, attackName, soundInfo); } - public bool PerformVoiceEx(int soundType = -1, const char[] attackName = "", SF2BossProfileSoundInfo soundInfo, bool isSet = false) + public bool PerformVoiceEx(int soundType = -1, const char[] attackName = "", ProfileSound soundInfo, bool isSet = false) { if (soundType == -1 && !isSet) { return false; } - SF2ChaserBossProfileData data; - data = this.Controller.GetProfileData(); - ArrayList soundList; + ChaserBossProfile data = this.Controller.GetProfileData(); + KeyMap_Array soundList; if (!isSet) { switch (soundType) { case SF2BossSound_Idle: { - soundInfo = data.IdleSounds; + soundInfo = data.GetIdleSounds(); } case SF2BossSound_Alert: { - soundInfo = data.AlertSounds; + soundInfo = data.GetAlertSounds(); } case SF2BossSound_Chasing: { - soundInfo = data.ChasingSounds; + soundInfo = data.GetChasingSounds(); } case SF2BossSound_ChaseInitial: { - soundInfo = data.ChaseInitialSounds; + soundInfo = data.GetChaseInitialSounds(); } case SF2BossSound_Stun: { - soundInfo = data.StunnedSounds; + soundInfo = data.GetStunSounds(); } case SF2BossSound_Death: { - soundInfo = data.DeathSounds; + soundInfo = data.GetDeathSounds(); } case SF2BossSound_Attack: { - return this.CheckNestedSoundSection(data.AttackSounds, attackName, soundInfo, soundList); + return this.CheckNestedSoundSection(data.GetAttackSounds(), attackName, soundInfo, soundList, "attack"); } case SF2BossSound_AttackKilled: { - soundInfo = data.AttackKilledSounds; + soundInfo = data.GetAttackKilledSounds(); } case SF2BossSound_TauntKill: { - soundInfo = data.TauntKillSounds; + soundInfo = data.GetTauntKillSounds(); } case SF2BossSound_Smell: { - soundInfo = data.SmellSounds; + soundInfo = data.GetSmellSounds(); } case SF2BossSound_AttackBegin: { - return this.CheckNestedSoundSection(data.AttackBeginSounds, attackName, soundInfo, soundList); + return this.CheckNestedSoundSection(data.GetAttackBeginSounds(), attackName, soundInfo, soundList, "attack_begin"); } case SF2BossSound_AttackEnd: { - return this.CheckNestedSoundSection(data.AttackEndSounds, attackName, soundInfo, soundList); + return this.CheckNestedSoundSection(data.GetAttackEndSounds(), attackName, soundInfo, soundList, "attack_end"); } case SF2BossSound_SelfHeal: { - soundInfo = data.SelfHealSounds; + soundInfo = data.GetSelfHealSounds(); } case SF2BossSound_RageAll: { - soundInfo = data.RageSounds1; + soundInfo = data.GetRageAllSounds(); } case SF2BossSound_RageTwo: { - soundInfo = data.RageSounds2; + soundInfo = data.GetRageTwoSounds(); } case SF2BossSound_RageThree: { - soundInfo = data.RageSounds3; + soundInfo = data.GetRageThreeSounds(); } case SF2BossSound_Despawn: { - soundInfo = data.DespawnSounds; + soundInfo = data.GetDespawnSounds(); + } + case SF2BossSound_Hurt: + { + soundInfo = data.GetHurtSounds(); + } + case SF2BossSound_Jump: + { + soundInfo = data.GetJumpSounds(); } } } + + if (soundInfo == null) + { + return false; + } + soundList = soundInfo.Paths; if (soundList != null && soundList.Length > 0) { @@ -1269,9 +1372,9 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss return false; } - public bool CheckNestedSoundSection(ArrayList list, const char[] attackName, SF2BossProfileSoundInfo soundInfo, ArrayList soundList) + public bool CheckNestedSoundSection(ProfileObject list, const char[] attackName, ProfileSound soundInfo, KeyMap_Array soundList, char[] section) { - if (this.SearchSoundsWithSectionName(list, attackName, soundInfo)) + if (this.SearchSoundsWithSectionName(list, attackName, soundInfo, section)) { soundList = soundInfo.Paths; if (soundList != null && soundList.Length > 0) @@ -1282,71 +1385,49 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss return false; } - public bool PerformVoiceCooldown(SF2BossProfileSoundInfo soundInfo, ArrayList soundList) + public bool PerformVoiceCooldown(ProfileSound soundInfo, KeyMap_Array soundList) { char buffer[PLATFORM_MAX_PATH]; float gameTime = GetGameTime(); soundList.GetString(GetRandomInt(0, soundList.Length - 1), buffer, sizeof(buffer)); if (buffer[0] != '\0') { - float threshold = GetRandomFloat(0.0, 1.0); - float cooldown = GetRandomFloat(soundInfo.CooldownMin, soundInfo.CooldownMax); - if (threshold > soundInfo.Chance) - { - this.NextVoiceTime = gameTime + cooldown; - return false; - } - soundInfo.EmitSound(_, this.index, _, _, SF_SpecialRound(SPECIALROUND_TINYBOSSES) ? 25 : 0); + float cooldown = GetRandomFloat(soundInfo.GetCooldownMin(this.Controller.Difficulty), soundInfo.GetCooldownMax(this.Controller.Difficulty)); this.NextVoiceTime = gameTime + cooldown; - return true; + return soundInfo.EmitSound(_, this.index, _, _, SF_SpecialRound(SPECIALROUND_TINYBOSSES) ? 25 : 0); } return false; } - public void CastAnimEvent(int event, bool footstep = false) + public void CastAnimEvent(int index) { - SF2BossProfileData data; - data = view_as(this.Controller).GetProfileData(); + ChaserBossProfile data = this.Controller.GetProfileData(); - ArrayList arraySounds = data.EventSounds; - ArrayList arrayEvents = data.EventIndexes; + BossProfileEventData event = data.GetEvents(index); - if (footstep) - { - arraySounds = data.FootstepEventSounds; - arrayEvents = data.FootstepEventIndexes; - } - - if (arraySounds == null || arrayEvents == null) + if (event == null) { return; } - int foundIndex = arrayEvents.FindValue(event); - if (foundIndex == -1) + if (event.GetSounds() != null) { - return; + event.GetSounds().EmitSound(.entity = this.index); } - SF2BossProfileSoundInfo soundInfo; - arraySounds.GetArray(foundIndex, soundInfo, sizeof(soundInfo)); - - if (soundInfo.Paths == null) + if (event.GetEffects() != null) { - return; + SlenderSpawnEffects(event.GetEffects(), this.Controller.Index, false, .noParenting = true); } - soundInfo.EmitSound(_, this.index); - SF2ChaserBossProfileData chaserData; - chaserData = this.Controller.GetProfileData(); - if (footstep && chaserData.EarthquakeFootsteps) + if (event.IsFootsteps && data.EarthquakeFootsteps) { float myPos[3]; this.GetAbsOrigin(myPos); - UTIL_ScreenShake(myPos, chaserData.EarthquakeFootstepAmplitude, - chaserData.EarthquakeFootstepFrequency, chaserData.EarthquakeFootstepDuration, - chaserData.EarthquakeFootstepRadius, 0, chaserData.EarthquakeFootstepAirShake); + UTIL_ScreenShake(myPos, data.EarthquakeFootstepAmplitude, + data.EarthquakeFootstepFrequency, data.EarthquakeFootstepDuration, + data.EarthquakeFootstepRadius, 0, data.EarthquakeFootstepAirShake); } } @@ -1357,10 +1438,8 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss { return; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileSoundInfo info; - info = data.FootstepSounds; + ChaserBossProfile data = controller.GetProfileData(); + ProfileSound info = data.GetFootstepSounds(); info.EmitSound(_, this.index); this.LegacyFootstepTime = this.LegacyFootstepInterval + GetGameTime(); @@ -1379,9 +1458,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } int difficulty = this.Controller.Difficulty; - SF2ChaserBossProfileData data; - data = this.Controller.GetProfileData(); - int threshold = data.SoundCountToAlert[difficulty]; + int threshold = this.Controller.GetProfileData().GetSoundSenseData().GetThreshold(difficulty); if (threshold <= 0) { @@ -1444,17 +1521,16 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } int difficulty = this.Controller.Difficulty; - SF2ChaserBossProfileData data; - data = this.Controller.GetProfileData(); + ChaserBossProfile data = this.Controller.GetProfileData(); - if (data.AutoChaseCount[difficulty] <= 0) + if (data.GetAutoChaseData().GetThreshold(difficulty) <= 0) { return; } this.SetAutoChaseCount(player, amount); - if (this.GetAutoChaseCount(player) >= data.AutoChaseCount[difficulty]) + if (this.GetAutoChaseCount(player) >= data.GetAutoChaseData().GetThreshold(difficulty)) { player.SetForceChaseState(this.Controller, true); SetTargetMarkState(this.Controller, player, true); @@ -1476,6 +1552,13 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss canAttack = false; } + ChaserBossProfile data = controller.GetProfileData(); + ProfileObject attacks = data.GetSection("attacks"); + if (attacks == null || attacks.Size == 0) + { + canAttack = false; + } + if (!canAttack) { return NULL_ACTION; @@ -1486,21 +1569,31 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss float gameTime = GetGameTime(); char attackName[64], posture[64]; this.GetPosture(posture, sizeof(posture)); - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); int difficulty = controller.Difficulty; - ArrayList arrayAttacks = new ArrayList(); - SF2ChaserBossProfileAttackData attackData; - for (int index = 0; index < data.Attacks.Length; index++) + ArrayList arrayAttacks = null; + ChaserBossProfileBaseAttack attackData; + for (int index = 0; index < data.GetAttackCount(); index++) { - data.GetAttackFromIndex(index, attackData); + attackData = data.GetAttackFromIndex(index); + if (attackData == null) + { + continue; + } + + attackData.Index = index; if (attackData.Type == SF2BossAttackType_Invalid) { continue; } - if (gameTime < this.GetNextAttackTime(attackData.Name)) + attackData.GetSectionName(attackName, sizeof(attackName)); + if (!attackData.IsEnabled(difficulty)) + { + continue; + } + + if (gameTime < this.GetNextAttackTime(attackName)) { continue; } @@ -1510,17 +1603,17 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss continue; } - if (attackData.DontInterruptChaseInitial[difficulty] && this.IsInChaseInitial) + if (attackData.ShouldNotInterruptChaseInitial(difficulty) && this.IsInChaseInitial) { continue; } - if (!attackData.CanBeUsedWithPosture(posture)) + if (!attackData.CanUseWithPosture(posture)) { continue; } - if (!attackData.StartThroughWalls[difficulty] && (this.InterruptConditions & COND_ENEMYVISIBLE) == 0) + if (!attackData.GetStartThroughWalls(difficulty) && (this.InterruptConditions & COND_ENEMYVISIBLE_NOGLASS) == 0) { continue; } @@ -1530,12 +1623,16 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss Action result = Plugin_Continue; Call_StartForward(g_OnChaserGetCustomAttackPossibleStatePFwd); Call_PushCell(this); - Call_PushString(attackData.Name); + Call_PushString(attackName); Call_PushCell(target); Call_Finish(result); if (result != Plugin_Continue) { + if (arrayAttacks == null) + { + arrayAttacks = new ArrayList(); + } arrayAttacks.Push(index); } continue; @@ -1555,32 +1652,37 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss { // Why must tanks use a different prop data, WHY? float health = strcmp(class, "tank_boss", false) != 0 ? float(target.GetProp(Prop_Send, "m_iHealth")) : float(target.GetProp(Prop_Data, "m_iHealth")); - if (attackData.UseOnHealth != -1.0 && health < attackData.UseOnHealth) + if (attackData.CanUseOnHealth(difficulty) != -1.0 && health < attackData.CanUseOnHealth(difficulty)) { continue; } - if (attackData.BlockOnHealth != -1.0 && health >= attackData.BlockOnHealth) + if (attackData.CanBlockOnHealth(difficulty) != -1.0 && health >= attackData.CanBlockOnHealth(difficulty)) { continue; } } - attackName = attackData.Name; - + if (arrayAttacks == null) + { + arrayAttacks = new ArrayList(); + } arrayAttacks.Push(index); } + if (arrayAttacks == null || arrayAttacks.Length == 0) + { + if (arrayAttacks != null) + { + delete arrayAttacks; + } + return NULL_ACTION; + } + Call_StartForward(g_OnBossPreAttackFwd); Call_PushCell(controller.Index); Call_PushCell(arrayAttacks); Call_Finish(); - if (arrayAttacks.Length == 0) - { - delete arrayAttacks; - return NULL_ACTION; - } - float eyePos[3], targetPos[3], direction[3], eyeAng[3]; this.GetAbsAngles(eyeAng); controller.GetEyePosition(eyePos); @@ -1595,11 +1697,12 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss arrayAttacks.Sort(Sort_Random, Sort_Integer); for (int i = 0; i < arrayAttacks.Length; i++) { - data.GetAttackFromIndex(arrayAttacks.Get(i), attackData); + data.GetAttackName(arrayAttacks.Get(i), attackName, sizeof(attackName)); + attackData = data.GetAttack(attackName); if (attackData.Type != SF2BossAttackType_Custom) { - float beginRange = attackData.BeginRange[difficulty]; - float beginFOV = attackData.BeginFOV[difficulty]; + float beginRange = attackData.GetBeginRange(difficulty); + float beginFOV = attackData.GetBeginFOV(difficulty); if (distance > Pow(beginRange, 2.0)) { continue; @@ -1610,9 +1713,10 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } - attackName = attackData.Name; + attackData.GetSectionName(attackName, sizeof(attackName)); + delete arrayAttacks; - return SF2_ChaserAttackAction(attackName, attackData.Index, attackData.Duration[difficulty] + gameTime); + return SF2_ChaserAttackAction(data, attackName, attackData.Index, attackData.GetDuration(difficulty)); } delete arrayAttacks; @@ -1623,15 +1727,12 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss { NextBotAction nbAction = NULL_ACTION; Action action = Plugin_Continue; - SF2ChaserBossProfileData data; - data = this.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfileBaseAttack attackData = this.Controller.GetProfileData().GetAttack(attackName); Call_StartForward(g_OnBossGetCustomAttackActionFwd); Call_PushCell(this); Call_PushString(attackName); - Call_PushArrayEx(attackData, sizeof(SF2ChaserBossProfileAttackData), SM_PARAM_COPYBACK); + Call_PushCell(attackData); Call_PushCell(target); Call_PushCellRef(nbAction); Call_Finish(action); @@ -1647,61 +1748,49 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss public bool IsCustomAttackPossible(const char[] attackName, CBaseEntity target) { Action action = Plugin_Continue; - SF2ChaserBossProfileData data; - data = this.Controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfileBaseAttack attackData = this.Controller.GetProfileData().GetAttack(attackName); Call_StartForward(g_OnIsBossCustomAttackPossibleFwd); Call_PushCell(this); Call_PushString(attackName); - Call_PushArrayEx(attackData, sizeof(SF2ChaserBossProfileAttackData), SM_PARAM_COPYBACK); + Call_PushCell(attackData); Call_PushCell(target); Call_Finish(action); return action == Plugin_Continue; } - public NextBotAction IsAttackTransitionPossible(const char[] attackName, bool end = false, float& duration = 0.0) + public NextBotAction IsAttackTransitionPossible(const char[] attackName, bool end = false) { - SF2NPC_BaseNPC baseController = view_as(this.Controller); - SF2BossProfileData data; - data = baseController.GetProfileData(); + SF2NPC_Chaser controller = this.Controller; + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileBaseAttack attackData = data.GetAttack(attackName); char section[32]; if (!end) { - strcopy(section, sizeof(section), g_SlenderAnimationsList[SF2BossAnimation_AttackBegin]); + strcopy(section, sizeof(section), attackData.GetAnimations() == null ? g_SlenderAnimationsList[SF2BossAnimation_AttackBegin] : "begin"); } else { - strcopy(section, sizeof(section), g_SlenderAnimationsList[SF2BossAnimation_AttackEnd]); + strcopy(section, sizeof(section), attackData.GetAnimations() == null ? g_SlenderAnimationsList[SF2BossAnimation_AttackEnd] : "end"); } - if (!data.AnimationData.HasAnimationSection(section)) + ProfileMasterAnimations animations = attackData.GetAnimations(); + if (animations == null) { - return NULL_ACTION; + animations = data.GetAnimations(); } - - char animName[64]; - float rate = 1.0, cycle = 0.0; - int difficulty = baseController.Difficulty; - if (!data.AnimationData.GetAnimation(section, difficulty, animName, sizeof(animName), rate, duration, cycle, _, _, _, attackName)) + if (!animations.HasAnimationSection(section)) { return NULL_ACTION; } - int sequence = this.SelectProfileAnimation(section, rate, duration, cycle, _, _, _, attackName); - if (sequence == -1) + ProfileAnimation animSection = animations.GetAnimation(section, .preDefinedName = attackData.GetAnimations() == null ? attackName : ""); + if (animSection == null) { return NULL_ACTION; } - if (duration <= 0.0) - { - duration = this.SequenceDuration(sequence) / rate; - duration *= (1.0 - cycle); - } - - return SF2_PlaySequenceAndWait(sequence, duration, rate, cycle); + return SF2_PlaySequenceAndWaitEx(section, attackData.GetAnimations() == null ? attackName : "", attackData.GetAnimations() == null ? null : attackData.GetAnimations()); } public void DoAttackMiscConditions(const char[] attackName) @@ -1713,10 +1802,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss { return; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); + ChaserBossProfileBaseAttack attackData = controller.GetProfileData().GetAttack(attackName); CBaseEntity target = this.Target; int difficulty = controller.Difficulty; @@ -1724,8 +1810,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss { bool aimAtTarget = false; - if ((controller.HasAttribute(SF2Attribute_AlwaysLookAtTarget) || controller.HasAttribute(SF2Attribute_AlwaysLookAtTargetWhileAttacking)) - && !attackData.IgnoreAlwaysLooking[difficulty]) + if (attackData.ShouldAutoAim(difficulty)) { aimAtTarget = true; } @@ -1769,39 +1854,34 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss return; } - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attackName, attackData); - SF2ChaserBossProfileShockwaveData shockwaveData; - shockwaveData = attackData.Shockwave; + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileBaseAttack attackData = data.GetAttack(attackName); + BossProfileShockwave shockwaveData = attackData.GetShockwave(); + int difficulty = controller.Difficulty; - if (!shockwaveData.Enabled) + if (shockwaveData == null || !shockwaveData.IsEnabled(difficulty)) { return; } - int difficulty = controller.Difficulty; - float radius = shockwaveData.Radius[difficulty]; + float radius = shockwaveData.GetRadius(difficulty); if (radius <= 0.0) { return; } - if (shockwaveData.Effects != null) + if (shockwaveData.GetEffects() != null) { - SlenderSpawnEffects(shockwaveData.Effects, controller.Index, false); + SlenderSpawnEffects(shockwaveData.GetEffects(), controller.Index, false); } - float force = shockwaveData.Force[difficulty]; + float force = shockwaveData.GetForce(difficulty); float myWorldSpace[3], myPos[3]; this.WorldSpaceCenter(myWorldSpace); this.GetAbsOrigin(myPos); bool eliminated = (controller.Flags & SFF_ATTACKWAITERS) != 0; - if (originalData.IsPvEBoss) + if (data.IsPvEBoss) { eliminated = true; } @@ -1823,16 +1903,17 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss continue; } - TR_TraceRayFilter(myWorldSpace, clientWorldSpace, + Handle trace = TR_TraceRayFilterEx(myWorldSpace, clientWorldSpace, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP, RayType_EndPoint, TraceRayDontHitAnyEntity, this.index); - if (!TR_DidHit() || TR_GetEntityIndex() == player.index) + if (!TR_DidHit(trace) || TR_GetEntityIndex(trace) == player.index) { float targetPos[3]; player.GetAbsOrigin(targetPos); - if (targetPos[2] > myPos[2] + shockwaveData.Height[difficulty]) + if (targetPos[2] > myPos[2] + shockwaveData.GetHeight(difficulty)) { + delete trace; continue; } @@ -1849,84 +1930,53 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss player.SetPropVector(Prop_Data, "m_vecBaseVelocity", velocity); } - float amount = shockwaveData.BatteryDrainPercent[difficulty]; + float amount = shockwaveData.GetBatteryDrainPercent(difficulty); if (!IsInfiniteFlashlightEnabled() && amount > 0.0) { player.FlashlightBatteryLife -= amount; } - float sprintAmount = shockwaveData.StaminaDrainPercent[difficulty]; + float sprintAmount = shockwaveData.GetStaminaDrainPercent(difficulty); if (!IsInfiniteSprintEnabled() && sprintAmount > 0.0) { player.Stamina -= sprintAmount; } - shockwaveData.ApplyDamageEffects(player, difficulty, SF2_ChaserBossEntity(this.index)); + shockwaveData.ApplyDamageEffects(player, difficulty, this); } + + delete trace; } } - public bool SearchSoundsWithSectionName(ArrayList base, const char[] name, SF2BossProfileSoundInfo output) + public bool SearchSoundsWithSectionName(ProfileObject base, const char[] name, ProfileSound& output, char[] section) { - if (base == null || base.Length <= 0) + if (base == null) { return false; } - if (base.Length == 1) + ChaserBossProfile data = this.Controller.GetProfileData(); + char arrayName[64]; + FormatEx(arrayName, sizeof(arrayName), "__chaser_%s_sounds", section); + KeyMap_Array array = data.GetArray(arrayName); + if (array == null) { - base.GetArray(0, output, sizeof(output)); - if (output.SectionName[0] == '\0') - { - return true; - } + output = view_as(base); + return true; } - for (int i = 0; i < base.Length; i++) + for (int i = 0; i < array.Length; i++) { - base.GetArray(i, output, sizeof(output)); - if (strcmp(output.SectionName, name) == 0) + char keyName[64]; + array.GetString(i, keyName, sizeof(keyName)); + if (keyName[0] != '\0' && strcmp(keyName, name) == 0) { + output = view_as(base.GetSection(keyName)); return true; } } return false; } - public void DoAlwaysLookAt(CBaseEntity target) - { - if (!target.IsValid()) - { - return; - } - - SF2_BasePlayer player = SF2_BasePlayer(target.index); - if (player.IsValid && !player.IsAlive) - { - return; - } - - INextBot bot = this.MyNextBotPointer(); - ILocomotion loco = bot.GetLocomotionInterface(); - bool tooClose = this.GetIsVisible(player) && bot.IsRangeLessThan(target.index, 16.0) && this.State != STATE_STUN && this.State != STATE_DEATH && !this.IsKillingSomeone; - SF2NPC_Chaser controller = this.Controller; - if (!controller.IsValid()) - { - return; - } - if (!tooClose && !controller.HasAttribute(SF2Attribute_AlwaysLookAtTarget) && !controller.HasAttribute(SF2Attribute_AlwaysLookAtTargetWhileChasing)) - { - return; - } - - if ((this.InterruptConditions & COND_ENEMYVISIBLE) == 0) - { - return; - } - - float pos[3]; - target.GetAbsOrigin(pos); - loco.FaceTowards(pos); - } - public void RegisterProjectiles(bool &isFake = false) { if (g_RestartSessionEnabled) @@ -1938,15 +1988,14 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss { return; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - if (!data.ProjectilesEnabled) + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProjectileData projectileData = data.GetProjectiles(); + int difficulty = controller.Difficulty; + if (!projectileData.IsEnabled(difficulty)) { return; } - int difficulty = controller.Difficulty; - float gameTime = GetGameTime(); if (this.ProjectileCooldown > gameTime) @@ -1954,12 +2003,12 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss return; } - if ((this.InterruptConditions & COND_ENEMYVISIBLE) == 0) + if ((this.InterruptConditions & COND_ENEMYVISIBLE_NOGLASS) == 0) { return; } - if (!data.ProjectileClips) + if (!projectileData.ProjectileClips) { if (controller.Flags & SFF_FAKE) { @@ -1980,12 +2029,12 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss { if (!this.IsReloadingProjectiles) { - this.ProjectileReloadTime = gameTime + data.ProjectileReloadTime[difficulty]; + this.ProjectileReloadTime = gameTime + projectileData.GetReloadTime(difficulty); this.IsReloadingProjectiles = true; } if (this.ProjectileReloadTime <= gameTime && this.IsReloadingProjectiles) { - this.ProjectileAmmo = data.ProjectileClipSize[difficulty]; + this.ProjectileAmmo = projectileData.GetClipSize(difficulty); this.IsReloadingProjectiles = false; } } @@ -2004,16 +2053,13 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss this.GetAbsAngles(myAng); this.Target.WorldSpaceCenter(targetPos); int difficulty = controller.Difficulty; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); - int randomPosMin = data.ProjectileRandomPosMin; - int randomPosMax = data.ProjectileRandomPosMax; - ArrayList array = data.ProjectilePosOffsets; + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProjectileData projectileData = data.GetProjectiles(); + int randomPosMin = projectileData.MinRandomPos; + int randomPosMax = projectileData.MaxRandomPos; bool attackWaiters = (controller.Flags & SFF_ATTACKWAITERS) != 0; - if (originalData.IsPvEBoss) + if (data.IsPvEBoss) { attackWaiters = true; } @@ -2022,121 +2068,142 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss if (randomPosMin == randomPosMax) { - array.GetArray(0, effectPos); + projectileData.GetOffset(1, effectPos); } else { - array.GetArray(GetRandomInt(randomPosMin, randomPosMax), effectPos); + projectileData.GetOffset(GetRandomInt(randomPosMin, randomPosMax), effectPos); } VectorTransform(effectPos, myPos, myAng, effectPos); - for (int i = 0; i < data.ProjectileCount[difficulty]; i++) + for (int i = 0; i < projectileData.GetCount(difficulty); i++) { float direction[3], angle[3]; SubtractVectors(targetPos, effectPos, direction); - float deviation = data.ProjectileDeviation[difficulty]; + float deviation = projectileData.GetDeviation(difficulty) / 10.0; - if (deviation != 0) - { - direction[0] += GetRandomFloat(-deviation, deviation); - direction[1] += GetRandomFloat(-deviation, deviation); - direction[2] += GetRandomFloat(-deviation, deviation); - } NormalizeVector(direction, direction); GetVectorAngles(direction, angle); + if (deviation != 0.0) + { + angle[0] += GetRandomFloat(-deviation, deviation); + angle[1] += GetRandomFloat(-deviation, deviation); + angle[2] += GetRandomFloat(-deviation, deviation); + } + + char sound[PLATFORM_MAX_PATH]; - switch (data.ProjectileType) + switch (projectileData.Type) { case SF2BossProjectileType_Fireball: { - SF2_ProjectileFireball.Create(this, effectPos, angle, data.ProjectileSpeed[difficulty], data.ProjectileDamage[difficulty], - data.ProjectileRadius[difficulty], data.FireballExplodeSound, data.FireballTrail, attackWaiters); + char trail[PLATFORM_MAX_PATH]; + projectileData.GetFireballExplodeSound(sound, sizeof(sound)); + projectileData.GetFireballTrail(trail, sizeof(trail)); + SF2_ProjectileFireball.Create(this, effectPos, angle, projectileData.GetSpeed(difficulty), projectileData.GetDamage(difficulty), + projectileData.GetRadius(difficulty), sound, trail, attackWaiters); + projectileData.GetFireballShootSound(sound, sizeof(sound)); if (i == 0) { - EmitSoundToAll(data.FireballShootSound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); + EmitSoundToAll(sound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); } } case SF2BossProjectileType_Iceball: { - SF2_ProjectileIceball.Create(this, effectPos, angle, data.ProjectileSpeed[difficulty], data.ProjectileDamage[difficulty], - data.ProjectileRadius[difficulty], data.FireballExplodeSound, data.IceballTrail, data.IceballSlowDuration[difficulty], data.IceballSlowPercent[difficulty], data.IceballSlowSound, attackWaiters); + char trail[PLATFORM_MAX_PATH], slow[PLATFORM_MAX_PATH]; + projectileData.GetFireballExplodeSound(sound, sizeof(sound)); + projectileData.GetIceballTrail(trail, sizeof(trail)); + projectileData.GetIceballSlowSound(slow, sizeof(slow)); + SF2_ProjectileIceball.Create(this, effectPos, angle, projectileData.GetSpeed(difficulty), projectileData.GetDamage(difficulty), + projectileData.GetRadius(difficulty), sound, trail, projectileData.GetIceballSlowDuration(difficulty), projectileData.GetIceballSlowPercent(difficulty), slow, attackWaiters); + projectileData.GetIceballSlowSound(sound, sizeof(sound)); if (i == 0) { - EmitSoundToAll(data.FireballShootSound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); + EmitSoundToAll(sound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); } } case SF2BossProjectileType_Rocket: { - SF2_ProjectileRocket.Create(this, effectPos, angle, data.ProjectileSpeed[difficulty], data.ProjectileDamage[difficulty], - data.ProjectileRadius[difficulty], data.CriticalProjectiles, data.RocketTrail, data.RocketExplodeParticle, data.RocketExplodeSound, data.RocketModel, attackWaiters); + char trail[PLATFORM_MAX_PATH], particle[PLATFORM_MAX_PATH], model[PLATFORM_MAX_PATH]; + projectileData.GetRocketExplodeSound(sound, sizeof(sound)); + projectileData.GetRocketTrail(trail, sizeof(trail)); + projectileData.GetRocketExplodeParticle(particle, sizeof(particle)); + projectileData.GetRocketModel(model, sizeof(model)); + SF2_ProjectileRocket.Create(this, effectPos, angle, projectileData.GetSpeed(difficulty), projectileData.GetDamage(difficulty), + projectileData.GetRadius(difficulty), projectileData.GetCritState(difficulty), trail, particle, sound, model, attackWaiters); + projectileData.GetRocketShootSound(sound, sizeof(sound)); if (i == 0) { - EmitSoundToAll(data.RocketShootSound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); + EmitSoundToAll(sound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); } } case SF2BossProjectileType_SentryRocket: { - SF2_ProjectileSentryRocket.Create(this, effectPos, angle, data.ProjectileSpeed[difficulty], data.ProjectileDamage[difficulty], - data.ProjectileRadius[difficulty], data.CriticalProjectiles, attackWaiters); + projectileData.GetSentryRocketShootSound(sound, sizeof(sound)); + SF2_ProjectileSentryRocket.Create(this, effectPos, angle, projectileData.GetSpeed(difficulty), projectileData.GetDamage(difficulty), + projectileData.GetRadius(difficulty), projectileData.GetCritState(difficulty), attackWaiters); if (i == 0) { - EmitSoundToAll(data.SentryRocketShootSound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); + EmitSoundToAll(sound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); } } case SF2BossProjectileType_Mangler: { - SF2_ProjectileCowMangler.Create(this, effectPos, angle, data.ProjectileSpeed[difficulty], data.ProjectileDamage[difficulty], - data.ProjectileRadius[difficulty], attackWaiters); + projectileData.GetManglerShootSound(sound, sizeof(sound)); + SF2_ProjectileCowMangler.Create(this, effectPos, angle, projectileData.GetSpeed(difficulty), projectileData.GetDamage(difficulty), + projectileData.GetRadius(difficulty), attackWaiters); if (i == 0) { - EmitSoundToAll(data.ManglerShootSound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); + EmitSoundToAll(sound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); } } case SF2BossProjectileType_Grenade: { - SF2_ProjectileGrenade.Create(this, effectPos, angle, data.ProjectileSpeed[difficulty], data.ProjectileDamage[difficulty], - data.ProjectileRadius[difficulty], data.CriticalProjectiles, "pipebombtrail_blue", ROCKET_EXPLODE_PARTICLE, ROCKET_IMPACT, "models/weapons/w_models/w_grenade_grenadelauncher.mdl", attackWaiters); + projectileData.GetGrenadeShootSound(sound, sizeof(sound)); + SF2_ProjectileGrenade.Create(this, effectPos, angle, projectileData.GetSpeed(difficulty), projectileData.GetDamage(difficulty), + projectileData.GetRadius(difficulty), projectileData.GetCritState(difficulty), "pipebombtrail_blue", ROCKET_EXPLODE_PARTICLE, ROCKET_IMPACT, "models/weapons/w_models/w_grenade_grenadelauncher.mdl", attackWaiters); if (i == 0) { - EmitSoundToAll(data.GrenadeShootSound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); + EmitSoundToAll(sound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); } } case SF2BossProjectileType_Arrow: { - SF2_ProjectileArrow.Create(this, effectPos, angle, data.ProjectileSpeed[difficulty], data.ProjectileDamage[difficulty], - data.CriticalProjectiles, "pipebombtrail_blue", "weapons/fx/rics/arrow_impact_flesh2.wav", "models/weapons/w_models/w_arrow.mdl", attackWaiters); + projectileData.GetArrowShootSound(sound, sizeof(sound)); + SF2_ProjectileArrow.Create(this, effectPos, angle, projectileData.GetSpeed(difficulty), projectileData.GetDamage(difficulty), + projectileData.GetCritState(difficulty), "effects/arrowtrail_red.vmt", "weapons/fx/rics/arrow_impact_flesh2.wav", "models/weapons/w_models/w_arrow.mdl", attackWaiters); if (i == 0) { - EmitSoundToAll(data.ArrowShootSound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); + EmitSoundToAll(sound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); } } case SF2BossProjectileType_Baseball: { - SF2_ProjectileBaseball.Create(this, effectPos, angle, data.ProjectileSpeed[difficulty], data.ProjectileDamage[difficulty], - data.CriticalProjectiles, "models/weapons/w_models/w_baseball.mdl", attackWaiters); + projectileData.GetBaseballShootSound(sound, sizeof(sound)); + SF2_ProjectileBaseball.Create(this, effectPos, angle, projectileData.GetSpeed(difficulty), projectileData.GetDamage(difficulty), + projectileData.GetCritState(difficulty), "models/weapons/w_models/w_baseball.mdl", attackWaiters); if (i == 0) { - EmitSoundToAll(data.BaseballShootSound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); + EmitSoundToAll(sound, this.index, SNDCHAN_ITEM, SNDLEVEL_SCREAMING); } } } } - if (data.ShootGestures) + char gesture[64]; + projectileData.GetShootGesture(gesture, sizeof(gesture)); + if (gesture[0] != '\0') { - this.RemoveAllGestures(); - char gesture[64]; - strcopy(gesture, sizeof(gesture), data.ShootGestureName); - int sequence = this.LookupSequence(gesture); if (sequence != -1) { + this.RemoveAllGestures(); this.AddGestureSequence(sequence); } } - this.ProjectileCooldown = GetRandomFloat(data.ProjectileCooldownMin[difficulty], data.ProjectileCooldownMax[difficulty]) + GetGameTime(); + this.ProjectileCooldown = GetRandomFloat(projectileData.GetMinCooldown(difficulty), projectileData.GetMaxCooldown(difficulty)) + GetGameTime(); } public void CheckTauntKill(SF2_BasePlayer player) @@ -2157,15 +2224,21 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss int difficulty = controller.Difficulty; SF2NPC_BaseNPC copyMaster = controller.CopyMaster; SF2NPC_BaseNPC companionMaster = controller.CompanionMaster; - SF2ChaserBossProfileData data, otherData; + ChaserBossProfile data, otherData; data = controller.GetProfileData(); + ChaserBossAlertOnStateData alertStateData, otherAlertStateData; + if (data.GetAlertBehavior() == null) + { + return; + } + alertStateData = data.GetAlertBehavior().GetAlertSyncData(); - if (!data.AlertOnAlertInfo.Enabled[difficulty]) + if (!alertStateData.IsEnabled(difficulty)) { return; } - if (!data.AlertOnAlertInfo.Copies[difficulty] && !data.AlertOnAlertInfo.Companions[difficulty]) + if (!alertStateData.GetCopies(difficulty) && !alertStateData.GetCompanions(difficulty)) { return; } @@ -2188,12 +2261,17 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } SF2NPC_BaseNPC otherController = SF2NPC_BaseNPC(index); - if (!otherController.IsValid() || otherController.Type != SF2BossType_Chaser || otherController.EntIndex == INVALID_ENT_REFERENCE) + if (!otherController.IsValid() || otherController.GetProfileData().Type != SF2BossType_Chaser || otherController.EntIndex == INVALID_ENT_REFERENCE) { continue; } otherData = view_as(otherController).GetProfileData(); + if (otherData.GetAlertBehavior() == null) + { + continue; + } + otherAlertStateData = otherData.GetAlertBehavior().GetAlertSyncData(); SF2_ChaserEntity otherChaser = SF2_ChaserEntity(otherController.EntIndex); if (!otherChaser.IsValid()) { @@ -2208,7 +2286,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss SF2NPC_BaseNPC otherCopyMaster = otherController.CopyMaster; SF2NPC_BaseNPC otherCompanionMaster = otherController.CompanionMaster; bool doContinue = false; - if (data.AlertOnAlertInfo.Copies[difficulty]) + if (alertStateData.GetCopies(difficulty)) { if (copyMaster != otherController && otherCopyMaster != copyMaster && otherCopyMaster != controller) { @@ -2216,7 +2294,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } - if (data.AlertOnAlertInfo.Companions[difficulty]) + if (alertStateData.GetCompanions(difficulty)) { SF2NPC_BaseNPC tempCompanionMaster = companionMaster, tempOtherCompanionMaster = otherCompanionMaster; if (otherController.IsCopy) @@ -2265,12 +2343,12 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } float distance = this.MyNextBotPointer().GetRangeSquaredTo(otherController.EntIndex); - if (distance > Pow(otherData.AlertOnAlertInfo.Radius[difficulty], 2.0)) + if (distance > Pow(otherAlertStateData.GetRadius(difficulty), 2.0)) { continue; } - if (otherData.AlertOnAlertInfo.ShouldBeVisible[difficulty] && !otherChaser.IsLOSClearFromTarget(this)) + if (otherAlertStateData.ShouldBeVisible(difficulty) && !otherChaser.IsLOSClearFromTarget(this)) { continue; } @@ -2290,21 +2368,27 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss int difficulty = controller.Difficulty; SF2NPC_BaseNPC copyMaster = controller.CopyMaster; SF2NPC_BaseNPC companionMaster = controller.CompanionMaster; - SF2ChaserBossProfileData data, otherData; + ChaserBossProfile data, otherData; data = controller.GetProfileData(); + ChaserBossAlertOnStateData alertStateData, otherAlertStateData; + if (data.GetAlertBehavior() == null) + { + return; + } + alertStateData = data.GetAlertBehavior().GetAlertSyncData(); float gameTime = GetGameTime(); - if (!data.AlertOnAlertInfo.Enabled[difficulty]) + if (!alertStateData.IsEnabled(difficulty)) { return; } - if (!data.AlertOnAlertInfo.Copies[difficulty] && !data.AlertOnAlertInfo.Companions[difficulty]) + if (!alertStateData.GetCopies(difficulty) && !alertStateData.GetCompanions(difficulty)) { return; } - if (!data.AlertOnAlertInfo.Follow[difficulty] || this.FollowedCompanionAlert || gameTime < this.FollowCooldownAlert) + if (!alertStateData.ShouldFollow(difficulty) || this.FollowedCompanionAlert || gameTime < this.FollowCooldownAlert) { return; } @@ -2322,7 +2406,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } SF2NPC_BaseNPC otherController = SF2NPC_BaseNPC(index); - if (!otherController.IsValid() || otherController.Type != SF2BossType_Chaser || otherController.EntIndex == INVALID_ENT_REFERENCE) + if (!otherController.IsValid() || otherController.GetProfileData().Type != SF2BossType_Chaser || otherController.EntIndex == INVALID_ENT_REFERENCE) { continue; } @@ -2334,8 +2418,13 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } otherData = view_as(otherController).GetProfileData(); + if (otherData.GetAlertBehavior() == null) + { + continue; + } + otherAlertStateData = otherData.GetAlertBehavior().GetAlertSyncData(); - if (!otherData.AlertOnAlertInfo.Copies[difficulty] && !otherData.AlertOnAlertInfo.Companions[difficulty]) + if (!otherAlertStateData.GetCopies(difficulty) && !otherAlertStateData.GetCompanions(difficulty)) { continue; } @@ -2343,7 +2432,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss SF2NPC_BaseNPC otherCopyMaster = otherController.CopyMaster; SF2NPC_BaseNPC otherCompanionMaster = otherController.CompanionMaster; bool doContinue = false; - if (data.AlertOnAlertInfo.Copies[difficulty]) + if (alertStateData.GetCopies(difficulty)) { if (copyMaster != otherController && otherCopyMaster != copyMaster && otherCopyMaster != controller) { @@ -2351,7 +2440,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } - if (data.AlertOnAlertInfo.Companions[difficulty]) + if (alertStateData.GetCompanions(difficulty)) { SF2NPC_BaseNPC tempCompanionMaster = companionMaster, tempOtherCompanionMaster = otherCompanionMaster; if (otherController.IsCopy) @@ -2400,12 +2489,12 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } float distance = this.MyNextBotPointer().GetRangeSquaredTo(otherController.EntIndex); - if (distance > Pow(otherData.AlertOnAlertInfo.Radius[difficulty], 2.0)) + if (distance > Pow(otherAlertStateData.GetRadius(difficulty), 2.0)) { continue; } - if (otherData.AlertOnAlertInfo.ShouldBeVisible[difficulty] && !otherChaser.IsLOSClearFromTarget(this)) + if (otherAlertStateData.ShouldBeVisible(difficulty) && !otherChaser.IsLOSClearFromTarget(this)) { continue; } @@ -2442,15 +2531,21 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss int difficulty = controller.Difficulty; SF2NPC_BaseNPC copyMaster = controller.CopyMaster; SF2NPC_BaseNPC companionMaster = controller.CompanionMaster; - SF2ChaserBossProfileData data, otherData; + ChaserBossProfile data, otherData; data = controller.GetProfileData(); + ChaserBossAlertOnStateData alertStateData, otherAlertStateData; + if (data.GetChaseBehavior() == null) + { + return; + } + alertStateData = data.GetChaseBehavior().GetChaseTogetherData(); - if (!data.AlertOnChaseInfo.Enabled[difficulty]) + if (!alertStateData.IsEnabled(difficulty)) { return; } - if (!data.AlertOnChaseInfo.Copies[difficulty] && !data.AlertOnChaseInfo.Companions[difficulty]) + if (!alertStateData.GetCopies(difficulty) && !alertStateData.GetCompanions(difficulty)) { return; } @@ -2463,12 +2558,17 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } SF2NPC_BaseNPC otherController = SF2NPC_BaseNPC(index); - if (!otherController.IsValid() || otherController.Type != SF2BossType_Chaser || otherController.EntIndex == INVALID_ENT_REFERENCE) + if (!otherController.IsValid() || otherController.GetProfileData().Type != SF2BossType_Chaser || otherController.EntIndex == INVALID_ENT_REFERENCE) { continue; } otherData = view_as(otherController).GetProfileData(); + if (otherData.GetChaseBehavior() == null) + { + continue; + } + otherAlertStateData = otherData.GetChaseBehavior().GetChaseTogetherData(); SF2_ChaserEntity otherChaser = SF2_ChaserEntity(otherController.EntIndex); if (!otherChaser.IsValid()) { @@ -2483,7 +2583,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss SF2NPC_BaseNPC otherCopyMaster = otherController.CopyMaster; SF2NPC_BaseNPC otherCompanionMaster = otherController.CompanionMaster; bool doContinue = false; - if (data.AlertOnChaseInfo.Copies[difficulty]) + if (alertStateData.GetCopies(difficulty)) { if (copyMaster != otherController && otherCopyMaster != copyMaster && otherCopyMaster != controller) { @@ -2491,7 +2591,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } - if (data.AlertOnChaseInfo.Companions[difficulty]) + if (alertStateData.GetCompanions(difficulty)) { SF2NPC_BaseNPC tempCompanionMaster = companionMaster, tempOtherCompanionMaster = otherCompanionMaster; if (otherController.IsCopy) @@ -2540,12 +2640,12 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } float distance = this.MyNextBotPointer().GetRangeSquaredTo(otherController.EntIndex); - if (distance > Pow(otherData.AlertOnChaseInfo.Radius[difficulty], 2.0)) + if (distance > Pow(otherAlertStateData.GetRadius(difficulty), 2.0)) { continue; } - if (otherData.AlertOnChaseInfo.ShouldBeVisible[difficulty] && !otherChaser.IsLOSClearFromTarget(this)) + if (otherAlertStateData.ShouldBeVisible(difficulty) && !otherChaser.IsLOSClearFromTarget(this)) { continue; } @@ -2568,16 +2668,22 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss int difficulty = controller.Difficulty; SF2NPC_BaseNPC copyMaster = controller.CopyMaster; SF2NPC_BaseNPC companionMaster = controller.CompanionMaster; - SF2ChaserBossProfileData data, otherData; + ChaserBossProfile data, otherData; data = controller.GetProfileData(); + ChaserBossAlertOnStateData alertStateData, otherAlertStateData; + if (data.GetChaseBehavior() == null) + { + return; + } + alertStateData = data.GetChaseBehavior().GetChaseTogetherData(); float gameTime = GetGameTime(); - if (!data.AlertOnChaseInfo.Enabled[difficulty] || !data.AlertOnChaseInfo.Follow[difficulty]) + if (!alertStateData.IsEnabled(difficulty) || !alertStateData.ShouldFollow(difficulty)) { return; } - if (!data.AlertOnChaseInfo.Copies[difficulty] && !data.AlertOnChaseInfo.Companions[difficulty]) + if (!alertStateData.GetCopies(difficulty) && !alertStateData.GetCompanions(difficulty)) { return; } @@ -2600,7 +2706,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } SF2NPC_BaseNPC otherController = SF2NPC_BaseNPC(index); - if (!otherController.IsValid() || otherController.Type != SF2BossType_Chaser || otherController.EntIndex == INVALID_ENT_REFERENCE) + if (!otherController.IsValid() || otherController.GetProfileData().Type != SF2BossType_Chaser || otherController.EntIndex == INVALID_ENT_REFERENCE) { continue; } @@ -2612,8 +2718,13 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } otherData = view_as(otherController).GetProfileData(); + if (otherData.GetChaseBehavior() == null) + { + continue; + } + otherAlertStateData = otherData.GetChaseBehavior().GetChaseTogetherData(); - if (!otherData.AlertOnChaseInfo.Copies[difficulty] && !otherData.AlertOnChaseInfo.Companions[difficulty]) + if (!otherAlertStateData.GetCopies(difficulty) && !otherAlertStateData.GetCompanions(difficulty)) { continue; } @@ -2621,7 +2732,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss SF2NPC_BaseNPC otherCopyMaster = otherController.CopyMaster; SF2NPC_BaseNPC otherCompanionMaster = otherController.CompanionMaster; bool doContinue = false; - if (data.AlertOnChaseInfo.Copies[difficulty]) + if (alertStateData.GetCopies(difficulty)) { if (copyMaster != otherController && otherCopyMaster != copyMaster && otherCopyMaster != controller) { @@ -2629,7 +2740,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } } - if (data.AlertOnChaseInfo.Companions[difficulty]) + if (alertStateData.GetCompanions(difficulty)) { SF2NPC_BaseNPC tempCompanionMaster = companionMaster, tempOtherCompanionMaster = otherCompanionMaster; if (otherController.IsCopy) @@ -2678,12 +2789,12 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } float distance = this.MyNextBotPointer().GetRangeSquaredTo(otherController.EntIndex); - if (distance > Pow(otherData.AlertOnChaseInfo.Radius[difficulty], 2.0)) + if (distance > Pow(otherAlertStateData.GetRadius(difficulty), 2.0)) { continue; } - if (otherData.AlertOnChaseInfo.ShouldBeVisible[difficulty] && !otherChaser.IsLOSClearFromTarget(this)) + if (otherAlertStateData.ShouldBeVisible(difficulty) && !otherChaser.IsLOSClearFromTarget(this)) { continue; } @@ -2706,11 +2817,11 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss { return; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileCloakData cloakData = data.GetCloakData(); int difficulty = controller.Difficulty; float gameTime = GetGameTime(); - if (!data.CloakData.Enabled[difficulty]) + if (!cloakData.IsEnabled(difficulty)) { return; } @@ -2733,13 +2844,13 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss float targetPos[3], myPos[3]; target.GetAbsOrigin(targetPos); this.GetAbsOrigin(myPos); - if (this.HasCloaked && GetVectorSquareMagnitude(targetPos, myPos) <= Pow(data.CloakData.DecloakRange[difficulty], 2.0)) + if (this.HasCloaked && GetVectorSquareMagnitude(targetPos, myPos) <= Pow(cloakData.GetDecloakRange(difficulty), 2.0)) { this.EndCloak(); return; } - if (!this.HasCloaked && GetVectorSquareMagnitude(targetPos, myPos) > Pow(data.CloakData.CloakRange[difficulty], 2.0)) + if (!this.HasCloaked && GetVectorSquareMagnitude(targetPos, myPos) > Pow(cloakData.GetCloakRange(difficulty), 2.0)) { return; } @@ -2765,22 +2876,28 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss } SF2NPC_Chaser controller = this.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileCloakData cloakData = data.GetCloakData(); int difficulty = controller.Difficulty; - if (!data.CloakData.Enabled[difficulty]) + if (!cloakData.IsEnabled(difficulty)) { return; } - this.SetRenderMode(view_as(data.CloakData.CloakRenderMode)); - this.SetRenderColor(data.CloakData.CloakRenderColor[0], data.CloakData.CloakRenderColor[1], data.CloakData.CloakRenderColor[2], data.CloakData.CloakRenderColor[3]); + this.SetRenderMode(cloakData.GetRenderMode(difficulty)); + this.SetRenderFx(cloakData.GetRenderFx(difficulty)); + int color[4]; + cloakData.GetRenderColor(color); + this.SetRenderColor(color[0], color[1], color[2], color[3]); this.HasCloaked = true; - this.CloakTime = GetGameTime() + data.CloakData.Duration[difficulty]; + this.CloakTime = GetGameTime() + cloakData.GetDuration(difficulty); float worldPos[3]; this.WorldSpaceCenter(worldPos); SlenderToggleParticleEffects(this.index); - SlenderSpawnEffects(data.CloakData.CloakEffects, controller.Index, false); + if (cloakData.GetCloakEffects() != null) + { + SlenderSpawnEffects(cloakData.GetCloakEffects(), controller.Index, false); + } Call_StartForward(g_OnBossCloakedFwd); Call_PushCell(controller.Index); Call_Finish(); @@ -2793,22 +2910,28 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss return; } SF2NPC_Chaser controller = this.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileCloakData cloakData = data.GetCloakData(); int difficulty = controller.Difficulty; - if (!data.CloakData.Enabled[difficulty]) + if (!cloakData.IsEnabled(difficulty)) { return; } - this.SetRenderMode(view_as(controller.GetRenderMode)); - this.SetRenderColor(controller.GetRenderColor(0), controller.GetRenderColor(1), controller.GetRenderColor(2), controller.GetRenderColor(3)); + this.SetRenderMode(data.GetRenderMode(difficulty)); + this.SetRenderFx(data.GetRenderFx(difficulty)); + int color[4]; + data.GetRenderColor(difficulty, color); + this.SetRenderColor(color[0], color[1], color[2], color[3]); this.HasCloaked = false; - this.CloakTime = GetGameTime() + data.CloakData.Cooldown[difficulty]; + this.CloakTime = GetGameTime() + cloakData.GetCooldown(difficulty); float worldPos[3]; this.WorldSpaceCenter(worldPos); SlenderToggleParticleEffects(this.index, true); - SlenderSpawnEffects(data.CloakData.DecloakEffects, controller.Index, false); + if (cloakData.GetDecloakEffects() != null) + { + SlenderSpawnEffects(cloakData.GetDecloakEffects(), controller.Index, false); + } Call_StartForward(g_OnBossDecloakedFwd); Call_PushCell(controller.Index); Call_Finish(); @@ -2817,16 +2940,17 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss public void DropItem(bool death = false) { SF2NPC_Chaser controller = this.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileStunData stunData = data.GetStunBehavior(); + ChaserBossProfileDeathData deathData = data.GetDeathBehavior(); int difficulty = controller.Difficulty; - bool check = !death ? data.StunData.ItemDrop[difficulty] : data.DeathData.ItemDrop[difficulty]; + bool check = !death ? stunData.CanDropItem(difficulty) : deathData.CanDropItem(difficulty); if (!check) { return; } - int type = !death ? data.StunData.ItemDropType[difficulty] : data.DeathData.ItemDropType[difficulty]; + int type = !death ? stunData.GetItemDropType(difficulty) : deathData.GetItemDropType(difficulty); char class[64]; switch (type) { @@ -2870,6 +2994,9 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss this.GetAbsOrigin(myPos); CBaseEntity item = CBaseEntity(CreateEntityByName(class)); item.KeyValue("OnPlayerTouch", "!self,Kill,,0,-1"); + SetVariantString("OnUser1 !self:Kill::60.0:1"); + item.AcceptInput("AddOutput"); + item.AcceptInput("FireUser1"); item.Spawn(); item.SetProp(Prop_Send, "m_iTeamNum", 0); item.Teleport(myPos, NULL_VECTOR, NULL_VECTOR); @@ -2878,15 +3005,20 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss public void ProcessTraps() { SF2NPC_Chaser controller = this.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + BossProfileTrapData trapData = data.GetTrapData(); + if (trapData == null) + { + return; + } + int difficulty = controller.Difficulty; - if (!data.Traps[difficulty]) + if (!trapData.IsEnabled(difficulty)) { return; } - if (this.State != STATE_IDLE && this.State != STATE_ALERT) + if (!trapData.CanPlaceOnState(difficulty, this.State)) { return; } @@ -2901,7 +3033,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss this.GetAbsOrigin(myPos); this.GetAbsAngles(myAng); Trap_SpawnTrap(myPos, myAng, controller); - this.TrapCooldown = gameTime + data.TrapCooldown[difficulty]; + this.TrapCooldown = gameTime + trapData.GetSpawnCooldown(difficulty); } public static SF2_ChaserEntity Create(SF2NPC_BaseNPC controller, const float pos[3], const float ang[3]) @@ -2920,10 +3052,8 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss controller.GetProfile(profile, sizeof(profile)); chaser.Controller = view_as(controller); - SF2ChaserBossProfileData data; - data = chaser.Controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(chaser.Controller).GetProfileData(); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + ChaserBossProfileStunData stunData = data.GetStunBehavior(); char buffer[PLATFORM_MAX_PATH]; @@ -2931,22 +3061,23 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss GetSlenderModel(controller.Index, _, buffer, sizeof(buffer)); chaser.SetModel(buffer); - chaser.SetRenderMode(view_as(g_SlenderRenderMode[controller.Index])); - chaser.SetRenderFx(view_as(g_SlenderRenderFX[controller.Index])); - chaser.SetRenderColor(g_SlenderRenderColor[controller.Index][0], g_SlenderRenderColor[controller.Index][1], - g_SlenderRenderColor[controller.Index][2], g_SlenderRenderColor[controller.Index][3]); + chaser.SetRenderMode(data.GetRenderMode(difficulty)); + chaser.SetRenderFx(data.GetRenderFx(difficulty)); + int color[4]; + data.GetRenderColor(difficulty, color); + chaser.SetRenderColor(color[0], color[1], color[2], color[3]); chaser.SetDefaultPosture(SF2_PROFILE_CHASER_DEFAULT_POSTURE); chaser.SetPosture(SF2_PROFILE_CHASER_DEFAULT_POSTURE); if (SF_SpecialRound(SPECIALROUND_TINYBOSSES)) { - float scaleModel = controller.ModelScale * 0.5; + float scaleModel = data.ModelScale * 0.5; chaser.SetPropFloat(Prop_Send, "m_flModelScale", scaleModel); } else { - chaser.SetPropFloat(Prop_Send, "m_flModelScale", controller.ModelScale); + chaser.SetPropFloat(Prop_Send, "m_flModelScale", data.ModelScale); } CBaseNPC npc = TheNPCs.FindNPCByEntIndex(chaser.index); @@ -2956,101 +3087,59 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss npc.flGravity = 800.0; npc.flDeathDropHeight = 99999.0; npc.flJumpHeight = 512.0; - npc.flFrictionForward = 0.0; - npc.flFrictionSideways = 3.0; + npc.flFrictionForward = data.GetForwardFriction(difficulty); + npc.flFrictionSideways = data.GetSidewaysFriction(difficulty); - npc.flMaxYawRate = originalData.TurnRate; + npc.flMaxYawRate = data.TurnRate; - float addStunHealth = data.StunData.AddHealthPerPlayer[difficulty]; + float addStunHealth = 0.0; float classAdd; int count; - for (int i = 1; i <= MaxClients; i++) + if (stunData != null) { - SF2_BasePlayer player = SF2_BasePlayer(i); - if (!player.IsValid) - { - continue; - } - - if (originalData.IsPvEBoss && !player.IsEliminated) - { - continue; - } - - if (!originalData.IsPvEBoss && (player.IsEliminated || player.HasEscaped)) - { - continue; - } - count++; - - switch (player.Class) + addStunHealth = stunData.GetAddHealthPerPlayer(difficulty); + for (int i = 1; i <= MaxClients; i++) { - case TFClass_Scout: - { - classAdd += data.StunData.AddHealthPerScout[difficulty]; - } - - case TFClass_Soldier: - { - classAdd += data.StunData.AddHealthPerSoldier[difficulty]; - } - - case TFClass_Pyro: - { - classAdd += data.StunData.AddHealthPerPyro[difficulty]; - } - - case TFClass_DemoMan: - { - classAdd += data.StunData.AddHealthPerDemoman[difficulty]; - } - - case TFClass_Heavy: - { - classAdd += data.StunData.AddHealthPerHeavy[difficulty]; - } - - case TFClass_Engineer: - { - classAdd += data.StunData.AddHealthPerEngineer[difficulty]; - } - - case TFClass_Medic: + SF2_BasePlayer player = SF2_BasePlayer(i); + if (!player.IsValid) { - classAdd += data.StunData.AddHealthPerMedic[difficulty]; + continue; } - case TFClass_Sniper: + if (data.IsPvEBoss && !player.IsEliminated) { - classAdd += data.StunData.AddHealthPerSniper[difficulty]; + continue; } - case TFClass_Spy: + if (!data.IsPvEBoss && (player.IsEliminated || player.HasEscaped)) { - classAdd += data.StunData.AddHealthPerSpy[difficulty]; + continue; } + count++; + classAdd += stunData.GetAddHealthPerClass(difficulty, player.Class); } } addStunHealth *= float(count); - chaser.StunHealth = data.StunData.Health[controller.Difficulty] + addStunHealth + classAdd; + chaser.StunHealth = stunData.GetHealth(difficulty) + addStunHealth + classAdd; chaser.MaxStunHealth = chaser.StunHealth; locomotion.SetCallback(LocomotionCallback_ShouldCollideWith, LocoCollideWith); - locomotion.SetCallback(LocomotionCallback_ClimbUpToLedge, ClimbUpCBase); + locomotion.SetCallback(LocomotionCallback_ClimbUpToLedge, ClimbUpToLedge); + locomotion.SetCallback(LocomotionCallback_JumpAcrossGap, JumpAcrossGap); float pathingMins[3], pathingMaxs[3]; - if (controller.RaidHitbox) + if (data.RaidHitbox) { - pathingMins = g_SlenderDetectMins[controller.Index]; - pathingMaxs = g_SlenderDetectMaxs[controller.Index]; + data.GetHullMins(pathingMins); + data.GetHullMaxs(pathingMaxs); - chaser.SetPropVector(Prop_Send, "m_vecMins", g_SlenderDetectMins[controller.Index]); - chaser.SetPropVector(Prop_Send, "m_vecMaxs", g_SlenderDetectMaxs[controller.Index]); + chaser.SetPropVector(Prop_Send, "m_vecMins", pathingMins); + chaser.SetPropVector(Prop_Send, "m_vecMaxs", pathingMaxs); - chaser.SetPropVector(Prop_Send, "m_vecMinsPreScaled", g_SlenderDetectMins[controller.Index]); - chaser.SetPropVector(Prop_Send, "m_vecMaxsPreScaled", g_SlenderDetectMaxs[controller.Index]); + chaser.SetPropVector(Prop_Send, "m_vecMinsPreScaled", pathingMins); + chaser.SetPropVector(Prop_Send, "m_vecMaxsPreScaled", pathingMaxs); } else { @@ -3067,29 +3156,34 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss npc.SetBodyMins(pathingMins); npc.SetBodyMaxs(pathingMaxs); - if (SF_IsBoxingMap() || originalData.IsPvEBoss) + if (SF_IsBoxingMap() || data.IsPvEBoss) { SetEntityCollisionGroup(chaser.index, COLLISION_GROUP_DEBRIS_TRIGGER); } - for (int difficulty2 = 0; difficulty2 < Difficulty_Max; difficulty2++) - { - g_SlenderTimeUntilKill[controller.Index] = GetGameTime() + NPCGetIdleLifetime(controller.Index, difficulty2); - } + g_SlenderTimeUntilKill[controller.Index] = GetGameTime() + data.GetIdleLifeTime(difficulty); IVision vision = chaser.MyNextBotPointer().GetVisionInterface(); - vision.SetFieldOfView(originalData.FOV); + vision.SetFieldOfView(data.FOV); vision.ForgetAllKnownEntities(); chaser.NextAttackTime = new StringMap(); - chaser.TrapCooldown = GetGameTime() + data.TrapCooldown[controller.Difficulty]; + if (data.GetTrapData() != null) + { + chaser.TrapCooldown = GetGameTime() + data.GetTrapData().GetSpawnCooldown(difficulty); + } chaser.Teleport(pos, ang, NULL_VECTOR); chaser.Spawn(); chaser.Activate(); + if (data.GetSection("events") != null) + { + chaser.Hook_HandleAnimEvent(HandleAnimationEvent); + } + return chaser; } @@ -3112,6 +3206,7 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss public static void SetupAPI() { CreateNative("SF2_ChaserBossEntity.IsValid.get", Native_GetIsValid); + CreateNative("SF2_ChaserBossEntity.Controller.get", Native_GetController); CreateNative("SF2_ChaserBossEntity.IsAttemptingToMove.get", Native_GetIsAttemptingToMove); CreateNative("SF2_ChaserBossEntity.IsAttacking.get", Native_GetIsAttacking); CreateNative("SF2_ChaserBossEntity.IsStunned.get", Native_GetIsStunned); @@ -3135,6 +3230,12 @@ methodmap SF2_ChaserEntity < SF2_BaseBoss CreateNative("SF2_ChaserBossEntity.CreateSoundHint", Native_CreateSoundHint); CreateNative("SF2_ChaserBossEntity.GroundSpeedOverride.get", Native_GetGroundSpeedOverride); CreateNative("SF2_ChaserBossEntity.GroundSpeedOverride.set", Native_SetGroundSpeedOverride); + CreateNative("SF2_ChaserBossEntity.MovementType.get", Native_GetMovementType); + CreateNative("SF2_ChaserBossEntity.MovementType.set", Native_SetMovementType); + CreateNative("SF2_ChaserBossEntity.LockMovementType.get", Native_GetLockMovementType); + CreateNative("SF2_ChaserBossEntity.LockMovementType.set", Native_SetLockMovementType); + + SF2_ChaserAttackAction.SetupAPI(); } } @@ -3197,18 +3298,18 @@ static void OnPlayerSpawn(SF2_BasePlayer client) } } -static Action OnPlayerTakeDamage(SF2_BasePlayer client, int &attacker, int &inflictor, float &damage, int &damageType) +static void OnPlayerTakeDamagePost(SF2_BasePlayer client, int attacker, int inflictor, float damage, int damageType) { SF2_ChaserEntity boss = SF2_ChaserEntity(inflictor); if (!boss.IsValid()) { - return Plugin_Continue; + return; } SF2NPC_Chaser controller = boss.Controller; if (!controller.IsValid()) { - return Plugin_Continue; + return; } Call_StartForward(g_OnClientDamagedByBossFwd); @@ -3219,24 +3320,25 @@ static Action OnPlayerTakeDamage(SF2_BasePlayer client, int &attacker, int &infl Call_PushCell(damageType); Call_Finish(); - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); char attack[64]; strcopy(attack, sizeof(attack), boss.GetAttackName()); if (attack[0] != '\0') { - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attack, attackData); + ChaserBossProfileBaseAttack attackData = data.GetAttack(attack); - if (attackData.HitEffects != null) + if (attackData.GetHitEffects() != null) { - SlenderSpawnEffects(attackData.HitEffects, controller.Index, false, _, _, _, client.index); + SlenderSpawnEffects(attackData.GetHitEffects(), controller.Index, false, _, _, _, client.index); + } + + if (attackData.GetHitInputs() != null) + { + attackData.GetHitInputs().AcceptInputs(boss, client, client); } } - - return Plugin_Continue; -} +} static void OnPlayerDeathPre(SF2_BasePlayer client, int attacker, int inflictor, bool fake) { @@ -3283,19 +3385,22 @@ static void OnPlayerDeathPre(SF2_BasePlayer client, int attacker, int inflictor, return; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); char attack[64]; strcopy(attack, sizeof(attack), boss.GetAttackName()); if (attack[0] != '\0') { - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(attack, attackData); + ChaserBossProfileBaseAttack attackData = data.GetAttack(attack); + + if (attackData.GetOnKillEffects() != null) + { + SlenderSpawnEffects(attackData.GetOnKillEffects(), controller.Index, false, _, _, _, client.index); + } - if (attackData.KillEffects != null) + if (attackData.GetOnKillInputs() != null) { - SlenderSpawnEffects(attackData.KillEffects, controller.Index, false, _, _, _, client.index); + attackData.GetOnKillInputs().AcceptInputs(boss, client, client); } } } @@ -3346,20 +3451,75 @@ static void OnPlayerDeath(SF2_BasePlayer client, int attacker, int inflictor, bo boss.PerformVoice(SF2BossSound_AttackKilled); - SF2BossProfileSoundInfo info; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - info = data.AttackKilledClientSounds; + ChaserBossProfile data = controller.GetProfileData(); + ProfileSound info = data.GetAttackKilledClientSounds(); info.EmitSound(true, client.index); - info = data.AttackKilledAllSounds; - for (int i = 1; i <= MaxClients; i++) + info = data.GetAttackKilledAllSounds(); + info.EmitToAllPlayers(); + + bool played = false; + TFClassType class = client.Class; + + if (data.GetLocalKillSounds() != null) { - if (!IsValidClient(i)) + info = data.GetLocalKillSounds().GetKilledClassSounds(class); + if (!info.EmitSound(_, boss.index)) { - continue; + info = data.GetLocalKillSounds().GetKilledAllSounds(); + info.EmitSound(_, boss.index); } - info.EmitSound(true, i); + } + + if (data.GetGlobalKillSounds() != null) + { + info = data.GetGlobalKillSounds().GetKilledClassSounds(class); + played = info.EmitToAllPlayers(); + if (!played) + { + info = data.GetGlobalKillSounds().GetKilledAllSounds(); + info.EmitToAllPlayers(); + } + } + + if (data.GetClientKillSounds() != null) + { + info = data.GetClientKillSounds().GetKilledClassSounds(class); + if (!info.EmitSound(true, client.index)) + { + info = data.GetClientKillSounds().GetKilledAllSounds(); + info.EmitSound(true, client.index); + } + } +} + +static void OnBuildingDestroyed(CBaseEntity building, CBaseEntity killer) +{ + SF2_ChaserEntity boss = SF2_ChaserEntity(killer.index); + if (!boss.IsValid()) + { + return; + } + + SF2NPC_Chaser controller = boss.Controller; + if (!controller.IsValid()) + { + return; + } + + ChaserBossProfile data = controller.GetProfileData(); + ProfileSound info = null; + + if (data.GetLocalKillSounds() != null) + { + info = data.GetLocalKillSounds().GetKilledBuildingSounds(); + info.EmitSound(_, boss.index); + } + + if (data.GetGlobalKillSounds() != null) + { + info = data.GetGlobalKillSounds().GetKilledBuildingSounds(); + info.EmitToAllPlayers(); } } @@ -3399,18 +3559,13 @@ static Action Think(int entIndex) chaser.InterruptConditions |= interruptConditions; chaser.Target = target; - if (chaser.State == STATE_CHASE && !chaser.IsRaging) - { - chaser.DoAlwaysLookAt(chaser.Target); - } - float gameTime = GetGameTime(); if (chaser.LegacyFootstepInterval > 0.0 && chaser.LegacyFootstepTime < gameTime) { chaser.CastFootstep(); } - chaser.CheckVelocityCancel(); + //chaser.CheckVelocityCancel(); return Plugin_Continue; } @@ -3419,26 +3574,30 @@ static void ThinkPost(int entIndex) { SF2_ChaserEntity chaser = SF2_ChaserEntity(entIndex); SF2NPC_Chaser controller = chaser.Controller; - SF2BossProfileData data; - data = view_as(controller).GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); ProcessSpeed(chaser); ProcessBody(chaser); - if (data.CustomOutlines && data.RainbowOutline) + if (data.GetOutlineData() != null && data.GetOutlineData().GetRainbowState(controller.Difficulty)) { chaser.ProcessRainbowOutline(); } - if (chaser.State == STATE_CHASE && !chaser.IsRaging) + chaser.InterruptConditions = 0; + chaser.SetNextThink(GetGameTime() + data.TickRate); + + if (!chaser.MyNextBotPointer().GetLocomotionInterface().IsOnGround() && (chaser.State == STATE_IDLE || chaser.State == STATE_ALERT || chaser.State == STATE_CHASE)) { - chaser.DoAlwaysLookAt(chaser.Target); + chaser.AirTime -= GetGameFrameTime(); + if (chaser.AirTime > -1.0 && chaser.AirTime <= 0.0 && chaser.ResetProfileAnimation(g_SlenderAnimationsList[SF2BossAnimation_Air])) + { + chaser.AirTime = -1.0; + chaser.IsInAirAnimation = true; + } } - chaser.InterruptConditions = 0; - chaser.SetNextThink(GetGameTime()); - #if defined DEBUG CBaseNPC npc = TheNPCs.FindNPCByEntIndex(chaser.index); for (int i = 1; i <= MaxClients; i++) @@ -3462,19 +3621,17 @@ static void SpawnPost(int entIndex) { SF2_ChaserEntity chaser = SF2_ChaserEntity(entIndex); SF2NPC_Chaser controller = chaser.Controller; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossProfileDeathData deathData = data.GetDeathBehavior(); int difficulty = controller.Difficulty; - if (!data.DeathData.Enabled[difficulty]) + if (!deathData.IsEnabled(difficulty)) { chaser.SetProp(Prop_Data, "m_iHealth", 2000000000); chaser.SetProp(Prop_Data, "m_takedamage", DAMAGE_EVENTS_ONLY); } else { - float addDeathHealth = data.DeathData.AddHealthPerPlayer[difficulty]; + float addDeathHealth = deathData.GetAddHealthPerPlayer(difficulty); int count; float add = 0.0; float result = controller.GetDeathHealth(difficulty); @@ -3485,69 +3642,32 @@ static void SpawnPost(int entIndex) { continue; } - if (originalData.IsPvEBoss) + if (g_Enabled) { - if (!player.IsEliminated) + if (data.IsPvEBoss) { - continue; + if (!player.IsEliminated) + { + continue; + } + } + else + { + if (player.IsEliminated || !player.IsAlive || player.HasEscaped) + { + continue; + } } } else { - if (player.IsEliminated || !player.IsAlive || player.HasEscaped) + if (player.Team != TFTeam_Red && player.Team != TFTeam_Blue) { continue; } } count++; - - switch (player.Class) - { - case TFClass_Scout: - { - add += data.DeathData.AddHealthPerScout[difficulty]; - } - - case TFClass_Soldier: - { - add += data.DeathData.AddHealthPerSoldier[difficulty]; - } - - case TFClass_Pyro: - { - add += data.DeathData.AddHealthPerPyro[difficulty]; - } - - case TFClass_DemoMan: - { - add += data.DeathData.AddHealthPerDemoman[difficulty]; - } - - case TFClass_Heavy: - { - add += data.DeathData.AddHealthPerHeavy[difficulty]; - } - - case TFClass_Engineer: - { - add += data.DeathData.AddHealthPerEngineer[difficulty]; - } - - case TFClass_Medic: - { - add += data.DeathData.AddHealthPerMedic[difficulty]; - } - - case TFClass_Sniper: - { - add += data.DeathData.AddHealthPerSniper[difficulty]; - } - - case TFClass_Spy: - { - add += data.DeathData.AddHealthPerSpy[difficulty]; - } - } + add += deathData.GetAddHealthPerClass(difficulty, player.Class); } addDeathHealth *= float(count); result += addDeathHealth + add; @@ -3566,19 +3686,33 @@ static void SpawnPost(int entIndex) } chaser.RageIndex = -1; - if (originalData.Healthbar) + if (data.Healthbar) { controller.Flags |= SFF_NOTELEPORT; - UpdateHealthBar(controller.Index); + CreateTimer(0.1, Timer_UpdateHealthBar, controller.UniqueID, TIMER_FLAG_NO_MAPCHANGE); + } +} + +static Action Timer_UpdateHealthBar(Handle timer, any id) +{ + int bossIndex = NPCGetFromUniqueID(id); + if (bossIndex == -1) + { + return Plugin_Continue; } + + UpdateHealthBar(bossIndex); + + return Plugin_Stop; } static Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damageType, int &weapon, float damageForce[3], float damagePosition[3], int damageCustom) { SF2_ChaserEntity chaser = SF2_ChaserEntity(victim); SF2_BasePlayer player = SF2_BasePlayer(attacker); - SF2BossProfileData data; - data = view_as(chaser.Controller).GetProfileData(); + ChaserBossProfile data = chaser.Controller.GetProfileData(); + float gameTime = GetGameTime(); + int difficulty = chaser.Controller.Difficulty; if (player.IsValid && player.IsProxy) { @@ -3592,40 +3726,40 @@ static Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &dam if (player.IsValid || g_Buildings.FindValue(EntIndexToEntRef(inflictor)) != -1) { - if (CBaseEntity(inflictor).GetProp(Prop_Data, "m_iTeamNum") == chaser.Team && (chaser.Controller.Flags & SFF_ATTACKWAITERS) == 0) + if (CBaseEntity(inflictor).IsValid() && CBaseEntity(inflictor).GetProp(Prop_Data, "m_iTeamNum") == chaser.Team && (chaser.Controller.Flags & SFF_ATTACKWAITERS) == 0) { return Plugin_Handled; } if (data.IsPvEBoss && player.IsValid && !player.IsInPvE) { - SDKHooks_TakeDamage(inflictor, chaser.index, chaser.index, CBaseEntity(inflictor).GetProp(Prop_Data, "m_iHealth") * 4.0); + SDKHooks_TakeDamage(inflictor, chaser.index, chaser.index, CBaseEntity(inflictor).GetProp(Prop_Data, "m_iHealth") * 4.0, .bypassHooks = false); return Plugin_Handled; } } - SF2ChaserBossProfileData chaserData; - chaserData = chaser.Controller.GetProfileData(); - int difficulty = chaser.Controller.Difficulty; bool changed = false; - if (chaserData.DamageResistances != null) + if (data.GetResistances() != null) { - SF2ChaserBossProfileResistanceData resistanceData; - for (int i = 0; i < chaserData.DamageResistances.Length; i++) + ChaserBossResistanceData resistanceData; + for (int i = 0; i < data.GetResistances().Size; i++) { - chaserData.DamageResistances.GetArray(i, resistanceData, sizeof(resistanceData)); - if (resistanceData.DamageTypes == null) + char name[64]; + data.GetResistances().GetSectionNameFromIndex(i, name, sizeof(name)); + resistanceData = view_as(data.GetResistances().GetSection(name)); + if (resistanceData.GetDamageTypes() == null) { continue; } - for (int i2 = 0; i2 < resistanceData.DamageTypes.Length; i++) + for (int i2 = 0; i2 < resistanceData.GetDamageTypes().Size; i++) { - int type = resistanceData.DamageTypes.Get(i); + resistanceData.GetDamageTypes().GetKeyNameFromIndex(i2, name, sizeof(name)); + int type = resistanceData.GetDamageTypes().GetInt(name); if (damageType & type || damageType == type) { - damage *= resistanceData.Multiplier[difficulty]; + damage *= resistanceData.GetMultiplier(difficulty); changed = true; break; } @@ -3641,24 +3775,28 @@ static Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &dam if (player.IsValid) { CBaseEntity activeWeapon = CBaseEntity(player.GetPropEnt(Prop_Send, "m_hActiveWeapon")); - if (activeWeapon.IsValid() && chaserData.DamageResistances != null) + if (activeWeapon.IsValid() && data.GetResistances() != null) { - SF2ChaserBossProfileResistanceData resistanceData; + ChaserBossResistanceData resistanceData; int itemIndex = activeWeapon.GetProp(Prop_Send, "m_iItemDefinitionIndex"); - for (int i = 0; i < chaserData.DamageResistances.Length; i++) + for (int i = 0; i < data.GetResistances().Size; i++) { - chaserData.DamageResistances.GetArray(i, resistanceData, sizeof(resistanceData)); - if (resistanceData.Weapons == null) + char name[64]; + data.GetResistances().GetSectionNameFromIndex(i, name, sizeof(name)); + resistanceData = view_as(data.GetResistances().GetSection(name)); + if (resistanceData.GetWeapons() == null) { continue; } - if (resistanceData.Weapons.FindValue(itemIndex) == -1) + char itemIndexString[8]; + IntToString(itemIndex, itemIndexString, sizeof(itemIndexString)); + if (!resistanceData.GetWeapons().ContainsString(itemIndexString)) { continue; } - damage *= resistanceData.Multiplier[difficulty]; + damage *= resistanceData.GetMultiplier(difficulty); changed = true; break; } @@ -3695,9 +3833,9 @@ static Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &dam } } - if (player.Class == TFClass_Spy && (data.IsPvEBoss || SF_IsBoxingMap()) && chaser.State != STATE_DEATH) + if (player.Class == TFClass_Spy && (data.IsPvEBoss || SF_IsBoxingMap() || SF_IsRaidMap()) && chaser.State != STATE_DEATH) { - if (FloatAbs(AngleDiff(myAng[1], buffer[1])) >= 75.0 && chaserData.BackstabDamageScale > 0.0) + if (FloatAbs(AngleDiff(myAng[1], buffer[1])) >= 75.0 && data.BackstabDamageScale > 0.0) { damageType = DMG_CRIT; EmitSoundToClient(player.index, "player/spy_shield_break.wav", _, _, SNDLEVEL_TRAFFIC, SND_NOFLAGS, 0.7, 100); @@ -3730,10 +3868,10 @@ static Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &dam model.SetProp(Prop_Send, "m_nSequence", sequence); } - damage = chaser.MaxHealth * chaserData.BackstabDamageScale; - if (!chaserData.DeathData.Enabled[difficulty]) + damage = chaser.MaxHealth * data.BackstabDamageScale; + if (!data.GetDeathBehavior().IsEnabled(difficulty)) { - damage = chaser.MaxStunHealth * chaserData.BackstabDamageScale; + damage = chaser.MaxStunHealth * data.BackstabDamageScale; } switch (weaponEnt.GetProp(Prop_Send, "m_iItemDefinitionIndex")) { @@ -3773,7 +3911,7 @@ static Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &dam case 40, 1146: // Backburner { - if (FloatAbs(AngleDiff(myAng[1], buffer[1])) >= 60.0 && chaserData.BackstabDamageScale > 0.0) + if (FloatAbs(AngleDiff(myAng[1], buffer[1])) >= 60.0 && data.BackstabDamageScale > 0.0) { damageType |= DMG_CRIT; changed = true; @@ -3783,6 +3921,12 @@ static Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &dam } } + if (damage > 0.0 && chaser.GetProp(Prop_Data, "m_iHealth") > damage && data.GetHurtSounds() != null && gameTime >= chaser.NextHurtVoiceTime) + { + chaser.NextHurtVoiceTime = gameTime + GetRandomFloat(data.GetHurtSounds().GetHurtCooldownMin(difficulty), data.GetHurtSounds().GetHurtCooldownMax(difficulty)); + chaser.PerformVoice(SF2BossSound_Hurt); + } + return changed ? Plugin_Changed : Plugin_Continue; } @@ -3790,8 +3934,7 @@ static void OnTakeDamageAlivePost(int victim, int attacker, int inflictor, float { SF2_ChaserEntity chaser = SF2_ChaserEntity(victim); SF2_BasePlayer player = SF2_BasePlayer(attacker); - SF2BossProfileData data; - data = view_as(chaser.Controller).GetProfileData(); + ChaserBossProfile data = chaser.Controller.GetProfileData(); bool broadcastDamage = false; @@ -3965,7 +4108,7 @@ static void OnTakeDamageAlivePost(int victim, int attacker, int inflictor, float } } - if (damage > 0.0 && player.IsValid && player.InCondition(TFCond_RegenBuffed) && (SF_IsBoxingMap() || data.IsPvEBoss)) + if (damage > 0.0 && player.IsValid && player.InCondition(TFCond_RegenBuffed) && (SF_IsBoxingMap() || data.IsPvEBoss || SF_IsRaidMap())) { int health = player.Health; int maxHealth = player.GetProp(Prop_Data, "m_iMaxHealth"); @@ -4070,25 +4213,28 @@ static Action TraceOnHit(int victim, int& attacker, int& inflictor, float& damag int difficulty = controller.Difficulty; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - if (data.DamageResistances != null) + ChaserBossProfile data = controller.GetProfileData(); + if (data.GetResistances() != null) { - SF2ChaserBossProfileResistanceData resistanceData; - for (int i = 0; i < data.DamageResistances.Length; i++) + ChaserBossResistanceData resistanceData; + for (int i = 0; i < data.GetResistances().Size; i++) { - data.DamageResistances.GetArray(i, resistanceData, sizeof(resistanceData)); - if (resistanceData.HitboxGroups == null) + char name[64]; + data.GetResistances().GetSectionNameFromIndex(i, name, sizeof(name)); + resistanceData = view_as(data.GetResistances().GetSection(name)); + if (resistanceData.GetHitboxes() == null) { continue; } - if (resistanceData.HitboxGroups.FindValue(hitgroup) == -1) + char hitgroupString[8]; + IntToString(hitgroup, hitgroupString, sizeof(hitgroupString)); + if (!resistanceData.GetHitboxes().ContainsString(hitgroupString)) { continue; } - damage *= resistanceData.Multiplier[difficulty]; + damage *= resistanceData.GetMultiplier(difficulty); changed = true; break; } @@ -4118,6 +4264,50 @@ static MRESReturn ShouldTransmit(int entIndex, DHookReturn ret, DHookParam param return MRES_Supercede; } +static MRESReturn HandleAnimationEvent(int entIndex, DHookParam params) +{ + SF2_ChaserEntity bossEntity = SF2_ChaserEntity(entIndex); + if (!bossEntity.IsValid() || !bossEntity.Controller.IsValid()) + { + return MRES_Ignored; + } + + ChaserBossProfile data = bossEntity.Controller.GetProfileData(); + + int index = params.GetObjectVar(1, 0, ObjectValueType_Int); + BossProfileEventData event = data.GetEvents(index); + if (event == null) + { + return MRES_Ignored; + } + + bossEntity.CastAnimEvent(index); + + return MRES_Ignored; +} + +static void JumpAcrossGap(CBaseNPC_Locomotion loco, const float landingGoal[3], const float landingForward[3]) +{ + INextBot bot = loco.GetBot(); + SF2_ChaserEntity boss = SF2_ChaserEntity(bot.GetEntity()); + if (boss.IsValid()) + { + boss.IsJumping = true; + } +} + +static bool ClimbUpToLedge(CBaseNPC_Locomotion loco, const float goal[3], const float fwd[3], int entity) +{ + INextBot bot = loco.GetBot(); + SF2_ChaserEntity boss = SF2_ChaserEntity(bot.GetEntity()); + + if (boss.IsValid()) + { + boss.IsJumping = true; + } + return loco.CallBaseFunction(goal, fwd, entity); +} + static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditions = 0) { interruptConditions = 0; @@ -4128,13 +4318,10 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio return CBaseEntity(-1); } bool attackEliminated = (controller.Flags & SFF_ATTACKWAITERS) != 0; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); - if (originalData.IsPvEBoss) + ChaserBossProfile data = controller.GetProfileData(); + if (data.IsPvEBoss) { - attackEliminated = originalData.IsPvEBoss; + attackEliminated = data.IsPvEBoss; } float gameTime = GetGameTime(); @@ -4155,26 +4342,29 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio int state = chaser.State; - if (originalData.EyeData.Type == 1) + if (data.GetEyes().Type == 1) { if (chaser.EyeBoneIndex <= 0) { - chaser.EyeBoneIndex = LookupBone(chaser.index, originalData.EyeData.Bone); + char bone[128]; + data.GetEyes().GetBone(bone, sizeof(bone)); + chaser.EyeBoneIndex = LookupBone(chaser.index, bone); } GetBonePosition(chaser.index, chaser.EyeBoneIndex, traceStartPos, myEyeAng); - float offset[3]; - controller.GetEyePositionOffset(offset); - AddVectors(originalData.EyeData.OffsetAng, myEyeAng, myEyeAng); + float offset[3], angOffset[3]; + data.GetEyes().GetOffsetPos(offset); + data.GetEyes().GetOffsetAng(angOffset); + AddVectors(angOffset, myEyeAng, myEyeAng); VectorTransform(offset, traceStartPos, myEyeAng, traceStartPos); } int oldTarget = chaser.OldTarget.index; - if (!originalData.IsPvEBoss && !IsTargetValidForSlender(chaser, CBaseEntity(oldTarget), attackEliminated)) + if (!data.IsPvEBoss && !IsTargetValidForSlender(chaser, CBaseEntity(oldTarget), attackEliminated)) { chaser.OldTarget = CBaseEntity(INVALID_ENT_REFERENCE); oldTarget = INVALID_ENT_REFERENCE; } - if (originalData.IsPvEBoss && !IsPvETargetValid(CBaseEntity(oldTarget))) + if (data.IsPvEBoss && !IsPvETargetValid(CBaseEntity(oldTarget))) { chaser.OldTarget = CBaseEntity(INVALID_ENT_REFERENCE); oldTarget = INVALID_ENT_REFERENCE; @@ -4197,16 +4387,12 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio } int bestNewTarget = oldTarget; - float searchRange = originalData.SearchRange[difficulty]; - float bestNewTargetDist = Pow(searchRange, 2.0); + float searchRange = data.GetSearchRange(difficulty); + float bestNewTargetDist = 1999999999.0; if (IsValidEntity(bestNewTarget)) { CBaseEntity(bestNewTarget).GetAbsOrigin(targetPos); bestNewTargetDist = GetVectorSquareMagnitude(myPos, targetPos); - if (bestNewTargetDist > Pow(searchRange, 2.0)) - { - bestNewTargetDist = Pow(searchRange, 2.0); - } } if (chaser.SmellPlayerList != null) @@ -4219,11 +4405,11 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio { SF2_BasePlayer client = SF2_BasePlayer(i); - if (!originalData.IsPvEBoss && !IsTargetValidForSlender(chaser, client, attackEliminated)) + if (!data.IsPvEBoss && !IsTargetValidForSlender(chaser, client, attackEliminated)) { continue; } - if (originalData.IsPvEBoss && !IsPvETargetValid(client)) + if (data.IsPvEBoss && !IsPvETargetValid(client)) { continue; } @@ -4231,34 +4417,37 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio valids.Push(EntIndexToEntRef(client.index)); } - for (int i = 0; i < g_Buildings.Length; i++) + if (!g_Enabled || data.IsPvEBoss || SF_IsRaidMap()) { - CBaseEntity entity = CBaseEntity(EntRefToEntIndex(g_Buildings.Get(i))); - if (!originalData.IsPvEBoss && !IsTargetValidForSlender(chaser, entity, attackEliminated)) - { - continue; - } - if (originalData.IsPvEBoss && !IsPvETargetValid(entity)) + for (int i = 0; i < g_Buildings.Length; i++) { - continue; - } - - valids.Push(EntIndexToEntRef(entity.index)); - } + CBaseEntity entity = CBaseEntity(EntRefToEntIndex(g_Buildings.Get(i))); + if (!data.IsPvEBoss && !IsTargetValidForSlender(chaser, entity, attackEliminated)) + { + continue; + } + if (data.IsPvEBoss && !IsPvETargetValid(entity)) + { + continue; + } - for (int i = 0; i < g_WhitelistedEntities.Length; i++) - { - CBaseEntity entity = CBaseEntity(EntRefToEntIndex(g_WhitelistedEntities.Get(i))); - if (!originalData.IsPvEBoss && !IsTargetValidForSlender(chaser, entity, attackEliminated)) - { - continue; + valids.Push(EntIndexToEntRef(entity.index)); } - if (originalData.IsPvEBoss && !IsPvETargetValid(entity)) + + for (int i = 0; i < g_WhitelistedEntities.Length; i++) { - continue; - } + CBaseEntity entity = CBaseEntity(EntRefToEntIndex(g_WhitelistedEntities.Get(i))); + if (!data.IsPvEBoss && !IsTargetValidForSlender(chaser, entity, attackEliminated)) + { + continue; + } + if (data.IsPvEBoss && !IsPvETargetValid(entity)) + { + continue; + } - valids.Push(EntIndexToEntRef(entity.index)); + valids.Push(EntIndexToEntRef(entity.index)); + } } for (int i = 0; i < valids.Length; i++) @@ -4297,9 +4486,9 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio entity.GetAbsOrigin(targetPos); - bool isVisible, isTraceVisible; - int traceHitEntity; - TR_TraceHullFilter(traceStartPos, + bool isVisible, isTraceVisible, isGlasslessVisible, isGlasslessTraceVisible; + int traceHitEntity, glasslesTraceHitEntity; + Handle trace = TR_TraceHullFilterEx(traceStartPos, traceEndPos, traceMins, traceMaxs, @@ -4307,8 +4496,21 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio TraceRayBossVisibility, chaser.index); - isVisible = !TR_DidHit(); - traceHitEntity = TR_GetEntityIndex(); + isVisible = !TR_DidHit(trace); + traceHitEntity = TR_GetEntityIndex(trace); + delete trace; + + trace = TR_TraceHullFilterEx(traceStartPos, + traceEndPos, + traceMins, + traceMaxs, + CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP | CONTENTS_GRATE | CONTENTS_WINDOW, + TraceRayBossVisibility, + chaser.index); + + isGlasslessVisible = !TR_DidHit(trace); + glasslesTraceHitEntity = TR_GetEntityIndex(trace); + delete trace; if (!isVisible && traceHitEntity == entity.index) { @@ -4316,12 +4518,19 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio isTraceVisible = true; } + if (!isGlasslessVisible && glasslesTraceHitEntity == entity.index) + { + isGlasslessVisible = true; + isGlasslessTraceVisible = true; + } + if (isVisible) { isVisible = NPCShouldSeeEntity(controller.Index, entity.index); } float dist = GetVectorSquareMagnitude(myPos, targetPos); + float flashlightDist = -1.0; if (player.IsValid && g_PlayerFogCtrlOffset != -1 && g_FogCtrlEnableOffset != -1 && g_FogCtrlEndOffset != -1) { @@ -4337,7 +4546,7 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio } } - if (dist > Pow(originalData.SearchRange[difficulty], 2.0)) + if (dist > Pow(data.GetSearchRange(difficulty), 2.0)) { isVisible = false; } @@ -4348,16 +4557,37 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio // Near radius check. if (chaser.GetIsVisible(entity) && - dist <= SquareFloat(data.WakeRadius)) + dist <= SquareFloat(data.GetWakeRadius(difficulty))) { chaser.SetIsNear(entity, true); playerInterruptFlags[entity.index] |= COND_ENEMYNEAR; } - if (player.IsValid && chaser.GetIsVisible(player) && SF_SpecialRound(SPECIALROUND_BOO) && GetVectorSquareMagnitude(traceEndPos, traceStartPos) < SquareFloat(SPECIALROUND_BOO_DISTANCE)) + if (!data.IsPvEBoss && player.IsValid && chaser.GetIsVisible(player) && SF_SpecialRound(SPECIALROUND_BOO) && GetVectorSquareMagnitude(traceEndPos, traceStartPos) < SquareFloat(SPECIALROUND_BOO_DISTANCE)) { TF2_StunPlayer(player.index, SPECIALROUND_BOO_DURATION, _, TF_STUNFLAGS_GHOSTSCARE); } + if (!data.IsPvEBoss && player.IsValid && chaser.State < STATE_ALERT && player.InCondition(TFCond_Taunting) && !controller.HasAttribute(SF2Attribute_IgnoreNonMarkedForChase) && GetVectorSquareMagnitude(traceEndPos, traceStartPos) < SquareFloat(data.GetTauntAlertRange(difficulty))) + { + if (chaser.GetTauntAlertStrikes(player) < 3) + { + chaser.SetTauntAlertStrikes(player, chaser.GetTauntAlertStrikes(player) + 1); + + chaser.InterruptConditions |= COND_ALERT_TRIGGER; + + float pos[3]; + player.GetAbsOrigin(pos); + + chaser.SetAlertTriggerPosition(player, pos); + chaser.AlertTriggerTarget = player; + } + else + { + chaser.SetTauntAlertStrikes(player, 0); + player.SetForceChaseState(controller, true); + } + } + // FOV check. SubtractVectors(traceEndPos, traceStartPos, buffer); GetVectorAngles(buffer, buffer); @@ -4367,6 +4597,33 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio chaser.SetInFOV(entity, true); } + if ((state == STATE_IDLE || state == STATE_ALERT || state == STATE_CHASE) && data.GetVisionSenseData().CanSeeFlashlights(difficulty) && player.IsValid && player.UsingFlashlight) + { + SF2PointSpotlightEntity flashlight = SF2PointSpotlightEntity(ClientGetFlashlightEntity(player.index)); + float flashlightPos[3], fov[3]; + flashlight.End.GetAbsOrigin(flashlightPos); + flashlightDist = controller.GetDistanceFrom(flashlight.End.index); + if (flashlight.IsValid() && flashlightDist <= Pow(data.GetSearchRange(difficulty), 2.0)) + { + trace = TR_TraceRayFilterEx(traceStartPos, + flashlightPos, + CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_MONSTERCLIP, + RayType_EndPoint, + TraceRayBossVisibility, + chaser.index); + + SubtractVectors(flashlightPos, traceStartPos, fov); + GetVectorAngles(fov, fov); + + if (!TR_DidHit(trace) && FloatAbs(AngleDiff(myEyeAng[1], fov[1])) <= (vision.GetFieldOfView() * 0.5)) + { + chaser.SetIsVisible(player, true); + chaser.SetInFOV(player, true); + } + delete trace; + } + } + if (chaser.GetIsVisible(entity)) { if (chaser.GetInFOV(entity)) @@ -4380,57 +4637,60 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio playerInterruptFlags[entity.index] |= COND_ENEMYVISIBLE; } + if (isGlasslessTraceVisible) + { + playerInterruptFlags[entity.index] |= COND_ENEMYVISIBLE_NOGLASS; + } + playerDists[entity.index] = dist; - if (chaser.SmellPlayerList != null && data.SmellData.Enabled[difficulty]) + if (chaser.SmellPlayerList != null && data.GetSmellData() != null && data.GetSmellData().IsEnabled(difficulty)) { - if (player.IsValid && dist < Pow(data.SmellData.PlayerRange[difficulty], 2.0)) + if (player.IsValid && dist < Pow(data.GetSmellData().GetRequiredPlayerRange(difficulty), 2.0)) { chaser.SmellPlayerList.Push(player.index); } } - if (player.IsValid && (player.IsTrapped || (player.IsReallySprinting && data.AutoChaseSprinters[difficulty] && chaser.GetAutoChaseCooldown(player) < gameTime)) + if (player.IsValid && (player.IsTrapped || (player.IsReallySprinting && data.GetAutoChaseData().ShouldChaseSprinters(difficulty) && chaser.GetAutoChaseCooldown(player) < gameTime)) && (chaser.State == STATE_IDLE || chaser.State == STATE_ALERT) && dist <= SquareFloat(searchRange)) { player.SetForceChaseState(controller, true); SetTargetMarkState(controller, player, true); } - if (data.ChaseOnLookData.Enabled[difficulty] && isTraceVisible && player.IsValid && controller.ChaseOnLookTargets.FindValue(player.index) == -1) + if (data.GetChaseOnLookData().IsEnabled(difficulty) && isTraceVisible && player.IsValid && controller.ChaseOnLookTargets.FindValue(player.index) == -1) { bool shouldCalculate = false; - if (data.ChaseOnLookData.RequiredFOV[difficulty] <= 0.0) + if (data.GetChaseOnLookData().GetRequiredFOV(difficulty) <= 0.0) { shouldCalculate = chaser.GetInFOV(player); } else { - shouldCalculate = FloatAbs(AngleDiff(myEyeAng[1], buffer[1])) <= (data.ChaseOnLookData.RequiredFOV[difficulty] * 0.5); + shouldCalculate = FloatAbs(AngleDiff(myEyeAng[1], buffer[1])) <= (data.GetChaseOnLookData().GetRequiredFOV(difficulty) * 0.5); } if (shouldCalculate) { float eyeAng[3], expectedAng[3], lookPos[3]; player.GetEyeAngles(eyeAng); chaser.GetAbsOrigin(myPos); - lookPos = data.ChaseOnLookData.RequiredLookPosition; + data.GetChaseOnLookData().GetRequiredLookPosition(lookPos); VectorTransform(lookPos, myPos, myEyeAng, lookPos); SubtractVectors(lookPos, traceEndPos, expectedAng); GetVectorAngles(expectedAng, expectedAng); - float minimumXAng = data.ChaseOnLookData.MinimumXAngle[difficulty] * 0.5; - float maximumXAng = data.ChaseOnLookData.MaximumXAngle[difficulty] * 0.5; - float minimumYAng = data.ChaseOnLookData.MinimumYAngle[difficulty] * 0.5; - float maximumYAng = data.ChaseOnLookData.MaximumYAngle[difficulty] * 0.5; + float minimumXAng = data.GetChaseOnLookData().GetMinXAngle(difficulty) * 0.5; + float maximumXAng = data.GetChaseOnLookData().GetMaxXAngle(difficulty) * 0.5; + float minimumYAng = data.GetChaseOnLookData().GetMinYAngle(difficulty) * 0.5; + float maximumYAng = data.GetChaseOnLookData().GetMaxYAngle(difficulty) * 0.5; float xAng, yAng; xAng = AngleDiff(eyeAng[0], expectedAng[0]); yAng = FloatAbs(AngleDiff(eyeAng[1], expectedAng[1])); if ((xAng >= minimumXAng && xAng < maximumXAng) && (yAng >= minimumYAng && yAng < maximumYAng) && - ((data.ChaseOnLookData.AddTargets[difficulty]) || (!data.ChaseOnLookData.AddTargets[difficulty] && controller.ChaseOnLookTargets.Length == 0))) + ((data.GetChaseOnLookData().ShouldAddTargets(difficulty)) || (!data.GetChaseOnLookData().ShouldAddTargets(difficulty) && controller.ChaseOnLookTargets.Length == 0))) { controller.ChaseOnLookTargets.Push(player.index); - SF2BossProfileSoundInfo soundInfo; - soundInfo = originalData.ScareSounds; - soundInfo.EmitSound(true, player.index); + data.GetScareSounds().EmitSound(true, player.index); player.ChangeCondition(TFCond_MarkedForDeathSilent); player.SetForceChaseState(controller, true); SetTargetMarkState(controller, player, true); @@ -4491,7 +4751,7 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio if (chaser.GetIsNear(entity) || (chaser.GetIsVisible(entity) && chaser.GetInFOV(entity))) { - if (dist <= SquareFloat(searchRange)) + if (dist <= SquareFloat(searchRange) || (flashlightDist > 0.0 && flashlightDist <= SquareFloat(searchRange))) { // Subtract distance to increase priority. dist -= ((dist * priorityValue)); @@ -4508,11 +4768,13 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio delete valids; - if (!originalData.IsPvEBoss && (SF_IsRaidMap() || SF_BossesChaseEndlessly() || SF_IsProxyMap() || SF_IsBoxingMap() || SF_IsSlaughterRunMap() || data.ChasesEndlessly || g_RenevantBossesChaseEndlessly)) + CNavArea myArea = chaser.GetLastKnownArea(); + + if (!data.IsPvEBoss && myArea != NULL_AREA && (SF_IsRaidMap() || SF_BossesChaseEndlessly() || SF_IsProxyMap() || SF_IsBoxingMap() || SF_IsSlaughterRunMap() || data.ChasesEndlessly || g_RenevantBossesChaseEndlessly)) { if (!IsTargetValidForSlender(chaser, CBaseEntity(bestNewTarget), attackEliminated)) { - if (state != STATE_CHASE && (NPCAreAvailablePlayersAlive() || g_Buildings.Length > 0 || g_WhitelistedEntities.Length > 0)) + if (state != STATE_CHASE) { ArrayList arrayRaidTargets = new ArrayList(); @@ -4522,6 +4784,7 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio { continue; } + arrayRaidTargets.Push(EntIndexToEntRef(i)); } @@ -4540,6 +4803,7 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio { continue; } + arrayRaidTargets.Push(g_Buildings.Get(i)); } @@ -4549,6 +4813,7 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio { continue; } + arrayRaidTargets.Push(g_WhitelistedEntities.Get(i)); } } @@ -4556,27 +4821,16 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio if (arrayRaidTargets.Length > 0) { int raidTarget = EntRefToEntIndex(arrayRaidTargets.Get(GetRandomInt(0, arrayRaidTargets.Length - 1))); - if (IsValidClient(raidTarget)) - { - if (!g_PlayerEliminated[raidTarget]) - { - bestNewTarget = raidTarget; - SetClientForceChaseState(controller, CBaseEntity(bestNewTarget), true); - } - } - else - { - bestNewTarget = raidTarget; - SetClientForceChaseState(controller, CBaseEntity(bestNewTarget), true); - } + bestNewTarget = raidTarget; + SetClientForceChaseState(controller, CBaseEntity(bestNewTarget), true); } delete arrayRaidTargets; } } - chaser.CurrentChaseDuration = data.ChaseDuration[difficulty] + GetGameTime(); + chaser.CurrentChaseDuration = data.GetChaseBehavior().GetMaxChaseDuration(difficulty) + GetGameTime(); } - if (originalData.IsPvEBoss) + if (data.IsPvEBoss) { if (!IsPvETargetValid(SF2_BasePlayer(bestNewTarget))) { @@ -4605,7 +4859,7 @@ static CBaseEntity ProcessVision(SF2_ChaserEntity chaser, int &interruptConditio delete arrayRaidTargets; } } - chaser.CurrentChaseDuration = data.ChaseDuration[difficulty] + GetGameTime(); + chaser.CurrentChaseDuration = data.GetChaseBehavior().GetMaxChaseDuration(difficulty) + GetGameTime(); } if (bestNewTarget != INVALID_ENT_REFERENCE) @@ -4632,24 +4886,25 @@ static void ProcessSpeed(SF2_ChaserEntity chaser) int difficulty = controller.Difficulty; SF2NPCMoveTypes moveType = chaser.MovementType; CBaseNPC npc = TheNPCs.FindNPCByEntIndex(chaser.index); + char profile[SF2_MAX_PROFILE_NAME_LENGTH]; + controller.GetProfile(profile, sizeof(profile)); + ChaserBossProfile profileData = controller.GetProfileData(); + float gameTime = GetGameTime(); - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = baseController.GetProfileData(); - SF2ChaserBossProfilePostureInfo postureInfo; + ChaserBossProfile data = controller.GetProfileData(); + char posture[64]; - if (data.Postures != null) + if (data.GetSection("postures") != null) { chaser.GetPosture(posture, sizeof(posture)); } float speed, acceleration; - acceleration = originalData.Acceleration[difficulty]; - if (!IsNullString(posture) && data.GetPosture(posture, postureInfo)) + acceleration = profileData.GetAcceleration(difficulty); + if (!IsNullString(posture) && data.GetPosture(posture) != null) { - speed = postureInfo.Acceleration[difficulty]; + acceleration = data.GetPostureAcceleration(posture, difficulty); } if (controller.HasAttribute(SF2Attribute_ReducedAccelerationOnLook) && controller.CanBeSeen(_, true)) @@ -4657,6 +4912,7 @@ static void ProcessSpeed(SF2_ChaserEntity chaser) acceleration *= controller.GetAttributeValue(SF2Attribute_ReducedAccelerationOnLook); } acceleration += controller.GetAddAcceleration(); + acceleration += controller.GetPersistentAddAcceleration(); Action action = Plugin_Continue; float forwardSpeed; @@ -4664,10 +4920,10 @@ static void ProcessSpeed(SF2_ChaserEntity chaser) { case SF2NPCMoveType_Walk: { - speed = data.WalkSpeed[difficulty]; - if (!IsNullString(posture) && data.GetPosture(posture, postureInfo)) + speed = profileData.GetWalkSpeed(difficulty); + if (!IsNullString(posture) && data.GetPosture(posture) != null) { - speed = postureInfo.WalkSpeed[difficulty]; + speed = data.GetPostureWalkSpeed(posture, difficulty); } if (controller.HasAttribute(SF2Attribute_ReducedWalkSpeedOnLook) && controller.CanBeSeen(_, true)) @@ -4675,6 +4931,7 @@ static void ProcessSpeed(SF2_ChaserEntity chaser) speed *= controller.GetAttributeValue(SF2Attribute_ReducedWalkSpeedOnLook); } + speed += baseController.GetPersistentAddWalkSpeed(); forwardSpeed = speed; Call_StartForward(g_OnBossGetWalkSpeedFwd); Call_PushCell(controller.Index); @@ -4687,10 +4944,10 @@ static void ProcessSpeed(SF2_ChaserEntity chaser) } case SF2NPCMoveType_Run: { - speed = originalData.RunSpeed[difficulty]; - if (!IsNullString(posture) && data.GetPosture(posture, postureInfo)) + speed = profileData.GetRunSpeed(difficulty); + if (!IsNullString(posture) && data.GetPosture(posture) != null) { - speed = postureInfo.Speed[difficulty]; + speed = data.GetPostureRunSpeed(posture, difficulty); } if (controller.HasAttribute(SF2Attribute_ReducedSpeedOnLook) && controller.CanBeSeen(_, true)) @@ -4699,6 +4956,7 @@ static void ProcessSpeed(SF2_ChaserEntity chaser) } speed += baseController.GetAddSpeed(); + speed += baseController.GetPersistentAddSpeed(); forwardSpeed = speed; Call_StartForward(g_OnBossGetSpeedFwd); @@ -4712,23 +4970,22 @@ static void ProcessSpeed(SF2_ChaserEntity chaser) } case SF2NPCMoveType_Attack: { - SF2ChaserBossProfileAttackData attackData; - data.GetAttack(chaser.GetAttackName(), attackData); - float attackSpeed = attackData.RunSpeed[difficulty]; - if (attackData.RunDelay[difficulty] > 0.0 && chaser.AttackRunDelay > gameTime) + ChaserBossProfileBaseAttack attackData = data.GetAttack(chaser.GetAttackName()); + float attackSpeed = attackData.GetRunSpeed(difficulty); + if (attackData.GetRunDelay(difficulty) > 0.0 && chaser.AttackRunDelay > gameTime) { attackSpeed = 0.0; } - if (attackData.RunDuration[difficulty] > 0.0 && chaser.AttackRunDuration < gameTime) + if (attackData.GetRunDuration(difficulty) > 0.0 && chaser.AttackRunDuration < gameTime) { attackSpeed = 0.0; } - if (attackData.RunGroundSpeed[difficulty]) + if (attackData.GetGroundSpeedOverride(difficulty)) { chaser.GroundSpeedOverride = true; } speed = attackSpeed; - acceleration = attackData.RunAcceleration[difficulty]; + acceleration = attackData.GetAcceleration(difficulty); } } @@ -4756,7 +5013,7 @@ static void ProcessSpeed(SF2_ChaserEntity chaser) if (SF_IsSlaughterRunMap()) { float slaughterSpeed = g_SlaughterRunMinimumBossRunSpeedConVar.FloatValue; - if (!originalData.SlaughterRunData.CustomMinimumSpeed[difficulty] && speed < slaughterSpeed) + if (!data.GetSlaughterRunData().ShouldUseCustomMinSpeed(difficulty) && speed < slaughterSpeed) { speed = slaughterSpeed; } @@ -4764,7 +5021,7 @@ static void ProcessSpeed(SF2_ChaserEntity chaser) } } - if ((!originalData.IsPvEBoss && IsBeatBoxBeating(2)) || chaser.IsKillingSomeone) + if ((!data.IsPvEBoss && IsBeatBoxBeating(2)) || chaser.IsKillingSomeone) { speed = 0.0; } @@ -4819,10 +5076,9 @@ static void ProcessBody(SF2_ChaserEntity chaser) return; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); - if (data.Postures != null && chaser.CanUpdatePosture()) + if (data.GetSection("postures") != null && chaser.CanUpdatePosture()) { char posture[64], resultPosture[64]; chaser.GetDefaultPosture(posture, sizeof(posture)); @@ -4864,6 +5120,11 @@ static void ProcessBody(SF2_ChaserEntity chaser) { chaser.SetPoseParameter(chaser.MoveScaleParameter, 0.0); } + + if (chaser.ShouldAnimationSyncWithGround) + { + chaser.SetPropFloat(Prop_Send, "m_flPlaybackRate", 0.0); + } return; } @@ -4872,6 +5133,53 @@ static void ProcessBody(SF2_ChaserEntity chaser) return; } + if (chaser.ShouldAnimationSyncWithGround) + { + float velocity, returnFloat, groundSpeed; + velocity = loco.GetGroundSpeed(); + groundSpeed = chaser.GetPropFloat(Prop_Data, "m_flGroundSpeed"); + if (groundSpeed != 0.0 && groundSpeed > 10.0) + { + returnFloat = (velocity / groundSpeed) * chaser.AnimationPlaybackRate; + + if (loco.IsOnGround() && chaser.IsAttemptingToMove) + { + if (returnFloat > 12.0) + { + returnFloat = 12.0; + } + if (returnFloat < -4.0) + { + returnFloat = -4.0; + } + chaser.SetPropFloat(Prop_Send, "m_flPlaybackRate", returnFloat); + } + } + else + { + float syncSpeed = chaser.GroundSyncSpeed; + if (syncSpeed <= 0.0) + { + syncSpeed = npc.flRunSpeed; + } + velocity = (velocity + ((syncSpeed * GetDifficultyModifier(controller.Difficulty)) / 15.0)) / syncSpeed; + + if (loco.IsOnGround() && chaser.IsAttemptingToMove) + { + float playbackSpeed = velocity * chaser.AnimationPlaybackRate; + if (playbackSpeed > 12.0) + { + playbackSpeed = 12.0; + } + if (playbackSpeed < -4.0) + { + playbackSpeed = -4.0; + } + chaser.SetPropFloat(Prop_Send, "m_flPlaybackRate", playbackSpeed); + } + } + } + switch (chaser.MoveParameterType) { case SF2NPCMoveParameter_XY: @@ -4951,48 +5259,6 @@ static void ProcessBody(SF2_ChaserEntity chaser) float scale = npc.flRunSpeed / speed; chaser.SetPoseParameter(chaser.MoveScaleParameter, scale); } - - if (data.OldAnimationAI) - { - float velocity, returnFloat, groundSpeed; - velocity = loco.GetGroundSpeed(); - groundSpeed = chaser.GetPropFloat(Prop_Data, "m_flGroundSpeed"); - if (groundSpeed != 0.0 && groundSpeed > 10.0) - { - returnFloat = (velocity / groundSpeed) * chaser.AnimationPlaybackRate; - - if (loco.IsOnGround() && chaser.IsAttemptingToMove && chaser.State != STATE_ATTACK) - { - if (returnFloat > 12.0) - { - returnFloat = 12.0; - } - if (returnFloat < -4.0) - { - returnFloat = -4.0; - } - chaser.SetPropFloat(Prop_Send, "m_flPlaybackRate", returnFloat); - } - } - else - { - velocity = (velocity + ((npc.flRunSpeed * GetDifficultyModifier(controller.Difficulty)) / 15.0)) / npc.flRunSpeed; - - if (loco.IsOnGround() && chaser.IsAttemptingToMove && chaser.State != STATE_ATTACK) - { - float playbackSpeed = velocity * chaser.AnimationPlaybackRate; - if (playbackSpeed > 12.0) - { - playbackSpeed = 12.0; - } - if (playbackSpeed < -4.0) - { - playbackSpeed = -4.0; - } - chaser.SetPropFloat(Prop_Send, "m_flPlaybackRate", playbackSpeed); - } - } - } } static bool LocoCollideWith(CBaseNPC_Locomotion loco, int other) @@ -5029,9 +5295,8 @@ static bool LocoCollideWith(CBaseNPC_Locomotion loco, int other) if (chaser.IsValid() && chaser.Controller.IsValid()) { - SF2BossProfileData data; SF2NPC_BaseNPC controller = view_as(chaser.Controller); - data = controller.GetProfileData(); + BaseBossProfile data = controller.GetProfileData(); if ((data.IsPvEBoss && player.IsInPvE) || (controller.Flags & SFF_ATTACKWAITERS) != 0) { return true; @@ -5051,9 +5316,7 @@ static bool LocoCollideWith(CBaseNPC_Locomotion loco, int other) if (chaser.IsValid() && chaser.Controller.IsValid()) { - SF2BossProfileData data; - SF2NPC_BaseNPC controller = view_as(chaser.Controller); - data = controller.GetProfileData(); + ChaserBossProfile data = chaser.Controller.GetProfileData(); if (data.IsPvEBoss && SF2_ChaserEntity(other).IsValid()) { return false; @@ -5138,27 +5401,58 @@ static Action Hook_ChaserSoundHook(int clients[64], int &numClients, char sample OnPlayerEmitSound(client, soundType); } - case SNDCHAN_ITEM, SNDCHAN_WEAPON: + case SNDCHAN_ITEM, SNDCHAN_WEAPON, SNDCHAN_STATIC: { - if (StrContains(sample, "swing", false) == -1 && StrContains(sample, "impact", false) == -1 && - StrContains(sample, "hit", false) == -1 && StrContains(sample, "slice", false) == -1 && - StrContains(sample, "reload", false) == -1 && StrContains(sample, "woosh", false) == -1 && - StrContains(sample, "eviction", false) == -1 && StrContains(sample, "holy", false) == -1 && - StrContains(sample, "flashlight", false) == -1) + static char matchSoundStrings[][] = { + "tf", + "flashlight", + "impact", + "hit", + "swing", + "slice", + "crit", + "shot", + "shoot", + "fire", + "doom", + "single", + "tf2", + "heal", + "break", + "diamond", + "start", + "loop", + "minigun", + "reload", + "woosh", + "eviction", + "holy", + "cbar", + "jingle_bells_nm", + "happy_birthday_tf" + }; + + bool isWeaponSound = false; + + for (int i = 0; !isWeaponSound && i < sizeof(matchSoundStrings); i++) { - return Plugin_Continue; + if (StrContains(sample, matchSoundStrings[i], false) != -1) + { + isWeaponSound = true; + } } - OnPlayerEmitSound(client, StrContains(sample, "flashlight", false) != -1 ? SoundType_Flashlight : SoundType_Weapon); - } - case SNDCHAN_STATIC: - { - if (StrContains(sample, "happy_birthday_tf", false) == -1 && StrContains(sample, "jingle_bells_nm", false) == -1) + bool voice = false; + if (StrContains(sample, "flashlight", false) != -1 || StrContains(sample, "happy_birthday_tf", false) != -1 || + StrContains(sample, "jingle_bells_nm", false) != -1) { - return Plugin_Continue; + voice = true; } - OnPlayerEmitSound(client, SoundType_Voice); + if (isWeaponSound) + { + OnPlayerEmitSound(client, voice ? SoundType_Flashlight : SoundType_Weapon); + } } } @@ -5192,8 +5486,15 @@ static void OnPlayerEmitSound(SF2_BasePlayer client, SoundType soundType) { continue; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + + if (chaser.State != STATE_IDLE && chaser.State != STATE_ALERT) + { + continue; + } + + ChaserBossProfile data = controller.GetProfileData(); + ChaserBossSoundSenseData senseData = data.GetSoundSenseData(); + ChaserBossAutoChaseData autoChaseData = data.GetAutoChaseData(); TFClassType class = client.Class; int classToInt = view_as(class); @@ -5204,7 +5505,7 @@ static void OnPlayerEmitSound(SF2_BasePlayer client, SoundType soundType) int difficulty = controller.Difficulty; - float hearRadius = view_as(controller).GetProfileData().SearchSoundRange[difficulty]; + float hearRadius = data.GetHearingRange(difficulty); if (hearRadius <= 0.0) { continue; @@ -5232,32 +5533,32 @@ static void OnPlayerEmitSound(SF2_BasePlayer client, SoundType soundType) if (soundType == SoundType_QuietFootstep) { - addThreshold = data.QuietFootstepSenses.AddCount[difficulty]; + addThreshold = senseData.GetQuietFootstepAdd(difficulty); if (addThreshold <= 0) { continue; } - cooldown = data.QuietFootstepSenses.Cooldown[difficulty]; + cooldown = senseData.GetQuietFootstepCooldown(difficulty); distance *= 1.85; } else if (soundType == SoundType_LoudFootstep) { - addThreshold = data.LoudFootstepSenses.AddCount[difficulty]; + addThreshold = senseData.GetLoudFootstepAdd(difficulty); if (addThreshold <= 0) { continue; } - cooldown = data.LoudFootstepSenses.Cooldown[difficulty]; + cooldown = senseData.GetLoudFootstepCooldown(difficulty); distance *= 0.66; } else { - addThreshold = data.FootstepSenses.AddCount[difficulty]; + addThreshold = senseData.GetFootstepAdd(difficulty); if (addThreshold <= 0) { continue; } - cooldown = data.FootstepSenses.Cooldown[difficulty]; + cooldown = senseData.GetFootstepCooldown(difficulty); } trace = TR_TraceRayFilterEx(myPos, hisPos, CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MIST | CONTENTS_GRATE | CONTENTS_WINDOW, RayType_EndPoint, TraceRayDontHitCharactersOrEntity, chaser.index); @@ -5284,26 +5585,26 @@ static void OnPlayerEmitSound(SF2_BasePlayer client, SoundType soundType) if (soundType == SoundType_Voice) { - addThreshold = data.VoiceSenses.AddCount[difficulty]; + addThreshold = senseData.GetVoiceAdd(difficulty); if (addThreshold <= 0) { continue; } - cooldown = data.VoiceSenses.Cooldown[difficulty]; + cooldown = senseData.GetVoiceCooldown(difficulty); } else if (soundType == SoundType_Flashlight) { - addThreshold = data.FlashlightSenses.AddCount[difficulty]; + addThreshold = senseData.GetFlashlightAdd(difficulty); if (addThreshold <= 0) { continue; } - cooldown = data.FlashlightSenses.Cooldown[difficulty]; + cooldown = senseData.GetFlashlightCooldown(difficulty); } } case SoundType_Weapon: { - addThreshold = data.WeaponSenses.AddCount[difficulty]; + addThreshold = senseData.GetWeaponAdd(difficulty); if (addThreshold <= 0) { continue; @@ -5329,7 +5630,7 @@ static void OnPlayerEmitSound(SF2_BasePlayer client, SoundType soundType) distance *= 0.66; - cooldown = data.WeaponSenses.Cooldown[difficulty]; + cooldown = senseData.GetWeaponCooldown(difficulty); } } @@ -5360,38 +5661,51 @@ static void OnPlayerEmitSound(SF2_BasePlayer client, SoundType soundType) continue; } - if (chaser.GetAlertSoundTriggerCooldown(client) > gameTime && cooldown > 0.0) + if (!data.ShouldIgnoreHearingPathChecking(difficulty)) { - continue; + CNavArea myArea = chaser.GetLastKnownArea(); + CNavArea theirArea = client.GetLastKnownArea(); + if (myArea != NULL_AREA && theirArea != NULL_AREA && myArea != theirArea) + { + CNavArea closestArea = NULL_AREA; + TheNavMesh.BuildPath(myArea, NULL_AREA, hisPos, .closestArea = closestArea, .maxPathLength = hearRadius * 1.5); + if (closestArea != theirArea) + { + continue; + } + } } - chaser.SetAlertSoundTriggerCooldown(client, gameTime + cooldown); - chaser.UpdateAlertTriggerCount(client, addThreshold); + if (gameTime > chaser.GetAlertSoundTriggerCooldown(client)) + { + chaser.SetAlertSoundTriggerCooldown(client, gameTime + cooldown); + chaser.UpdateAlertTriggerCount(client, addThreshold); + } - if (data.AutoChaseEnabled[difficulty] && chaser.GetAutoChaseAddCooldown(client) < gameTime) + if (autoChaseData.IsEnabled(difficulty) && chaser.GetAutoChaseAddCooldown(client) < gameTime) { int count = 0; switch (soundType) { case SoundType_Footstep: { - count = data.AutoChaseAddFootstep[difficulty]; + count = autoChaseData.GetAddFootsteps(difficulty); } case SoundType_QuietFootstep: { - count = data.AutoChaseAddQuietFootstep[difficulty]; + count = autoChaseData.GetAddQuietFootsteps(difficulty); } case SoundType_LoudFootstep: { - count = data.AutoChaseAddLoudFootstep[difficulty]; + count = autoChaseData.GetAddLoudFootsteps(difficulty); } case SoundType_Voice: { - count = data.AutoChaseAddVoice[difficulty]; + count = autoChaseData.GetAddVoice(difficulty); } case SoundType_Flashlight, SoundType_Weapon: { - count = data.AutoChaseAddWeapon[difficulty]; + count = autoChaseData.GetAddWeapon(difficulty); } } if (count > 0) @@ -5410,6 +5724,22 @@ static any Native_GetIsValid(Handle plugin, int numParams) return bossEntity.IsValid(); } +static any Native_GetController(Handle plugin, int numParams) +{ + int ent = GetNativeCell(1); + if (!IsValidEntity(ent)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid entity index %d", ent); + } + + SF2_ChaserEntity boss = SF2_ChaserEntity(ent); + if (!boss.Controller.IsValid()) + { + return -1; + } + return boss.Controller; +} + static any Native_GetIsAttemptingToMove(Handle plugin, int numParams) { int entity = GetNativeCell(1); @@ -5550,10 +5880,7 @@ static any Native_GetProfileData(Handle plugin, int numParams) SF2_ChaserEntity bossEntity = SF2_ChaserEntity(entity); - SF2ChaserBossProfileData data; - data = bossEntity.Controller.GetProfileData(); - SetNativeArray(2, data, sizeof(data)); - return 0; + return bossEntity.Controller.GetProfileData(); } static any Native_PerformVoice(Handle plugin, int numParams) @@ -5581,8 +5908,7 @@ static any Native_PerformCustomVoice(Handle plugin, int numParams) SF2_ChaserEntity bossEntity = SF2_ChaserEntity(entity); - SF2BossProfileSoundInfo soundInfo; - GetNativeArray(2, soundInfo, sizeof(soundInfo)); + ProfileSound soundInfo = GetNativeCell(2); return bossEntity.PerformVoiceCooldown(soundInfo, soundInfo.Paths); } @@ -5617,7 +5943,7 @@ static any Native_SetDefaultPosture(Handle plugin, int numParams) bufferSize++; char[] buffer = new char[bufferSize]; GetNativeString(2, buffer, bufferSize); - bossEntity.SetDefaultPosture(buffer); + bossEntity.SetDefaultPosture(buffer, GetNativeCell(3)); return 0; } @@ -5748,3 +6074,56 @@ static any Native_SetGroundSpeedOverride(Handle plugin, int numParams) bossEntity.GroundSpeedOverride = GetNativeCell(2); return 0; } + +static any Native_GetMovementType(Handle plugin, int numParams) +{ + int entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid entity index %d", entity); + } + + SF2_ChaserEntity bossEntity = SF2_ChaserEntity(entity); + return bossEntity.MovementType; +} + +static any Native_SetMovementType(Handle plugin, int numParams) +{ + int entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid entity index %d", entity); + } + + SF2_ChaserEntity bossEntity = SF2_ChaserEntity(entity); + bool old = bossEntity.LockMovementType; + bossEntity.LockMovementType = false; + bossEntity.MovementType = GetNativeCell(2); + bossEntity.LockMovementType = old; + return 0; +} + +static any Native_GetLockMovementType(Handle plugin, int numParams) +{ + int entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid entity index %d", entity); + } + + SF2_ChaserEntity bossEntity = SF2_ChaserEntity(entity); + return bossEntity.LockMovementType; +} + +static any Native_SetLockMovementType(Handle plugin, int numParams) +{ + int entity = GetNativeCell(1); + if (!IsValidEntity(entity)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid entity index %d", entity); + } + + SF2_ChaserEntity bossEntity = SF2_ChaserEntity(entity); + bossEntity.LockMovementType = GetNativeCell(2); + return 0; +} diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_alert.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_alert.sp index 8717a3cd..0c6391f6 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_alert.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_alert.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required void InitializePostureOnAlert() { @@ -7,41 +8,58 @@ void InitializePostureOnAlert() static Action OnChaserUpdatePosture(SF2NPC_Chaser controller, char[] buffer, int bufferSize) { - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - StringMap postures = data.Postures; - if (postures == null) + SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); + if (!chaser.IsValid()) { return Plugin_Continue; } - SF2ChaserBossProfilePostureInfo postureInfo; - StringMapSnapshot snapshot = postures.Snapshot(); - for (int i = 0; i < snapshot.Length; i++) + int difficulty = controller.Difficulty; + + ChaserBossProfile data = controller.GetProfileData(); + ProfileObject obj = data.GetSection("postures"); + if (obj == null) + { + return Plugin_Continue; + } + + for (int i = 0; i < obj.SectionLength; i++) { - if (!data.GetPostureFromIndex(i, postureInfo)) + ProfileObject posture = data.GetPostureFromIndex(i); + if (posture == null) { continue; } - int difficulty = controller.Difficulty; - - SF2PostureConditionAlertInfo alertInfo; - alertInfo = postureInfo.AlertInfo; - if (!alertInfo.Enabled[difficulty]) + ProfileObject conditions = posture.GetSection("conditions"); + if (conditions == null || conditions.SectionLength == 0) { continue; } - SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); - if (chaser.IsValid() && chaser.State == STATE_ALERT) + for (int j = 0; j < conditions.SectionLength; j++) { - strcopy(buffer, bufferSize, postureInfo.Name); - delete snapshot; - return Plugin_Changed; + char name[64]; + conditions.GetSectionNameFromIndex(j, name, sizeof(name)); + if (strcmp(name, "on_alert") != 0) + { + continue; + } + + ChaserBossPostureCondition condition = view_as(conditions.GetSection(name)); + if (condition == null || !condition.GetEnabled(difficulty)) + { + continue; + } + + if (chaser.State == STATE_ALERT) + { + posture.GetSectionName(name, sizeof(name)); + strcopy(buffer, bufferSize, name); + return Plugin_Changed; + } } } - delete snapshot; return Plugin_Continue; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_cloak.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_cloak.sp index 83e0c159..f6bc6ab6 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_cloak.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_cloak.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required void InitializePostureOnCloak() { @@ -7,41 +8,58 @@ void InitializePostureOnCloak() static Action OnChaserUpdatePosture(SF2NPC_Chaser controller, char[] buffer, int bufferSize) { - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - StringMap postures = data.Postures; - if (postures == null) + SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); + if (!chaser.IsValid()) { return Plugin_Continue; } - SF2ChaserBossProfilePostureInfo postureInfo; - StringMapSnapshot snapshot = postures.Snapshot(); - for (int i = 0; i < snapshot.Length; i++) + int difficulty = controller.Difficulty; + + ChaserBossProfile data = controller.GetProfileData(); + ProfileObject obj = data.GetSection("postures"); + if (obj == null) + { + return Plugin_Continue; + } + + for (int i = 0; i < obj.SectionLength; i++) { - if (!data.GetPostureFromIndex(i, postureInfo)) + ProfileObject posture = data.GetPostureFromIndex(i); + if (posture == null) { continue; } - int difficulty = controller.Difficulty; - - SF2PostureConditionCloakInfo cloakInfo; - cloakInfo = postureInfo.CloakInfo; - if (!cloakInfo.Enabled[difficulty]) + ProfileObject conditions = posture.GetSection("conditions"); + if (conditions == null || conditions.SectionLength == 0) { continue; } - SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); - if (chaser.IsValid() && chaser.HasCloaked) + for (int j = 0; j < conditions.SectionLength; j++) { - strcopy(buffer, bufferSize, postureInfo.Name); - delete snapshot; - return Plugin_Changed; + char name[64]; + conditions.GetSectionNameFromIndex(j, name, sizeof(name)); + if (strcmp(name, "on_cloak") != 0) + { + continue; + } + + ChaserBossPostureCondition condition = view_as(conditions.GetSection(name)); + if (condition == null || !condition.GetEnabled(difficulty)) + { + continue; + } + + if (chaser.HasCloaked) + { + posture.GetSectionName(name, sizeof(name)); + strcopy(buffer, bufferSize, name); + return Plugin_Changed; + } } } - delete snapshot; return Plugin_Continue; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_look.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_look.sp index 556d917a..e667a02f 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_look.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/on_look.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required void InitializePostureOnLook() { @@ -7,40 +8,58 @@ void InitializePostureOnLook() static Action OnChaserUpdatePosture(SF2NPC_Chaser controller, char[] buffer, int bufferSize) { - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - StringMap postures = data.Postures; - if (postures == null) + SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); + if (!chaser.IsValid()) { return Plugin_Continue; } - SF2ChaserBossProfilePostureInfo postureInfo; - StringMapSnapshot snapshot = postures.Snapshot(); - for (int i = 0; i < snapshot.Length; i++) + int difficulty = controller.Difficulty; + + ChaserBossProfile data = controller.GetProfileData(); + ProfileObject obj = data.GetSection("postures"); + if (obj == null) + { + return Plugin_Continue; + } + + for (int i = 0; i < obj.SectionLength; i++) { - if (!data.GetPostureFromIndex(i, postureInfo)) + ProfileObject posture = data.GetPostureFromIndex(i); + if (posture == null) { continue; } - int difficulty = controller.Difficulty; - - SF2PostureConditionLookAtInfo lookAtInfo; - lookAtInfo = postureInfo.LookAtInfo; - if (!lookAtInfo.Enabled[difficulty]) + ProfileObject conditions = posture.GetSection("conditions"); + if (conditions == null || conditions.SectionLength == 0) { continue; } - if (controller.CanBeSeen(_, true)) + for (int j = 0; j < conditions.SectionLength; j++) { - strcopy(buffer, bufferSize, postureInfo.Name); - delete snapshot; - return Plugin_Changed; + char name[64]; + conditions.GetSectionNameFromIndex(j, name, sizeof(name)); + if (strcmp(name, "on_look") != 0) + { + continue; + } + + ChaserBossPostureCondition condition = view_as(conditions.GetSection(name)); + if (condition == null || !condition.GetEnabled(difficulty)) + { + continue; + } + + if (controller.CanBeSeen(_, true)) + { + posture.GetSectionName(name, sizeof(name)); + strcopy(buffer, bufferSize, name); + return Plugin_Changed; + } } } - delete snapshot; return Plugin_Continue; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/rage_phase.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/rage_phase.sp index 46ead006..8e04f142 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/rage_phase.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/rage_phase.sp @@ -1,4 +1,24 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap ChaserBossPostureCondition_RagePhase < ChaserBossPostureCondition +{ + public bool IsPhaseValid(const char[] buffer) + { + char phases[1024]; + this.GetString("phases", phases, sizeof(phases)); + char phase[64][64]; + int maxLength = ExplodeString(phases, " ", phase, sizeof(phase), sizeof(phase)); + for (int i = 0; i < maxLength; i++) + { + if (strcmp(buffer, phase[i]) == 0) + { + return true; + } + } + return false; + } +} void InitializePostureRagePhase() { @@ -7,48 +27,64 @@ void InitializePostureRagePhase() static Action OnChaserUpdatePosture(SF2NPC_Chaser controller, char[] buffer, int bufferSize) { - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - StringMap postures = data.Postures; - if (postures == null) + SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); + if (!chaser.IsValid()) + { + return Plugin_Continue; + } + + int difficulty = controller.Difficulty; + + ChaserBossProfile data = controller.GetProfileData(); + ProfileObject obj = data.GetSection("postures"); + if (obj == null) { return Plugin_Continue; } - SF2ChaserBossProfilePostureInfo postureInfo; - StringMapSnapshot snapshot = postures.Snapshot(); - for (int i = 0; i < snapshot.Length; i++) + for (int i = 0; i < obj.SectionLength; i++) { - if (!data.GetPostureFromIndex(i, postureInfo)) + ProfileObject posture = data.GetPostureFromIndex(i); + if (posture == null) { continue; } - SF2PostureConditionRagePhase ragePhaseInfo; - ragePhaseInfo = postureInfo.RagePhaseInfo; - if (!ragePhaseInfo.Enabled) + ProfileObject conditions = posture.GetSection("conditions"); + if (conditions == null || conditions.SectionLength == 0) { continue; } - SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); - if (!chaser.IsValid() || chaser.RageIndex == -1) + for (int j = 0; j < conditions.SectionLength; j++) { - continue; - } + char name[64]; + conditions.GetSectionNameFromIndex(j, name, sizeof(name)); + if (strcmp(name, "in_phases") != 0) + { + continue; + } - SF2ChaserRageInfo rageInfo; - data.Rages.GetArray(chaser.RageIndex, rageInfo, sizeof(rageInfo)); - int index = ragePhaseInfo.Names.FindString(rageInfo.Name); + ChaserBossPostureCondition_RagePhase condition = view_as(conditions.GetSection(name)); + if (condition == null || !condition.GetEnabled(difficulty)) + { + continue; + } - if (index != -1) - { - strcopy(buffer, bufferSize, postureInfo.Name); - delete snapshot; - return Plugin_Changed; + if (chaser.RageIndex == -1) + { + continue; + } + + data.GetRages().GetSectionNameFromIndex(chaser.RageIndex, name, sizeof(name)); + if (condition.IsPhaseValid(name)) + { + posture.GetSectionName(name, sizeof(name)); + strcopy(buffer, bufferSize, name); + return Plugin_Changed; + } } } - delete snapshot; return Plugin_Continue; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/running_away.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/running_away.sp index c9532dd0..59807577 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/running_away.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/running_away.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required void InitializePostureRunningAway() { @@ -7,38 +8,58 @@ void InitializePostureRunningAway() static Action OnChaserUpdatePosture(SF2NPC_Chaser controller, char[] buffer, int bufferSize) { - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - StringMap postures = data.Postures; - if (postures == null) + SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); + if (!chaser.IsValid()) { return Plugin_Continue; } - SF2ChaserBossProfilePostureInfo postureInfo; - StringMapSnapshot snapshot = postures.Snapshot(); - for (int i = 0; i < snapshot.Length; i++) + int difficulty = controller.Difficulty; + + ChaserBossProfile data = controller.GetProfileData(); + ProfileObject obj = data.GetSection("postures"); + if (obj == null) + { + return Plugin_Continue; + } + + for (int i = 0; i < obj.SectionLength; i++) { - if (!data.GetPostureFromIndex(i, postureInfo)) + ProfileObject posture = data.GetPostureFromIndex(i); + if (posture == null) { continue; } - SF2PostureConditionRunAwayInfo runAwayInfo; - runAwayInfo = postureInfo.RunAwayInfo; - if (!runAwayInfo.Enabled) + ProfileObject conditions = posture.GetSection("conditions"); + if (conditions == null || conditions.SectionLength == 0) { continue; } - SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); - if (chaser.IsValid() && chaser.IsRunningAway) + for (int j = 0; j < conditions.SectionLength; j++) { - strcopy(buffer, bufferSize, postureInfo.Name); - delete snapshot; - return Plugin_Changed; + char name[64]; + conditions.GetSectionNameFromIndex(j, name, sizeof(name)); + if (strcmp(name, "running_away") != 0) + { + continue; + } + + ChaserBossPostureCondition condition = view_as(conditions.GetSection(name)); + if (condition == null || !condition.GetEnabled(difficulty)) + { + continue; + } + + if (chaser.IsRunningAway) + { + posture.GetSectionName(name, sizeof(name)); + strcopy(buffer, bufferSize, name); + return Plugin_Changed; + } } } - delete snapshot; + return Plugin_Continue; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/within_bounds.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/within_bounds.sp index 1ae3d000..9559201f 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/within_bounds.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/within_bounds.sp @@ -1,4 +1,18 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap ChaserBossPostureCondition_WithinBounds < ChaserBossPostureCondition +{ + public void GetMins(int difficulty, float buffer[3]) + { + this.GetDifficultyVector("mins", difficulty, buffer); + } + + public void GetMaxs(int difficulty, float buffer[3]) + { + this.GetDifficultyVector("maxs", difficulty, buffer); + } +} void InitializePostureWithinBounds() { @@ -7,45 +21,62 @@ void InitializePostureWithinBounds() static Action OnChaserUpdatePosture(SF2NPC_Chaser controller, char[] buffer, int bufferSize) { - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - StringMap postures = data.Postures; - if (postures == null) + SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); + if (!chaser.IsValid()) + { + return Plugin_Continue; + } + + int difficulty = controller.Difficulty; + + ChaserBossProfile data = controller.GetProfileData(); + ProfileObject obj = data.GetSection("postures"); + if (obj == null) { return Plugin_Continue; } - SF2ChaserBossProfilePostureInfo postureInfo; - StringMapSnapshot snapshot = postures.Snapshot(); - for (int i = 0; i < snapshot.Length; i++) + for (int i = 0; i < obj.SectionLength; i++) { - if (!data.GetPostureFromIndex(i, postureInfo)) + ProfileObject posture = data.GetPostureFromIndex(i); + if (posture == null) { continue; } - SF2PostureConditionWithinBoundsInfo boundsInfo; - boundsInfo = postureInfo.BoundsInfo; - if (!boundsInfo.Enabled) + ProfileObject conditions = posture.GetSection("conditions"); + if (conditions == null || conditions.SectionLength == 0) { continue; } - SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); - if (!chaser.IsValid()) + for (int j = 0; j < conditions.SectionLength; j++) { - continue; - } - float myPos[3]; - chaser.GetAbsOrigin(myPos); - if (IsSpaceOccupiedIgnorePlayersAndEnts(myPos, boundsInfo.Mins, boundsInfo.Maxs, chaser.index)) - { - strcopy(buffer, bufferSize, postureInfo.Name); - delete snapshot; - return Plugin_Changed; + char name[64]; + conditions.GetSectionNameFromIndex(j, name, sizeof(name)); + if (strcmp(name, "within_bounds") != 0) + { + continue; + } + + ChaserBossPostureCondition_WithinBounds condition = view_as(conditions.GetSection(name)); + if (condition == null || !condition.GetEnabled(difficulty)) + { + continue; + } + + float mins[3], maxs[3], myPos[3]; + condition.GetMins(difficulty, mins); + condition.GetMaxs(difficulty, maxs); + chaser.GetAbsOrigin(myPos); + if (IsSpaceOccupiedIgnorePlayersAndEnts(myPos, mins, maxs, chaser.index)) + { + posture.GetSectionName(name, sizeof(name)); + strcopy(buffer, bufferSize, name); + return Plugin_Changed; + } } } - delete snapshot; return Plugin_Continue; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/within_range.sp b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/within_range.sp index 1beadd3d..9a48cd8e 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/within_range.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/chaser/postures/within_range.sp @@ -1,4 +1,38 @@ #pragma semicolon 1 +#pragma newdecls required + +methodmap ChaserBossPostureCondition_WithinRange < ChaserBossPostureCondition +{ + public float GetMinRange(int difficulty) + { + return this.GetDifficultyFloat("min_range", difficulty, 0.0); + } + + public float GetMaxRange(int difficulty) + { + return this.GetDifficultyFloat("max_range", difficulty, 512.0); + } + + public float GetCooldown(int difficulty) + { + return this.GetDifficultyFloat("cooldown", difficulty, 1.0); + } + + property float CurrentCooldown + { + public get() + { + float value = 0.0; + this.GetValue("__current_cooldown", value); + return value; + } + + public set(float value) + { + this.SetValue("__current_cooldown", value); + } + } +} void InitializePostureWithinRange() { @@ -7,70 +41,86 @@ void InitializePostureWithinRange() static Action OnChaserUpdatePosture(SF2NPC_Chaser controller, char[] buffer, int bufferSize) { - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - StringMap postures = data.Postures; - if (postures == null) + SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); + if (!chaser.IsValid()) { return Plugin_Continue; } + int difficulty = controller.Difficulty; float gameTime = GetGameTime(); - SF2ChaserBossProfilePostureInfo postureInfo; - StringMapSnapshot snapshot = postures.Snapshot(); - for (int i = 0; i < snapshot.Length; i++) + ChaserBossProfile data = controller.GetProfileData(); + ProfileObject obj = data.GetSection("postures"); + if (obj == null) { - if (!data.GetPostureFromIndex(i, postureInfo)) - { - continue; - } - - int difficulty = controller.Difficulty; + return Plugin_Continue; + } - SF2PostureConditionWithinRangeInfo rangeInfo; - rangeInfo = postureInfo.RangeInfo; - if (!rangeInfo.Enabled[difficulty]) + for (int i = 0; i < obj.SectionLength; i++) + { + ProfileObject posture = data.GetPostureFromIndex(i); + if (posture == null) { continue; } - if (rangeInfo.CurrentCooldown > gameTime) + ProfileObject conditions = posture.GetSection("conditions"); + if (conditions == null || conditions.SectionLength == 0) { continue; } - SF2_ChaserEntity chaser = SF2_ChaserEntity(controller.EntIndex); - if (!chaser.IsValid() || chaser.State != STATE_CHASE) + for (int j = 0; j < conditions.SectionLength; j++) { - continue; - } + char name[64]; + conditions.GetSectionNameFromIndex(j, name, sizeof(name)); + if (strcmp(name, "within_range") != 0) + { + continue; + } - if (chaser.MyNextBotPointer().GetLocomotionInterface().GetGroundSpeed() < 0.01) - { - continue; - } + ChaserBossPostureCondition_WithinRange condition = view_as(conditions.GetSection(name)); + if (condition == null || !condition.GetEnabled(difficulty)) + { + continue; + } - CBaseEntity target = chaser.Target; - if (!target.IsValid()) - { - continue; - } + if (condition.CurrentCooldown > gameTime) + { + continue; + } - float range = controller.Path.GetLength() - controller.Path.GetCursorPosition(); + if (chaser.State != STATE_CHASE) + { + continue; + } - if (range > rangeInfo.MinRange[difficulty] && range < rangeInfo.MaxRange[difficulty]) - { - strcopy(buffer, bufferSize, postureInfo.Name); - delete snapshot; - return Plugin_Changed; - } - else - { - rangeInfo.CurrentCooldown = gameTime + rangeInfo.Cooldown[difficulty]; + if (chaser.MyNextBotPointer().GetLocomotionInterface().GetGroundSpeed() < 0.01) + { + continue; + } + + CBaseEntity target = chaser.Target; + if (!target.IsValid()) + { + continue; + } + + float range = controller.Path.GetLength() - controller.Path.GetCursorPosition(); + + if (range > condition.GetMinRange(difficulty) && range < condition.GetMaxRange(difficulty)) + { + posture.GetSectionName(name, sizeof(name)); + strcopy(buffer, bufferSize, name); + return Plugin_Changed; + } + else + { + condition.CurrentCooldown = gameTime + condition.GetCooldown(difficulty); + } } } - delete snapshot; return Plugin_Continue; } \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/npc/entities/initialize.sp b/addons/sourcemod/scripting/sf2/npc/entities/initialize.sp index f2069a50..7a223b8d 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/initialize.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/initialize.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required #include "base/entity.sp" #include "chaser/entity.sp" diff --git a/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/chase.sp b/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/chase.sp index 74817078..f1dd47c0 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/chase.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/chase.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -21,9 +22,8 @@ static int OnStart(SF2_StatueChaseAction action, SF2_StatueEntity actor, NextBot { SF2NPC_Statue controller = actor.Controller; int difficulty = controller.Difficulty; - SF2StatueBossProfileData data; - data = controller.GetProfileData(); - actor.CurrentChaseDuration = data.ChaseDuration[difficulty]; + StatueBossProfile data = controller.GetProfileData(); + actor.CurrentChaseDuration = data.GetChaseDuration(difficulty); if (actor.InitialChaseDuration > 0.0) { actor.CurrentChaseDuration = actor.InitialChaseDuration; @@ -57,10 +57,7 @@ static int Update(SF2_StatueChaseAction action, SF2_StatueEntity actor) } SF2NPC_Statue controller = actor.Controller; - SF2StatueBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); + StatueBossProfile data = controller.GetProfileData(); bool attackEliminated = (controller.Flags & SFF_ATTACKWAITERS) != 0; int difficulty = controller.Difficulty; INextBot bot = actor.MyNextBotPointer(); @@ -86,20 +83,20 @@ static int Update(SF2_StatueChaseAction action, SF2_StatueEntity actor) if (visible) { - float maxRange = data.ChaseDurationAddMaxRange[difficulty]; + float maxRange = data.GetChaseDurationAddMaxRange(difficulty); if (maxRange > 0.0 && player.IsValid && player.CanSeeSlender(controller.Index, false, _, !attackEliminated)) { float distanceRatio = bot.GetRangeTo(player.index) / maxRange; if (distanceRatio < 1.0) { - float durationTimeAddMin = data.ChaseDurationAddVisibilityMin[difficulty]; - float durationTimeAddMax = data.ChaseDurationAddVisibilityMax[difficulty]; + float durationTimeAddMin = data.GetChaseDurationAddVisibilityMin(difficulty); + float durationTimeAddMax = data.GetChaseDurationAddVisibilityMax(difficulty); float durationAdd = durationTimeAddMin + ((durationTimeAddMax - durationTimeAddMin) * distanceRatio); actor.CurrentChaseDuration += durationAdd * GetGameFrameTime(); - if (actor.CurrentChaseDuration > data.ChaseDuration[difficulty]) + if (actor.CurrentChaseDuration > data.GetChaseDuration(difficulty)) { - actor.CurrentChaseDuration = data.ChaseDuration[difficulty]; + actor.CurrentChaseDuration = data.GetChaseDuration(difficulty); } } } @@ -115,12 +112,12 @@ static int Update(SF2_StatueChaseAction action, SF2_StatueEntity actor) if (SF_IsRaidMap() || SF_BossesChaseEndlessly() || SF_IsProxyMap() || SF_IsBoxingMap() || SF_IsSlaughterRunMap()) { - actor.CurrentChaseDuration = data.ChaseDuration[difficulty]; + actor.CurrentChaseDuration = data.GetChaseDuration(difficulty); } bool tooClose = target.IsValid() && visible && - bot.IsRangeLessThan(target.index, 8.0); + bot.GetRangeSquaredTo(target.index) <= 32.0; if ((tooClose || !actor.IsMoving) && path.IsValid()) { @@ -132,15 +129,12 @@ static int Update(SF2_StatueChaseAction action, SF2_StatueEntity actor) target.GetAbsOrigin(pos); if (actor.Teleporters.Length > 0) { - CBaseEntity(actor.Teleporters.Get(0)).GetAbsOrigin(pos); + CBaseEntity(EntRefToEntIndex(actor.Teleporters.Get(0))).GetAbsOrigin(pos); } - if (!bot.IsRangeLessThanEx(pos, 8.0) && actor.IsMoving) + if (path.GetAge() > 0.3 || (path.IsValid() && (path.GetLength() - path.GetCursorPosition()) < 256.0)) { - if (path.GetAge() > 0.3 || (path.IsValid() && (path.GetLength() - path.GetCursorPosition()) < 256.0)) - { - path.ComputeToPos(bot, pos); - } + path.ComputeToPos(bot, pos); } } @@ -155,9 +149,9 @@ static int Update(SF2_StatueChaseAction action, SF2_StatueEntity actor) if (actor.IsMoving) { - g_SlenderStatueIdleLifeTime[controller.Index] = gameTime + data.IdleLifeTime[difficulty]; + g_SlenderStatueIdleLifeTime[controller.Index] = gameTime + data.GetIdleLifeTime(difficulty); - if (bot.GetRangeSquaredTo(target.index) <= Pow(originalData.InstantKillRadius, 2.0) && visible) + if (bot.GetRangeSquaredTo(target.index) <= Pow(data.GetInstantKillRadius(difficulty), 2.0) && visible) { if (controller.Flags & SFF_FAKE) { @@ -166,7 +160,7 @@ static int Update(SF2_StatueChaseAction action, SF2_StatueEntity actor) } else { - actor.LastKillTime = gameTime + originalData.InstantKillCooldown[difficulty]; + actor.LastKillTime = gameTime + data.GetInstantKillCooldown(difficulty); player.StartDeathCam(controller.Index, myPos); } } diff --git a/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/idle.sp b/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/idle.sp index c21f6dff..47ca655f 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/idle.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/idle.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; diff --git a/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/main.sp b/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/main.sp index a424187c..628872b8 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/main.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/main.sp @@ -61,7 +61,7 @@ static int Update(SF2_StatueBaseAction action, SF2_StatueEntity actor, float int } SF2_BasePlayer origTarget = SF2_BasePlayer(actor.Target); - if (SF_BossesChaseEndlessly() || SF_IsSlaughterRunMap() && origTarget.IsValid) + if ((SF_BossesChaseEndlessly() || SF_IsSlaughterRunMap()) && origTarget.IsValid) { actor.ChaseTime = gameTime + controller.GetChaseDuration(difficulty); } diff --git a/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/mainlayer.sp b/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/mainlayer.sp index 03b2cd07..5c1b05e8 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/mainlayer.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/statue/actions/mainlayer.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static NextBotActionFactory g_Factory; @@ -72,15 +73,12 @@ static int Update(SF2_StatueBaseAction action, SF2_StatueEntity actor, float int return action.Done("I'm a faker"); } - SF2StatueBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); - SF2BossProfileSoundInfo soundInfo; + StatueBossProfile data = controller.GetProfileData(); CBaseEntity target = actor.Target; float myPos[3]; actor.GetAbsOrigin(myPos); int difficulty = controller.Difficulty; + float gameTime = GetGameTime(); if (actor.LastKillTime > GetGameTime()) { @@ -89,10 +87,13 @@ static int Update(SF2_StatueBaseAction action, SF2_StatueEntity actor, float int if (actor.IsMoving) { - soundInfo = data.SingleMoveSounds; - soundInfo.EmitSound(_, actor.index); - soundInfo = data.MoveSounds; - soundInfo.EmitSound(_, actor.index); + if (actor.NextMoveTime <= gameTime) + { + data.GetSingleMoveSounds().EmitSound(_, actor.index, .difficulty = difficulty); + actor.NextMoveTime = gameTime + GetRandomFloat(data.GetSingleMoveSounds().GetCooldownMin(difficulty), data.GetSingleMoveSounds().GetCooldownMax(difficulty)); + } + + data.GetMoveSounds().EmitSound(_, actor.index, .difficulty = difficulty); if (target.IsValid()) { @@ -100,24 +101,24 @@ static int Update(SF2_StatueBaseAction action, SF2_StatueEntity actor, float int target.GetAbsOrigin(targetPos); float distance = GetVectorSquareMagnitude(targetPos, myPos); - float maxRange = Pow(data.ModelChangeDistanceMax[difficulty], 2.0); + float maxRange = Pow(data.GetMaxModelChangeDistance(difficulty), 2.0); char model[PLATFORM_MAX_PATH]; if (distance < maxRange * 0.33) { - data.ModelsCloseDist.GetString(difficulty, model, sizeof(model)); + data.GetCloseDistanceModel(difficulty, model, sizeof(model)); } else if (distance < maxRange * 0.66) { - data.ModelsAverageDist.GetString(difficulty, model, sizeof(model)); + data.GetAverageDistanceModel(difficulty, model, sizeof(model)); } else { - originalData.Models.GetString(difficulty, model, sizeof(model)); + data.GetModel(difficulty, model, sizeof(model)); } if (model[0] == '\0' || strcmp(model, "models/") == 0) { - originalData.Models.GetString(difficulty, model, sizeof(model)); + data.GetModel(difficulty, model, sizeof(model)); } actor.SetModel(model); @@ -125,8 +126,12 @@ static int Update(SF2_StatueBaseAction action, SF2_StatueEntity actor, float int } else { - soundInfo = data.MoveSounds; - soundInfo.StopAllSounds(actor.index); + data.GetMoveSounds().StopAllSounds(actor.index, difficulty); + } + + if (actor.IsKillingSomeone) + { + return action.SuspendFor(SF2_DeathCamAction()); } UnstuckCheck(action, actor); diff --git a/addons/sourcemod/scripting/sf2/npc/entities/statue/entity.sp b/addons/sourcemod/scripting/sf2/npc/entities/statue/entity.sp index 299c486d..52819433 100644 --- a/addons/sourcemod/scripting/sf2/npc/entities/statue/entity.sp +++ b/addons/sourcemod/scripting/sf2/npc/entities/statue/entity.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required #include "actions/mainlayer.sp" #include "actions/idle.sp" @@ -31,6 +32,7 @@ methodmap SF2_StatueEntity < SF2_BaseBoss g_Factory.BeginDataMapDesc() .DefineBoolField("m_IsMoving") .DefineFloatField("m_LastKillTime") + .DefineFloatField("m_NextMoveTime") .EndDataMapDesc(); g_Factory.Install(); } @@ -60,6 +62,7 @@ methodmap SF2_StatueEntity < SF2_BaseBoss this.SetProp(Prop_Data, "m_IsMoving", value); } } + property float LastKillTime { public get() @@ -73,6 +76,19 @@ methodmap SF2_StatueEntity < SF2_BaseBoss } } + property float NextMoveTime + { + public get() + { + return this.GetPropFloat(Prop_Data, "m_NextMoveTime"); + } + + public set(float value) + { + this.SetPropFloat(Prop_Data, "m_NextMoveTime", value); + } + } + public void DoAlwaysLookAt(CBaseEntity target) { if (!target.IsValid()) @@ -114,26 +130,27 @@ methodmap SF2_StatueEntity < SF2_BaseBoss controller.GetProfile(profile, sizeof(profile)); statue.Controller = view_as(controller); - SF2BossProfileData originalData; - originalData = view_as(statue.Controller).GetProfileData(); + StatueBossProfile profileData = statue.Controller.GetProfileData(); + int difficulty = g_DifficultyConVar.IntValue; char buffer[PLATFORM_MAX_PATH]; GetSlenderModel(controller.Index, _, buffer, sizeof(buffer)); statue.SetModel(buffer); - statue.SetRenderMode(view_as(g_SlenderRenderMode[controller.Index])); - statue.SetRenderFx(view_as(g_SlenderRenderFX[controller.Index])); - statue.SetRenderColor(g_SlenderRenderColor[controller.Index][0], g_SlenderRenderColor[controller.Index][1], - g_SlenderRenderColor[controller.Index][2], g_SlenderRenderColor[controller.Index][3]); + statue.SetRenderMode(profileData.GetRenderMode(difficulty)); + statue.SetRenderFx(profileData.GetRenderFx(difficulty)); + int color[4]; + profileData.GetRenderColor(difficulty, color); + statue.SetRenderColor(color[0], color[1], color[2], color[3]); if (SF_SpecialRound(SPECIALROUND_TINYBOSSES)) { - float scaleModel = controller.ModelScale * 0.5; + float scaleModel = profileData.ModelScale * 0.5; statue.SetPropFloat(Prop_Send, "m_flModelScale", scaleModel); } else { - statue.SetPropFloat(Prop_Send, "m_flModelScale", controller.ModelScale); + statue.SetPropFloat(Prop_Send, "m_flModelScale", profileData.ModelScale); } CBaseNPC npc = TheNPCs.FindNPCByEntIndex(statue.index); @@ -144,9 +161,10 @@ methodmap SF2_StatueEntity < SF2_BaseBoss npc.flGravity = 800.0; npc.flDeathDropHeight = 99999.0; npc.flJumpHeight = 512.0; - npc.flMaxYawRate = originalData.TurnRate; + npc.flFrictionForward = profileData.GetForwardFriction(difficulty); + npc.flFrictionSideways = profileData.GetSidewaysFriction(difficulty); + npc.flMaxYawRate = profileData.TurnRate; loco.SetCallback(LocomotionCallback_ShouldCollideWith, LocoCollideWith); - loco.SetCallback(LocomotionCallback_ClimbUpToLedge, ClimbUpCBase); statue.SetPropVector(Prop_Send, "m_vecMins", HULL_HUMAN_MINS); statue.SetPropVector(Prop_Send, "m_vecMaxs", HULL_HUMAN_MAXS); @@ -171,6 +189,7 @@ methodmap SF2_StatueEntity < SF2_BaseBoss public static void SetupAPI() { CreateNative("SF2_StatueBossEntity.IsValid.get", Native_GetIsValid); + CreateNative("SF2_StatueBossEntity.Controller.get", Native_GetController); CreateNative("SF2_StatueBossEntity.IsMoving.get", Native_GetIsMoving); CreateNative("SF2_StatueBossEntity.LastKillTime.get", Native_GetLastKillTime); CreateNative("SF2_StatueBossEntity.ProfileData", Native_GetProfileData); @@ -213,10 +232,12 @@ static Action Think(int entIndex) static void ThinkPost(int entIndex) { SF2_StatueEntity statue = SF2_StatueEntity(entIndex); + SF2NPC_Statue controller = statue.Controller; + StatueBossProfile data = controller.GetProfileData(); ProcessSpeed(statue); - if (NPCGetCustomOutlinesState(statue.Controller.Index) && NPCGetRainbowOutlineState(statue.Controller.Index)) + if (data.GetOutlineData() != null && data.GetOutlineData().GetRainbowState(controller.Difficulty)) { statue.ProcessRainbowOutline(); } @@ -227,7 +248,7 @@ static void ThinkPost(int entIndex) } statue.InterruptConditions = 0; - statue.SetNextThink(GetGameTime()); + statue.SetNextThink(GetGameTime() + statue.Controller.GetProfileData().TickRate); } static MRESReturn UpdateTransmitState(int entIndex, DHookReturn ret, DHookParam params) @@ -261,10 +282,7 @@ static CBaseEntity ProcessVision(SF2_StatueEntity statue, int &interruptConditio return CBaseEntity(-1); } bool attackEliminated = (controller.Flags & SFF_ATTACKWAITERS) != 0; - SF2StatueBossProfileData data; - data = controller.GetProfileData(); - SF2BossProfileData originalData; - originalData = view_as(controller).GetProfileData(); + StatueBossProfile data = controller.GetProfileData(); int difficulty = controller.Difficulty; float playerDists[MAXTF2PLAYERS]; @@ -288,7 +306,7 @@ static CBaseEntity ProcessVision(SF2_StatueEntity statue, int &interruptConditio } int bestNewTarget = oldTarget; - float searchRange = originalData.SearchRange[difficulty]; + float searchRange = data.GetSearchRange(difficulty); float bestNewTargetDist = Pow(searchRange, 2.0); if (IsValidEntity(bestNewTarget)) { @@ -337,7 +355,7 @@ static CBaseEntity ProcessVision(SF2_StatueEntity statue, int &interruptConditio bool isVisible = false; int traceHitEntity; - TR_TraceHullFilter(traceStartPos, + Handle trace = TR_TraceHullFilterEx(traceStartPos, traceEndPos, traceMins, traceMaxs, @@ -345,14 +363,16 @@ static CBaseEntity ProcessVision(SF2_StatueEntity statue, int &interruptConditio TraceRayBossVisibility, statue.index); - isVisible = !TR_DidHit(); - traceHitEntity = TR_GetEntityIndex(); + isVisible = !TR_DidHit(trace); + traceHitEntity = TR_GetEntityIndex(trace); if (!isVisible && traceHitEntity == client.index) { isVisible = true; } + delete trace; + if (isVisible) { isVisible = NPCShouldSeeEntity(controller.Index, client.index); @@ -373,7 +393,7 @@ static CBaseEntity ProcessVision(SF2_StatueEntity statue, int &interruptConditio } } - if (dist > Pow(originalData.SearchRange[difficulty], 2.0)) + if (dist > Pow(data.GetSearchRange(difficulty), 2.0)) { isVisible = false; } @@ -449,7 +469,7 @@ static CBaseEntity ProcessVision(SF2_StatueEntity statue, int &interruptConditio delete arrayRaidTargets; } } - statue.CurrentChaseDuration = data.ChaseDuration[difficulty]; + statue.CurrentChaseDuration = data.GetChaseDuration(difficulty); } if (bestNewTarget != INVALID_ENT_REFERENCE) @@ -464,28 +484,28 @@ static CBaseEntity ProcessVision(SF2_StatueEntity statue, int &interruptConditio static void ProcessSpeed(SF2_StatueEntity statue) { SF2NPC_Statue controller = statue.Controller; - SF2NPC_BaseNPC baseController = view_as(controller); int difficulty = controller.Difficulty; CBaseNPC npc = TheNPCs.FindNPCByEntIndex(statue.index); - SF2BossProfileData originalData; - originalData = baseController.GetProfileData(); + StatueBossProfile data = controller.GetProfileData(); float speed, acceleration; - acceleration = originalData.Acceleration[difficulty]; + acceleration = data.GetAcceleration(difficulty); if (controller.HasAttribute(SF2Attribute_ReducedAccelerationOnLook) && controller.CanBeSeen(_, true)) { acceleration *= controller.GetAttributeValue(SF2Attribute_ReducedAccelerationOnLook); } acceleration += controller.GetAddAcceleration(); + acceleration += controller.GetPersistentAddAcceleration(); - speed = originalData.RunSpeed[difficulty] * 10.0; // Backwards compatibility + speed = data.GetRunSpeed(difficulty) * 10.0; // Backwards compatibility if (controller.HasAttribute(SF2Attribute_ReducedSpeedOnLook) && controller.CanBeSeen(_, true)) { speed *= controller.GetAttributeValue(SF2Attribute_ReducedSpeedOnLook); } - speed += baseController.GetAddSpeed(); + speed += controller.GetAddSpeed(); + speed += controller.GetPersistentAddSpeed(); float forwardSpeed = speed; Action action = Plugin_Continue; @@ -517,14 +537,14 @@ static void ProcessSpeed(SF2_StatueEntity statue) if (SF_IsSlaughterRunMap()) { float slaughterSpeed = g_SlaughterRunMinimumBossRunSpeedConVar.FloatValue; - if (!originalData.SlaughterRunData.CustomMinimumSpeed[difficulty] && speed < slaughterSpeed) + if ((data.GetSlaughterRunData() == null || data.GetSlaughterRunData().ShouldUseCustomMinSpeed(difficulty)) && speed < slaughterSpeed) { speed = slaughterSpeed; } acceleration += 10000.0; } - if ((!originalData.IsPvEBoss && IsBeatBoxBeating(2)) || statue.IsKillingSomeone || !statue.IsMoving) + if ((!data.IsPvEBoss && IsBeatBoxBeating(2)) || statue.IsKillingSomeone || !statue.IsMoving) { speed = 0.0; } @@ -541,6 +561,22 @@ static any Native_GetIsValid(Handle plugin, int numParams) return bossEntity.IsValid(); } +static any Native_GetController(Handle plugin, int numParams) +{ + int ent = GetNativeCell(1); + if (!IsValidEntity(ent)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid entity index %d", ent); + } + + SF2_StatueEntity boss = SF2_StatueEntity(ent); + if (!boss.Controller.IsValid()) + { + return -1; + } + return boss.Controller; +} + static any Native_GetIsMoving(Handle plugin, int numParams) { int ent = GetNativeCell(1); @@ -575,8 +611,5 @@ static any Native_GetProfileData(Handle plugin, int numParams) SF2_StatueEntity bossEntity = SF2_StatueEntity(entity); - SF2StatueBossProfileData data; - data = bossEntity.Controller.GetProfileData(); - SetNativeArray(2, data, sizeof(data)); - return 0; + return bossEntity.Controller.GetProfileData(); } diff --git a/addons/sourcemod/scripting/sf2/npc/glow.sp b/addons/sourcemod/scripting/sf2/npc/glow.sp index 627000a1..ae62f341 100644 --- a/addons/sourcemod/scripting/sf2/npc/glow.sp +++ b/addons/sourcemod/scripting/sf2/npc/glow.sp @@ -1,3 +1,6 @@ +#pragma semicolon 1 +#pragma newdecls required + static const int g_DefaultColor[4] = { 150, 0, 255, 255 }; void SetupNPCGlows() @@ -23,7 +26,34 @@ static void OnPlayerSpawn(SF2_BasePlayer client) static void OnDifficultyChange(int oldDifficulty, int newDifficulty) { + for (int i = 0; i < MAX_BOSSES; i++) + { + SF2NPC_BaseNPC npc = SF2NPC_BaseNPC(i); + if (!npc.IsValid()) + { + continue; + } + if (!IsValidEntity(npc.EntIndex)) + { + continue; + } + + BaseBossProfile data = npc.GetProfileData(); + if (data.IsPvEBoss) + { + continue; + } + + int color[4]; + color = g_DefaultColor; + if (data.GetOutlineData() != null) + { + data.GetOutlineData().GetOutlineColor(color, npc.Difficulty); + } + SetGlowColor(npc.EntIndex, color); + UpdateVisibility(npc); + } } static void OnSpecialRoundStart(int specialRound) @@ -45,8 +75,7 @@ static void OnSpecialRoundStart(int specialRound) static void OnBossSpawn(SF2NPC_BaseNPC controller) { - SF2BossProfileData data; - data = controller.GetProfileData(); + BaseBossProfile data = controller.GetProfileData(); if (data.IsPvEBoss) { return; @@ -54,9 +83,9 @@ static void OnBossSpawn(SF2NPC_BaseNPC controller) int color[4]; color = g_DefaultColor; - if (data.CustomOutlines) + if (data.GetOutlineData() != null) { - color = data.OutlineColor; + data.GetOutlineData().GetOutlineColor(color, controller.Difficulty); } CreateGlowEntity(controller.EntIndex, color); UpdateVisibility(controller); diff --git a/addons/sourcemod/scripting/sf2/npc/npc_chaser.sp b/addons/sourcemod/scripting/sf2/npc/npc_chaser.sp index 2d8ffd0c..5e8293aa 100644 --- a/addons/sourcemod/scripting/sf2/npc/npc_chaser.sp +++ b/addons/sourcemod/scripting/sf2/npc/npc_chaser.sp @@ -4,27 +4,17 @@ #define _sf2_npc_chaser_included #pragma semicolon 1 - -static SF2ChaserBossProfileData g_NpcChaserProfileData[MAX_BOSSES]; +#pragma newdecls required static float g_NpcStunAddHealth[MAX_BOSSES]; static float g_NpcDeathInitialHealth[MAX_BOSSES][Difficulty_Max]; static float g_NpcDeathHealth[MAX_BOSSES][Difficulty_Max]; -static float g_NpcAlertGracetime[MAX_BOSSES][Difficulty_Max]; -static float g_NpcAlertDuration[MAX_BOSSES][Difficulty_Max]; -static float g_NpcChaseDuration[MAX_BOSSES][Difficulty_Max]; - ArrayList g_NpcChaseOnLookTarget[MAX_BOSSES] = { null, ... }; -static float g_NpcSearchWanderRangeMin[MAX_BOSSES][Difficulty_Max]; -static float g_NpcSearchWanderRangeMax[MAX_BOSSES][Difficulty_Max]; - bool g_NpcCopyAlerted[MAX_BOSSES]; -static bool g_NpcHasIsBoxingBoss[MAX_BOSSES] = { false, ... }; - bool g_NpcStealingLife[MAX_BOSSES]; Handle g_NpcLifeStealTimer[MAX_BOSSES]; @@ -32,7 +22,6 @@ static Handle g_NpcInstantKillThink[MAX_BOSSES]; //Boxing stuff static int g_NpcBoxingCurrentDifficulty[MAX_BOSSES]; -static int g_NpcBoxingRagePhase[MAX_BOSSES]; static bool g_ClientShouldBeForceChased[MAX_BOSSES][2049]; static bool g_IsTargetMarked[MAX_BOSSES][2049]; @@ -85,22 +74,12 @@ static void OnPlayerEscape(SF2_BasePlayer client) static void OnBossRemoved(SF2NPC_BaseNPC npc) { - if (npc.Type == SF2BossType_Chaser) + if (npc.GetProfileData().Type == SF2BossType_Chaser) { NPCChaserOnRemoveProfile(npc.Index); } } -SF2ChaserBossProfileData NPCChaserGetProfileData(int npcIndex) -{ - return g_NpcChaserProfileData[npcIndex]; -} - -void NPCChaserSetProfileData(int npcIndex, SF2ChaserBossProfileData value) -{ - g_NpcChaserProfileData[npcIndex] = value; -} - float NPCChaserGetAddStunHealth(int npcIndex) { return g_NpcStunAddHealth[npcIndex]; @@ -126,11 +105,6 @@ void NPCChaserSetDeathHealth(int npcIndex, int difficulty, float amount) g_NpcDeathHealth[npcIndex][difficulty] = amount; } -bool NPCChaserIsBoxingBoss(int npcIndex) -{ - return g_NpcHasIsBoxingBoss[npcIndex]; -} - int NPCChaserGetBoxingDifficulty(int npcIndex) { return g_NpcBoxingCurrentDifficulty[npcIndex]; @@ -179,13 +153,11 @@ void NPCChaserOnSelectProfile(int npcIndex) SF2NPC_Chaser chaser = SF2NPC_Chaser(npcIndex); char profile[SF2_MAX_PROFILE_NAME_LENGTH]; NPCGetProfile(npcIndex, profile, sizeof(profile)); - SF2ChaserBossProfileData profileData; - g_ChaserBossProfileData.GetArray(profile, profileData, sizeof(profileData)); - g_NpcChaserProfileData[npcIndex] = profileData; + ChaserBossProfile profileData = chaser.GetProfileData(); for (int difficulty = 0; difficulty < Difficulty_Max; difficulty++) { - g_NpcDeathInitialHealth[npcIndex][difficulty] = GetChaserProfileDeathHealth(profile, difficulty); + g_NpcDeathInitialHealth[npcIndex][difficulty] = profileData.GetDeathBehavior().GetHealth(difficulty); g_NpcDeathHealth[npcIndex][difficulty] = g_NpcDeathInitialHealth[npcIndex][difficulty]; } @@ -196,15 +168,12 @@ void NPCChaserOnSelectProfile(int npcIndex) chaser.SetAddSpeed(-chaser.GetAddSpeed()); chaser.SetAddAcceleration(-chaser.GetAddAcceleration()); - chaser.StunHealthAdd = -chaser.StunHealthAdd; - - g_NpcHasIsBoxingBoss[npcIndex] = GetChaserProfileBoxingState(profile); + chaser.SetAddStunHealth(-chaser.GetAddStunHealth()); g_NpcStealingLife[npcIndex] = false; g_NpcLifeStealTimer[npcIndex] = null; g_NpcBoxingCurrentDifficulty[npcIndex] = 1; - g_NpcBoxingRagePhase[npcIndex] = 0; g_NpcCopyAlerted[npcIndex] = false; @@ -226,19 +195,10 @@ static void NPCChaserResetValues(int npcIndex) { for (int difficulty = 0; difficulty < Difficulty_Max; difficulty++) { - g_NpcAlertGracetime[npcIndex][difficulty] = 0.0; - g_NpcAlertDuration[npcIndex][difficulty] = 0.0; - g_NpcChaseDuration[npcIndex][difficulty] = 0.0; - - g_NpcSearchWanderRangeMin[npcIndex][difficulty] = 0.0; - g_NpcSearchWanderRangeMax[npcIndex][difficulty] = 0.0; - g_NpcDeathInitialHealth[npcIndex][difficulty] = 0.0; NPCChaserSetDeathHealth(npcIndex, difficulty, 0.0); } - g_NpcHasIsBoxingBoss[npcIndex] = false; - g_NpcStunAddHealth[npcIndex] = 0.0; g_NpcStealingLife[npcIndex] = false; @@ -254,7 +214,6 @@ static void NPCChaserResetValues(int npcIndex) NPCChaserSetAddStunHealth(npcIndex, -NPCChaserGetAddStunHealth(npcIndex)); g_NpcBoxingCurrentDifficulty[npcIndex] = 0; - g_NpcBoxingRagePhase[npcIndex] = 0; g_NpcCopyAlerted[npcIndex] = false; } @@ -393,7 +352,7 @@ bool IsTargetValidForSlenderEx(CBaseEntity target, int bossIndex, bool includeEl return false; } - if (!g_SlenderTeleportIgnoreChases[bossIndex]) + if (!SF_BossesChaseEndlessly() && !SF_IsRenevantMap() && !SF_IsSurvivalMap() && !SF_IsRaidMap() && !SF2NPC_BaseNPC(bossIndex).GetProfileData().TeleportIgnoreChases) { for (int i = 0; i < MAX_BOSSES; i++) { @@ -409,11 +368,8 @@ bool IsTargetValidForSlenderEx(CBaseEntity target, int bossIndex, bool includeEl continue; } - SF2BossProfileData data; - data = view_as(npc).GetProfileData(); - int state = chaser.State; - if (!data.IsPvEBoss && (state == STATE_CHASE || state == STATE_ATTACK || state == STATE_STUN)) + if (!npc.GetProfileData().IsPvEBoss && (state == STATE_CHASE || state == STATE_ATTACK || state == STATE_STUN)) { return false; } @@ -636,6 +592,18 @@ static void TriggerKey(int caller) AcceptEntityInput(ent, "Enable"); } } + ent = -1; + if (strcmp(targetName, "sf2_trigger_escape", false) == 0) + { + while ((ent = FindEntityByClassname(ent, "sf2_trigger_escape")) != -1) + { + SF2TriggerEscapeEntity trigger = SF2TriggerEscapeEntity(ent); + if (trigger.IsValid()) + { + trigger.AcceptInput("Enable"); + } + } + } RemoveEntity(caller); EmitSoundToAll("ui/itemcrate_smash_ultrarare_short.wav", caller, SNDCHAN_AUTO, SNDLEVEL_SCREAMING); } @@ -692,12 +660,11 @@ static any Native_PerformVoice(Handle plugin, int numParams) return 0; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttackFromIndex(GetNativeCell(3), attackData); + ChaserBossProfile data = controller.GetProfileData(); + char name[64]; + data.GetAttackName(GetNativeCell(3), name, sizeof(name)); - return chaser.PerformVoice(GetNativeCell(2), attackData.Name); + return chaser.PerformVoice(GetNativeCell(2), name); } static any Native_CreateBossSoundHint(Handle plugin, int numParams) @@ -736,12 +703,10 @@ static any Native_GetBossAttackIndexType(Handle plugin, int numParams) return 0; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttackFromIndex(GetNativeCell(2), attackData); - - return attackData.Type; + ChaserBossProfile data = controller.GetProfileData(); + char name[64]; + data.GetAttackName(GetNativeCell(2), name, sizeof(name)); + return data.GetAttack(name).Type; } static any Native_GetBossAttackIndexDamage(Handle plugin, int numParams) @@ -758,12 +723,10 @@ static any Native_GetBossAttackIndexDamage(Handle plugin, int numParams) return 0; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttackFromIndex(GetNativeCell(2), attackData); - - return attackData.Damage[GetNativeCell(3)]; + ChaserBossProfile data = controller.GetProfileData(); + char name[64]; + data.GetAttackName(GetNativeCell(2), name, sizeof(name)); + return data.GetAttack(name).GetDamage(GetNativeCell(3)); } static any Native_UpdateBossAnimation(Handle plugin, int numParams) @@ -842,12 +805,10 @@ static any Native_GetBossAttackIndexDamageType(Handle plugin, int numParams) return 0; } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SF2ChaserBossProfileAttackData attackData; - data.GetAttackFromIndex(GetNativeCell(2), attackData); - - return attackData.DamageType[1]; + ChaserBossProfile data = controller.GetProfileData(); + char name[64]; + data.GetAttackName(GetNativeCell(2), name, sizeof(name)); + return data.GetAttack(name).GetDamageType(1); } static any Native_GetProfileData(Handle plugin, int numParams) @@ -858,24 +819,14 @@ static any Native_GetProfileData(Handle plugin, int numParams) return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", controller.Index); } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - SetNativeArray(2, data, sizeof(data)); - return 0; + return controller.GetProfileData(); } static any Native_GetProfileDataEx(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, sizeof(profile)); - SF2ChaserBossProfileData data; - if (!g_ChaserBossProfileData.GetArray(profile, data, sizeof(data))) - { - return false; - } - - SetNativeArray(2, data, sizeof(data)); - return true; + return view_as(GetBossProfile(profile)); } static any Native_SetForceChaseState(Handle plugin, int numParams) diff --git a/addons/sourcemod/scripting/sf2/npc/npc_creeper.sp b/addons/sourcemod/scripting/sf2/npc/npc_creeper.sp index 3aabc616..7fb4d521 100644 --- a/addons/sourcemod/scripting/sf2/npc/npc_creeper.sp +++ b/addons/sourcemod/scripting/sf2/npc/npc_creeper.sp @@ -4,53 +4,29 @@ #define _sf2_npc_creeper_included #pragma semicolon 1 - -static float g_NpcStatueIdleLifetime[MAX_BOSSES][Difficulty_Max]; -static SF2StatueBossProfileData g_StatueProfileData[MAX_BOSSES]; - -SF2StatueBossProfileData NPCStatueGetProfileData(int npcIndex) -{ - return g_StatueProfileData[npcIndex]; -} - -void NPCStatueSetProfileData(int npcIndex, SF2StatueBossProfileData value) -{ - g_StatueProfileData[npcIndex] = value; -} - -float NPCStatueGetIdleLifetime(int npcIndex, int difficulty) -{ - return g_NpcStatueIdleLifetime[npcIndex][difficulty]; -} +#pragma newdecls required void NPCStatueOnSelectProfile(int npcIndex) { SF2NPC_Statue statue = SF2NPC_Statue(npcIndex); - char profile[SF2_MAX_PROFILE_NAME_LENGTH]; - NPCGetProfile(npcIndex, profile, sizeof(profile)); - g_StatueBossProfileData.GetArray(profile, g_StatueProfileData[npcIndex], sizeof(g_StatueProfileData[])); - for (int difficulty = 0; difficulty < Difficulty_Max; difficulty++) - { - g_NpcStatueIdleLifetime[npcIndex][difficulty] = g_StatueProfileData[npcIndex].IdleLifeTime[difficulty]; - } statue.SetAffectedBySight(true); } SF2_StatueEntity Spawn_Statue(SF2NPC_BaseNPC controller, const float pos[3], const float ang[3]) { - g_SlenderStatueIdleLifeTime[controller.Index] = GetGameTime() + g_NpcStatueIdleLifetime[controller.Index][controller.Difficulty]; + g_SlenderStatueIdleLifeTime[controller.Index] = GetGameTime() + view_as(controller).GetProfileData().GetIdleLifeTime(controller.Difficulty); return SF2_StatueEntity.Create(controller, pos, ang); } void Despawn_Statue(SF2NPC_Statue controller, CBaseEntity bossEnt) { - SF2StatueBossProfileData data; - data = controller.GetProfileData(); + StatueBossProfile data = controller.GetProfileData(); // Stop all possible looping sounds. - SF2BossProfileSoundInfo soundInfo; - soundInfo = data.MoveSounds; - soundInfo.StopAllSounds(bossEnt.index); + for (int i = 0; i < Difficulty_Max; i++) + { + data.GetMoveSounds().StopAllSounds(bossEnt.index, i); + } } void NPCStatue_InitializeAPI() @@ -69,22 +45,12 @@ static any Native_GetProfileData(Handle plugin, int numParams) return ThrowNativeError(SP_ERROR_NATIVE, "Invalid boss index %d", controller.Index); } - SF2StatueBossProfileData data; - data = controller.GetProfileData(); - SetNativeArray(2, data, sizeof(data)); - return 0; + return controller.GetProfileData(); } static any Native_GetProfileDataEx(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, sizeof(profile)); - SF2StatueBossProfileData data; - if (!g_StatueBossProfileData.GetArray(profile, data, sizeof(data))) - { - return false; - } - - SetNativeArray(2, data, sizeof(data)); - return true; + return view_as(GetBossProfile(profile)); } diff --git a/addons/sourcemod/scripting/sf2/old_pvp.sp b/addons/sourcemod/scripting/sf2/old_pvp.sp index ce8a5dd5..c9b75e35 100644 --- a/addons/sourcemod/scripting/sf2/old_pvp.sp +++ b/addons/sourcemod/scripting/sf2/old_pvp.sp @@ -102,7 +102,7 @@ enum struct PvPProjectile_BallOfFire } TF2_IgnitePlayer(otherEntity, ownerEntity); - SDKHooks_TakeDamage(otherEntity, ownerEntity, ownerEntity, damage * damageBonus, 0x1220000, GetEntPropEnt(ownerEntity, Prop_Send, "m_hActiveWeapon"), NULL_VECTOR, damagePos); + SDKHooks_TakeDamage(otherEntity, ownerEntity, ownerEntity, damage * damageBonus, 0x1220000, GetEntPropEnt(ownerEntity, Prop_Send, "m_hActiveWeapon"), NULL_VECTOR, damagePos, .bypassHooks = false); } } } @@ -1039,6 +1039,7 @@ static Action Timer_PlayerPvPLeaveCountdown(Handle timer, any userid) return Plugin_Continue; } + bool IsClientInPvP(int client) { return g_PlayerInPvP[client]; diff --git a/addons/sourcemod/scripting/sf2/playergroups.sp b/addons/sourcemod/scripting/sf2/playergroups.sp index a95d07af..86506693 100644 --- a/addons/sourcemod/scripting/sf2/playergroups.sp +++ b/addons/sourcemod/scripting/sf2/playergroups.sp @@ -4,6 +4,7 @@ #define _sf2_playergroups_included #pragma semicolon 1 +#pragma newdecls required #define SF2_MAX_PLAYER_GROUPS MAXPLAYERS #define SF2_MAX_PLAYER_GROUP_NAME_LENGTH 32 diff --git a/addons/sourcemod/scripting/sf2/playergroups/menus.sp b/addons/sourcemod/scripting/sf2/playergroups/menus.sp index cf8705ed..57732f70 100644 --- a/addons/sourcemod/scripting/sf2/playergroups/menus.sp +++ b/addons/sourcemod/scripting/sf2/playergroups/menus.sp @@ -5,6 +5,7 @@ #define _sf2_playergroups_menus #pragma semicolon 1 +#pragma newdecls required void DisplayGroupMainMenuToClient(int client) { diff --git a/addons/sourcemod/scripting/sf2/profiles.sp b/addons/sourcemod/scripting/sf2/profiles.sp index a5538756..f5ba24cb 100644 --- a/addons/sourcemod/scripting/sf2/profiles.sp +++ b/addons/sourcemod/scripting/sf2/profiles.sp @@ -4,6 +4,7 @@ #define _sf2_profiles_included #pragma semicolon 1 +#pragma newdecls required #define FILE_PROFILES_DIR "configs/sf2/profiles" #define FILE_PROFILES_PACKS "configs/sf2/profiles_packs.cfg" @@ -16,8 +17,6 @@ ArrayList g_BossProfileList = null; static ArrayList g_SelectableBossProfileList = null; static ArrayList g_SelectableAdminBossProfileList = null; -static ArrayList g_SelectableBoxingBossProfileList = null; -static ArrayList g_SelectableRenevantBossProfileList = null; static ArrayList g_SelectableRenevantBossAdminProfileList = null; static ArrayList g_SelectableBossProfileQueueList = null; @@ -43,6 +42,8 @@ static char mapBossPack[64]; GlobalForward g_OnBossProfileLoadedFwd; static GlobalForward g_OnBossProfileUnloadedFwd; +#include "profiles/keymap.sp" +#include "profiles/objects.sp" #include "profiles/profiles_boss_functions.sp" #include "profiles/profile_chaser.sp" #include "profiles/profile_statue.sp" @@ -62,11 +63,14 @@ void SetupBossProfileNatives() CreateNative("SF2_GetBossAttributeName", Native_GetBossAttributeName); CreateNative("SF2_GetBossAttributeValue", Native_GetBossAttributeValue); - CreateNative("SF2_GetBossProfileData", Native_GetBossProfileData); - CreateNative("SF2_GetChaserBossProfileData", Native_GetChaserBossProfileData); - CreateNative("SF2_GetStatueBossProfileData", Native_GetStatueBossProfileData); CreateNative("SF2_TranslateProfileActivityFromName", Native_TranslateProfileActivityFromName); CreateNative("SF2_LookupProfileAnimation", Native_LookupProfileAnimation); + + CreateNative("SF2_BaseBossProfile.Type.get", Native_GetType); + CreateNative("SF2_BaseBossProfile.IsPvEBoss.get", Native_GetIsPvEBoss); + + SetupProfileObjectNatives(); + ProfileChaser_InititalizeAPI(); } void InitializeBossProfiles() @@ -76,8 +80,8 @@ void InitializeBossProfiles() g_Activities = new StringMap(); - g_OnBossProfileLoadedFwd = new GlobalForward("SF2_OnBossProfileLoaded", ET_Ignore, Param_String, Param_Any); - g_OnBossProfileUnloadedFwd = new GlobalForward("SF2_OnBossProfileUnloaded", ET_Ignore, Param_String); + g_OnBossProfileLoadedFwd = new GlobalForward("SF2_OnBossProfileLoaded", ET_Ignore, Param_String, Param_Cell); + g_OnBossProfileUnloadedFwd = new GlobalForward("SF2_OnBossProfileUnloaded", ET_Ignore, Param_String, Param_Cell); AddProfileActivities(); @@ -89,9 +93,6 @@ void InitializeBossProfiles() g_BossPackVoteShuffleConVar = CreateConVar("sf2_boss_profile_pack_endvote_shuffle", "0", "Shuffles the menu options of boss pack endvotes if enabled."); g_MaxCorePackBosses = CreateConVar("sf2_max_core_pack_bosses", "-1", "Determines how many bosses can load randomly from the core pack, if set to less than 0 will keep this feature off. Note that companion bosses will still load if needed."); - - InitializeStatueProfiles(); - InitializeChaserProfiles(); } static void AddProfileActivities() @@ -261,6 +262,10 @@ Activity TranslateProfileActivityFromName(const char[] activityName) int LookupProfileAnimation(int entity, const char[] animName) { CBaseAnimating animator = CBaseAnimating(entity); + if (animName[0] == '\0') + { + return -1; + } int sequence = -1; Activity activity = TranslateProfileActivityFromName(animName); @@ -276,6 +281,11 @@ int LookupProfileAnimation(int entity, const char[] animName) return sequence; } +int GetMaxProfileDifficultySuffixSize() +{ + return 11; +} + void GetCurrentBossPack(char[] bossPackName, int length) { g_BossPackConfig.Rewind(); @@ -351,27 +361,15 @@ void BossProfilesOnMapEnd() ClearBossProfiles(); } -static void PreUnloadBossProfile(const char[] profile) +BaseBossProfile GetBossProfile(const char[] profile) { - SF2BossProfileData profileData; - g_BossProfileData.GetArray(profile, profileData, sizeof(profileData)); - - LogSF2Message("Unloading %s...", profile); - - int bossType = GetBossProfileType(profile); - switch (bossType) + BaseBossProfile data = null; + if (g_BossProfileData.GetValue(profile, data)) { - case SF2BossType_Statue: - { - UnloadStatueBossProfile(profile); - } - case SF2BossType_Chaser: - { - UnloadChaserBossProfile(profile); - } + return data; } - profileData.Destroy(); + return null; } void UnloadBossProfile(const char[] profile) @@ -381,12 +379,16 @@ void UnloadBossProfile(const char[] profile) return; } + LogSF2Message("Unloading %s...", profile); + + BaseBossProfile profileData; + g_BossProfileData.GetValue(profile, profileData); + Call_StartForward(g_OnBossProfileUnloadedFwd); Call_PushString(profile); + Call_PushCell(profileData); Call_Finish(); - PreUnloadBossProfile(profile); - int index = g_BossProfileList.FindString(profile); if (index != -1) { @@ -405,40 +407,20 @@ void UnloadBossProfile(const char[] profile) g_SelectableAdminBossProfileList.Erase(index); } - index = g_SelectableBoxingBossProfileList.FindString(profile); - if (index != -1) - { - g_SelectableBoxingBossProfileList.Erase(index); - } - - index = g_SelectableRenevantBossProfileList.FindString(profile); - if (index != -1) - { - g_SelectableRenevantBossProfileList.Erase(index); - } - index = g_SelectableRenevantBossAdminProfileList.FindString(profile); if (index != -1) { g_SelectableRenevantBossAdminProfileList.Erase(index); } - SF2BossProfileData data; - g_BossProfileData.GetArray(profile, data, sizeof(data)); - if (data.IsPvEBoss) + if (profileData.IsPvEBoss) { - char setProfile[SF2_MAX_PROFILE_NAME_LENGTH]; - strcopy(setProfile, sizeof(setProfile), profile); - UnregisterPvESlenderBoss(setProfile); + UnregisterPvESlenderBoss(profile); } - g_BossProfileData.Remove(profile); + CleanupKeyMap(profileData); - g_Config.Rewind(); - if (g_Config.JumpToKey(profile)) - { - g_Config.DeleteThis(); - } + g_BossProfileData.Remove(profile); } /** @@ -461,11 +443,8 @@ void ClearBossProfiles() continue; } - Call_StartForward(g_OnBossProfileUnloadedFwd); - Call_PushString(profile); - Call_Finish(); - - PreUnloadBossProfile(profile); + UnloadBossProfile(profile); + i--; } if (g_SelectableBossProfileList != null) @@ -478,16 +457,6 @@ void ClearBossProfiles() delete g_SelectableAdminBossProfileList; } - if (g_SelectableBoxingBossProfileList != null) - { - delete g_SelectableBoxingBossProfileList; - } - - if (g_SelectableRenevantBossProfileList != null) - { - delete g_SelectableRenevantBossProfileList; - } - if (g_SelectableRenevantBossAdminProfileList != null) { delete g_SelectableRenevantBossAdminProfileList; @@ -499,11 +468,6 @@ void ClearBossProfiles() void ReloadBossProfiles() { - if (g_Config != null) - { - delete g_Config; - } - if (g_BossPackConfig != null) { delete g_BossPackConfig; @@ -515,7 +479,6 @@ void ReloadBossProfiles() // Clear and reload the lists. ClearBossProfiles(); - g_Config = new KeyValues("root"); g_BossPackConfig = new KeyValues("root"); if (g_BossProfileList == null) @@ -533,16 +496,6 @@ void ReloadBossProfiles() g_SelectableAdminBossProfileList = new ArrayList(SF2_MAX_PROFILE_NAME_LENGTH); } - if (g_SelectableBoxingBossProfileList == null) - { - g_SelectableBoxingBossProfileList = new ArrayList(SF2_MAX_PROFILE_NAME_LENGTH); - } - - if (g_SelectableRenevantBossProfileList == null) - { - g_SelectableRenevantBossProfileList = new ArrayList(SF2_MAX_PROFILE_NAME_LENGTH); - } - if (g_SelectableRenevantBossAdminProfileList == null) { g_SelectableRenevantBossAdminProfileList = new ArrayList(SF2_MAX_PROFILE_NAME_LENGTH); @@ -723,11 +676,180 @@ static bool LoadProfileFile(const char[] profilePath, char[] profileName, int pr kv.GetSectionName(profileName, profileNameLen); - bool result = LoadBossProfile(kv, profileName, errorReason, errorReasonLen, lookIntoLoads, originalDir); + BaseBossProfile profileData = view_as(KeyValuesToKeyMap(kv)); delete kv; - return result; + bool selectable = true; + if (profileData.GetMapSelectionBlacklist() != null) + { + char currentMap[128]; + GetCurrentMap(currentMap, sizeof(currentMap)); + + for (int i = 0; i < profileData.GetMapSelectionBlacklist().KeyLength; i++) + { + char key[64], map[128]; + profileData.GetMapSelectionBlacklist().GetKeyNameFromIndex(i, key, sizeof(key)); + profileData.GetMapSelectionBlacklist().GetString(key, map, sizeof(map)); + + if (StrContains(currentMap, map, false) != -1) + { + selectable = false; + } + } + } + + if (profileData.GetModeSelectionBlacklist() != null) + { + if (selectable && SF_IsBoxingMap() && profileData.GetModeSelectionBlacklist().GetBool("boxing", false)) + { + selectable = false; + } + + if (selectable && SF_IsProxyMap() && profileData.GetModeSelectionBlacklist().GetBool("proxy", false)) + { + selectable = false; + } + + if (selectable && SF_IsRaidMap() && profileData.GetModeSelectionBlacklist().GetBool("raid", false)) + { + selectable = false; + } + + if (selectable && SF_IsRenevantMap() && profileData.GetModeSelectionBlacklist().GetBool("renevant", false)) + { + selectable = false; + } + + if (selectable && SF_IsSlaughterRunMap() && profileData.GetModeSelectionBlacklist().GetBool("slaughter_run", false)) + { + selectable = false; + } + + if (selectable && SF_IsSurvivalMap() && profileData.GetModeSelectionBlacklist().GetBool("survival", false)) + { + selectable = false; + } + } + + char path[PLATFORM_MAX_PATH]; + + if (lookIntoLoads) + { + bool skip = true; + if (selectable) + { + skip = false; + } + + if (profileData.GetBool("admin_only", false)) + { + skip = false; + } + + if (profileData.GetBool("enable_random_selection_renevant_admin", false)) + { + skip = false; + } + + if (profileData.GetBool("is_pve", false) && profileData.GetBool("pve_selectable", true)) + { + skip = false; + } + + if (profileData.GetBool("always_load", false)) + { + skip = false; + } + + ProfileObject pve = profileData.GetSection("pve"); + if (pve != null && pve.GetBool("selectable", true)) + { + skip = false; + } + + if (skip) + { + FormatEx(errorReason, errorReasonLen, "is not selectable, skipping!"); + CleanupKeyMap(profileData); + return false; + } + } + + if (profileData.Type <= SF2BossType_Unknown || profileData.Type >= SF2BossType_MaxTypes) + { + FormatEx(errorReason, errorReasonLen, "boss type is unknown!"); + return false; + } + + for (int i = 0; i < Difficulty_Max; i++) + { + profileData.GetModel(i, path, sizeof(path)); + if (path[0] == '\0') + { + FormatEx(errorReason, errorReasonLen, "model cannot be blank!"); + CleanupKeyMap(profileData); + return false; + } + } + + UnloadBossProfile(profileName); + + profileData.Precache(); + + g_BossProfileData.SetValue(profileName, profileData); + + int index = g_BossProfileList.FindString(profileName); + if (index == -1) + { + g_BossProfileList.PushString(profileName); + } + + if (profileData.IsPvEBoss) + { + selectable = false; + if (profileData.GetPvEData().IsSelectable) + { + RegisterPvESlenderBoss(profileName); + } + } + + if (selectable) + { + if (profileData.GetBool("enable_random_selection", true)) + { + if (GetSelectableBossProfileList().FindString(profileName) == -1) + { + // Add to the selectable boss list if it isn't there already. + GetSelectableBossProfileList().PushString(profileName); + } + } + + if (profileData.GetBool("admin_only", false)) + { + if (GetSelectableAdminBossProfileList().FindString(profileName) == -1) + { + // Add to the selectable boss list if it isn't there already. + GetSelectableAdminBossProfileList().PushString(profileName); + } + } + + if (profileData.GetBool("enable_random_selection_renevant_admin", false)) + { + if (GetSelectableRenevantBossAdminProfileList().FindString(profileName) == -1) + { + // Add to the selectable boss list if it isn't there already. + GetSelectableRenevantBossAdminProfileList().PushString(profileName); + } + } + } + + Call_StartForward(g_OnBossProfileLoadedFwd); + Call_PushString(profileName); + Call_PushCell(profileData); + Call_Finish(); + + return true; } static void LoadProfilesFromDirectory(const char[] relDirPath, int maxLoadedBosses = -1) @@ -1145,33 +1267,27 @@ bool IsProfileValid(const char[] profile) return GetBossProfileList().FindString(profile) != -1; } -int GetProfileNum(const char[] profile, const char[] keyValue,int defaultValue=0) +int GetProfileNum(const char[] profile, const char[] keyValue, int defaultValue = 0) { if (!IsProfileValid(profile)) { return defaultValue; } - g_Config.Rewind(); - g_Config.JumpToKey(profile); - - return g_Config.GetNum(keyValue, defaultValue); + return GetBossProfile(profile).GetInt(keyValue, defaultValue); } -float GetProfileFloat(const char[] profile, const char[] keyValue, float defaultValue=0.0) +float GetProfileFloat(const char[] profile, const char[] keyValue, float defaultValue = 0.0) { if (!IsProfileValid(profile)) { return defaultValue; } - g_Config.Rewind(); - g_Config.JumpToKey(profile); - - return g_Config.GetFloat(keyValue, defaultValue); + return GetBossProfile(profile).GetFloat(keyValue, defaultValue); } -bool GetProfileVector(const char[] profile, const char[] keyValue, float buffer[3], const float defaultValue[3]=NULL_VECTOR) +bool GetProfileVector(const char[] profile, const char[] keyValue, float buffer[3], const float defaultValue[3] = NULL_VECTOR) { for (int i = 0; i < 3; i++) { @@ -1183,49 +1299,11 @@ bool GetProfileVector(const char[] profile, const char[] keyValue, float buffer[ return false; } - g_Config.Rewind(); - g_Config.JumpToKey(profile); - - g_Config.GetVector(keyValue, buffer, defaultValue); + GetBossProfile(profile).GetVector(keyValue, buffer, defaultValue); return true; } -/* -bool GetProfileColor(const char[] profile, - const char[] keyValue, - int &r, - int &g, - int &b, - int &a, - int dr=255, - int dg=255, - int db=255, - int da=255) -{ - r = dr; - g = dg; - b = db; - a = da; - - if (!IsProfileValid(profile)) - { - return false; - } - g_Config.Rewind(); - g_Config.JumpToKey(profile); - - char value[64]; - g_Config.GetString(keyValue, value, sizeof(value)); - - if (value[0] != '\0') - { - g_Config.GetColor(keyValue, r, g, b, a); - } - - return true; -} -*/ -bool GetProfileString(const char[] profile, const char[] keyValue, char[] buffer,int bufferLen, const char[] defaultValue="") +bool GetProfileString(const char[] profile, const char[] keyValue, char[] buffer, int bufferLen, const char[] defaultValue = "") { strcopy(buffer, bufferLen, defaultValue); @@ -1234,15 +1312,12 @@ bool GetProfileString(const char[] profile, const char[] keyValue, char[] buffer return false; } - g_Config.Rewind(); - g_Config.JumpToKey(profile); - - g_Config.GetString(keyValue, buffer, bufferLen, defaultValue); + GetBossProfile(profile).GetString(keyValue, buffer, bufferLen, defaultValue); return true; } // Code originally from FF2. Credits to the original authors Rainbolt Dash and FlaminSarge. -bool GetRandomStringFromProfile(const char[] profile, const char[] strKeyValue, char[] buffer,int bufferLen,int index = -1,int attackIndex = -1,int &result = 0) +bool GetRandomStringFromProfile(const char[] profile, const char[] keyValue, char[] buffer, int bufferLen, int index = -1, int attackIndex = -1, int &result = 0) { buffer[0] = '\0'; result = 0; @@ -1252,60 +1327,42 @@ bool GetRandomStringFromProfile(const char[] profile, const char[] strKeyValue, return false; } - g_Config.Rewind(); - if (!g_Config.JumpToKey(profile)) - { - return false; - } - if (!g_Config.JumpToKey(strKeyValue)) + BaseBossProfile profileData = GetBossProfile(profile); + ProfileObject section = profileData.GetSection(keyValue); + if (section == null) { return false; } + ProfileObject selectedSection = null; - char s[32], s2[PLATFORM_MAX_PATH], s3[3], s4[PLATFORM_MAX_PATH], s5[3]; + char s[32], s2[PLATFORM_MAX_PATH], s3[64]; int i = 1; if (attackIndex != -1) { FormatEx(s3, sizeof(s3), "%d", attackIndex); - FormatEx(s5, sizeof(s5), "%d", attackIndex); - g_Config.GetString(s5, s4, sizeof(s4)); - if (s4[0] == '\0') + ProfileObject attack = section.GetSection(s3); + if (attack == null) { - if (g_Config.JumpToKey(s3)) + selectedSection = attack; + for (;;) { - for (;;) + FormatEx(s, sizeof(s), "%d", i); + attack.GetString(s, s2, sizeof(s2)); + if (s2[0] == '\0') { - FormatEx(s, sizeof(s), "%d", i); - g_Config.GetString(s, s2, sizeof(s2)); - if (s2[0] == '\0') - { - break; - } - - i++; + break; } - } - else - { - for (;;) - { - FormatEx(s, sizeof(s), "%d", i); - g_Config.GetString(s, s2, sizeof(s2)); - if (s2[0] == '\0') - { - break; - } - i++; - } + i++; } } else { + selectedSection = section; for (;;) { FormatEx(s, sizeof(s), "%d", i); - g_Config.GetString(s, s2, sizeof(s2)); + section.GetString(s, s2, sizeof(s2)); if (s2[0] == '\0') { break; @@ -1317,10 +1374,11 @@ bool GetRandomStringFromProfile(const char[] profile, const char[] strKeyValue, } else { + selectedSection = section; for (;;) { FormatEx(s, sizeof(s), "%d", i); - g_Config.GetString(s, s2, sizeof(s2)); + section.GetString(s, s2, sizeof(s2)); if (s2[0] == '\0') { break; @@ -1336,7 +1394,7 @@ bool GetRandomStringFromProfile(const char[] profile, const char[] strKeyValue, } int randomReturn = GetRandomInt(1, i - 1); FormatEx(s, sizeof(s), "%d", index < 0 ? randomReturn : index); - g_Config.GetString(s, buffer, bufferLen); + selectedSection.GetString(s, buffer, bufferLen); result = randomReturn; return true; } @@ -1357,37 +1415,16 @@ ArrayList GetSelectableBossProfileList() return g_SelectableBossProfileList; } -ArrayList GetSelectableBoxingBossProfileList() -{ - return g_SelectableBoxingBossProfileList; -} - ArrayList GetSelectableAdminBossProfileList() { return g_SelectableAdminBossProfileList; } -ArrayList GetSelectableRenevantBossProfileList() -{ - return g_SelectableRenevantBossProfileList; -} - ArrayList GetSelectableRenevantBossAdminProfileList() { return g_SelectableRenevantBossAdminProfileList; } -bool GetRandomRenevantBossProfile(char[] sBuffer, int iBufferLen) -{ - if (g_SelectableRenevantBossProfileList.Length == 0) - { - return false; - } - - g_SelectableRenevantBossProfileList.GetString(GetRandomInt(0, g_SelectableRenevantBossProfileList.Length - 1), sBuffer, iBufferLen); - return true; -} - /** * Returns an array of boss that didn't play in game yet. */ @@ -1416,21 +1453,6 @@ void RemoveBossProfileFromQueueList(const char[] profile) } } -static any Native_GetBossProfileData(Handle plugin,int numParams) -{ - return g_BossProfileData; -} - -static any Native_GetChaserBossProfileData(Handle plugin,int numParams) -{ - return g_ChaserBossProfileData; -} - -static any Native_GetStatueBossProfileData(Handle plugin,int numParams) -{ - return g_StatueBossProfileData; -} - static any Native_TranslateProfileActivityFromName(Handle plugin, int numParams) { char activityName[64]; @@ -1445,7 +1467,7 @@ static any Native_LookupProfileAnimation(Handle plugin, int numParams) return LookupProfileAnimation(GetNativeCell(1), animationName); } -static any Native_IsBossProfileValid(Handle plugin,int numParams) +static any Native_IsBossProfileValid(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1453,7 +1475,7 @@ static any Native_IsBossProfileValid(Handle plugin,int numParams) return IsProfileValid(profile); } -static any Native_GetBossProfileNum(Handle plugin,int numParams) +static any Native_GetBossProfileNum(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1464,7 +1486,7 @@ static any Native_GetBossProfileNum(Handle plugin,int numParams) return GetProfileNum(profile, keyValue, GetNativeCell(3)); } -static any Native_GetBossProfileFloat(Handle plugin,int numParams) +static any Native_GetBossProfileFloat(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1475,7 +1497,7 @@ static any Native_GetBossProfileFloat(Handle plugin,int numParams) return GetProfileFloat(profile, keyValue, GetNativeCell(3)); } -static any Native_GetBossProfileString(Handle plugin,int numParams) +static any Native_GetBossProfileString(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1495,7 +1517,7 @@ static any Native_GetBossProfileString(Handle plugin,int numParams) return success; } -static any Native_GetBossProfileVector(Handle plugin,int numParams) +static any Native_GetBossProfileVector(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1513,7 +1535,7 @@ static any Native_GetBossProfileVector(Handle plugin,int numParams) return success; } -static any Native_GetBossAttackProfileNum(Handle plugin,int numParams) +static any Native_GetBossAttackProfileNum(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1524,7 +1546,7 @@ static any Native_GetBossAttackProfileNum(Handle plugin,int numParams) return GetProfileAttackNum(profile, keyValue, GetNativeCell(3), GetNativeCell(4)); } -static any Native_GetBossAttackProfileFloat(Handle plugin,int numParams) +static any Native_GetBossAttackProfileFloat(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1535,7 +1557,7 @@ static any Native_GetBossAttackProfileFloat(Handle plugin,int numParams) return GetProfileAttackFloat(profile, keyValue, GetNativeCell(3), GetNativeCell(4)); } -static any Native_GetBossAttackProfileString(Handle plugin,int numParams) +static any Native_GetBossAttackProfileString(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1555,7 +1577,7 @@ static any Native_GetBossAttackProfileString(Handle plugin,int numParams) return success; } -static any Native_GetBossAttackProfileVector(Handle plugin,int numParams) +static any Native_GetBossAttackProfileVector(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1573,7 +1595,7 @@ static any Native_GetBossAttackProfileVector(Handle plugin,int numParams) return success; } -static any Native_GetRandomStringFromBossProfile(Handle plugin,int numParams) +static any Native_GetRandomStringFromBossProfile(Handle plugin, int numParams) { char profile[SF2_MAX_PROFILE_NAME_LENGTH]; GetNativeString(1, profile, SF2_MAX_PROFILE_NAME_LENGTH); @@ -1591,7 +1613,7 @@ static any Native_GetRandomStringFromBossProfile(Handle plugin,int numParams) return success; } -static any Native_GetBossAttributeName(Handle plugin,int numParams) +static any Native_GetBossAttributeName(Handle plugin, int numParams) { int attributeIndex = GetNativeCell(2); @@ -1599,7 +1621,7 @@ static any Native_GetBossAttributeName(Handle plugin,int numParams) return success; } -static any Native_GetBossAttributeValue(Handle plugin,int numParams) +static any Native_GetBossAttributeValue(Handle plugin, int numParams) { int attributeIndex = GetNativeCell(2); @@ -1609,3 +1631,25 @@ static any Native_GetBossAttributeValue(Handle plugin,int numParams) } return NPCGetAttributeValue(GetNativeCell(1), attributeIndex); } + +static any Native_GetType(Handle plugin, int numParams) +{ + BaseBossProfile profileData = GetNativeCell(1); + if (profileData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", profileData); + } + + return profileData.Type; +} + +static any Native_GetIsPvEBoss(Handle plugin, int numParams) +{ + BaseBossProfile profileData = GetNativeCell(1); + if (profileData == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", profileData); + } + + return profileData.IsPvEBoss; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/profiles/keymap.sp b/addons/sourcemod/scripting/sf2/profiles/keymap.sp index 58a54259..1856ba6f 100644 --- a/addons/sourcemod/scripting/sf2/profiles/keymap.sp +++ b/addons/sourcemod/scripting/sf2/profiles/keymap.sp @@ -324,7 +324,7 @@ methodmap KeyMap < StringMap return newValue; } - public void RemoveKey(const char[] key) + public void RemoveKey(const char[] key, bool del = true) { int index = -1, currentIndex = -1, size; if (this.GetType(key) == Key_Type_Section) @@ -346,7 +346,11 @@ methodmap KeyMap < StringMap currentIndex++; } - CleanupKeyMap(section); + if (del) + { + CleanupKeyMap(section); + } + char formatter4[128]; FormatEx(formatter4, sizeof(formatter4), "%i||__keyindex", currentIndex - 1); this.Super.Remove(formatter4); @@ -407,7 +411,7 @@ methodmap KeyMap < StringMap this.Super.SetString(newKey, value); index = this.GetIndexFromKey(key); } - this.RemoveKey(key); + this.RemoveKey(key, false); this.SetKeyIndex(newKey, index); this.SetIndexKey(newKey, index); @@ -422,12 +426,6 @@ methodmap KeyMap < StringMap public KeyMap Clone() { KeyMap clone = CloneKeyMap(this); - clone.Parent = null; - KeyMap section = clone.GetSection("execution"); - if (section != null) - { - section.GetSection("paths"); - } return clone; } diff --git a/addons/sourcemod/scripting/sf2/profiles/objects.sp b/addons/sourcemod/scripting/sf2/profiles/objects.sp index 07d8a873..8f18ba93 100644 --- a/addons/sourcemod/scripting/sf2/profiles/objects.sp +++ b/addons/sourcemod/scripting/sf2/profiles/objects.sp @@ -377,6 +377,57 @@ methodmap ProfileObject < KeyMap return value; } + public KeyMap_Array GetDifficultyArray(const char[] key, int difficulty, KeyMap_Array def = view_as(INVALID_HANDLE)) + { + if (this == null) + { + return def; + } + KeyMap_Array value = def; + + if (this.ContainsKey(key)) + { + value = this.GetArray(key, def); + } + + int diffKeySize = strlen(key) + GetMaxProfileDifficultySuffixSize(); + char[] diffKey = new char[diffKeySize]; + GetProfileKeyWithDifficultySuffix(key, difficulty, diffKey, diffKeySize); + if (this.ContainsKey(diffKey)) + { + value = this.GetArray(diffKey, def); + } + else + { + if (difficulty > 0) + { + for (int i2 = difficulty; i2 >= 0; i2--) + { + GetProfileKeyWithDifficultySuffix(key, i2, diffKey, diffKeySize); + if (this.ContainsKey(diffKey)) + { + value = this.GetArray(diffKey, def); + return value; + } + } + } + else + { + for (int i2 = 0; i2 < difficulty; i2++) + { + GetProfileKeyWithDifficultySuffix(key, i2, diffKey, diffKeySize); + if (this.ContainsKey(diffKey)) + { + value = this.GetArray(diffKey, def); + return value; + } + } + } + } + + return value; + } + public ProfileObject GetDifficultySection(const char[] key, int difficulty, ProfileObject def = null) { if (this == null) @@ -441,6 +492,19 @@ methodmap ProfileObject < KeyMap this.SetSection(diffKey, value); } + public void SetDifficultyKeyValue(const char[] key, int difficulty, char[] value) + { + int diffKeySize = strlen(key) + GetMaxProfileDifficultySuffixSize(); + char[] diffKey = new char[diffKeySize]; + GetProfileKeyWithDifficultySuffix(key, difficulty, diffKey, diffKeySize); + this.SetType(diffKey, Key_Type_Value); + this.Super.Super.SetString(diffKey, value); + this.SetKeyIndex(diffKey, this.KeyLength); + this.SetIndexKey(diffKey, this.KeyLength); + this.KeyLength++; + this.SetKeyValueLength(diffKey, strlen(value) + 1); + } + public int GetDifficultyInt(const char[] key, int difficulty, int def = 0) { if (this == null) @@ -823,6 +887,15 @@ methodmap ProfileObject < KeyMap return newObj; } + public ProfileObject InsertDifficultySection(const char[] name, int difficulty) + { + int diffKeySize = strlen(name) + GetMaxProfileDifficultySuffixSize(); + char[] diffKey = new char[diffKeySize]; + GetProfileKeyWithDifficultySuffix(name, difficulty, diffKey, diffKeySize); + + return this.InsertNewSection(diffKey); + } + public void AddExistingSection(ProfileObject newObj) { if (newObj == null) @@ -846,10 +919,17 @@ methodmap ProfileObject < KeyMap { return; } - this.SetKeyValue(newKey, keyValue); + if (this.ContainsKey(newKey)) + { + this.Super.Super.SetString(newKey, keyValue); + } + else + { + this.SetKeyValue(newKey, keyValue); + } } - public void TransferDifficultyKey(ProfileObject target, const char[] oldKey, const char[] newKey, int difficulty) + public void TransferDifficultyKey(ProfileObject origin, const char[] oldKey, const char[] newKey, int difficulty) { int oldDiffKeySize = strlen(oldKey) + GetMaxProfileDifficultySuffixSize(); char[] oldDiffKey = new char[oldDiffKeySize]; @@ -860,11 +940,40 @@ methodmap ProfileObject < KeyMap GetProfileKeyWithDifficultySuffix(newKey, difficulty, newDiffKey, newDiffKeySize); char keyValue[2048]; - if (!this.Super.GetString(oldDiffKey, keyValue, sizeof(keyValue))) + if (!origin.Super.Super.GetString(oldDiffKey, keyValue, sizeof(keyValue))) { return; } - target.SetKeyValue(newDiffKey, keyValue); + if (this.ContainsKey(newDiffKey)) + { + this.Super.Super.SetString(newDiffKey, keyValue); + } + else + { + this.SetKeyValue(newDiffKey, keyValue); + } + } + + public void ConvertDifficultyValuesSectionToArray(const char[] key) + { + for (int i = 0; i < Difficulty_Max; i++) + { + int diffKeySize = strlen(key) + GetMaxProfileDifficultySuffixSize(); + char[] diffKey = new char[diffKeySize]; + GetProfileKeyWithDifficultySuffix(key, i, diffKey, diffKeySize); + this.ConvertValuesSectionToArray(diffKey); + } + } + + public void ConvertDifficultySectionsSectionToArray(const char[] key) + { + for (int i = 0; i < Difficulty_Max; i++) + { + int diffKeySize = strlen(key) + GetMaxProfileDifficultySuffixSize(); + char[] diffKey = new char[diffKeySize]; + GetProfileKeyWithDifficultySuffix(key, i, diffKey, diffKeySize); + this.ConvertSectionsSectionToArray(diffKey); + } } } @@ -984,7 +1093,7 @@ methodmap ProfileSound < ProfileObject break; } - TryPrecacheBossProfileSoundPath(path, g_FileCheckConVar.BoolValue); + PrecacheSound2(path, g_FileCheckConVar.BoolValue); paths.PushString(path); obj.RemoveKey(num); } @@ -1346,6 +1455,15 @@ methodmap ProfileAnimation < ProfileObject // This covers each animation index i return this.GetDifficultyBool("ground_sync", difficulty); } + public float GetGroundSyncSpeed(int difficulty, bool legacy = false) + { + if (legacy) + { + return 0.0; + } + return this.GetDifficultyFloat("ground_sync_speed", difficulty, -1.0); + } + public bool PlayAnimation(CBaseAnimating actor, int difficulty, bool loops = false) { int sequence; @@ -1425,8 +1543,6 @@ methodmap ProfileMasterAnimations < ProfileObject // This covers the whole "anim strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_DeathCam]) == 0 || strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_Death]) == 0 || strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_TauntKill]) == 0 || - strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_AttackBegin]) == 0 || - strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_AttackEnd]) == 0 || strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_ProjectileShoot]) == 0 || strcmp(animType, g_SlenderAnimationsList[SF2BossAnimation_Despawn]) == 0) { @@ -1506,8 +1622,13 @@ methodmap ProfileMasterAnimations < ProfileObject // This covers the whole "anim } } -methodmap ProfileEntityInputObject < ProfileObject +methodmap ProfileInput < ProfileObject { + public void GetTarget(char[] buffer, int bufferSize) + { + this.GetString("target", buffer, bufferSize); + } + public void GetInput(char[] buffer, int bufferSize) { this.GetString("input", buffer, bufferSize); @@ -1518,58 +1639,78 @@ methodmap ProfileEntityInputObject < ProfileObject this.GetString("parameter", buffer, bufferSize); } - public int GetParameterSize() + public float GetDelay() { - return this.GetKeyValueLength("parameter"); + return this.GetFloat("delay"); } - public bool AcceptInput(int entity, int activator = -1, int caller = -1) + public void AcceptInput(CBaseEntity entity, CBaseEntity activator = view_as(-1), CBaseEntity caller = view_as(-1)) { - char input[64]; - this.GetInput(input, sizeof(input)); - - bool result = false; + char targetName[128], input[64], parameter[512]; + this.GetTarget(targetName, sizeof(targetName)); + CBaseEntity target = entity; + if (targetName[0] != '\0') + { + int ent = -1; + char name[128]; + while ((ent = FindEntityByClassname(ent, "*")) != -1) + { + if (!IsValidEntity(ent)) + { + continue; + } - int size = this.GetParameterSize(); - char[] parameter = new char[size]; - this.GetParameter(parameter, size); + GetEntPropString(ent, Prop_Data, "m_iName", name, sizeof(name)); + if (strcmp(name, targetName, false) == 0) + { + target = CBaseEntity(ent); + break; + } + } + } + this.GetInput(input, sizeof(input)); + this.GetParameter(parameter, sizeof(parameter)); + if (this.GetDelay() > 0.0) + { + DataPack pack; + CreateDataTimer(this.GetDelay(), Timer_DelayInput, pack, TIMER_FLAG_NO_MAPCHANGE); + pack.WriteCell(EnsureEntRef(target.index)); + pack.WriteCell(EnsureEntRef(activator.index)); + pack.WriteCell(EnsureEntRef(caller.index)); + pack.WriteString(input); + pack.WriteString(parameter); + return; + } SetVariantString(parameter); - result = AcceptEntityInput(entity, input, activator, caller); - - return result; + target.AcceptInput(input, activator.index, caller.index); } } -methodmap ProfileEntityInputsArray < ProfileObject +methodmap ProfileInputsList < ProfileObject { - public void AcceptInputs(int entity, int activator = -1, int caller = -1) + public void AcceptInputs(CBaseEntity entity, CBaseEntity activator = view_as(-1), CBaseEntity caller = view_as(-1)) { for (int i = 0; i < this.SectionLength; i++) { char section[64]; this.GetSectionNameFromIndex(i, section, sizeof(section)); - ProfileEntityInputObject output = view_as(this.GetSection(section)); - if (output == null) + ProfileInput input = view_as(this.GetSection(section)); + if (input == null) { continue; } - output.AcceptInput(entity, activator, caller); + input.AcceptInput(entity, activator, caller); } } } -methodmap ProfileEntityOutputObject < ProfileObject +methodmap ProfileOutput < ProfileObject { public void GetTarget(char[] buffer, int bufferSize) { - this.GetString("target", buffer, bufferSize); - } - - public int GetTargetSize() - { - return this.GetKeyValueLength("target"); + this.GetString("target", buffer, bufferSize, "!self"); } public void GetInput(char[] buffer, int bufferSize) @@ -1577,21 +1718,11 @@ methodmap ProfileEntityOutputObject < ProfileObject this.GetString("input", buffer, bufferSize); } - public int GetInputSize() - { - return this.GetKeyValueLength("input"); - } - public void GetParameter(char[] buffer, int bufferSize) { this.GetString("parameter", buffer, bufferSize); } - public int GetParameterSize() - { - return this.GetKeyValueLength("parameter"); - } - public float GetDelay() { return this.GetFloat("delay"); @@ -1602,34 +1733,21 @@ methodmap ProfileEntityOutputObject < ProfileObject return this.GetInt("times_to_fire", -1); } - public void AddOutput(int entity, const char[] output) + public void AddOutput(CBaseEntity entity, const char[] output) { - int size = this.GetTargetSize(); - char[] target = new char[size]; - this.GetTarget(target, size); - - size = this.GetInputSize(); - char[] input = new char[size]; - this.GetInput(input, size); - - size = this.GetParameterSize(); - char[] parameter = new char[size]; - this.GetParameter(parameter, size); - - size = 98 + strlen(output) + strlen(target) + strlen(input) + strlen(parameter); - char[] addOutput = new char[size]; - FormatEx(addOutput, size, "EntityOutputs.AddOutput(self, `%s`, `%s`, `%s`, `%s`, %0.3f, %d)", output, target, input, parameter, this.GetDelay(), this.GetTimesToFire()); - - PrintToChatAll("%s", addOutput); - - SetVariantString(addOutput); - AcceptEntityInput(entity, "RunScriptCode"); + char target[128], input[64], parameter[512], formatter[2048]; + this.GetTarget(target, sizeof(target)); + this.GetInput(input, sizeof(input)); + this.GetParameter(parameter, sizeof(parameter)); + FormatEx(formatter, sizeof(formatter), "EntityOutputs.AddOutput(self, `%s`, `%s`, `%s`, `%s`, %f, %d)", output, target, input, parameter, this.GetDelay(), this.GetTimesToFire()); + SetVariantString(formatter); + entity.AcceptInput("RunScriptCode"); } } -methodmap ProfileEntityOutputsArray < ProfileObject +methodmap ProfileOutputsList < ProfileObject { - public void AddOutputs(int entity) + public void AddOutputs(CBaseEntity entity) { for (int i = 0; i < this.SectionLength; i++) { @@ -1645,7 +1763,7 @@ methodmap ProfileEntityOutputsArray < ProfileObject { char subSection[64]; obj.GetSectionNameFromIndex(i2, subSection, sizeof(subSection)); - ProfileEntityOutputObject output = view_as(obj.GetSection(subSection)); + ProfileOutput output = view_as(obj.GetSection(subSection)); if (output == null) { continue; @@ -1657,6 +1775,33 @@ methodmap ProfileEntityOutputsArray < ProfileObject } } +static Action Timer_DelayInput(Handle timer, DataPack pack) +{ + pack.Reset(); + int entity = EntRefToEntIndex(pack.ReadCell()); + if (entity == INVALID_ENT_REFERENCE || !entity) + { + return Plugin_Stop; + } + + int activator = EntRefToEntIndex(pack.ReadCell()); + if (activator == INVALID_ENT_REFERENCE || !activator) + { + activator = -1; + } + int caller = EntRefToEntIndex(pack.ReadCell()); + if (caller == INVALID_ENT_REFERENCE || !caller) + { + caller = -1; + } + char input[64], parameter[512]; + pack.ReadString(input, sizeof(input)); + pack.ReadString(parameter, sizeof(parameter)); + SetVariantString(parameter); + CBaseEntity(entity).AcceptInput(input, activator, caller); + return Plugin_Stop; +} + int ColorToString(int buffer[4], char[] string, int maxlength) { char value[32], num[4]; @@ -1720,7 +1865,7 @@ void StringToColor(const char[] string, int buffer[4]) bool StringToBool(const char[] string) { - return strcmp(string, "1", false) == 0; + return strcmp(string, "1", false) == 0 || strcmp(string, "true", false) == 0; } int BoolToString(bool value, char[] buffer, int bufferSize) @@ -1728,25 +1873,14 @@ int BoolToString(bool value, char[] buffer, int bufferSize) return FormatEx(buffer, bufferSize, "%b", value); } -void RunScriptCode(int entity, int activator, int caller, const char[] format, any ...) -{ - if (!IsValidEntity(entity)) - { - return; - } - - static char buffer[1024]; - VFormat(buffer, sizeof(buffer), format, 5); - - SetVariantString(buffer); - AcceptEntityInput(entity, "RunScriptCode", activator, caller); -} - void SetupProfileObjectNatives() { CreateNative("SF2_ProfileObject.Parent.get", Native_GetObjectParent); CreateNative("SF2_ProfileObject.KeyLength.get", Native_GetObjectKeyLength); CreateNative("SF2_ProfileObject.SectionLength.get", Native_GetObjectSectionLength); + CreateNative("SF2_ProfileObject.GetSectionName", Native_GetObjectSectionName); + CreateNative("SF2_ProfileObject.GetKeyNameFromIndex", Native_GetObjectKeyNameFromIndex); + CreateNative("SF2_ProfileObject.GetSectionNameFromIndex", Native_GetObjectSectionNameFromIndex); CreateNative("SF2_ProfileObject.GetInt", Native_GetObjectInt); CreateNative("SF2_ProfileObject.SetInt", Native_SetObjectInt); CreateNative("SF2_ProfileObject.GetBool", Native_GetObjectBool); @@ -1849,6 +1983,57 @@ static any Native_GetObjectSectionLength(Handle plugin, int numParams) return obj.SectionLength; } +static any Native_GetObjectSectionName(Handle plugin, int numParams) +{ + ProfileObject obj = view_as(GetNativeCell(1)); + if (obj == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", obj); + } + + int bufferSize = GetNativeCell(3); + char[] buffer = new char[bufferSize]; + + obj.GetSectionName(buffer, bufferSize); + + SetNativeString(2, buffer, bufferSize); + return 0; +} + +static any Native_GetObjectKeyNameFromIndex(Handle plugin, int numParams) +{ + ProfileObject obj = view_as(GetNativeCell(1)); + if (obj == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", obj); + } + + int bufferSize = GetNativeCell(4); + char[] buffer = new char[bufferSize]; + + bool state = obj.GetKeyNameFromIndex(GetNativeCell(2), buffer, bufferSize); + + SetNativeString(3, buffer, bufferSize); + return state; +} + +static any Native_GetObjectSectionNameFromIndex(Handle plugin, int numParams) +{ + ProfileObject obj = view_as(GetNativeCell(1)); + if (obj == null) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid profile object handle %x", obj); + } + + int bufferSize = GetNativeCell(4); + char[] buffer = new char[bufferSize]; + + bool state = obj.GetSectionNameFromIndex(GetNativeCell(2), buffer, bufferSize); + + SetNativeString(3, buffer, bufferSize); + return state; +} + static any Native_GetObjectInt(Handle plugin, int numParams) { ProfileObject obj = view_as(GetNativeCell(1)); diff --git a/addons/sourcemod/scripting/sf2/profiles/profile_chaser.sp b/addons/sourcemod/scripting/sf2/profiles/profile_chaser.sp index 9dfcb168..cf8de2b1 100644 --- a/addons/sourcemod/scripting/sf2/profiles/profile_chaser.sp +++ b/addons/sourcemod/scripting/sf2/profiles/profile_chaser.sp @@ -5,117 +5,3749 @@ #define _sf2_profiles_chaser #pragma semicolon 1 +#pragma newdecls required -StringMap g_ChaserBossProfileData; +methodmap ChaserBossProfile < BaseBossProfile +{ + property bool ClearLayersOnAnimUpdate + { + public get() + { + return this.GetBool("animation_clear_layers_on_update", false); + } + } + + property bool BoxingBoss + { + public get() + { + return this.GetBool("boxing_boss", false); + } + } + + property bool ChasesEndlessly + { + public get() + { + return this.GetBool("boss_chases_endlessly", false); + } + } + + property bool NormalSoundHook + { + public get() + { + return this.GetBool("normal_sound_hook", false); + } + } + + property bool OldAnimationAI + { + public get() + { + return this.GetBool("old_animation_ai", false); + } + } + + public float GetWalkSpeed(int difficulty) + { + return this.GetDifficultyFloat("walkspeed", difficulty, 90.0); + } + + public float GetWakeRadius(int difficulty) + { + return this.GetDifficultyFloat("wake_radius", difficulty, 90.0); + } + + public float GetHearingRange(int difficulty) + { + float value = 1024.0; + value = this.GetDifficultyFloat("hearing_range", difficulty, value); + value = this.GetDifficultyFloat("search_sound_range", difficulty, value); + return value; + } + + public float GetTauntAlertRange(int difficulty) + { + return this.GetDifficultyFloat("taunt_alert_range", difficulty, 512.0); + } + + public bool ShouldIgnoreHearingPathChecking(int difficulty) + { + return this.GetDifficultyBool("hearing_ignore_path_checking", difficulty, false); + } + + public bool CanWander(int difficulty) + { + return this.GetDifficultyBool("wander_move", difficulty, true); + } + + public float GetWanderMinRange(int difficulty) + { + return this.GetDifficultyFloat("wander_range_min", difficulty, 800.0); + } + + public float GetWanderMaxRange(int difficulty) + { + return this.GetDifficultyFloat("wander_range_max", difficulty, 1600.0); + } + + public float GetWanderMinTime(int difficulty) + { + return this.GetDifficultyFloat("wander_time_min", difficulty, 8.0); + } + + public float GetWanderMaxTime(int difficulty) + { + return this.GetDifficultyFloat("wander_time_max", difficulty, 12.0); + } + + public float GetWanderEnterMinTime(int difficulty) + { + return this.GetDifficultyFloat("wander_enter_time_min", difficulty, 2.0); + } + + public float GetWanderEnterMaxTime(int difficulty) + { + return this.GetDifficultyFloat("wander_enter_time_max", difficulty, 4.5); + } + + property float BackstabDamageScale + { + public get() + { + return this.GetFloat("backstab_damage_scale", 0.025); + } + } + + public ChaserBossProfileIdleData GetIdleBehavior() + { + return view_as(this.GetSection("idle")); + } + + public ChaserBossProfileAlertData GetAlertBehavior() + { + return view_as(this.GetSection("alert")); + } + + public ChaserBossProfileChaseData GetChaseBehavior() + { + return view_as(this.GetSection("chase")); + } + + public ChaserBossProfileStunData GetStunBehavior() + { + return view_as(this.GetSection("stun")); + } + + property bool Healthbar + { + public get() + { + return this.GetBool("healthbar", false); + } + } + + public ChaserBossProfileDeathData GetDeathBehavior() + { + return view_as(this.GetSection("death")); + } + + public int GetAttackCount() + { + KeyMap_Array arr = this.GetArray("__chaser_attack_names"); + if (arr == null) + { + return -1; + } + + return arr.Length; + } + + public void GetAttackName(int index, char[] buffer, int bufferSize) + { + KeyMap_Array arr = this.GetArray("__chaser_attack_names"); + if (arr == null) + { + return; + } + + arr.GetString(index, buffer, bufferSize); + } + + public int GetAttackIndex(const char[] attackName) + { + KeyMap_Array arr = this.GetArray("__chaser_attack_names"); + if (arr == null) + { + return -1; + } + + return arr.IndexOf(attackName); + } + + public ChaserBossProfileBaseAttack GetAttack(const char[] name) + { + ProfileObject obj = this.GetSection("attacks"); + if (obj == null) + { + return null; + } + + return view_as(obj.GetSection(name)); + } + + public ChaserBossProfileBaseAttack GetAttackFromIndex(int index) + { + char name[256]; + this.GetAttackName(index, name, sizeof(name)); + return this.GetAttack(name); + } + + public ProfileObject GetPosture(const char[] name) + { + ProfileObject obj = this.GetSection("postures"); + if (obj == null || strcmp(name, SF2_PROFILE_CHASER_DEFAULT_POSTURE) == 0) + { + return null; + } + + return obj.GetSection(name); + } + + public ProfileObject GetPostureFromIndex(int index) + { + ProfileObject obj = this.GetSection("postures"); + if (obj == null) + { + return null; + } + + char name[64]; + obj.GetSectionNameFromIndex(index, name, sizeof(name)); + if (strcmp(name, SF2_PROFILE_CHASER_DEFAULT_POSTURE) == 0) + { + return null; + } + + return obj.GetSection(name); + } + + public float GetPostureRunSpeed(const char[] name, int difficulty) + { + float value = this.GetRunSpeed(difficulty); + + ProfileObject obj = this.GetPosture(name); + if (obj != null) + { + value = obj.GetDifficultyFloat("speed", difficulty, value); + } + + return value; + } + + public float GetPostureWalkSpeed(const char[] name, int difficulty) + { + float value = this.GetWalkSpeed(difficulty); + + ProfileObject obj = this.GetPosture(name); + if (obj != null) + { + value = obj.GetDifficultyFloat("walkspeed", difficulty, value); + } + + return value; + } + + public float GetPostureAcceleration(const char[] name, int difficulty) + { + float value = this.GetAcceleration(difficulty); + + ProfileObject obj = this.GetPosture(name); + if (obj != null) + { + value = obj.GetDifficultyFloat("acceleration", difficulty, value); + } + + return value; + } + + public ProfileMasterAnimations GetPostureAnimations(const char[] name) + { + ProfileObject obj = this.GetPosture(name); + obj = obj != null ? obj.GetSection("animations") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ChaserBossProfileSmellData GetSmellData() + { + ProfileObject obj = this.GetSection("senses"); + obj = obj != null ? obj.GetSection("smell") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ChaserBossSoundSenseData GetSoundSenseData() + { + ProfileObject obj = this.GetSection("senses"); + obj = obj != null ? obj.GetSection("hearing") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ChaserBossVisionSenseData GetVisionSenseData() + { + ProfileObject obj = this.GetSection("senses"); + obj = obj != null ? obj.GetSection("vision") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileMusic GetIdleMusics() + { + return view_as(this.GetSection("sound_idle_music")); + } + + public ProfileMusic GetAlertMusics() + { + return view_as(this.GetSection("sound_alert_music")); + } + + public ProfileMusic GetChaseMusics() + { + return view_as(this.GetSection("sound_chase_music")); + } + + public ProfileMusic GetVisibleIdleMusics() + { + return view_as(this.GetSection("sound_idle_visible")); + } + + public ProfileMusic GetVisibleAlertMusics() + { + return view_as(this.GetSection("sound_alert_visible")); + } + + public ProfileMusic GetVisibleChaseMusics() + { + return view_as(this.GetSection("sound_chase_visible")); + } + + public BossProfileTrapData GetTrapData() + { + return view_as(this.GetSection("traps")); + } + + public bool HasTraps(int difficulty) + { + return this.GetDifficultyBool("traps_enabled", difficulty, false); + } + + public int GetTrapType(int difficulty) + { + return this.GetDifficultyInt("trap_type", difficulty, 0); + } + + public float GetTrapCooldown(int difficulty) + { + return this.GetDifficultyFloat("trap_spawn_cooldown", difficulty, 8.0); + } + + public void GetTrapModel(char[] buffer, int bufferSize) + { + this.GetString("trap_model", buffer, bufferSize, TRAP_MODEL); + } + + public void GetTrapDeploySound(char[] buffer, int bufferSize) + { + this.GetString("trap_deploy_sound", buffer, bufferSize, TRAP_DEPLOY); + } + + public void GetTrapMissSound(char[] buffer, int bufferSize) + { + this.GetString("trap_miss_sound", buffer, bufferSize, TRAP_CLOSE); + } + + public void GetTrapCatchSound(char[] buffer, int bufferSize) + { + this.GetString("trap_catch_sound", buffer, bufferSize, TRAP_CLOSE); + } + + public void GetTrapIdleAnimation(char[] buffer, int bufferSize) + { + this.GetString("trap_animation_idle", buffer, bufferSize, "trapopenend"); + } + + public void GetTrapClosedAnimation(char[] buffer, int bufferSize) + { + this.GetString("trap_animation_closed", buffer, bufferSize, "trapclosed"); + } + + public void GetTrapOpenAnimation(char[] buffer, int bufferSize) + { + this.GetString("trap_animation_open", buffer, bufferSize); + } + + property bool EarthquakeFootsteps + { + public get() + { + return this.GetBool("earthquake_footsteps", false); + } + } + + property float EarthquakeFootstepRadius + { + public get() + { + return this.GetFloat("earthquake_footsteps_radius", 1000.0); + } + } + + property float EarthquakeFootstepDuration + { + public get() + { + return this.GetFloat("earthquake_footsteps_duration", 1.0); + } + } + + property bool EarthquakeFootstepAirShake + { + public get() + { + return this.GetBool("earthquake_footsteps_airshake", false); + } + } + + property float EarthquakeFootstepAmplitude + { + public get() + { + return this.GetFloat("earthquake_footsteps_amplitude", 5.0); + } + } + + property float EarthquakeFootstepFrequency + { + public get() + { + return this.GetFloat("earthquake_footsteps_frequency", 25.0); + } + } + + public ProfileObject GetRages() + { + return this.GetSection("rages"); + } + + public ChaserBossAutoChaseData GetAutoChaseData() + { + return view_as(this.GetSection("autochase")); + } + + public ChaserBossChaseOnLookData GetChaseOnLookData() + { + return view_as(this.GetSection("chase_on_look")); + } + + public ChaserBossProfileCloakData GetCloakData() + { + return view_as(this.GetSection("cloaking")); + } + + public ProfileObject GetResistances() + { + return this.GetSection("resistances"); + } + + public ChaserBossProjectileData GetProjectiles() + { + return view_as(this); + } + + public ProfileSound GetIdleSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_Idle])); + } + + public ProfileSound GetAlertSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_Alert])); + } + + public ProfileSound GetChasingSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_Chasing])); + } + + public ProfileSound GetChaseInitialSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_ChaseInitial])); + } + + public ProfileSound GetStunSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_Stun])); + } + + public ProfileSound GetAttackKilledSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_AttackKilled])); + } + + public ProfileSound GetAttackKilledAllSounds() + { + return view_as(this.GetSection("sound_attack_killed_all")); + } + + public ProfileSound GetAttackKilledClientSounds() + { + return view_as(this.GetSection("sound_attack_killed_client")); + } + + public ProfileSound GetHurtSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_Hurt])); + } + + public ProfileSound GetJumpSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_Jump])); + } + + public ProfileSound GetDeathSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_Death])); + } + + public ProfileSound GetTauntKillSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_TauntKill])); + } + + public ProfileSound GetSmellSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_Smell])); + } + + public ProfileSound GetSelfHealSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_SelfHeal])); + } + + public ProfileSound GetRageAllSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_RageAll])); + } + + public ProfileSound GetRageTwoSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_RageTwo])); + } + + public ProfileSound GetRageThreeSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_RageThree])); + } + + public ProfileSound GetDespawnSounds() + { + return view_as(this.GetSection(g_SlenderVoiceList[SF2BossSound_Despawn])); + } + + public ProfileSound GetFootstepSounds() + { + return view_as(this.GetSection("sound_footsteps")); + } + + public ProfileObject GetAttackSounds() + { + return this.GetSection(g_SlenderVoiceList[SF2BossSound_Attack]); + } + + public ProfileObject GetAttackBeginSounds() + { + return this.GetSection(g_SlenderVoiceList[SF2BossSound_AttackBegin]); + } + + public ProfileObject GetAttackLoopSounds() + { + return this.GetSection("sound_attack_loop"); + } + + public ProfileObject GetAttackEndSounds() + { + return this.GetSection(g_SlenderVoiceList[SF2BossSound_AttackEnd]); + } + + public ProfileObject GetHitSounds() + { + return this.GetSection("sound_hitenemy"); + } + + public ProfileObject GetMissSounds() + { + return this.GetSection("sound_missenemy"); + } + + public ProfileObject GetBulletShootSounds() + { + return this.GetSection("sound_bulletshoot"); + } + + public ProfileObject GetProjectileShootSounds() + { + return this.GetSection("sound_attackshootprojectile"); + } + + public void Precache() + { + char path[PLATFORM_MAX_PATH]; + + ProfileObject obj = this.GetSection("attacks"); + if (obj != null) + { + KeyMap_Array attackNames = new KeyMap_Array(Key_Type_Value); + this.SetSection("__chaser_attack_names", attackNames); + this.SetType("__chaser_attack_names", Key_Type_Section); + attackNames.Parent = this; + + for (int i = 0; i < obj.SectionLength; i++) + { + char name[64]; + obj.GetSectionNameFromIndex(i, name, sizeof(name)); + + ChaserBossProfileBaseAttack attack = view_as(obj.GetSection(name)); + if (attack != null) + { + attackNames.PushString(name); + } + } + } + + // ======================== + // LEGACY KEY CONVERSION + // ======================== + ProfileObject newObj = this.GetIdleBehavior(); + ArrayList keys = new ArrayList(ByteCountToCells(256)); + newObj = this.InsertNewSection("idle"); + + keys.PushString("alert_gracetime"); + keys.PushString("alert_duration"); + keys.PushString("search_alert_gracetime"); + keys.PushString("search_alert_duration"); + if (this.ContainsAnyDifficultyKey(keys)) + { + newObj = this.InsertNewSection("alert"); + for (int i = 0; i < Difficulty_Max; i++) + { + float def = 0.5; + if (this.ContainsDifficultyKey("alert_gracetime", i) || this.ContainsDifficultyKey("search_alert_gracetime", i)) + { + def = this.GetDifficultyFloat("alert_gracetime", i, def); + def = this.GetDifficultyFloat("search_alert_gracetime", i, def); + newObj.SetDifficultyFloat("gracetime", i, def); + } + + def = 10.0; + if (this.ContainsDifficultyKey("alert_duration", i) || this.ContainsDifficultyKey("search_alert_duration", i)) + { + def = this.GetDifficultyFloat("alert_duration", i, def); + def = this.GetDifficultyFloat("search_alert_duration", i, def); + newObj.SetDifficultyFloat("duration", i, def); + } + } + } + + keys.Clear(); + ProfileObject temp = null, temp2 = null, temp3 = null; + keys.PushString("chase_duration"); + keys.PushString("search_chase_duration"); + keys.PushString("chase_duration_add_max_range"); + keys.PushString("chase_duration_add_visible_min"); + keys.PushString("chase_duration_add_visible_max"); + if (this.ContainsAnyDifficultyKey(keys)) + { + newObj = this.InsertNewSection("chase"); + temp = newObj.InsertNewSection("duration"); + temp2 = temp.InsertNewSection("add"); + for (int i = 0; i < Difficulty_Max; i++) + { + float def = 10.0; + if (this.ContainsDifficultyKey("chase_duration", i) || this.ContainsDifficultyKey("search_chase_duration", i)) + { + def = this.GetDifficultyFloat("chase_duration", i, def); + def = this.GetDifficultyFloat("search_chase_duration", i, def); + temp.SetDifficultyFloat("max", i, def); + } + + if (this.ContainsDifficultyKey("chase_duration_add_max_range", i)) + { + temp2.SetDifficultyFloat("target_range", i, this.GetDifficultyFloat("chase_duration_add_max_range", i, this.GetSearchRange(i))); + } + + if (this.ContainsDifficultyKey("chase_duration_add_visible_min", i)) + { + temp2.SetDifficultyFloat("visible_target_near", i, this.GetDifficultyFloat("chase_duration_add_visible_min", i, 0.01)); + } + + if (this.ContainsDifficultyKey("chase_duration_add_visible_max", i)) + { + temp2.SetDifficultyFloat("visible_target_far", i, this.GetDifficultyFloat("chase_duration_add_visible_max", i, 0.05)); + } + } + } + + keys.Clear(); + keys.PushString("stun_enabled"); + keys.PushString("stun_health"); + keys.PushString("stun_cooldown"); + keys.PushString("disappear_on_stun"); + keys.PushString("stun_damage_flashlight_enabled"); + keys.PushString("stun_damage_flashlight"); + keys.PushString("chase_initial_on_stun"); + keys.PushString("drop_item_on_stun"); + keys.PushString("drop_item_type"); + if (this.ContainsAnyDifficultyKey(keys)) + { + newObj = this.InsertNewSection("stun"); + bool defBool = false; + char defString[PLATFORM_MAX_PATH]; + for (int i = 0; i < Difficulty_Max; i++) + { + if (this.ContainsDifficultyKey("stun_enabled", i)) + { + newObj.SetDifficultyBool("enabled", i, this.GetDifficultyBool("stun_enabled", i, false)); + } + + if (this.ContainsDifficultyKey("stun_health", i)) + { + newObj.SetDifficultyFloat("health", i, this.GetDifficultyFloat("stun_health", i, 50.0)); + } + + if (this.ContainsDifficultyKey("stun_cooldown", i)) + { + newObj.SetDifficultyFloat("cooldown", i, this.GetDifficultyFloat("stun_cooldown", i, 3.5)); + } + + if (this.ContainsDifficultyKey("disappear_on_stun", i)) + { + newObj.SetDifficultyBool("disappear", i, this.GetDifficultyBool("disappear_on_stun", i, false)); + } + + defBool = this.GetDifficultyBool("stun_damage_flashlight_enabled", i, false); + if (defBool) + { + temp = newObj.InsertNewSection("flashlight_stun"); + temp.SetDifficultyBool("enabled", i, defBool); + + if (this.ContainsDifficultyKey("stun_damage_flashlight", i)) + { + temp.SetDifficultyFloat("damage", i, this.GetDifficultyFloat("stun_damage_flashlight", i, 0.0)); + } + } + + if (this.ContainsDifficultyKey("chase_initial_on_stun", i)) + { + newObj.SetDifficultyBool("chase_initial_on_end", i, this.GetDifficultyBool("chase_initial_on_stun", i, false)); + } + + if (this.ContainsDifficultyKey("drop_item_on_stun", i)) + { + newObj.SetDifficultyBool("item_drop", i, this.GetDifficultyBool("drop_item_on_stun", i, false)); + } + + if (this.ContainsDifficultyKey("drop_item_type", i)) + { + newObj.SetDifficultyInt("item_drop_type", i, this.GetDifficultyInt("drop_item_type", i, 1)); + } + + keys.Clear(); + keys.PushString("stun_health_per_player"); + keys.PushString("stun_health_per_scout"); + keys.PushString("stun_health_per_soldier"); + keys.PushString("stun_health_per_pyro"); + keys.PushString("stun_health_per_demoman"); + keys.PushString("stun_health_per_heavyweapons"); + keys.PushString("stun_health_per_engineer"); + keys.PushString("stun_health_per_medic"); + keys.PushString("stun_health_per_sniper"); + keys.PushString("stun_health_per_spy"); + if (this.ContainsAnyDifficultyKey(keys, i)) + { + temp = newObj.InsertNewSection("add_health"); + if (this.ContainsDifficultyKey("stun_health_per_player", i)) + { + temp.SetDifficultyFloat("player", i, this.GetDifficultyFloat("stun_health_per_player", i, 0.0)); + } + + if (this.ContainsDifficultyKey("stun_health_per_scout", i)) + { + temp.SetDifficultyFloat("scout", i, this.GetDifficultyFloat("stun_health_per_scout", i, 0.0)); + } + + if (this.ContainsDifficultyKey("stun_health_per_soldier", i)) + { + temp.SetDifficultyFloat("soldier", i, this.GetDifficultyFloat("stun_health_per_soldier", i, 0.0)); + } + + if (this.ContainsDifficultyKey("stun_health_per_pyro", i)) + { + temp.SetDifficultyFloat("pyro", i, this.GetDifficultyFloat("stun_health_per_pyro", i, 0.0)); + } + + if (this.ContainsDifficultyKey("stun_health_per_demoman", i)) + { + temp.SetDifficultyFloat("demoman", i, this.GetDifficultyFloat("stun_health_per_demoman", i, 0.0)); + } + + if (this.ContainsDifficultyKey("stun_health_per_heavyweapons", i)) + { + temp.SetDifficultyFloat("heavy", i, this.GetDifficultyFloat("stun_health_per_heavyweapons", i, 0.0)); + } + + if (this.ContainsDifficultyKey("stun_health_per_engineer", i)) + { + temp.SetDifficultyFloat("engineer", i, this.GetDifficultyFloat("stun_health_per_engineer", i, 0.0)); + } + + if (this.ContainsDifficultyKey("stun_health_per_medic", i)) + { + temp.SetDifficultyFloat("medic", i, this.GetDifficultyFloat("stun_health_per_medic", i, 0.0)); + } + + if (this.ContainsDifficultyKey("stun_health_per_sniper", i)) + { + temp.SetDifficultyFloat("sniper", i, this.GetDifficultyFloat("stun_health_per_sniper", i, 0.0)); + } + + if (this.ContainsDifficultyKey("stun_health_per_spy", i)) + { + temp.SetDifficultyFloat("spy", i, this.GetDifficultyFloat("stun_health_per_spy", i, 0.0)); + } + } + } + + keys.Clear(); + keys.PushString("keydrop_enabled"); + keys.PushString("key_model"); + keys.PushString("key_trigger"); + if (this.ContainsAnyKey(keys)) + { + if (this.ContainsKey("keydrop_enabled")) + { + newObj.SetBool("key_drop", this.GetBool("keydrop_enabled", false)); + } + + defString = SF_KEYMODEL; + if (this.ContainsKey("key_model")) + { + this.GetString("key_model", defString, sizeof(defString), defString); + newObj.SetString("key_model", defString); + } + + defString = ""; + if (this.ContainsKey("key_trigger")) + { + this.GetString("key_trigger", defString, sizeof(defString), defString); + newObj.SetString("key_trigger", defString); + } + } + } + + keys.Clear(); + keys.PushString("chase_upon_look"); + keys.PushString("auto_chase_upon_look"); + if (this.ContainsAnyDifficultyKey(keys)) + { + newObj = this.GetChaseOnLookData(); + if (newObj == null) + { + newObj = this.InsertNewSection("chase_on_look"); + + for (int i = 0; i < Difficulty_Max; i++) + { + bool def = false; + def = this.GetDifficultyBool("chase_upon_look", i, def); + def = this.GetDifficultyBool("auto_chase_upon_look", i, def); + newObj.SetDifficultyBool("enabled", i, def); + } + } + } + + keys.Clear(); + keys.PushString("auto_chase_enabled"); + keys.PushString("auto_chase_sound_threshold"); + keys.PushString("auto_chase_cooldown_after_chase"); + keys.PushString("auto_chase_sprinters"); + if (this.ContainsAnyDifficultyKey(keys)) + { + newObj = this.InsertNewSection("autochase"); + int defInt = 0; + + keys.Clear(); + keys.PushString("auto_chase_sound_add"); + keys.PushString("auto_chase_sound_add_footsteps"); + keys.PushString("auto_chase_sound_add_footsteps_loud"); + keys.PushString("auto_chase_sound_add_footsteps_quiet"); + keys.PushString("auto_chase_sound_add_voice"); + keys.PushString("auto_chase_sound_add_weapon"); + + for (int i = 0; i < Difficulty_Max; i++) + { + if (this.ContainsDifficultyKey("auto_chase_enabled", i)) + { + newObj.SetDifficultyBool("enabled", i, this.GetDifficultyBool("auto_chase_enabled", i, false)); + } + + if (this.ContainsDifficultyKey("auto_chase_sound_threshold", i)) + { + newObj.SetDifficultyInt("threshold", i, this.GetDifficultyInt("auto_chase_sound_threshold", i, 100)); + } + + if (this.ContainsDifficultyKey("auto_chase_cooldown_after_chase", i)) + { + newObj.SetDifficultyFloat("cooldown_after_chase", i, this.GetDifficultyFloat("auto_chase_cooldown_after_chase", i, 3.0)); + } + + if (this.ContainsDifficultyKey("auto_chase_sprinters", i)) + { + newObj.SetDifficultyBool("sprinters", i, this.GetDifficultyBool("auto_chase_sprinters", i, false)); + } + + if (this.ContainsAnyDifficultyKey(keys, i)) + { + temp = newObj.InsertNewSection("add"); + + if (this.ContainsDifficultyKey("auto_chase_sound_add", i)) + { + temp.SetDifficultyInt("on_state_change", i, this.GetDifficultyInt("auto_chase_sound_add", i, 0)); + } + + if (this.ContainsDifficultyKey("auto_chase_sound_add_footsteps", i)) + { + temp.SetDifficultyInt("footsteps", i, this.GetDifficultyInt("auto_chase_sound_add_footsteps", i, 2)); + } + + defInt = temp.GetDifficultyInt("footsteps", i); + if (this.ContainsDifficultyKey("auto_chase_sound_add_footsteps_loud", i)) + { + defInt = this.GetDifficultyInt("auto_chase_sound_add_footsteps_loud", i, defInt); + temp.SetDifficultyInt("footsteps_loud", i, defInt); + } + + defInt = temp.GetDifficultyInt("footsteps", i); + if (this.ContainsDifficultyKey("auto_chase_sound_add_footsteps_quiet", i)) + { + defInt = this.GetDifficultyInt("auto_chase_sound_add_footsteps_quiet", i, defInt); + temp.SetDifficultyInt("footsteps_quiet", i, defInt); + } + + if (this.ContainsDifficultyKey("auto_chase_sound_add_voice", i)) + { + temp.SetDifficultyInt("voice", i, this.GetDifficultyInt("auto_chase_sound_add_voice", i, 8)); + } + + if (this.ContainsDifficultyKey("auto_chase_sound_add_weapon", i)) + { + temp.SetDifficultyInt("weapon", i, this.GetDifficultyInt("auto_chase_sound_add_weapon", i, 8)); + } + } + } + } + + if (this.GetBool("cloak_enable", false)) + { + newObj = this.InsertNewSection("cloaking"); + + float multipliers[Difficulty_Max] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + bool addPosture = false; + for (int i = 0; i < Difficulty_Max; i++) + { + newObj.SetDifficultyBool("enabled", i, true); + if (this.ContainsDifficultyKey("cloak_cooldown", i)) + { + newObj.SetDifficultyFloat("cooldown", i, this.GetDifficultyFloat("cloak_cooldown", i, 10.0)); + } + if (this.ContainsDifficultyKey("cloak_range", i)) + { + newObj.SetDifficultyFloat("cloak_range", i, this.GetDifficultyFloat("cloak_range", i, 350.0)); + } + if (this.ContainsDifficultyKey("cloak_decloak_range", i)) + { + newObj.SetDifficultyFloat("decloak_range", i, this.GetDifficultyFloat("cloak_decloak_range", i, 150.0)); + } + if (this.ContainsDifficultyKey("cloak_decloak_range", i)) + { + newObj.SetDifficultyFloat("duration", i, this.GetDifficultyFloat("cloak_duration", i, 8.0)); + } + multipliers[i] = this.GetDifficultyFloat("cloak_speed_multiplier", i, multipliers[i]); + if (multipliers[i] != 1.0) + { + addPosture = true; + } + } + + int color[4]; + if (this.ContainsKey("cloak_rendercolor")) + { + this.GetColor("cloak_rendercolor", color, { 0, 0, 0, 0 }); + newObj.SetColor("color", color); + } + + if (this.ContainsKey("cloak_rendermode")) + { + newObj.SetInt("rendermode", this.GetInt("cloak_rendermode", view_as(RENDER_TRANSCOLOR))); + } + + char particle[128], sound[PLATFORM_MAX_PATH]; + temp = newObj.InsertNewSection("effects"); + ProfileEffectMaster effectSection = view_as(temp.InsertNewSection("cloak")); + + this.GetString("cloak_particle", particle, sizeof(particle), "drg_cow_explosioncore_charged_blue"); + this.GetString("cloak_on_sound", sound, sizeof(sound), "weapons/medi_shield_deploy.wav"); + ProfileEffect effect = view_as(effectSection.InsertNewSection("Legacy Cloak Particle")); + effect.SetString("type", "particle"); + effect.SetFloat("lifetime", 0.1); + effect.SetVector("origin", { 0.0, 0.0, 35.0 }); + effect.SetString("particlename", particle); + + effect = view_as(effectSection.InsertNewSection("Legacy Cloak Sound")); + effect.SetString("type", "sound"); + temp = effect.InsertNewSection("paths"); + temp.SetString("1", sound); + + this.GetString("cloak_off_sound", sound, sizeof(sound), "weapons/medi_shield_retract.wav"); + temp = newObj.GetSection("effects"); + effectSection = view_as(temp.InsertNewSection("decloak")); + + effect = view_as(effectSection.InsertNewSection("Legacy Cloak Particle")); + effect.SetString("type", "particle"); + effect.SetFloat("lifetime", 0.1); + effect.SetVector("origin", { 0.0, 0.0, 35.0 }); + effect.SetString("particlename", particle); + + effect = view_as(effectSection.InsertNewSection("Legacy Cloak Sound")); + effect.SetString("type", "sound"); + temp = effect.InsertNewSection("paths"); + temp.SetString("1", sound); + + if (addPosture) + { + temp = this.InsertNewSection("postures"); + temp2 = temp.InsertNewSection("Legacy Cloak"); + + for (int i = 0; i < Difficulty_Max; i++) + { + temp2.SetDifficultyFloat("speed", i, this.GetRunSpeed(i) * multipliers[i]); + temp2.SetDifficultyFloat("walkspeed", i, this.GetWalkSpeed(i) * multipliers[i]); + temp2.SetDifficultyFloat("acceleration", i, this.GetAcceleration(i) * multipliers[i]); + } + + temp = temp2.InsertNewSection("conditions"); + temp2 = temp.InsertNewSection("on_cloak"); + + for (int i = 0; i < Difficulty_Max; i++) + { + temp2.SetDifficultyBool("enabled", i, true); + } + } + } + + temp = this.GetSection("attacks"); + if (temp != null) + { + for (int i = 0; i < this.GetAttackCount(); i++) + { + ChaserBossProfileBaseAttack attack = this.GetAttackFromIndex(i); + + keys.Clear(); + keys.PushString("ignore_always_looking"); + keys.PushString("attack_ignore_always_looking"); + + if (this.GetAttributes() != null && (this.GetAttributes().GetValue(SF2Attribute_AlwaysLookAtTargetWhileAttacking) > 0.0 || this.GetAttributes().GetValue(SF2Attribute_AlwaysLookAtTarget) > 0.0)) + { + for (int i2 = 0; i2 < Difficulty_Max; i2++) + { + if (!attack.ContainsAnyDifficultyKey(keys, i2)) + { + attack.SetDifficultyBool("autoaim", i2, true); + } + } + } + } + } + + bool exists = false; + bool state[Difficulty_Max] = { false, false, false, false, false, false }; + for (int i = 0; i < Difficulty_Max; i++) + { + state[i] = this.GetDifficultyBool("traps_enabled", i, false); + if (state[i]) + { + exists = true; + } + } + + if (exists) + { + newObj = this.InsertNewSection("traps"); + for (int i = 0; i < Difficulty_Max; i++) + { + if (!state[i]) + { + this.SetDifficultyKeyValue("enabled", i, "0"); + } + newObj.TransferDifficultyKey(this, "trap_spawn_cooldown", "spawn_cooldown", i); + } + this.GetString("trap_model", path, sizeof(path), TRAP_MODEL); + temp = newObj.InsertNewSection("types"); + temp = temp.InsertNewSection("bear_trap"); + temp.SetKeyValue("type", "bear_trap"); + temp.SetKeyValue("model", path); + this.RemoveKey("trap_model"); + temp2 = temp.InsertNewSection("sounds"); + this.GetString("trap_deploy_sound", path, sizeof(path), TRAP_DEPLOY); + temp3 = temp2.InsertNewSection("spawn"); + temp3 = temp3.InsertNewSection("paths"); + temp3.SetKeyValue("1", path); + this.RemoveKey("trap_deploy_sound"); + this.GetString("trap_miss_sound", path, sizeof(path), TRAP_CLOSE); + temp3 = temp2.InsertNewSection("miss"); + temp3 = temp3.InsertNewSection("paths"); + temp3.SetKeyValue("1", path); + this.RemoveKey("trap_miss_sound"); + this.GetString("trap_catch_sound", path, sizeof(path), TRAP_CLOSE); + temp3 = temp2.InsertNewSection("catch"); + temp3 = temp3.InsertNewSection("paths"); + temp3.SetKeyValue("1", path); + this.RemoveKey("trap_catch_sound"); + + temp2 = temp.InsertNewSection("animations"); + this.GetString("trap_animation_idle", path, sizeof(path), "trapopened"); + temp3 = temp2.InsertNewSection("opened"); + temp3 = temp3.InsertNewSection("1"); + temp3.SetKeyValue("name", path); + this.RemoveKey("trap_animation_idle"); + this.GetString("trap_animation_closed", path, sizeof(path), "trapclosed"); + temp3 = temp2.InsertNewSection("closed"); + temp3 = temp3.InsertNewSection("1"); + temp3.SetKeyValue("name", path); + this.RemoveKey("trap_animation_closed"); + this.GetString("trap_animation_open", path, sizeof(path), "trapclosed"); + if (path[0] != '\0') + { + temp3 = temp2.InsertNewSection("open"); + temp3 = temp3.InsertNewSection("1"); + temp3.SetKeyValue("name", path); + this.RemoveKey("trap_animation_open"); + } + } + + if (this.GetBool("player_damage_effects")) + { + if (this.GetBool("player_damage_random_effects")) + { + this.LoadLegacyEffects(SF2DamageType_Random); + } + + if (this.GetBool("player_jarate_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Jarate); + } + + if (this.GetBool("player_milk_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Milk); + } + + if (this.GetBool("player_gas_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Gas); + } + + if (this.GetBool("player_mark_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Mark); + } + + if (this.GetBool("player_silent_mark_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Mark, true); + } + + if (this.GetBool("player_ignite_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Ignite); + } + + if (this.GetBool("player_stun_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Stun); + } + + if (this.GetBool("player_bleed_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Bleed); + } + + if (this.GetBool("player_electric_slow_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Stun, true); + } + + if (this.GetBool("player_smite_on_hit", false)) + { + this.LoadLegacyEffects(SF2DamageType_Smite); + } + } + + if (this.GetProjectiles() != null) + { + this.GetProjectiles().Precache(); + } + + if (this.GetIdleSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetIdleSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetIdleSounds().Precache(); + } + + if (this.GetAlertSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetAlertSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetAlertSounds().Precache(); + } + + if (this.GetChasingSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetChasingSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetChasingSounds().Precache(); + } + + if (this.GetChaseInitialSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetChaseInitialSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetChaseInitialSounds().Precache(); + } + + if (this.GetStunSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetStunSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetStunSounds().Precache(); + } + + if (this.GetHurtSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetHurtSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetHurtSounds().Precache(); + } + + if (this.GetJumpSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetJumpSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetJumpSounds().Precache(); + } + + if (this.GetDeathSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetDeathSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetDeathSounds().Precache(); + } + + if (this.GetTauntKillSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetTauntKillSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetTauntKillSounds().Precache(); + } + + if (this.GetAttackKilledSounds() != null) + { + this.GetAttackKilledSounds().Precache(); + } + + if (this.GetAttackKilledAllSounds() != null) + { + this.GetAttackKilledAllSounds().Precache(); + } + + if (this.GetAttackKilledClientSounds() != null) + { + this.GetAttackKilledClientSounds().Precache(); + } + + if (this.GetSmellSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetSmellSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetSmellSounds().Precache(); + } + + if (this.GetSelfHealSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetSelfHealSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetSelfHealSounds().Precache(); + } + + if (this.GetRageAllSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetRageAllSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetRageAllSounds().Precache(); + } + + if (this.GetRageTwoSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetRageTwoSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetRageTwoSounds().Precache(); + } + + if (this.GetRageThreeSounds() != null) + { + if (this.NormalSoundHook) + { + this.GetRageThreeSounds().SetDefaultChannel(SNDCHAN_VOICE); + } + this.GetRageThreeSounds().Precache(); + } + + if (this.GetDespawnSounds() != null) + { + this.GetDespawnSounds().Precache(); + } + + if (this.GetFootstepSounds() != null) + { + this.GetFootstepSounds().Precache(); + } + + if (this.GetIdleMusics() != null) + { + this.GetIdleMusics().Precache(); + } + + if (this.GetAlertMusics() != null) + { + this.GetAlertMusics().Precache(); + } + + if (this.GetChaseMusics() != null) + { + this.GetChaseMusics().Precache(); + } + + if (this.GetVisibleIdleMusics() != null) + { + this.GetVisibleIdleMusics().Precache(); + } + + if (this.GetVisibleAlertMusics() != null) + { + this.GetVisibleAlertMusics().Precache(); + } + + if (this.GetVisibleChaseMusics() != null) + { + this.GetVisibleChaseMusics().Precache(); + } + + if (this.GetCloakData() != null) + { + this.GetCloakData().Precache(); + } + + if (this.GetStunBehavior() != null) + { + this.GetStunBehavior().Precache(); + } + + if (this.GetDeathBehavior() != null) + { + this.GetDeathBehavior().Precache(); + } + + if (this.GetTrapData() != null) + { + this.GetTrapData().Precache(); + } + + this.PrecacheAttackSounds(this.GetAttackSounds(), "attack", this.NormalSoundHook); + + this.PrecacheAttackSounds(this.GetHitSounds(), "hitenemy", false); + + this.PrecacheAttackSounds(this.GetMissSounds(), "missenemy", false); + + this.PrecacheAttackSounds(this.GetAttackBeginSounds(), "attack_begin", this.NormalSoundHook); + + this.PrecacheAttackSounds(this.GetAttackLoopSounds(), "attack_loop", false); + + this.PrecacheAttackSounds(this.GetAttackEndSounds(), "attack_end", this.NormalSoundHook); + + this.PrecacheAttackSounds(this.GetBulletShootSounds(), "bulletshoot", false); + + this.PrecacheAttackSounds(this.GetProjectileShootSounds(), "attackshootprojectile", false); + + if (this.GetSection("attacks") != null) + { + for (int i = 0; i < this.GetAttackCount(); i++) + { + ChaserBossProfileBaseAttack attack = this.GetAttackFromIndex(i); + if (attack != null) + { + attack.Precache(); + } + } + } + + if (this.OldAnimationAI && this.GetAnimations() != null && !this.GetAnimations().IsLegacy) + { + for (int i = 0; i < this.GetAnimations().SectionLength; i++) + { + char key[64]; + this.GetAnimations().GetSectionNameFromIndex(i, key, sizeof(key)); + obj = this.GetAnimations().GetSection(key); + if (obj != null && strcmp(key, g_SlenderAnimationsList[SF2BossAnimation_Walk]) == 0 || strcmp(key, g_SlenderAnimationsList[SF2BossAnimation_Run]) == 0) + { + for (int i2 = 0; i2 < obj.SectionLength; i2++) + { + obj.GetSectionNameFromIndex(i2, key, sizeof(key)); + ProfileAnimation animObj = view_as(obj.GetSection(key)); + if (animObj != null) + { + for (int i3 = 0; i3 < Difficulty_Max; i3++) + { + animObj.SetDifficultyBool("ground_sync", i3, true); + } + } + } + } + } + } + } + + public void PrecacheAttackSounds(ProfileObject base, char[] section, bool hook = false) + { + if (base == null) + { + return; + } + + if (hook) + { + view_as(base).SetDefaultChannel(SNDCHAN_VOICE); + } + + bool sections = false; + if (base.GetSection("paths") != null) + { + view_as(base).Precache(); + } + else + { + for (int i = 0; i < base.SectionLength; i++) + { + char key[64]; + base.GetSectionNameFromIndex(i, key, sizeof(key)); + if (base.GetSection(key) != null) + { + sections = true; + break; + } + } + + if (!sections) + { + view_as(base).Precache(); + } + else + { + char formatter[64]; + FormatEx(formatter, sizeof(formatter), "__chaser_%s_sounds", section); + KeyMap_Array sounds = new KeyMap_Array(Key_Type_Value); + this.SetSection(formatter, sounds); + this.SetType(formatter, Key_Type_Section); + sounds.Parent = this; + + for (int i = 0; i < base.SectionLength; i++) + { + char key[64]; + base.GetSectionNameFromIndex(i, key, sizeof(key)); + ProfileObject obj = base.GetSection(key); + if (obj != null) + { + sounds.PushString(key); + if (hook) + { + view_as(obj).SetDefaultChannel(SNDCHAN_VOICE); + } + view_as(obj).Precache(); + } + } + } + } + } + + // #define THIS_IS_A_FUCKING_MESS + public void LoadLegacyEffects(int type = SF2DamageType_Jarate, bool alt = false) + { + char key[64], path[PLATFORM_MAX_PATH]; + bool attach = this.GetBool("player_attach_particle", true); + char flag[32]; + flag[0] = '\0'; + switch (this.GetInt("player_stun_type", 0)) + { + case 1: + { + flag = "slow"; + } + + case 2: + { + flag = "loser"; + } + + case 3: + { + flag = "no_fx stuck"; + } + + case 4: + { + flag = "boo"; + } + } + + switch (type) + { + case SF2DamageType_Bleed: + { + key = "player_bleed_attack_indexs"; + } + + case SF2DamageType_Gas: + { + key = "player_gas_attack_indexs"; + } + + case SF2DamageType_Ignite: + { + key = "player_ignite_attack_indexs"; + } + + case SF2DamageType_Jarate: + { + key = "player_jarate_attack_indexs"; + } + + case SF2DamageType_Mark: + { + key = "player_mark_attack_indexs"; + if (alt) + { + key = "player_silent_mark_attack_indexs"; + } + } + + case SF2DamageType_Milk: + { + key = "player_milk_attack_indexs"; + } + + case SF2DamageType_Random: + { + key = "player_random_attack_indexes"; + } + + case SF2DamageType_Smite: + { + key = "player_smite_attack_indexs"; + } + + case SF2DamageType_Stun: + { + key = "player_stun_attack_indexs"; + if (alt) + { + key = "player_electrocute_attack_indexs"; + } + } + } + + char indexes[128], index[64][64]; + this.GetString(key, indexes, sizeof(indexes), "1"); + int nums = ExplodeString(indexes, " ", index, sizeof(index), sizeof(index)); + + for (int i = 0; i < this.GetAttackCount(); i++) + { + ChaserBossProfileBaseAttack attack = this.GetAttackFromIndex(i); + ProfileObject temp = attack.InsertNewSection("apply_conditions"); + + char name[64]; + attack.GetSectionName(name, sizeof(name)); + for (int i2 = 0; i2 < nums; i2++) + { + if (strcmp(name, index[i2]) != 0) + { + continue; + } + + BossProfileDamageEffect damageEffect = null; + switch (type) + { + case SF2DamageType_Bleed: + { + damageEffect = view_as(temp.InsertNewSection("bleed")); + } + + case SF2DamageType_Gas: + { + damageEffect = view_as(temp.InsertNewSection("gas")); + } + + case SF2DamageType_Ignite: + { + damageEffect = view_as(temp.InsertNewSection("ignite")); + } + + case SF2DamageType_Jarate: + { + damageEffect = view_as(temp.InsertNewSection("jarate")); + } + + case SF2DamageType_Mark: + { + damageEffect = view_as(temp.InsertNewSection("mark")); + } + + case SF2DamageType_Milk: + { + damageEffect = view_as(temp.InsertNewSection("milk")); + } + + case SF2DamageType_Random: + { + damageEffect = view_as(temp.InsertNewSection("random")); + } + + case SF2DamageType_Smite: + { + damageEffect = view_as(temp.InsertNewSection("smite")); + } + + case SF2DamageType_Stun: + { + damageEffect = view_as(temp.InsertNewSection("stun")); + } + } + + if (damageEffect == null) + { + continue; + } + + switch (damageEffect.Type) + { + case SF2DamageType_Bleed: + { + key = "player_bleed_duration"; + } + + case SF2DamageType_Gas: + { + key = "player_gas_duration"; + } + + case SF2DamageType_Ignite: + { + key = "player_ignite_duration"; + } + + case SF2DamageType_Jarate: + { + key = "player_jarate_duration"; + } + + case SF2DamageType_Mark: + { + key = "player_mark_duration"; + if (alt) + { + key = "player_silent_mark_duration"; + } + } + + case SF2DamageType_Milk: + { + key = "player_milk_duration"; + } + + case SF2DamageType_Random: + { + key = "player_random_duration"; + } + + case SF2DamageType_Stun: + { + key = "player_stun_duration"; + if (alt) + { + key = "player_electric_slow_duration"; + } + } + + default: + { + key[0] = '\0'; + } + } + + if (key[0] != '\0') + { + for (int i3 = 0; i3 < Difficulty_Max; i3++) + { + damageEffect.SetDifficultyFloat("duration", i3, this.GetDifficultyFloat(key, i3, 8.0)); + } + } + + if (type == SF2DamageType_Mark && alt) + { + for (int i3 = 0; i3 < Difficulty_Max; i3++) + { + damageEffect.SetDifficultyBool("silent", i3, true); + } + } + + if (type == SF2DamageType_Stun) + { + if (alt) + { + key = "player_electric_slow_slowdown"; + } + else + { + key = "player_stun_slowdown"; + } + + for (int i3 = 0; i3 < Difficulty_Max; i3++) + { + damageEffect.SetDifficultyFloat("slow_multiplier", i3, this.GetDifficultyFloat(key, i3, 0.5)); + damageEffect.SetDifficultyString("flags", i3, flag); + } + } + + if (type == SF2DamageType_Random) + { + damageEffect.SetString("random_types", "ignite gas bleed mark jarate milk stun"); + switch (this.GetInt("player_random_stun_type", 0)) + { + case 1: + { + flag = "slow"; + } + + case 2: + { + flag = "loser"; + } + + case 3: + { + flag = "no_fx stuck"; + } + + case 4: + { + flag = "boo"; + } + } + + for (int i3 = 0; i3 < Difficulty_Max; i3++) + { + damageEffect.SetDifficultyFloat("slow_multiplier", i3, this.GetDifficultyFloat("player_random_slowdown", i3, 0.5)); + damageEffect.SetDifficultyString("flags", i3, flag); + } + } + + if (type == SF2DamageType_Smite) + { + for (int i3 = 0; i3 < Difficulty_Max; i3++) + { + damageEffect.SetDifficultyFloat("damage", i3, this.GetDifficultyFloat("player_smite_damage", i3, 9001.0)); + + damageEffect.SetDifficultyInt("damagetype", i3, this.GetDifficultyInt("player_smite_damage_type", i3, (1 << 20))); + } + + int color[4]; + color[0] = this.GetInt("player_smite_color_r", 255); + color[1] = this.GetInt("player_smite_color_g", 255); + color[2] = this.GetInt("player_smite_color_b", 255); + color[3] = this.GetInt("player_smite_transparency", 255); + damageEffect.SetColor("color", color); + damageEffect.SetBool("message", this.GetBool("player_smite_message")); + this.GetString("player_smite_sound", path, sizeof(path), SOUND_THUNDER); + damageEffect.SetString("hit_sound", path); + } + + if (type == SF2DamageType_Gas || type == SF2DamageType_Jarate || type == SF2DamageType_Milk || type == SF2DamageType_Stun) + { + char particleName[128]; + switch (type) + { + case SF2DamageType_Gas: + { + key = "player_gas_particle"; + particleName = "gas_can_impact_blue"; + } + + case SF2DamageType_Jarate: + { + key = "player_jarate_particle"; + particleName = "peejar_impact"; + } + + case SF2DamageType_Milk: + { + key = "player_milk_particle"; + particleName = "peejar_impact_milk"; + } + + case SF2DamageType_Stun: + { + key = "player_stun_particle"; + particleName = "xms_icicle_melt"; + if (alt) + { + key = "player_electric_red_particle"; + particleName = "electrocuted_gibbed_red"; + } + } + } + + this.GetString(key, particleName, sizeof(particleName), particleName); + if (particleName[0] != '\0') + { + ProfileObject temp2 = damageEffect.InsertNewSection("particles"); + BossProfileParticleData particle = view_as(temp2.InsertNewSection("Legacy Particle")); + particle.SetString("particle", particleName); + switch (type) + { + case SF2DamageType_Gas: + { + key = "player_gas_beam_particle"; + } + + case SF2DamageType_Jarate: + { + key = "player_jarate_beam_particle"; + } + + case SF2DamageType_Milk: + { + key = "player_milk_beam_particle"; + } + + case SF2DamageType_Stun: + { + key = "player_stun_beam_particle"; + if (alt) + { + key = "player_electric_beam_particle"; + } + } + } + particle.SetBool("beam", this.GetBool(key, false)); + particle.SetBool("attach", attach); + } + + switch (type) + { + case SF2DamageType_Gas: + { + key = "player_gas_sound"; + path = "weapons/jar_single.wav"; + } + + case SF2DamageType_Jarate: + { + key = "player_jarate_sound"; + path = "weapons/jar_single.wav"; + } + + case SF2DamageType_Milk: + { + key = "player_milk_sound"; + path = "weapons/jar_single.wav"; + } + + case SF2DamageType_Stun: + { + key = "player_stun_sound"; + path = "weapons/icicle_freeze_victim_01.wav"; + } + } + + if (!alt) + { + this.GetString(key, path, sizeof(path), path); + if (path[0] != '\0') + { + ProfileObject temp2 = damageEffect.InsertNewSection("sounds"); + temp2 = temp2.InsertNewSection("paths"); + temp2.SetKeyValue("1", path); + } + } + } + } + } + } +} + +methodmap ChaserBossProfileIdleData < ProfileObject +{ + public bool IsTurnEnabled(int difficulty) + { + ProfileObject obj = this.GetSection("turn"); + if (obj == null) + { + return true; + } + + return obj.GetDifficultyBool("enabled", difficulty, true); + } + + public float GetTurnAngle(int difficulty) + { + float def = 360.0; + ProfileObject obj = this.GetSection("turn"); + if (obj == null) + { + return def; + } + + return obj.GetDifficultyFloat("angle", difficulty, def); + } + + public float GetTurnMinCooldown(int difficulty) + { + float def = 1.5; + ProfileObject obj = this.GetSection("turn"); + if (obj == null) + { + return def; + } + + return obj.GetDifficultyFloat("cooldown_min", difficulty, def); + } + + public float GetTurnMaxCooldown(int difficulty) + { + float def = 3.0; + ProfileObject obj = this.GetSection("turn"); + if (obj == null) + { + return def; + } + + return obj.GetDifficultyFloat("cooldown_max", difficulty, def); + } +} + +methodmap ChaserBossProfileAlertData < ProfileObject +{ + public float GetGraceTime(int difficulty) + { + return this.GetDifficultyFloat("gracetime", difficulty, 0.5); + } + + public float GetDuration(int difficulty) + { + return this.GetDifficultyFloat("duration", difficulty, 10.0); + } + + public bool ShouldRunOnWander(int difficulty) + { + return this.GetDifficultyBool("run_on_wander", difficulty, false); + } + + public bool ShouldRunOnSuspect(int difficulty) + { + return this.GetDifficultyBool("run_on_suspect", difficulty, false); + } + + public bool IsTurnEnabled(int difficulty) + { + ProfileObject obj = this.GetSection("turn"); + if (obj == null) + { + return true; + } + + return obj.GetDifficultyBool("enabled", difficulty, true); + } + + public float GetTurnAngle(int difficulty) + { + float def = 360.0; + ProfileObject obj = this.GetSection("turn"); + if (obj == null) + { + return def; + } + + return obj.GetDifficultyFloat("angle", difficulty, def); + } + + public float GetTurnMinCooldown(int difficulty) + { + float def = 1.5; + ProfileObject obj = this.GetSection("turn"); + if (obj == null) + { + return def; + } + + return obj.GetDifficultyFloat("cooldown_min", difficulty, def); + } + + public float GetTurnMaxCooldown(int difficulty) + { + float def = 3.0; + ProfileObject obj = this.GetSection("turn"); + if (obj == null) + { + return def; + } + + return obj.GetDifficultyFloat("cooldown_max", difficulty, def); + } + + public ChaserBossAlertOnStateData GetAlertSyncData() + { + return view_as(this.GetSection("sync")); + } +} + +methodmap ChaserBossProfileChaseData < ProfileObject +{ + public float GetMaxChaseDuration(int difficulty) + { + float def = 10.0; + ProfileObject obj = this.GetSection("duration"); + if (obj != null) + { + def = obj.GetDifficultyFloat("max", difficulty, def); + } + return def; + } + + public float GetDurationTargetRange(int difficulty) + { + float def = 1024.0; + if (this == null) + { + return def; + } + def = view_as(this.Parent).GetSearchRange(difficulty); + ProfileObject obj = this.GetSection("duration"); + obj = obj != null ? obj.GetSection("add") : null; + if (obj != null) + { + def = obj.GetDifficultyFloat("target_range", difficulty, def); + } + return def; + } + + public float GetDurationAddVisibleTargetNear(int difficulty) + { + float def = 0.01; + ProfileObject obj = this.GetSection("duration"); + obj = obj != null ? obj.GetSection("add") : null; + if (obj != null) + { + def = obj.GetDifficultyFloat("visible_target_near", difficulty, def); + } + return def; + } + + public float GetDurationAddVisibleTargetFar(int difficulty) + { + float def = 0.05; + ProfileObject obj = this.GetSection("duration"); + obj = obj != null ? obj.GetSection("add") : null; + if (obj != null) + { + def = obj.GetDifficultyFloat("visible_target_far", difficulty, def); + } + return def; + } + + public float GetDurationAddOnAttack(int difficulty) + { + float def = 0.0; + ProfileObject obj = this.GetSection("duration"); + obj = obj != null ? obj.GetSection("add") : null; + if (obj != null) + { + def = obj.GetDifficultyFloat("attack", difficulty, def); + } + return def; + } + + public float GetDurationAddOnStunned(int difficulty) + { + float def = 0.0; + ProfileObject obj = this.GetSection("duration"); + obj = obj != null ? obj.GetSection("add") : null; + if (obj != null) + { + def = obj.GetDifficultyFloat("stunned", difficulty, def); + } + return def; + } + + public ChaserBossAlertOnStateData GetChaseTogetherData() + { + return view_as(this.GetSection("chase_together")); + } +} + +methodmap ChaserBossProfileStunData < ProfileObject +{ + public bool IsEnabled(int difficulty) + { + bool def = false; + if (this == null) + { + return def; + } + return this.GetDifficultyBool("enabled", difficulty, def); + } + + public float GetHealth(int difficulty) + { + float def = 50.0; + if (this == null) + { + return def; + } + return this.GetDifficultyFloat("health", difficulty, def); + } + + public float GetCooldown(int difficulty) + { + return this.GetDifficultyFloat("cooldown", difficulty, 3.5); + } + + public bool ShouldDisappear(int difficulty) + { + return this.GetDifficultyBool("disappear", difficulty, false); + } + + public bool CanFlashlightStun(int difficulty) + { + bool def = false; + ProfileObject obj = this.GetSection("flashlight_stun"); + if (obj != null) + { + def = obj.GetDifficultyBool("enabled", difficulty, def); + } + return def; + } + + public float GetFlashlightDamage(int difficulty) + { + float def = 0.0; + ProfileObject obj = this.GetSection("flashlight_stun"); + if (obj != null) + { + def = obj.GetDifficultyFloat("damage", difficulty, def); + } + return def; + } + + public bool CanChaseInitialOnEnd(int difficulty) + { + return this.GetDifficultyBool("chase_initial_on_end", difficulty, false); + } + + property bool KeyDrop + { + public get() + { + return this.GetBool("key_drop", false); + } + } + + public void GetKeyModel(char[] buffer, int bufferSize) + { + this.GetString("key_model", buffer, bufferSize, SF_KEYMODEL); + } + + public void GetKeyTrigger(char[] buffer, int bufferSize) + { + this.GetString("key_trigger", buffer, bufferSize); + } + + public bool CanDropItem(int difficulty) + { + return this.GetDifficultyBool("item_drop", difficulty, false); + } + + public int GetItemDropType(int difficulty) + { + return this.GetDifficultyInt("item_drop_type", difficulty, 1); + } + + public float GetAddHealthPerClass(int difficulty, TFClassType class) + { + float def = 0.0; + ProfileObject obj = this.GetSection("add_health"); + if (obj != null) + { + switch (class) + { + case TFClass_Scout: + { + def = obj.GetDifficultyFloat("scout", difficulty, def); + } + + case TFClass_Soldier: + { + def = obj.GetDifficultyFloat("soldier", difficulty, def); + } + + case TFClass_Pyro: + { + def = obj.GetDifficultyFloat("pyro", difficulty, def); + } + + case TFClass_DemoMan: + { + def = obj.GetDifficultyFloat("demoman", difficulty, def); + } + + case TFClass_Heavy: + { + def = obj.GetDifficultyFloat("heavy", difficulty, def); + } + + case TFClass_Engineer: + { + def = obj.GetDifficultyFloat("engineer", difficulty, def); + } + + case TFClass_Medic: + { + def = obj.GetDifficultyFloat("medic", difficulty, def); + } + + case TFClass_Sniper: + { + def = obj.GetDifficultyFloat("sniper", difficulty, def); + } + + case TFClass_Spy: + { + def = obj.GetDifficultyFloat("spy", difficulty, def); + } + } + } + return def; + } + + public float GetAddHealthPerPlayer(int difficulty) + { + float def = 0.0; + ProfileObject obj = this.GetSection("add_health"); + if (obj != null) + { + def = obj.GetDifficultyFloat("player", difficulty, def); + } + return def; + } + + public float GetAddHealthPerStun(int difficulty) + { + float def = 0.0; + ProfileObject obj = this.GetSection("add_health"); + if (obj != null) + { + def = obj.GetDifficultyFloat("on_stun", difficulty, def); + } + return def; + } + + public float GetAddRunSpeed(int difficulty) + { + return this.GetDifficultyFloat("add_speed", difficulty, 0.0); + } + + public float GetAddAcceleration(int difficulty) + { + return this.GetDifficultyFloat("add_acceleration", difficulty, 0.0); + } + + public ProfileEffectMaster GetOnStartEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("on_start") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileInputsList GetOnStartInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) + { + return view_as(obj.GetSection("on_start")); + } + return null; + } + + public ProfileEffectMaster GetOnEndEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("on_end") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileInputsList GetOnEndInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) + { + return view_as(obj.GetSection("on_end")); + } + return null; + } + + public void Precache() + { + if (this.GetOnStartEffects() != null) + { + this.GetOnStartEffects().Precache(); + } + + if (this.GetOnEndEffects() != null) + { + this.GetOnEndEffects().Precache(); + } + } +} + +methodmap ChaserBossProfileDeathData < ProfileObject +{ + public bool IsEnabled(int difficulty) + { + bool def = false; + if (this == null) + { + return def; + } + return this.GetDifficultyBool("enabled", difficulty, def); + } + + public float GetHealth(int difficulty) + { + float def = 400.0; + if (this == null) + { + return def; + } + return this.GetDifficultyFloat("health", difficulty, def); + } + + property bool RemoveOnDeath + { + public get() + { + return this.GetBool("remove", false); + } + } + + property bool DisappearOnDeath + { + public get() + { + return this.GetBool("disappear", false); + } + } + + property bool RagdollOnDeath + { + public get() + { + return this.GetBool("become_ragdoll", false); + } + } + + property bool KeyDrop + { + public get() + { + return this.GetBool("key_drop", false); + } + } + + public void GetKeyModel(char[] buffer, int bufferSize) + { + this.GetString("key_model", buffer, bufferSize, SF_KEYMODEL); + } + + public void GetKeyTrigger(char[] buffer, int bufferSize) + { + this.GetString("key_trigger", buffer, bufferSize); + } + + public bool CanDropItem(int difficulty) + { + return this.GetDifficultyBool("item_drop", difficulty, false); + } + + public int GetItemDropType(int difficulty) + { + return this.GetDifficultyInt("item_drop_type", difficulty, 1); + } + + public float GetAddHealthPerClass(int difficulty, TFClassType class) + { + float def = 0.0; + ProfileObject obj = this.GetSection("add_health"); + if (obj != null) + { + switch (class) + { + case TFClass_Scout: + { + def = obj.GetDifficultyFloat("scout", difficulty, def); + } + + case TFClass_Soldier: + { + def = obj.GetDifficultyFloat("soldier", difficulty, def); + } + + case TFClass_Pyro: + { + def = obj.GetDifficultyFloat("pyro", difficulty, def); + } + + case TFClass_DemoMan: + { + def = obj.GetDifficultyFloat("demoman", difficulty, def); + } + + case TFClass_Heavy: + { + def = obj.GetDifficultyFloat("heavy", difficulty, def); + } + + case TFClass_Engineer: + { + def = obj.GetDifficultyFloat("engineer", difficulty, def); + } + + case TFClass_Medic: + { + def = obj.GetDifficultyFloat("medic", difficulty, def); + } + + case TFClass_Sniper: + { + def = obj.GetDifficultyFloat("sniper", difficulty, def); + } + + case TFClass_Spy: + { + def = obj.GetDifficultyFloat("spy", difficulty, def); + } + } + } + return def; + } + + public float GetAddHealthPerPlayer(int difficulty) + { + float def = 0.0; + ProfileObject obj = this.GetSection("add_health"); + if (obj != null) + { + def = obj.GetDifficultyFloat("player", difficulty, def); + } + return def; + } + + public float GetAddHealthPerDeath(int difficulty) + { + float def = 0.0; + ProfileObject obj = this.GetSection("add_health"); + if (obj != null) + { + def = obj.GetDifficultyFloat("on_death", difficulty, def); + } + return def; + } + + public float GetAddRunSpeed(int difficulty) + { + return this.GetDifficultyFloat("add_speed", difficulty, 0.0); + } + + public float GetAddAcceleration(int difficulty) + { + return this.GetDifficultyFloat("add_acceleration", difficulty, 0.0); + } + + property int GibSkin + { + public get() + { + int def = 0; + ProfileObject obj = this.GetGibs(); + if (obj != null) + { + obj.GetInt("skin", def); + } + return def; + } + } + + public ProfileObject GetGibs() + { + return this.GetSection("gibs"); + } + + public ProfileEffectMaster GetOnStartEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("on_start") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileInputsList GetOnStartInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) + { + return view_as(obj.GetSection("on_start")); + } + return null; + } + + public ProfileEffectMaster GetOnEndEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("on_end") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileInputsList GetOnEndInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) + { + return view_as(obj.GetSection("on_end")); + } + return null; + } + + public void Precache() + { + if (this.GetOnStartEffects() != null) + { + this.GetOnStartEffects().Precache(); + } + + if (this.GetOnEndEffects() != null) + { + this.GetOnEndEffects().Precache(); + } + } +} + +methodmap ChaserBossProfileRageData < ProfileObject +{ + property float PercentageThreshold + { + public get() + { + return this.GetFloat("health_percent", 0.75); + } + } + + property bool IncreaseDifficulty + { + public get() + { + return this.GetBool("increase_difficulty", true); + } + } + + property bool Invincible + { + public get() + { + return this.GetBool("invincible", false); + } + } + + property bool IsHealing + { + public get() + { + return this.GetSection("heal") != null; + } + } + + property bool HealCloak + { + public get() + { + bool def = false; + ProfileObject obj = this.GetSection("heal"); + if (obj != null) + { + def = obj.GetBool("cloak", def); + } + return def; + } + } + + property float FleeMinRange + { + public get() + { + float def = 512.0; + ProfileObject obj = this.GetSection("heal"); + if (obj != null) + { + def = obj.GetFloat("flee_range_min", def); + } + return def; + } + } + + property float FleeMaxRange + { + public get() + { + float def = 1024.0; + ProfileObject obj = this.GetSection("heal"); + if (obj != null) + { + def = obj.GetFloat("flee_range_max", def); + } + return def; + } + } + + property float HealAmount + { + public get() + { + float def = 0.5; + ProfileObject obj = this.GetSection("heal"); + if (obj != null) + { + def = obj.GetFloat("amount", def); + } + return def; + } + } + + property float HealDelay + { + public get() + { + float def = 0.0; + ProfileObject obj = this.GetSection("heal"); + if (obj != null) + { + def = obj.GetFloat("delay", def); + } + return def; + } + } + + property float HealDuration + { + public get() + { + float def = 1.0; + ProfileObject obj = this.GetSection("heal"); + if (obj != null) + { + def = obj.GetFloat("duration", def); + } + return def; + } + } + + public ProfileSound GetStartSounds() + { + ProfileSound def = null; + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + def = view_as(obj.GetSection("start")); + } + return def; + } + + public ProfileSound GetHealSounds() + { + ProfileSound def = null; + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + def = view_as(obj.GetSection("healing")); + } + return def; + } + + public ProfileMasterAnimations GetAnimations() + { + return view_as(this.GetSection("animations")); + } + + public void Precache() + { + + } +} + +methodmap ChaserBossProfileCloakData < ProfileObject +{ + public bool IsEnabled(int difficulty) + { + if (this == null) + { + return false; + } + return this.GetDifficultyBool("enabled", difficulty, false); + } + + public float GetDuration(int difficulty) + { + return this.GetDifficultyFloat("duration", difficulty, 8.0); + } + + public float GetCooldown(int difficulty) + { + return this.GetDifficultyFloat("cooldown", difficulty, 10.0); + } + + public float GetCloakRange(int difficulty) + { + return this.GetDifficultyFloat("cloak_range", difficulty, 350.0); + } + + public float GetDecloakRange(int difficulty) + { + return this.GetDifficultyFloat("decloak_range", difficulty, 150.0); + } + + public void GetRenderColor(int buffer[4]) + { + this.GetColor("color", buffer, { 0, 0, 0, 0 }); + } + + public RenderMode GetRenderMode(int difficulty) + { + return view_as(this.GetDifficultyInt("rendermode", difficulty, view_as(RENDER_TRANSCOLOR))); + } + + public RenderFx GetRenderFx(int difficulty) + { + return view_as(this.GetDifficultyInt("renderfx", difficulty, view_as(RENDERFX_NONE))); + } + + public ProfileEffectMaster GetCloakEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("cloak") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileEffectMaster GetDecloakEffects() + { + ProfileObject obj = this.GetSection("effects"); + obj = obj != null ? obj.GetSection("decloak") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public void Precache() + { + if (this.GetCloakEffects() != null) + { + this.GetCloakEffects().Precache(); + } + + if (this.GetDecloakEffects() != null) + { + this.GetDecloakEffects().Precache(); + } + } +} + +methodmap ChaserBossProfileSmellData < ProfileObject +{ + public bool IsEnabled(int difficulty) + { + if (this == null) + { + return false; + } + return this.GetDifficultyBool("enabled", difficulty, false); + } + + public float GetMinCooldown(int difficulty) + { + return this.GetDifficultyFloat("cooldown_min", difficulty, 6.0); + } + + public float GetMaxCooldown(int difficulty) + { + return this.GetDifficultyFloat("cooldown_max", difficulty, 12.0); + } + + public float GetMinCooldownAfterState(int difficulty) + { + return this.GetDifficultyFloat("cooldown_after_state_min", difficulty, 16.0); + } + + public float GetMaxCooldownAfterState(int difficulty) + { + return this.GetDifficultyFloat("cooldown_after_state_min", difficulty, 24.0); + } + + public int GetRequiredPlayers(int difficulty) + { + return this.GetDifficultyInt("required_players", difficulty, 1); + } + + public float GetRequiredPlayerRange(int difficulty) + { + return this.GetDifficultyFloat("required_player_range", difficulty, 1000.0); + } + + public float GetSmellRange(int difficulty) + { + return this.GetDifficultyFloat("smelling_range", difficulty, 1500.0); + } + + public bool GetShouldChaseState(int difficulty) + { + return this.GetDifficultyBool("should_chase_upon_smelled", difficulty, false); + } +} + +methodmap ChaserBossSoundSenseData < ProfileObject +{ + public int GetThreshold(int difficulty) + { + int def = 8; + if (this == null) + { + return def; + } + return this.GetDifficultyInt("threshold", difficulty, def); + } + + public float GetDiscardTime(int difficulty) + { + float def = 2.0; + if (this == null) + { + return def; + } + return this.GetDifficultyFloat("discard_time", difficulty, def); + } + + public float GetDistanceTolerance(int difficulty) + { + float def = 512.0; + if (this == null) + { + return def; + } + return this.GetDifficultyFloat("distance_tolerance", difficulty, def); + } + + public float GetFootstepCooldown(int difficulty) + { + float def = 0.0; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("cooldown"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyFloat("footstep", difficulty, def); + } + + public float GetLoudFootstepCooldown(int difficulty) + { + float def = 0.0; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("cooldown"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyFloat("footstep_loud", difficulty, def); + } + + public float GetQuietFootstepCooldown(int difficulty) + { + float def = 0.0; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("cooldown"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyFloat("footstep_quiet", difficulty, def); + } + + public float GetVoiceCooldown(int difficulty) + { + float def = 0.0; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("cooldown"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyFloat("voice", difficulty, def); + } + + public float GetWeaponCooldown(int difficulty) + { + float def = 0.0; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("cooldown"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyFloat("weapon", difficulty, def); + } + + public float GetFlashlightCooldown(int difficulty) + { + float def = 0.0; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("cooldown"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyFloat("flashlight", difficulty, def); + } + + public int GetFootstepAdd(int difficulty) + { + int def = 1; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("add"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyInt("footstep", difficulty, def); + } + + public int GetLoudFootstepAdd(int difficulty) + { + int def = 2; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("add"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyInt("footstep_loud", difficulty, def); + } + + public int GetQuietFootstepAdd(int difficulty) + { + int def = 0; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("add"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyInt("footstep_quiet", difficulty, def); + } + + public int GetVoiceAdd(int difficulty) + { + int def = 10; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("add"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyInt("voice", difficulty, def); + } + + public int GetWeaponAdd(int difficulty) + { + int def = 5; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("add"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyInt("weapon", difficulty, def); + } + + public int GetFlashlightAdd(int difficulty) + { + int def = 5; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection("add"); + if (obj == null) + { + return def; + } + return obj.GetDifficultyInt("flashlight", difficulty, def); + } +} + +methodmap ChaserBossVisionSenseData < ProfileObject +{ + public bool CanSeeFlashlights(int difficulty) + { + bool def[Difficulty_Max] = { false, false, false, false, true, true }; + if (this == null) + { + return def[difficulty]; + } -#include "profile_chaser_precache.sp" + return this.GetDifficultyBool("can_see_flashlights", difficulty, def[difficulty]); + } +} -void InitializeChaserProfiles() +methodmap ChaserBossPostureCondition < ProfileObject { - g_ChaserBossProfileData = new StringMap(); + public bool GetEnabled(int difficulty) + { + if (this == null) + { + return false; + } + return this.GetDifficultyBool("enabled", difficulty, true); + } } -void UnloadChaserBossProfile(const char[] profile) +methodmap ChaserBossChaseOnLookData < ProfileObject { - SF2ChaserBossProfileData chaserProfileData; - if (!g_ChaserBossProfileData.GetArray(profile, chaserProfileData, sizeof(chaserProfileData))) + public bool IsEnabled(int difficulty) { - return; + if (this == null) + { + return false; + } + return this.GetDifficultyBool("enabled", difficulty, false); } - chaserProfileData.Destroy(); + public bool ShouldAddTargets(int difficulty) + { + return this.GetDifficultyBool("add_targets", difficulty, true); + } - g_ChaserBossProfileData.Remove(profile); -} + public void GetRequiredLookPosition(float buffer[3]) + { + this.GetVector("look_position", buffer, { 0.0, 0.0, 35.0 }); + } -static SF2ChaserBossProfileData g_CachedProfileData; + public float GetMinXAngle(int difficulty) + { + return this.GetDifficultyFloat("minimum_x_angle", difficulty, -45.0); + } -float GetChaserProfileDeathHealth(const char[] profile, int difficulty) -{ - g_ChaserBossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DeathData.Health[difficulty]; -} + public float GetMaxXAngle(int difficulty) + { + return this.GetDifficultyFloat("maximum_x_angle", difficulty, 180.0); + } -bool GetChaserProfileBoxingState(const char[] profile) -{ - g_ChaserBossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.BoxingBoss; + public float GetMinYAngle(int difficulty) + { + return this.GetDifficultyFloat("minimum_y_angle", difficulty, 0.0); + } + + public float GetMaxYAngle(int difficulty) + { + return this.GetDifficultyFloat("maximum_y_angle", difficulty, 105.0); + } + + public float GetRequiredFOV(int difficulty) + { + return this.GetDifficultyFloat("required_fov", difficulty, -1.0); + } } -void GetChaserProfileChaseMusics(const char[] profile, SF2BossProfileSoundInfo params) +methodmap ChaserBossAutoChaseData < ProfileObject { - g_ChaserBossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.ChaseMusics; + public bool IsEnabled(int difficulty) + { + return this.GetDifficultyBool("enabled", difficulty, false); + } + + public int GetThreshold(int difficulty) + { + return this.GetDifficultyInt("threshold", difficulty, 100); + } + + public float GetCooldownAfterChase(int difficulty) + { + return this.GetDifficultyFloat("cooldown_after_chase", difficulty, 3.0); + } + + public bool ShouldChaseSprinters(int difficulty) + { + return this.GetDifficultyBool("sprinters", difficulty, false); + } + + public int GetAddOnStateChange(int difficulty) + { + int def = 0; + ProfileObject obj = this.GetSection("add"); + if (obj != null) + { + def = obj.GetDifficultyInt("on_state_change", difficulty, def); + } + return def; + } + + public int GetAddFootsteps(int difficulty) + { + int def = 2; + ProfileObject obj = this.GetSection("add"); + if (obj != null) + { + def = obj.GetDifficultyInt("footsteps", difficulty, def); + } + return def; + } + + public int GetAddLoudFootsteps(int difficulty) + { + int def = this.GetAddFootsteps(difficulty); + ProfileObject obj = this.GetSection("add"); + if (obj != null) + { + def = obj.GetDifficultyInt("footsteps_loud", difficulty, def); + } + return def; + } + + public int GetAddQuietFootsteps(int difficulty) + { + int def = this.GetAddFootsteps(difficulty); + ProfileObject obj = this.GetSection("add"); + if (obj != null) + { + def = obj.GetDifficultyInt("footsteps_quiet", difficulty, def); + } + return def; + } + + public int GetAddVoice(int difficulty) + { + int def = 8; + ProfileObject obj = this.GetSection("add"); + if (obj != null) + { + def = obj.GetDifficultyInt("voice", difficulty, def); + } + return def; + } + + public int GetAddWeapon(int difficulty) + { + int def = 8; + ProfileObject obj = this.GetSection("add"); + if (obj != null) + { + def = obj.GetDifficultyInt("weapon", difficulty, def); + } + return def; + } } -void GetChaserProfileChaseVisibleMusics(const char[] profile, SF2BossProfileSoundInfo params) +methodmap ChaserBossAlertOnStateData < ProfileObject { - g_ChaserBossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.ChaseVisibleMusics; + public bool IsEnabled(int difficulty) + { + if (this == null) + { + return false; + } + + return this.GetDifficultyBool("enabled", difficulty, false); + } + + public bool ShouldStartOnStateChange(int difficulty) + { + bool def = true; + if (this == null) + { + return def; + } + + return this.GetDifficultyBool("on_change_state", difficulty, true); + } + + public float GetRadius(int difficulty) + { + float def = 1024.0; + if (this == null) + { + return def; + } + + return this.GetDifficultyFloat("radius", difficulty, def); + } + + public bool ShouldBeVisible(int difficulty) + { + bool def = false; + if (this == null) + { + return def; + } + + return this.GetDifficultyBool("should_be_visible", difficulty, def); + } + + public bool ShouldFollow(int difficulty) + { + bool def = false; + if (this == null) + { + return def; + } + + return this.GetDifficultyBool("follow_leader", difficulty, def); + } + + public float GetFollowCooldown(int difficulty) + { + float def = 10.0; + if (this == null) + { + return def; + } + + return this.GetDifficultyFloat("follow_cooldown", difficulty, def); + } + + public bool GetCopies(int difficulty) + { + bool def = false; + if (this == null) + { + return def; + } + + return this.GetDifficultyBool("copies", difficulty, def); + } + + public bool GetCompanions(int difficulty) + { + bool def = false; + if (this == null) + { + return def; + } + + return this.GetDifficultyBool("companions", difficulty, def); + } } -void GetChaserProfileAlertMusics(const char[] profile, SF2BossProfileSoundInfo params) +methodmap ChaserBossResistanceData < ProfileObject { - g_ChaserBossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.AlertMusics; + public bool IsEnabled(int difficulty) + { + if (this == null) + { + return false; + } + return this.GetDifficultyBool("enabled", difficulty, true); + } + + public float GetMultiplier(int difficulty) + { + return this.GetDifficultyFloat("multiplier", difficulty, 1.0); + } + + public ProfileObject GetDamageTypes() + { + return this.GetSection("damage_type"); + } + + public ProfileObject GetHitboxes() + { + return this.GetSection("hitbox"); + } + + public ProfileObject GetWeapons() + { + return this.GetSection("weapon"); + } } -void GetChaserProfileIdleMusics(const char[] profile, SF2BossProfileSoundInfo params) +methodmap ChaserBossProjectileData < ProfileObject { - g_ChaserBossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.IdleMusics; + public bool IsEnabled(int difficulty) + { + return this.GetDifficultyBool("projectile_enable", difficulty, false); + } + + public float GetMinCooldown(int difficulty) + { + return this.GetDifficultyFloat("projectile_cooldown_min", difficulty, 1.0); + } + + public float GetMaxCooldown(int difficulty) + { + return this.GetDifficultyFloat("projectile_cooldown_max", difficulty, 2.0); + } + + public float GetSpeed(int difficulty) + { + return this.GetDifficultyFloat("projectile_speed", difficulty, 1100.0); + } + + public float GetDamage(int difficulty) + { + return this.GetDifficultyFloat("projectile_damage", difficulty, 50.0); + } + + public float GetRadius(int difficulty) + { + return this.GetDifficultyFloat("projectile_damageradius", difficulty, 128.0); + } + + public float GetDeviation(int difficulty) + { + return this.GetDifficultyFloat("projectile_deviation", difficulty, 0.0); + } + + public int GetCount(int difficulty) + { + return this.GetDifficultyInt("projectile_count", difficulty, 1); + } + + property int Type + { + public get() + { + return this.GetInt("projectile_type", SF2BossProjectileType_Fireball); + } + } + + public bool GetCritState(int difficulty) + { + return this.GetDifficultyBool("enable_crit_rockets", difficulty, false); + } + + public void GetShootGesture(char[] buffer, int bufferSize) + { + this.GetString("gesture_shootprojectile", buffer, bufferSize); + } + + property bool ProjectileClips + { + public get() + { + return this.GetBool("projectile_clips_enable", false); + } + } + + public int GetClipSize(int difficulty) + { + return this.GetDifficultyInt("projectile_ammo_loaded", difficulty, 3); + } + + public float GetReloadTime(int difficulty) + { + return this.GetDifficultyFloat("projectile_reload_time", difficulty, 2.0); + } + + property int MinRandomPos + { + public get() + { + return this.GetInt("projectile_pos_number_min", 1); + } + } + + property int MaxRandomPos + { + public get() + { + return this.GetInt("projectile_pos_number_max", 1); + } + } + + public void GetOffset(int index, float buffer[3]) + { + if (index == 1) + { + this.GetVector("projectile_pos_offset", buffer, buffer); + this.GetVector("projectile_pos_offset_1", buffer, buffer); + return; + } + char key[64]; + FormatEx(key, sizeof(key), "projectile_pos_offset_%i", index); + this.GetVector(key, buffer); + } + + public void GetFireballExplodeSound(char[] buffer, int bufferSize) + { + this.GetString("fire_explode_sound", buffer, bufferSize, FIREBALL_IMPACT); + } + + public void GetFireballShootSound(char[] buffer, int bufferSize) + { + this.GetString("fire_shoot_sound", buffer, bufferSize, FIREBALL_SHOOT); + } + + public void GetFireballTrail(char[] buffer, int bufferSize) + { + this.GetString("fire_trail", buffer, bufferSize, FIREBALL_TRAIL); + } + + public void GetRocketTrail(char[] buffer, int bufferSize) + { + this.GetString("rocket_trail_particle", buffer, bufferSize, ROCKET_TRAIL); + } + + public void GetRocketExplodeParticle(char[] buffer, int bufferSize) + { + this.GetString("rocket_explode_particle", buffer, bufferSize, ROCKET_EXPLODE_PARTICLE); + } + + public void GetRocketExplodeSound(char[] buffer, int bufferSize) + { + this.GetString("rocket_explode_sound", buffer, bufferSize, ROCKET_IMPACT); + } + + public void GetRocketShootSound(char[] buffer, int bufferSize) + { + this.GetString("rocket_shoot_sound", buffer, bufferSize, ROCKET_SHOOT); + } + + public void GetRocketModel(char[] buffer, int bufferSize) + { + this.GetString("rocket_model", buffer, bufferSize, ROCKET_MODEL); + } + + public float GetIceballSlowDuration(int difficulty) + { + return this.GetDifficultyFloat("projectile_iceslow_duration", difficulty, 2.0); + } + + public float GetIceballSlowPercent(int difficulty) + { + return this.GetDifficultyFloat("projectile_iceslow_percent", difficulty, 0.55); + } + + public void GetIceballSlowSound(char[] buffer, int bufferSize) + { + this.GetString("fire_iceball_slow_sound", buffer, bufferSize, ICEBALL_IMPACT); + } + + public void GetIceballTrail(char[] buffer, int bufferSize) + { + this.GetString("fire_iceball_trail", buffer, bufferSize, ICEBALL_TRAIL); + } + + public void GetGrenadeShootSound(char[] buffer, int bufferSize) + { + this.GetString("grenade_shoot_sound", buffer, bufferSize, GRENADE_SHOOT); + } + + public void GetSentryRocketShootSound(char[] buffer, int bufferSize) + { + this.GetString("sentryrocket_shoot_sound", buffer, bufferSize, SENTRYROCKET_SHOOT); + } + + public void GetArrowShootSound(char[] buffer, int bufferSize) + { + this.GetString("arrow_shoot_sound", buffer, bufferSize, ARROW_SHOOT); + } + + public void GetManglerShootSound(char[] buffer, int bufferSize) + { + this.GetString("mangler_shoot_sound", buffer, bufferSize, MANGLER_SHOOT); + } + + public void GetBaseballShootSound(char[] buffer, int bufferSize) + { + this.GetString("baseball_shoot_sound", buffer, bufferSize, BASEBALL_SHOOT); + } + + public void GetBaseballModel(char[] buffer, int bufferSize) + { + this.GetString("baseball_model", buffer, bufferSize, BASEBALL_MODEL); + } + + public void Precache() + { + char path[PLATFORM_MAX_PATH]; + this.GetRocketShootSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetRocketExplodeSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetFireballShootSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetFireballExplodeSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetIceballSlowSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetGrenadeShootSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetSentryRocketShootSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetArrowShootSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetManglerShootSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetBaseballShootSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } + + this.GetBaseballModel(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheModel2(path, .checkFile = g_FileCheckConVar.BoolValue); + } + } } -int GetProfileAttackNum(const char[] profile, const char[] keyValue, int defaultValue=0, const int attackIndex) +int GetProfileAttackNum(const char[] profile, const char[] keyValue, int defaultValue = 0, const int attackIndex) { if (!IsProfileValid(profile)) { return defaultValue; } - char key[4]; - g_Config.Rewind(); - g_Config.JumpToKey(profile); - g_Config.JumpToKey("attacks"); - FormatEx(key, sizeof(key), "%d", attackIndex); - g_Config.JumpToKey(key); - return g_Config.GetNum(keyValue, defaultValue); + ChaserBossProfile profileData = view_as(GetBossProfile(profile)); + if (profileData.GetSection("attacks") == null) + { + return defaultValue; + } + + ChaserBossProfileBaseAttack attack = profileData.GetAttackFromIndex(attackIndex); + return attack.GetInt(keyValue, defaultValue); } -float GetProfileAttackFloat(const char[] profile, const char[] keyValue,float defaultValue=0.0, const int attackIndex) +float GetProfileAttackFloat(const char[] profile, const char[] keyValue, float defaultValue = 0.0, const int attackIndex) { if (!IsProfileValid(profile)) { return defaultValue; } - char key[4]; - g_Config.Rewind(); - g_Config.JumpToKey(profile); - g_Config.JumpToKey("attacks"); - FormatEx(key, sizeof(key), "%d", attackIndex); - g_Config.JumpToKey(key); - return g_Config.GetFloat(keyValue, defaultValue); + ChaserBossProfile profileData = view_as(GetBossProfile(profile)); + if (profileData.GetSection("attacks") == null) + { + return defaultValue; + } + + ChaserBossProfileBaseAttack attack = profileData.GetAttackFromIndex(attackIndex); + return attack.GetFloat(keyValue, defaultValue); } bool GetProfileAttackString(const char[] profile, const char[] keyValue, char[] buffer, int length, const char[] defaultValue = "", const int attackIndex) { + strcopy(buffer, length, defaultValue); + if (!IsProfileValid(profile)) { return false; } - char key[4]; - g_Config.Rewind(); - g_Config.JumpToKey(profile); - g_Config.JumpToKey("attacks"); - FormatEx(key, sizeof(key), "%d", attackIndex); - g_Config.JumpToKey(key); - g_Config.GetString(keyValue, buffer, length, defaultValue); + ChaserBossProfile profileData = view_as(GetBossProfile(profile)); + if (profileData.GetSection("attacks") == null) + { + return false; + } + + ChaserBossProfileBaseAttack attack = profileData.GetAttackFromIndex(attackIndex); + attack.GetString(keyValue, buffer, length, defaultValue); + return true; } -bool GetProfileAttackVector(const char[] profile, const char[] keyValue, float buffer[3], const float defaultValue[3]=NULL_VECTOR, const int attackIndex) +bool GetProfileAttackVector(const char[] profile, const char[] keyValue, float buffer[3], const float defaultValue[3] = NULL_VECTOR, const int attackIndex) { for (int i = 0; i < 3; i++) { @@ -127,12 +3759,59 @@ bool GetProfileAttackVector(const char[] profile, const char[] keyValue, float b return false; } - char key[4]; - g_Config.Rewind(); - g_Config.JumpToKey(profile); - g_Config.JumpToKey("attacks"); - FormatEx(key, sizeof(key), "%d", attackIndex); - g_Config.JumpToKey(key); - g_Config.GetVector(keyValue, buffer, defaultValue); + ChaserBossProfile profileData = view_as(GetBossProfile(profile)); + if (profileData.GetSection("attacks") == null) + { + return false; + } + + ChaserBossProfileBaseAttack attack = profileData.GetAttackFromIndex(attackIndex); + attack.GetVector(keyValue, buffer, defaultValue); + return true; -} \ No newline at end of file +} + +void ProfileChaser_InititalizeAPI() +{ + CreateNative("SF2_ChaserBossProfile.GetAttackCount", Native_GetAttackCount); + CreateNative("SF2_ChaserBossProfile.GetAttack", Native_GetAttack); + CreateNative("SF2_ChaserBossProfile.GetAttackFromIndex", Native_GetAttackFromIndex); +} + +static any Native_GetAttackCount(Handle plugin, int numParams) +{ + ChaserBossProfile profileData = GetNativeCell(1); + if (profileData == null || profileData.Type != SF2BossType_Chaser) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid Chaser boss profile handle %x", profileData); + } + + return profileData.GetAttackCount(); +} + +static any Native_GetAttack(Handle plugin, int numParams) +{ + ChaserBossProfile profileData = GetNativeCell(1); + if (profileData == null || profileData.Type != SF2BossType_Chaser) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid Chaser boss profile handle %x", profileData); + } + + char attackName[256]; + GetNativeString(2, attackName, sizeof(attackName)); + + return profileData.GetAttack(attackName); +} + +static any Native_GetAttackFromIndex(Handle plugin, int numParams) +{ + ChaserBossProfile profileData = GetNativeCell(1); + if (profileData == null || profileData.Type != SF2BossType_Chaser) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid Chaser boss profile handle %x", profileData); + } + + int attackIndex = GetNativeCell(2); + + return profileData.GetAttackFromIndex(attackIndex); +} diff --git a/addons/sourcemod/scripting/sf2/profiles/profile_chaser_precache.sp b/addons/sourcemod/scripting/sf2/profiles/profile_chaser_precache.sp index 5c7b17fe..a8760df2 100644 --- a/addons/sourcemod/scripting/sf2/profiles/profile_chaser_precache.sp +++ b/addons/sourcemod/scripting/sf2/profiles/profile_chaser_precache.sp @@ -5,6 +5,7 @@ #define _sf2_profiles_chaser_precache #pragma semicolon 1 +#pragma newdecls required /** * Parses and stores the unique values of a chaser profile from the current position in the profiles config. diff --git a/addons/sourcemod/scripting/sf2/profiles/profile_statue.sp b/addons/sourcemod/scripting/sf2/profiles/profile_statue.sp index 9f40351f..f93568e7 100644 --- a/addons/sourcemod/scripting/sf2/profiles/profile_statue.sp +++ b/addons/sourcemod/scripting/sf2/profiles/profile_statue.sp @@ -5,100 +5,85 @@ #define _sf2_profiles_statue #pragma semicolon 1 +#pragma newdecls required -StringMap g_StatueBossProfileData; - -void InitializeStatueProfiles() -{ - g_StatueBossProfileData = new StringMap(); -} - -void UnloadStatueBossProfile(const char[] profile) +methodmap StatueBossProfile < BaseBossProfile { - SF2StatueBossProfileData statueProfileData; - if (!g_StatueBossProfileData.GetArray(profile, statueProfileData, sizeof(statueProfileData))) + public void GetAverageDistanceModel(int difficulty, char[] buffer, int bufferSize) { - return; + this.GetDifficultyString("model_averagedist", difficulty, buffer, bufferSize); + ReplaceString(buffer, bufferSize, "\\", "/", false); } - statueProfileData.Destroy(); - - g_StatueBossProfileData.Remove(profile); -} + public void GetCloseDistanceModel(int difficulty, char[] buffer, int bufferSize) + { + this.GetDifficultyString("model_closedist", difficulty, buffer, bufferSize); + ReplaceString(buffer, bufferSize, "\\", "/", false); + } -bool LoadStatueBossProfile(KeyValues kv, const char[] profile, char[] loadFailReasonBuffer, int loadFailReasonBufferLen, SF2BossProfileData baseData) -{ - SF2StatueBossProfileData profileData; - profileData.Init(); + public float GetMaxModelChangeDistance(int difficulty) + { + return this.GetDifficultyFloat("model_change_dist_max", difficulty, 1024.0); + } - strcopy(loadFailReasonBuffer, loadFailReasonBufferLen, ""); + public float GetChaseDuration(int difficulty) + { + return this.GetDifficultyFloat("chase_duration", difficulty, 5.0); + } - profileData.ModelsAverageDist = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - profileData.ModelsCloseDist = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - SetProfileDifficultyStringArrayValues(kv, "model_averagedist", profileData.ModelsAverageDist, true); - SetProfileDifficultyStringArrayValues(kv, "model_closedist", profileData.ModelsCloseDist, true); - char modelName[PLATFORM_MAX_PATH]; - for (int i = 0; i < profileData.ModelsAverageDist.Length; i++) + public float GetChaseDurationAddMaxRange(int difficulty) { - profileData.ModelsAverageDist.GetString(i, modelName, sizeof(modelName)); - if (modelName[0] != '\0' && strcmp(modelName, "models/", true) != 0 && strcmp(modelName, "models\\", true) != 0) - { - PrecacheModel2(modelName, _, _, g_FileCheckConVar.BoolValue); - } + return this.GetDifficultyFloat("chase_duration_add_max_range", difficulty, 1024.0); } - for (int i = 0; i < profileData.ModelsCloseDist.Length; i++) + + public float GetChaseDurationAddVisibilityMin(int difficulty) { - profileData.ModelsCloseDist.GetString(i, modelName, sizeof(modelName)); - if (modelName[0] != '\0' && strcmp(modelName, "models/", true) != 0 && strcmp(modelName, "models\\", true) != 0) - { - PrecacheModel2(modelName, _, _, g_FileCheckConVar.BoolValue); - } + return this.GetDifficultyFloat("chase_duration_add_visible_min", difficulty, 0.025); } - GetProfileDifficultyFloatValues(kv, "chase_duration", profileData.ChaseDuration, profileData.ChaseDuration); - for (int i = 0; i < Difficulty_Max; i++) + public float GetChaseDurationAddVisibilityMax(int difficulty) { - profileData.ChaseDurationAddMaxRange[i] = baseData.SearchRange[i]; + return this.GetDifficultyFloat("chase_duration_add_visible_max", difficulty, 0.15); } - GetProfileDifficultyFloatValues(kv, "chase_duration_add_max_range", profileData.ChaseDurationAddMaxRange, profileData.ChaseDurationAddMaxRange); - GetProfileDifficultyFloatValues(kv, "chase_duration_add_visible_min", profileData.ChaseDurationAddVisibilityMin, profileData.ChaseDurationAddVisibilityMin); - GetProfileDifficultyFloatValues(kv, "chase_duration_add_visible_max", profileData.ChaseDurationAddVisibilityMax, profileData.ChaseDurationAddVisibilityMax); - GetProfileDifficultyFloatValues(kv, "model_change_dist_max", profileData.ModelChangeDistanceMax, profileData.ModelChangeDistanceMax); - GetProfileDifficultyFloatValues(kv, "idle_lifetime", profileData.IdleLifeTime, profileData.IdleLifeTime); + public ProfileSound GetMoveSounds() + { + return view_as(this.GetSection("sound_move")); + } - ArrayList validSections = new ArrayList(ByteCountToCells(128)); + public ProfileSound GetSingleMoveSounds() + { + return view_as(this.GetSection("sound_move_single")); + } - if (kv.GotoFirstSubKey()) + public void Precache() { - char s2[64]; + if (this.GetMoveSounds() != null) + { + this.GetMoveSounds().Precache(); + } - do + if (this.GetSingleMoveSounds() != null) { - kv.GetSectionName(s2, sizeof(s2)); + this.GetSingleMoveSounds().SetDefaultCooldownMin(0.125); + this.GetSingleMoveSounds().SetDefaultCooldownMax(0.125); + this.GetSingleMoveSounds().Precache(); + } - if (validSections.FindString(s2) != -1) + char path[PLATFORM_MAX_PATH]; + for (int i = 0; i < Difficulty_Max; i++) + { + this.GetAverageDistanceModel(i, path, sizeof(path)); + if (path[0] != '\0') { - continue; + PrecacheModel2(path, _, _, g_FileCheckConVar.BoolValue); } - validSections.PushString(s2); - - if (!StrContains(s2, "sound_")) + this.GetCloseDistanceModel(i, path, sizeof(path)); + if (path[0] != '\0') { - profileData.SortSoundSections(kv, s2, g_FileCheckConVar.BoolValue); + PrecacheModel2(path, _, _, g_FileCheckConVar.BoolValue); } } - while (kv.GotoNextKey()); - - kv.GoBack(); } - - delete validSections; - - profileData.PostLoad(); - - g_StatueBossProfileData.SetArray(profile, profileData, sizeof(profileData)); - - return true; } diff --git a/addons/sourcemod/scripting/sf2/profiles/profiles_boss_functions.sp b/addons/sourcemod/scripting/sf2/profiles/profiles_boss_functions.sp index 01261cc4..cb31c154 100644 --- a/addons/sourcemod/scripting/sf2/profiles/profiles_boss_functions.sp +++ b/addons/sourcemod/scripting/sf2/profiles/profiles_boss_functions.sp @@ -4,2429 +4,3774 @@ #define _sf2_profiles_precache_included #pragma semicolon 1 +#pragma newdecls required -/** - * Loads a profile in the current KeyValues position in kv. - */ -bool LoadBossProfile(KeyValues kv, const char[] profile, char[] loadFailReasonBuffer, int loadFailReasonBufferLen, bool lookIntoLoads = false, const char[] originalDir = "") +methodmap BaseBossProfile < ProfileObject { - SF2BossProfileData profileData; - profileData.Init(); + public ProfileObject GetBlacklistedMaps() + { + return this.GetSection("map_blacklist"); + } - if (kv.JumpToKey("map_blacklist")) + property int Type { - char s1[4], s2[64], s3[64]; - GetCurrentMap(s3, sizeof(s3)); - for (int i = 1;; i++) + public get() { - FormatEx(s1, sizeof(s1), "%d", i); - kv.GetString(s1, s2, sizeof(s2)); - if (s2[0] == '\0') + return this.GetInt("type", SF2BossType_Chaser); + } + } + + property bool IgnoreNavPrefer + { + public get() + { + return true; + } + } + + property int Flags + { + public get() + { + int bossFlags = 0; + if (this.GetBool("static_on_look", false)) { - break; + bossFlags |= SFF_STATICONLOOK; } - - if (StrContains(s3, s2, false) != -1) + if (this.GetBool("static_on_radius", false)) { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "is blacklisted on %s!", s3); - return false; + bossFlags |= SFF_STATICONRADIUS; + } + if (this.GetBool("proxies", false)) + { + bossFlags |= SFF_PROXIES; + } + if (this.GetBool("jumpscare", false)) + { + bossFlags |= SFF_HASJUMPSCARE; + } + if (this.GetBool("sound_static_loop_local_enabled", false)) + { + bossFlags |= SFF_HASSTATICLOOPLOCALSOUND; + } + if (this.GetBool("view_shake", true)) + { + bossFlags |= SFF_HASVIEWSHAKE; + } + if (this.GetBool("wander_move", true)) + { + bossFlags |= SFF_WANDERMOVE; + } + if (this.GetBool("attack_props", false)) + { + bossFlags |= SFF_ATTACKPROPS; + } + if (this.GetBool("attack_weaponsenable", false)) + { + bossFlags |= SFF_WEAPONKILLS; + } + if (this.GetBool("kill_weaponsenable", false)) + { + bossFlags |= SFF_WEAPONKILLSONRADIUS; } + return bossFlags; } + } - kv.GoBack(); + public void GetModel(int difficulty, char[] buffer, int bufferSize) + { + this.GetDifficultyString("model", difficulty, buffer, bufferSize); + ReplaceString(buffer, bufferSize, "\\", "/", false); } - if (lookIntoLoads) + public void GetName(int difficulty, char[] buffer, int bufferSize) { - // In this, we're basically just gonna go look for companion bosses and skip them here - bool skip = true; + this.GetDifficultyString("name", difficulty, buffer, bufferSize); + } - if (kv.GetNum("enable_random_selection", true) != 0) + property float ModelScale + { + public get() { - skip = false; + return this.GetFloat("model_scale", 1.0); } + } - if (kv.GetNum("admin_only", false) != 0) - { - skip = false; - } + public int GetSkin(int difficulty) + { + return this.GetDifficultyInt("skin", difficulty); + } - if (kv.GetNum("enable_random_selection_boxing", false) != 0) + property int SkinMax + { + public get() { - skip = false; + return this.GetInt("skin_max"); } + } - if (kv.GetNum("enable_random_selection_renevant", false) != 0) - { - skip = false; - } + public int GetBodyGroup(int difficulty) + { + return this.GetDifficultyInt("body", difficulty); + } - if (kv.GetNum("enable_random_selection_renevant_admin", false) != 0) + property int BodyMax + { + public get() { - skip = false; + return this.GetInt("body_max"); } + } - if (kv.GetNum("is_pve", false) != 0 && kv.GetNum("pve_selectable", 1) != 0) + property bool BodyDifficultiesOn + { + public get() { - skip = false; + return this.GetBool("body_difficulty"); } + } - if (kv.GetNum("always_load", false) != 0) + property bool RaidHitbox + { + public get() { - skip = false; + return this.GetBool("use_raid_hitbox"); } + } - if (kv.JumpToKey("pve") && kv.GetNum("selectable", 1) != 0) - { - kv.GoBack(); - skip = false; - } + public float GetInstantKillRadius(int difficulty) + { + return this.GetDifficultyFloat("kill_radius", difficulty, -1.0); + } - if (skip) - { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "is not selectable, skipping!"); - return false; - } + public float GetInstantKillCooldown(int difficulty) + { + return this.GetDifficultyFloat("kill_cooldown", difficulty, 0.0); } - profileData.Models = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - profileData.Names = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - SetProfileDifficultyStringArrayValues(kv, "model", profileData.Models, true); - char modelName[PLATFORM_MAX_PATH]; - for (int i = 0; i < profileData.Models.Length; i++) + property int TeleportType { - profileData.Models.GetString(i, modelName, sizeof(modelName)); - if (modelName[0] != '\0' && strcmp(modelName, "models/", true) != 0 && strcmp(modelName, "models\\", true) != 0) + public get() { - PrecacheModel2(modelName, _, _, g_FileCheckConVar.BoolValue); + return this.GetInt("teleport_type", 2); } } - SetProfileDifficultyStringArrayValues(kv, "name", profileData.Names); - profileData.Type = kv.GetNum("type", profileData.Type); - if (profileData.Type == SF2BossType_Unknown || profileData.Type >= SF2BossType_MaxTypes) + public bool IsTeleportAllowed(int difficulty) { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "boss type is unknown!"); - return false; + return this.GetDifficultyBool("teleport_allowed", difficulty, true); } - profileData.ModelScale = kv.GetFloat("model_scale", profileData.ModelScale); - if (profileData.ModelScale <= 0.0) + public float GetMinTeleportRange(int difficulty) { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "model_scale must be a value greater than 0!"); - return false; + return this.GetDifficultyFloat("teleport_range_min", difficulty, 450.0); } - profileData.Skin[1] = kv.GetNum("skin", profileData.Skin[1]); - if (profileData.Skin[1] < 0) + public float GetMaxTeleportRange(int difficulty) { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "skin must be a value that is at least 0!"); - return false; + return this.GetDifficultyFloat("teleport_range_max", difficulty, 1500.0); } - GetProfileDifficultyNumValues(kv, "skin", profileData.Skin, profileData.Skin); - profileData.SkinMax = kv.GetNum("skin_max", profileData.SkinMax); - if (profileData.SkinMax < 0) + public float GetMinTeleportTime(int difficulty) { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "skin_max must be a value that is at least 0!"); - return false; + return this.GetDifficultyFloat("teleport_time_min", difficulty, 5.0); } - profileData.SkinDifficultiesOn = kv.GetNum("skin_difficulty", profileData.SkinDifficultiesOn) != 0; + public float GetMaxTeleportTime(int difficulty) + { + return this.GetDifficultyFloat("teleport_time_max", difficulty, 9.0); + } - profileData.Body[1] = kv.GetNum("body", profileData.Body[1]); - if (profileData.Body[1] < 0) + public float GetTeleportRestPeriod(int difficulty) { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "body must be a value that is at least 0!"); - return false; + return this.GetDifficultyFloat("teleport_target_rest_period", difficulty, 15.0); } - GetProfileDifficultyNumValues(kv, "body", profileData.Body, profileData.Body); - profileData.BodyMax = kv.GetNum("body_max", profileData.BodyMax); - if (profileData.BodyMax < 0) + public float GetMinTeleportStress(int difficulty) { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "body_max must be a value that is at least 0!"); - return false; + return this.GetDifficultyFloat("teleport_target_stress_min", difficulty, 0.2); } - profileData.BodyDifficultiesOn = kv.GetNum("body_difficulty", profileData.BodyDifficultiesOn) != 0; + public float GetMaxTeleportStress(int difficulty) + { + return this.GetDifficultyFloat("teleport_target_stress_max", difficulty, 1.0); + } - profileData.RaidHitbox = kv.GetNum("use_raid_hitbox", profileData.RaidHitbox) != 0; + public float GetTeleportPersistencyPeriod(int difficulty) + { + return this.GetDifficultyFloat("teleport_target_persistency_period", difficulty, 13.0); + } - profileData.InstantKillRadius = kv.GetFloat("kill_radius", profileData.InstantKillRadius); + property int TeleportIgnoreChases + { + public get() + { + return this.GetInt("teleport_target_ignore_chases", false); + } + } - profileData.ScareRadius = kv.GetFloat("scare_radius", profileData.ScareRadius); - if (profileData.ScareRadius < 0.0) + property int TeleportIgnoreVis { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "scare_radius must be a value that is at least 0!"); - return false; + public get() + { + return this.GetInt("teleport_target_ignore_visibility", false); + } } - profileData.TeleportType = kv.GetNum("teleport_type", profileData.TeleportType); - if (profileData.TeleportType < 0) + property float FOV { - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "unknown teleport type!"); - return false; + public get() + { + return this.GetFloat("fov", 90.0); + } } - FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "unknown!"); + property float TurnRate + { + public get() + { + float val = 250.0; + val = this.GetFloat("maxyawrate", val); + val = this.GetFloat("turnrate", val); + return val; + } + } - profileData.FOV = kv.GetFloat("fov", profileData.FOV); - if (profileData.FOV < 0.0) + property float ScareRadius { - profileData.FOV = 0.0; + public get() + { + return this.GetFloat("scare_radius", 0.0); + } } - else if (profileData.FOV > 360.0) + + property float ScareCooldown { - profileData.FOV = 360.0; + public get() + { + return this.GetFloat("scare_cooldown", 0.0); + } } - profileData.TurnRate = kv.GetFloat("maxyawrate", profileData.TurnRate); - profileData.TurnRate = kv.GetFloat("turnrate", profileData.TurnRate); + property float ScareReplenishSprintAmount + { + public get() + { + return this.GetFloat("scare_player_replenish_sprint_amount", 0.0); + } + } - switch (profileData.Type) + property float ScareSpeedBoostDuration { - case SF2BossType_Chaser: + public get() { - profileData.Description.Type = "Chaser"; + return this.GetFloat("scare_player_speed_boost_duration", 0.0); } + } - case SF2BossType_Statue: + property int ScareReactionType + { + public get() { - profileData.Description.Type = "Statue"; + return this.GetInt("scare_player_reaction_type", 0); } } - if (kv.JumpToKey("description")) + public void GetCustomScareReaction(char[] buffer, int bufferSize) { - profileData.Description.Load(kv); - kv.GoBack(); + this.GetString("scare_player_reaction_response_custom", buffer, bufferSize); } - profileData.ScareCooldown = kv.GetFloat("scare_cooldown", profileData.ScareCooldown); - if (profileData.ScareCooldown < 0.0) + public float GetJumpscareDistance(int difficulty) { - // clamp value - profileData.ScareCooldown = 0.0; + return this.GetDifficultyFloat("jumpscare_distance", difficulty, 0.0); } - kv.GetVector("mins", profileData.Mins, profileData.Mins); - kv.GetVector("maxs", profileData.Maxs, profileData.Maxs); + public float GetJumpscareDuration(int difficulty) + { + return this.GetDifficultyFloat("jumpscare_duration", difficulty, 0.0); + } - profileData.StepSize = kv.GetFloat("stepsize", profileData.StepSize); + public float GetJumpscareCooldown(int difficulty) + { + return this.GetDifficultyFloat("jumpscare_cooldown", difficulty, 0.0); + } - profileData.NodeDistanceLookAhead = kv.GetFloat("search_node_dist_lookahead", profileData.NodeDistanceLookAhead); + property bool JumpscareOnScare + { + public get() + { + return this.GetBool("jumpscare_on_scare", false); + } + } - GetProfileColorNoBacks(kv, "effect_rendercolor", profileData.RenderColor[0], profileData.RenderColor[1], profileData.RenderColor[2], profileData.RenderColor[3], - profileData.RenderColor[0], profileData.RenderColor[1], profileData.RenderColor[2], profileData.RenderColor[3]); - profileData.RenderFX = kv.GetNum("effect_renderfx", profileData.RenderFX); - profileData.RenderMode = kv.GetNum("effect_rendermode", profileData.RenderMode); + property bool JumpscareNoSight + { + public get() + { + return this.GetBool("jumpscare_no_sight", false); + } + } - kv.GetString("kill_weapontype", profileData.WeaponString, sizeof(profileData.WeaponString), profileData.WeaponString); - profileData.WeaponInt = kv.GetNum("kill_weapontype", profileData.WeaponInt); + public ProfileSound GetJumpscareSounds() + { + return view_as(this.GetSection("sound_jumpscare")); + } - profileData.DiscoMode = kv.GetNum("disco_mode", profileData.DiscoMode) != 0; - if (profileData.DiscoMode) + public KeyMap_Array GetJumpscareOverlays() { - profileData.DiscoDistanceMin = kv.GetFloat("disco_mode_rng_distance_min", profileData.DiscoDistanceMin); - profileData.DiscoDistanceMax = kv.GetFloat("disco_mode_rng_distance_max", profileData.DiscoDistanceMax); - kv.GetVector("disco_mode_pos", profileData.DiscoPos, profileData.DiscoPos); + return this.GetArray("overlay_jumpscare"); } - profileData.FestiveLights = kv.GetNum("festive_lights", profileData.FestiveLights) != 0; - if (profileData.FestiveLights) + public KeyMap_Array GetRedCameraOverlays() { - profileData.FestiveLightBrightness = kv.GetNum("festive_light_brightness", profileData.FestiveLightBrightness); - profileData.FestiveLightDistance = kv.GetFloat("festive_light_distance", profileData.FestiveLightDistance); - profileData.FestiveLightRadius = kv.GetFloat("festive_light_radius", profileData.FestiveLightRadius); - kv.GetVector("festive_lights_pos", profileData.FestiveLightPos, profileData.FestiveLightPos); - kv.GetVector("festive_lights_ang", profileData.FestiveLightAng, profileData.FestiveLightAng); + return this.GetArray("overlay_red_camera"); } - if (kv.GetNum("tp_effect_spawn", false) != 0) + public void GetActiveRedCameraOverlay(char[] buffer, int bufferSize) { - if (profileData.SpawnEffects == null) + this.GetString("__active_red_camera_overlay", buffer, bufferSize); + } + + public void SetActiveRedCameraOverlay(char[] value) + { + this.SetString("__active_red_camera_overlay", value); + } + + public void GetHullMins(float vec[3]) + { + this.GetVector("mins", vec, HULL_HUMAN_MINS); + } + + public void GetHullMaxs(float vec[3]) + { + this.GetVector("maxs", vec, HULL_HUMAN_MAXS); + } + + property float StepSize + { + public get() { - profileData.SpawnEffects = new StringMap(); + return this.GetFloat("stepsize", 18.0); } - ArrayList listEffects = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); + } - SF2BossProfileBaseEffectInfo particle; - SF2BossProfileBaseEffectInfo sound; + property float NodeDistanceLookAhead + { + public get() + { + return this.GetFloat("search_node_dist_lookahead", 256.0); + } + } - particle.Init(); - particle.Type = EffectType_Particle; - particle.Event = EffectEvent_Constant; - kv.GetString("tp_effect_spawn_particle", particle.ParticleName, sizeof(particle.ParticleName), particle.ParticleName); - particle.LifeTime = 0.1; - kv.GetVector("tp_effect_origin", particle.Origin, particle.Origin); - particle.PostLoad(); - listEffects.PushArray(particle); + public void GetRenderColor(int difficulty, int buffer[4]) + { + this.GetDifficultyColor("effect_rendercolor", difficulty, buffer); + } - char soundName[PLATFORM_MAX_PATH]; - sound.Init(); - sound.Type = EffectType_Sound; - sound.Event = EffectEvent_Constant; - kv.GetString("tp_effect_spawn_sound", soundName, sizeof(soundName), soundName); - TryPrecacheBossProfileSoundPath(soundName, g_FileCheckConVar.BoolValue); - sound.SoundSounds.Paths = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - sound.SoundSounds.Paths.PushString(soundName); - sound.SoundSounds.Volume = kv.GetFloat("tp_effect_spawn_sound_volume", sound.SoundSounds.Volume); - sound.SoundSounds.Pitch = kv.GetNum("tp_effect_spawn_sound_pitch", sound.SoundSounds.Pitch); - sound.SoundSounds.PostLoad(); - listEffects.PushArray(sound); + public RenderFx GetRenderFx(int difficulty) + { + return view_as(this.GetDifficultyInt("effect_renderfx", difficulty, view_as(RENDERFX_NONE))); + } - profileData.SpawnEffects.SetValue("TPEffectSpawnBackwards", listEffects); + public RenderMode GetRenderMode(int difficulty) + { + return view_as(this.GetDifficultyInt("effect_rendermode", difficulty, view_as(RENDER_NORMAL))); } - if (kv.GetNum("tp_effect_despawn", false) != 0) + public BossProfileEyeData GetEyes() { - if (profileData.DespawnEffects == null) + ProfileObject obj = this.GetSection("eyes"); + if (obj == null) { - profileData.DespawnEffects = new StringMap(); + obj = this; } - ArrayList listEffects = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); + return view_as(obj); + } - SF2BossProfileBaseEffectInfo particle; - SF2BossProfileBaseEffectInfo sound; + public bool HasStaticOnRadius(int difficulty) + { + return this.GetDifficultyBool("static_on_radius", difficulty); + } - particle.Init(); - particle.Type = EffectType_Particle; - particle.Event = EffectEvent_Constant; - kv.GetString("tp_effect_despawn_particle", particle.ParticleName, sizeof(particle.ParticleName), particle.ParticleName); - particle.LifeTime = 0.1; - kv.GetVector("tp_effect_origin", particle.Origin, particle.Origin); - particle.PostLoad(); - listEffects.PushArray(particle); + public float GetStaticRadius(int difficulty) + { + return this.GetDifficultyFloat("static_radius", difficulty, 150.0); + } - char soundName[PLATFORM_MAX_PATH]; - sound.Init(); - sound.Type = EffectType_Sound; - sound.Event = EffectEvent_Constant; - kv.GetString("tp_effect_despawn_sound", soundName, sizeof(soundName), soundName); - TryPrecacheBossProfileSoundPath(soundName, g_FileCheckConVar.BoolValue); - sound.SoundSounds.Paths = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - sound.SoundSounds.Paths.PushString(soundName); - sound.SoundSounds.Volume = kv.GetFloat("tp_effect_despawn_sound_volume", sound.SoundSounds.Volume); - sound.SoundSounds.Pitch = kv.GetNum("tp_effect_despawn_sound_pitch", sound.SoundSounds.Pitch); - sound.SoundSounds.PostLoad(); - listEffects.PushArray(sound); + public float GetStaticRate(int difficulty) + { + static const float defaultValue[Difficulty_Max] = { 0.8, 0.8, 0.75, 0.7, 0.5, 0.4 }; - profileData.DespawnEffects.SetValue("TPEffectDespawnBackwards", listEffects); + return this.GetDifficultyFloat("static_rate", difficulty, defaultValue[difficulty]); } - profileData.BlinkLookRate = kv.GetFloat("blink_look_rate_multiply", profileData.BlinkLookRate); - profileData.BlinkStaticRate = kv.GetFloat("blink_static_rate_multiply", profileData.BlinkStaticRate); - - profileData.DeathCam = kv.GetNum("death_cam", profileData.DeathCam) != 0; - if (profileData.DeathCam) + public float GetStaticRateDecay(int difficulty) { - profileData.DeathCamScareSound = kv.GetNum("death_cam_play_scare_sound", profileData.DeathCamScareSound) != 0; - profileData.PublicDeathCam = kv.GetNum("death_cam_public", profileData.PublicDeathCam) != 0; - if (profileData.PublicDeathCam) - { - profileData.PublicDeathCamSpeed = kv.GetFloat("death_cam_speed", profileData.PublicDeathCamSpeed); - profileData.PublicDeathCamAcceleration = kv.GetFloat("death_cam_acceleration", profileData.PublicDeathCamAcceleration); - profileData.PublicDeathCamDeceleration = kv.GetFloat("death_cam_deceleration", profileData.PublicDeathCamDeceleration); - profileData.PublicDeathCamBackwardOffset = kv.GetFloat("deathcam_death_backward_offset", profileData.PublicDeathCamBackwardOffset); - profileData.PublicDeathCamDownwardOffset = kv.GetFloat("deathcam_death_downward_offset", profileData.PublicDeathCamDownwardOffset); - } - profileData.DeathCamOverlay = kv.GetNum("death_cam_overlay", profileData.DeathCamOverlay) != 0; - profileData.DeathCamOverlayStartTime = kv.GetFloat("death_cam_time_overlay_start", profileData.DeathCamOverlayStartTime); - if (profileData.DeathCamOverlayStartTime < 0.0) - { - profileData.DeathCamOverlayStartTime = 0.0; - } - profileData.DeathCamTime = kv.GetFloat("death_cam_time_death", profileData.DeathCamTime); - if (profileData.DeathCamTime < 0.0) - { - profileData.DeathCamTime = 0.0; - } - kv.GetVector("death_cam_pos", profileData.DeathCamPos, profileData.DeathCamPos); - kv.GetString("death_cam_attachtment_target_point", profileData.PublicDeathCamAttachmentTarget, sizeof(profileData.PublicDeathCamAttachmentTarget), profileData.PublicDeathCamAttachmentTarget); - kv.GetString("death_cam_attachtment_point", profileData.PublicDeathCamAttachment, sizeof(profileData.PublicDeathCamAttachment), profileData.PublicDeathCamAttachment); + static const float defaultValue[Difficulty_Max] = { 0.2, 0.2, 0.25, 0.3, 0.4, 0.55 }; + + return this.GetDifficultyFloat("static_rate_decay", difficulty, defaultValue[difficulty]); } - if (kv.JumpToKey("public_death_cam")) + public bool HasStaticOnLook(int difficulty) { - profileData.DeathCamData.Load(kv, g_FileCheckConVar.BoolValue); - kv.GoBack(); + return this.GetDifficultyBool("static_on_look", difficulty); } - GetProfileDifficultyFloatValues(kv, "sound_music_loop", profileData.SoundMusicLoop, profileData.SoundMusicLoop); - GetProfileDifficultyFloatValues(kv, "kill_cooldown", profileData.InstantKillCooldown, profileData.InstantKillCooldown); - - GetProfileDifficultyFloatValues(kv, "search_view_distance", profileData.SearchRange, profileData.SearchRange); - GetProfileDifficultyFloatValues(kv, "search_range", profileData.SearchRange, profileData.SearchRange); - GetProfileDifficultyFloatValues(kv, "hearing_range", profileData.SearchSoundRange, profileData.SearchSoundRange); - GetProfileDifficultyFloatValues(kv, "search_sound_range", profileData.SearchSoundRange, profileData.SearchSoundRange); - GetProfileDifficultyFloatValues(kv, "taunt_alert_range", profileData.TauntAlertRange, profileData.TauntAlertRange); + public float GetStaticOnLookGraceTime(int difficulty) + { + return this.GetDifficultyFloat("static_on_look_gracetime", difficulty, 1.0); + } - GetProfileDifficultyBoolValues(kv, "teleport_allowed", profileData.TeleportAllowed, profileData.TeleportAllowed); - GetProfileDifficultyFloatValues(kv, "teleport_range_min", profileData.TeleportRangeMin, profileData.TeleportRangeMin); - GetProfileDifficultyFloatValues(kv, "teleport_range_max", profileData.TeleportRangeMax, profileData.TeleportRangeMax); - GetProfileDifficultyFloatValues(kv, "teleport_time_min", profileData.TeleportTimeMin, profileData.TeleportTimeMin); - GetProfileDifficultyFloatValues(kv, "teleport_time_max", profileData.TeleportTimeMax, profileData.TeleportTimeMax); - GetProfileDifficultyFloatValues(kv, "teleport_target_rest_period", profileData.TeleportRestPeriod, profileData.TeleportRestPeriod); - GetProfileDifficultyFloatValues(kv, "teleport_target_stress_min", profileData.TeleportStressMin, profileData.TeleportStressMin); - GetProfileDifficultyFloatValues(kv, "teleport_target_stress_max", profileData.TeleportStressMax, profileData.TeleportStressMax); - GetProfileDifficultyFloatValues(kv, "teleport_target_persistency_period", profileData.TeleportPersistencyPeriod, profileData.TeleportPersistencyPeriod); - profileData.TeleportIgnoreChases = kv.GetNum("teleport_target_ignore_chases", profileData.TeleportIgnoreChases) != 0; - profileData.TeleportIgnoreVis = kv.GetNum("teleport_target_ignore_visibility", profileData.TeleportIgnoreVis) != 0; + public float GetScareStaticAmount(int difficulty) + { + return this.GetDifficultyFloat("scare_static_amount", difficulty); + } - GetProfileDifficultyFloatValues(kv, "jumpscare_distance", profileData.JumpscareDistance, profileData.JumpscareDistance); - GetProfileDifficultyFloatValues(kv, "jumpscare_duration", profileData.JumpscareDuration, profileData.JumpscareDuration); - GetProfileDifficultyFloatValues(kv, "jumpscare_cooldown", profileData.JumpscareCooldown, profileData.JumpscareCooldown); - profileData.JumpscareOnScare = kv.GetNum("jumpscare_on_scare", profileData.JumpscareOnScare) != 0; - profileData.JumpscareNoSight = kv.GetNum("jumpscare_no_sight", profileData.JumpscareNoSight) != 0; + public float GetRunSpeed(int difficulty) + { + return this.GetDifficultyFloat("speed", difficulty, 300.0); + } - GetProfileDifficultyFloatValues(kv, "speed", profileData.RunSpeed, profileData.RunSpeed); - GetProfileDifficultyFloatValues(kv, "acceleration", profileData.Acceleration, profileData.Acceleration); + public float GetForwardFriction(int difficulty) + { + return this.GetDifficultyFloat("friction_forward", difficulty, 0.0); + } - GetProfileDifficultyFloatValues(kv, "idle_lifetime", profileData.IdleLifeTime, profileData.IdleLifeTime); + public float GetSidewaysFriction(int difficulty) + { + return this.GetDifficultyFloat("friction_sideways", difficulty, 3.0); + } - profileData.CustomOutlines = kv.GetNum("customizable_outlines", profileData.CustomOutlines) != 0; - if (profileData.CustomOutlines) + public float GetAcceleration(int difficulty) { - profileData.OutlineColor[0] = kv.GetNum("outline_color_r", profileData.OutlineColor[0]); - profileData.OutlineColor[1] = kv.GetNum("outline_color_g", profileData.OutlineColor[1]); - profileData.OutlineColor[2] = kv.GetNum("outline_color_b", profileData.OutlineColor[2]); - profileData.OutlineColor[3] = kv.GetNum("outline_color_transparency", profileData.OutlineColor[3]); - profileData.RainbowOutline = !!kv.GetNum("enable_rainbow_outline", profileData.RainbowOutline); - if (profileData.RainbowOutline) + float def; + + switch (this.Type) { - profileData.RainbowOutlineCycle = kv.GetFloat("rainbow_outline_cycle_rate", profileData.RainbowOutlineCycle); - if (profileData.RainbowOutlineCycle < 0.0) + case SF2BossType_Statue: { - profileData.RainbowOutlineCycle = 0.0; + def = 10000.0; + } + default: + { + def = 4000.0; } } + + return this.GetDifficultyFloat("acceleration", difficulty, def); } - profileData.SpeedBoostOnScare = !!kv.GetNum("scare_player_speed_boost", profileData.SpeedBoostOnScare); - if (profileData.SpeedBoostOnScare) + public float GetSearchRange(int difficulty) { - profileData.ScareSpeedBoostDuration = kv.GetFloat("scare_player_speed_boost_duration", profileData.ScareSpeedBoostDuration); + float value = 1024.0; + value = this.GetDifficultyFloat("search_view_distance", difficulty, value); + value = this.GetDifficultyFloat("search_range", difficulty, value); + return value; } - profileData.ScareReaction = !!kv.GetNum("scare_player_reaction", profileData.ScareReaction); - if (profileData.ScareReaction) + public ProfileMasterAnimations GetAnimations() { - profileData.ScareReactionType = kv.GetNum("scare_player_reaction_type", profileData.ScareReactionType); - if (profileData.ScareReactionType < 1) - { - profileData.ScareReactionType = 1; - } - if (profileData.ScareReactionType > 3) + return view_as(this.GetSection("animations")); + } + + public BossProfileCopies GetCopies() + { + ProfileObject obj = this.GetSection("copies"); + if (obj == null) { - profileData.ScareReactionType = 3; + obj = this; } - kv.GetString("scare_player_reaction_response_custom", profileData.ScareReactionCustom, sizeof(profileData.ScareReactionCustom), profileData.ScareReactionCustom); + return view_as(obj); } - profileData.ScareReplenishSprint = kv.GetNum("scare_player_replenish_sprint", profileData.ScareReplenishSprint) != 0; - if (profileData.ScareReplenishSprint) + public BossProfileCompanions GetCompanions() { - profileData.ScareReplenishSprintAmount = kv.GetFloat("scare_player_replenish_sprint_amount", profileData.ScareReplenishSprintAmount); + return view_as(this.GetSection("companions")); } - GetProfileDifficultyFloatValues(kv, "static_radius", profileData.StaticRadius, profileData.StaticRadius); - GetProfileDifficultyFloatValues(kv, "static_rate", profileData.StaticRate, profileData.StaticRate); - GetProfileDifficultyFloatValues(kv, "static_rate_decay", profileData.StaticRateDecay, profileData.StaticRateDecay); - GetProfileDifficultyFloatValues(kv, "static_on_look_gracetime", profileData.StaticGraceTime, profileData.StaticGraceTime); - profileData.StaticScareAmount = kv.GetFloat("scare_static_amount", profileData.StaticScareAmount); - - profileData.StaticShakeLocalLevel = kv.GetNum("sound_static_loop_local_level", profileData.StaticShakeLocalLevel); - profileData.StaticShakeVolumeMin = kv.GetFloat("sound_static_shake_local_volume_min", profileData.StaticShakeVolumeMin); - profileData.StaticShakeVolumeMax = kv.GetFloat("sound_static_shake_local_volume_max", profileData.StaticShakeVolumeMax); + public BossProfileProxyData GetProxies() + { + return view_as(this.GetSection("proxies")); + } - profileData.DrainCredits = !!kv.GetNum("drain_credits_on_kill", profileData.DrainCredits); - GetProfileDifficultyNumValues(kv, "drain_credits_amount", profileData.DrainCreditAmount, profileData.DrainCreditAmount); + public BossProfileDeathCamData GetDeathCamData() + { + return view_as(this.GetSection("death_cam")); + } - profileData.Healthbar = !!kv.GetNum("healthbar", profileData.Healthbar); + public BossProfilePvEData GetPvEData() + { + return view_as(this.GetSection("pve")); + } - profileData.DeathMessageDifficultyIndexes = kv.GetNum("chat_message_upon_death_difficulty_indexes", profileData.DeathMessageDifficultyIndexes); - if (kv.JumpToKey("chat_message_upon_death")) + property bool IsPvEBoss { - profileData.DeathMessagesArray = new ArrayList(ByteCountToCells(256)); - char message[256], section[64]; - for (int i = 1;; i++) + public get() { - FormatEx(section, sizeof(section), "%d", i); - kv.GetString(section, message, sizeof(message)); - if (message[0] == '\0') - { - break; - } - - profileData.DeathMessagesArray.PushString(message); + return this.GetPvEData().IsEnabled; } - kv.GoBack(); } - kv.GetString("chat_message_upon_death_prefix", profileData.DeathMessagePrefix, sizeof(profileData.DeathMessagePrefix), profileData.DeathMessagePrefix); - profileData.BurnRagdoll = kv.GetNum("burn_ragdoll_on_kill", profileData.BurnRagdoll) != 0; - profileData.CloakRagdoll = kv.GetNum("cloak_ragdoll_on_kill", profileData.CloakRagdoll) != 0; - profileData.DecapRagdoll = kv.GetNum("decap_ragdoll_on_kill", profileData.DecapRagdoll) != 0; - profileData.GibRagdoll = kv.GetNum("gib_ragdoll_on_kill", profileData.GibRagdoll) != 0; - profileData.IceRagdoll = kv.GetNum("ice_ragdoll_on_kill", profileData.IceRagdoll) != 0; - profileData.GoldRagdoll = kv.GetNum("gold_ragdoll_on_kill", profileData.GoldRagdoll) != 0; - profileData.ElectrocuteRagdoll = kv.GetNum("electrocute_ragdoll_on_kill", profileData.ElectrocuteRagdoll) != 0; - profileData.AshRagdoll = kv.GetNum("disintegrate_ragdoll_on_kill", profileData.AshRagdoll) != 0; - profileData.DeleteRagdoll = kv.GetNum("delete_ragdoll_on_kill", profileData.DeleteRagdoll) != 0; - profileData.PushRagdoll = kv.GetNum("push_ragdoll_on_kill", profileData.PushRagdoll) != 0; - if (profileData.PushRagdoll) + public BossProfileOutlineData GetOutlineData() { - kv.GetVector("push_ragdoll_force", profileData.PushRagdollForce, profileData.PushRagdollForce); + return view_as(this.GetSection("outline")); } - profileData.DissolveRagdoll = kv.GetNum("dissolve_ragdoll_on_kill", profileData.DissolveRagdoll) != 0; - if (profileData.DissolveRagdoll) + + public void GetStaticSound(char[] buffer, int bufferSize) { - profileData.DissolveKillType = kv.GetNum("dissolve_ragdoll_type", profileData.DissolveKillType); + ProfileObject obj = this.GetSection("sound_static"); + if (obj != null) + { + obj.GetString("1", buffer, bufferSize); + } } - profileData.PlasmaRagdoll = kv.GetNum("plasma_ragdoll_on_kill", profileData.PlasmaRagdoll) != 0; - profileData.ResizeRagdoll = kv.GetNum("resize_ragdoll_on_kill", profileData.ResizeRagdoll) != 0; - if (profileData.ResizeRagdoll) + + public void GetStaticLocalLoopSound(char[] buffer, int bufferSize) { - profileData.ResizeRagdollHead = kv.GetFloat("resize_ragdoll_head", profileData.ResizeRagdollHead); - profileData.ResizeRagdollHands = kv.GetFloat("resize_ragdoll_hands", profileData.ResizeRagdollHands); - profileData.ResizeRagdollTorso = kv.GetFloat("resize_ragdoll_torso", profileData.ResizeRagdollTorso); + ProfileObject obj = this.GetSection("sound_static_loop_local"); + if (obj != null) + { + obj.GetString("1", buffer, bufferSize); + } } - profileData.DecapOrGibRagdoll = kv.GetNum("decap_or_gib_ragdoll_on_kill", profileData.DecapOrGibRagdoll) != 0; - profileData.SilentKill = kv.GetNum("silent_kill", profileData.SilentKill) != 0; - profileData.MultiEffectRagdoll = kv.GetNum("multieffect_ragdoll_on_kill", profileData.MultiEffectRagdoll) != 0; - profileData.CustomDeathFlag = kv.GetNum("attack_custom_deathflag_enabled", profileData.CustomDeathFlag) != 0; - if (profileData.CustomDeathFlag) + + public void GetStaticShakeLocalSound(char[] buffer, int bufferSize) { - profileData.CustomDeathFlagType = kv.GetNum("attack_custom_deathflag", profileData.CustomDeathFlagType); + ProfileObject obj = this.GetSection("sound_static_shake_local"); + if (obj != null) + { + obj.GetString("1", buffer, bufferSize); + } } - profileData.OutroMusic = kv.GetNum("sound_music_outro_enabled", profileData.OutroMusic) != 0; - - profileData.EngineSoundLevel = kv.GetNum("constant_sound_level", profileData.EngineSoundLevel); - profileData.EngineSoundLevel = kv.GetNum("engine_sound_level", profileData.EngineSoundLevel); - profileData.EngineSoundVolume = kv.GetFloat("constant_sound_volume", profileData.EngineSoundVolume); - profileData.EngineSoundVolume = kv.GetFloat("engine_sound_volume", profileData.EngineSoundVolume); - - kv.GetVector("eye_pos", profileData.EyePosOffset, profileData.EyePosOffset); - kv.GetVector("eye_ang_offset", profileData.EyeAngOffset, profileData.EyeAngOffset); - - if (kv.JumpToKey("eyes")) + property float StaticShakeMinVolume { - profileData.EyeData.Load(kv); - kv.GoBack(); - profileData.EyePosOffset = profileData.EyeData.OffsetPos; - profileData.EyeAngOffset = profileData.EyeData.OffsetAng; + public get() + { + return this.GetFloat("sound_static_shake_local_volume_min", 0.0); + } } - if (kv.JumpToKey("slaughter_run")) + property float StaticShakeMaxVolume { - profileData.SlaughterRunData.Load(kv); - kv.GoBack(); + public get() + { + return this.GetFloat("sound_static_shake_local_volume_max", 0.0); + } } - // Parse through flags. - int bossFlags = 0; - if (kv.GetNum("static_on_look")) + property int StaticShakeLocalLevel { - bossFlags |= SFF_STATICONLOOK; + public get() + { + return this.GetInt("sound_static_loop_local_level", SNDLEVEL_NORMAL); + } } - if (kv.GetNum("static_on_radius")) + + property float BlinkLookRate { - bossFlags |= SFF_STATICONRADIUS; + public get() + { + return this.GetFloat("blink_look_rate_multiply", 1.0); + } } - if (kv.GetNum("proxies")) + + property float BlinkStaticRate { - bossFlags |= SFF_PROXIES; + public get() + { + return this.GetFloat("blink_static_rate_multiply", 1.0); + } } - if (kv.GetNum("jumpscare")) + + public ProfileSound GetIntroSounds() { - bossFlags |= SFF_HASJUMPSCARE; + return view_as(this.GetSection("sound_spawn_all")); } - if (kv.GetNum("sound_static_loop_local_enabled")) + + public ProfileSound GetLocalSpawnSounds() { - bossFlags |= SFF_HASSTATICLOOPLOCALSOUND; + return view_as(this.GetSection("sound_spawn_local")); } - if (kv.GetNum("view_shake", 1)) + + public ProfileSound GetScareSounds() { - bossFlags |= SFF_HASVIEWSHAKE; + return view_as(this.GetSection("sound_scare_player")); } - if (kv.GetNum("wander_move", 1)) + + public ProfileSound GetClientDeathCamSounds() { - bossFlags |= SFF_WANDERMOVE; + ProfileObject obj = this.GetSection("sound_player_deathcam"); + if (obj == null) + { + obj = this.GetSection("sound_player_death"); + } + return view_as(obj); } - if (kv.GetNum("attack_props", 0)) + + public ProfileSound GetGlobalDeathCamSounds() { - bossFlags |= SFF_ATTACKPROPS; + return view_as(this.GetSection("sound_player_deathcam_all")); } - if (kv.GetNum("attack_weaponsenable", 0)) + + public ProfileSound GetLocalDeathCamSounds() { - bossFlags |= SFF_WEAPONKILLS; + return view_as(this.GetSection("sound_player_deathcam_local")); } - if (kv.GetNum("kill_weaponsenable", 0)) + + public ProfileSound GetOverlayDeathCamSounds() { - bossFlags |= SFF_WEAPONKILLSONRADIUS; + return view_as(this.GetSection("sound_player_deathcam_overlay")); } - if (kv.GetNum("static_shake"), 0) + + public ProfileSound GetSightSounds() { - bossFlags |= SFF_HASSTATICSHAKE; + return view_as(this.GetSection("sound_sight")); } - profileData.Flags = bossFlags; - profileData.CopiesInfo.Load(kv); + public void GetWeaponString(char[] buffer, int bufferSize) + { + this.GetString("kill_weapontype", buffer, bufferSize); + } - if (profileData.Flags & SFF_PROXIES) + property int WeaponInt { - kv.GetString("proxies_classes", profileData.ProxyClasses, sizeof(profileData.ProxyClasses), profileData.ProxyClasses); - GetProfileDifficultyFloatValues(kv, "proxies_damage_scale_vs_enemy", profileData.ProxyDamageVsEnemy, profileData.ProxyDamageVsEnemy); - GetProfileDifficultyFloatValues(kv, "proxies_damage_scale_vs_enemy_backstab", profileData.ProxyDamageVsBackstab, profileData.ProxyDamageVsBackstab); - GetProfileDifficultyFloatValues(kv, "proxies_damage_scale_vs_self", profileData.ProxyDamageVsSelf, profileData.ProxyDamageVsSelf); - GetProfileDifficultyNumValues(kv, "proxies_controlgain_hitenemy", profileData.ProxyControlGainHitEnemy, profileData.ProxyControlGainHitEnemy); - GetProfileDifficultyNumValues(kv, "proxies_controlgain_hitbyenemy", profileData.ProxyControlGainHitByEnemy, profileData.ProxyControlGainHitByEnemy); - GetProfileDifficultyFloatValues(kv, "proxies_controldrainrate", profileData.ProxyControlDrainRate, profileData.ProxyControlDrainRate); - GetProfileDifficultyNumValues(kv, "proxies_max", profileData.MaxProxies, profileData.MaxProxies); - GetProfileDifficultyFloatValues(kv, "proxies_spawn_chance_min", profileData.ProxySpawnChanceMin, profileData.ProxySpawnChanceMin); - GetProfileDifficultyFloatValues(kv, "proxies_spawn_chance_max", profileData.ProxySpawnChaceMax, profileData.ProxySpawnChaceMax); - GetProfileDifficultyFloatValues(kv, "proxies_spawn_chance_threshold", profileData.ProxySpawnChanceThreshold, profileData.ProxySpawnChanceThreshold); - GetProfileDifficultyNumValues(kv, "proxies_spawn_num_min", profileData.ProxySpawnNumMin, profileData.ProxySpawnNumMin); - GetProfileDifficultyNumValues(kv, "proxies_spawn_num_max", profileData.ProxySpawnNumMax, profileData.ProxySpawnNumMax); - GetProfileDifficultyFloatValues(kv, "proxies_spawn_cooldown_min", profileData.ProxySpawnCooldownMin, profileData.ProxySpawnCooldownMin); - GetProfileDifficultyFloatValues(kv, "proxies_spawn_cooldown_max", profileData.ProxySpawnCooldownMax, profileData.ProxySpawnCooldownMax); - GetProfileDifficultyFloatValues(kv, "proxies_teleport_range_min", profileData.ProxyTeleportRangeMin, profileData.ProxyTeleportRangeMin); - GetProfileDifficultyFloatValues(kv, "proxies_teleport_range_max", profileData.ProxyTeleportRangeMax, profileData.ProxyTeleportRangeMax); - profileData.ProxyAllowVoices = !!kv.GetNum("proxies_allownormalvoices", profileData.ProxyAllowVoices); - profileData.ProxyWeapons = !!kv.GetNum("proxies_weapon", profileData.ProxyWeapons); - profileData.ProxyDeathAnimations = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - char className[15], deathSection[64], storedAnim[PLATFORM_MAX_PATH]; - for (int i = 0; i < 10; i++) + public get() { - TF2_GetClassName(view_as(i), className, sizeof(className)); - if (i == 0) - { - deathSection = "proxies_death_anim_all"; - } - else - { - FormatEx(deathSection, sizeof(deathSection), "proxies_death_anim_%s", className); - } - kv.GetString(deathSection, storedAnim, sizeof(storedAnim)); - profileData.ProxyDeathAnimations.PushString(storedAnim); - if (i == 0) - { - deathSection = "proxies_death_anim_frames_all"; - } - else - { - FormatEx(deathSection, sizeof(deathSection), "proxies_death_anim_frames_%s", className); - } - profileData.ProxyDeathAnimFrames[i] = kv.GetNum(deathSection); + return this.GetInt("kill_weapontypeint", 0); } - if (profileData.ProxyWeapons) + } + + property bool AshRagdoll + { + public get() { - profileData.ProxyWeaponClassNames = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - profileData.ProxyWeaponStats = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - char classKey[15], keyValue[45], arrayStringValue[PLATFORM_MAX_PATH]; - for (int i = 1; i < 10; i++) - { - TF2_GetClassName(view_as(i), classKey, sizeof(classKey)); + return this.GetBool("disintegrate_ragdoll_on_kill", false); + } + } - FormatEx(keyValue, sizeof(keyValue), "proxies_weapon_class_%s", classKey); - kv.GetString(keyValue, arrayStringValue, sizeof(arrayStringValue)); - profileData.ProxyWeaponClassNames.PushString(arrayStringValue); + property bool CloakRagdoll + { + public get() + { + return this.GetBool("cloak_ragdoll_on_kill", false); + } + } - FormatEx(keyValue, sizeof(keyValue), "proxies_weapon_stats_%s", classKey); - kv.GetString(keyValue, arrayStringValue, sizeof(arrayStringValue)); - profileData.ProxyWeaponStats.PushString(arrayStringValue); + property bool DecapRagdoll + { + public get() + { + return this.GetBool("decap_ragdoll_on_kill", false); + } + } - FormatEx(keyValue, sizeof(keyValue), "proxies_weapon_index_%s", classKey); - profileData.ProxyWeaponIndexes[i] = kv.GetNum(keyValue, profileData.ProxyWeaponIndexes[i]); + property bool DeleteRagdoll + { + public get() + { + return this.GetBool("delete_ragdoll_on_kill", false); + } + } - FormatEx(keyValue, sizeof(keyValue), "proxies_weapon_slot_%s", classKey); - profileData.ProxyWeaponSlots[i] = kv.GetNum(keyValue, profileData.ProxyWeaponSlots[i]); - } + property bool DissolveRagdoll + { + public get() + { + return this.GetBool("dissolve_ragdoll_on_kill", false); } - profileData.ProxySpawnEffect = kv.GetNum("proxies_spawn_effect_enabled", profileData.ProxySpawnEffect) != 0; - if (profileData.ProxySpawnEffect) + } + + property int DissolveKillType + { + public get() { - kv.GetString("proxies_spawn_effect", profileData.ProxySpawnEffectName, sizeof(profileData.ProxySpawnEffectName), profileData.ProxySpawnEffectName); - profileData.ProxySpawnEffectZOffset = kv.GetFloat("proxies_spawn_effect_z_offset", profileData.ProxySpawnEffectZOffset); + return this.GetInt("dissolve_ragdoll_type", 0); } - profileData.ProxyZombies = kv.GetNum("proxies_zombie", profileData.ProxyZombies) != 0; - profileData.ProxyRobots = kv.GetNum("proxies_robot", profileData.ProxyRobots) != 0; - profileData.ProxyDifficultyModels = kv.GetNum("proxy_difficulty_models", profileData.ProxyDifficultyModels) != 0; + } - char index[64], modelDirectory[PLATFORM_MAX_PATH]; - if (profileData.ProxyDifficultyModels) + property bool ElectrocuteRagdoll + { + public get() { - for (int i = 0; i < 10; i++) - { - for (int j = 1; j < Difficulty_Max; j--) - { - TF2_GetClassName(view_as(i), className, sizeof(className)); - if (i == 0) - { - switch (j) - { - case Difficulty_Normal: - { - deathSection = "mod_proxy_all"; - } - case Difficulty_Hard: - { - deathSection = "mod_proxy_all_hard"; - } - case Difficulty_Insane: - { - deathSection = "mod_proxy_all_insane"; - } - case Difficulty_Nightmare: - { - deathSection = "mod_proxy_all_nightmare"; - } - case Difficulty_Apollyon: - { - deathSection = "mod_proxy_all_apollyon"; - } - } - } - else - { - switch (j) - { - case Difficulty_Normal: - { - FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s", className); - } - case Difficulty_Hard: - { - FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s_hard", className); - } - case Difficulty_Insane: - { - FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s_insane", className); - } - case Difficulty_Nightmare: - { - FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s_nightmare", className); - } - case Difficulty_Apollyon: - { - FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s_apollyon", className); - } - } - } - if (kv.JumpToKey(deathSection)) - { - switch (j) - { - case Difficulty_Normal: - { - profileData.ProxyModels[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - } - case Difficulty_Hard: - { - profileData.ProxyModelsHard[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - } - case Difficulty_Insane: - { - profileData.ProxyModelsInsane[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - } - case Difficulty_Nightmare: - { - profileData.ProxyModelsNightmare[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - } - case Difficulty_Apollyon: - { - profileData.ProxyModelsApollyon[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - } - } - for (int i2 = 1;; i2++) - { - FormatEx(index, sizeof(index), "%d", i2); - kv.GetString(index, modelDirectory, sizeof(modelDirectory)); - if (modelDirectory[0] == '\0') - { - break; - } + return this.GetBool("electrocute_ragdoll_on_kill", false); + } + } - if (!PrecacheModel(modelDirectory, true)) - { - LogSF2Message("Proxy model file %s failed to be precached, this model will not be used.", modelDirectory); - } - else - { - switch (j) - { - case Difficulty_Normal: - { - profileData.ProxyModels[i].PushString(modelDirectory); - } - case Difficulty_Hard: - { - profileData.ProxyModelsHard[i].PushString(modelDirectory); - } - case Difficulty_Insane: - { - profileData.ProxyModelsInsane[i].PushString(modelDirectory); - } - case Difficulty_Nightmare: - { - profileData.ProxyModelsNightmare[i].PushString(modelDirectory); - } - case Difficulty_Apollyon: - { - profileData.ProxyModelsApollyon[i].PushString(modelDirectory); - } - } - PrecacheModel2(modelDirectory, _, _, g_FileCheckConVar.BoolValue); - } - } - kv.GoBack(); - } - } - } - } - else + property bool GoldRagdoll + { + public get() { - for (int i = 0; i < 10; i++) - { - TF2_GetClassName(view_as(i), className, sizeof(className)); - if (i == 0) - { - deathSection = "mod_proxy_all"; - } - else - { - FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s", className); - } - if (kv.JumpToKey(deathSection)) - { - profileData.ProxyModels[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - for (int i2 = 1;; i2++) - { - FormatEx(index, sizeof(index), "%d", i2); - kv.GetString(index, modelDirectory, sizeof(modelDirectory)); - if (modelDirectory[0] == '\0') - { - break; - } - - if (!PrecacheModel(modelDirectory, true)) - { - LogSF2Message("Proxy model file %s failed to be precached, this model will not be used.", modelDirectory); - } - else - { - profileData.ProxyModels[i].PushString(modelDirectory); - } - } - kv.GoBack(); - } - } + return this.GetBool("gold_ragdoll_on_kill", false); } + } - profileData.ProxyOverrideMaxSpeed = !!kv.GetNum("proxies_override_max_speed", profileData.ProxyOverrideMaxSpeed); - if (profileData.ProxyOverrideMaxSpeed) + property bool IceRagdoll + { + public get() { - GetProfileDifficultyFloatValues(kv, "proxies_max_speed", profileData.ProxyMaxSpeed, profileData.ProxyMaxSpeed); + return this.GetBool("ice_ragdoll_on_kill", false); } } - UnloadBossProfile(profile); - - profileData.AnimationData.Load(kv, true); - - switch (profileData.Type) + property bool PlasmaRagdoll { - case SF2BossType_Statue: + public get() { - if (!LoadStatueBossProfile(kv, profile, loadFailReasonBuffer, loadFailReasonBufferLen, profileData)) - { - return false; - } + return this.GetBool("plasma_ragdoll_on_kill", false); } - case SF2BossType_Chaser: + } + + property bool PushRagdoll + { + public get() { - if (!LoadChaserBossProfile(kv, profile, loadFailReasonBuffer, loadFailReasonBufferLen, profileData)) - { - return false; - } + return this.GetBool("push_ragdoll_on_kill", false); } } - // Add the section to our config. - g_Config.Rewind(); - g_Config.JumpToKey(profile, true); - g_Config.Import(kv); - kv.GetString("constant_sound", profileData.EngineSound, sizeof(profileData.EngineSound), profileData.EngineSound); - kv.GetString("engine_sound", profileData.EngineSound, sizeof(profileData.EngineSound), profileData.EngineSound); + public void GetPushRagdollForce(float buffer[3]) + { + this.GetVector("push_ragdoll_force", buffer); + } - TryPrecacheBossProfileSoundPath(profileData.EngineSound, g_FileCheckConVar.BoolValue); + property bool ResizeRagdoll + { + public get() + { + return this.GetBool("resize_ragdoll_on_kill", false); + } + } - int index = g_BossProfileList.FindString(profile); - if (index == -1) + property float ResizeRagdollHead { - g_BossProfileList.PushString(profile); + public get() + { + return this.GetFloat("resize_ragdoll_head", 1.0); + } } - if (kv.GetNum("enable_random_selection", true) != 0) + property float ResizeRagdollTorso { - if (GetSelectableBossProfileList().FindString(profile) == -1) + public get() { - // Add to the selectable boss list if it isn't there already. - GetSelectableBossProfileList().PushString(profile); + return this.GetFloat("resize_ragdoll_torso", 1.0); } } - else + + property float ResizeRagdollHands { - int selectIndex = GetSelectableBossProfileList().FindString(profile); - if (selectIndex != -1) + public get() { - GetSelectableBossProfileList().Erase(selectIndex); + return this.GetFloat("resize_ragdoll_hands", 1.0); } } - if (kv.GetNum("admin_only", false) != 0) + property bool BurnRagdoll { - if (GetSelectableAdminBossProfileList().FindString(profile) == -1) + public get() { - // Add to the selectable boss list if it isn't there already. - GetSelectableAdminBossProfileList().PushString(profile); + return this.GetBool("burn_ragdoll_on_kill", false); } } - else + + property bool GibRagdoll { - int selectIndex = GetSelectableAdminBossProfileList().FindString(profile); - if (selectIndex != -1) + public get() { - GetSelectableAdminBossProfileList().Erase(selectIndex); + return this.GetBool("gib_ragdoll_on_kill", false); } } - if (kv.GetNum("enable_random_selection_boxing", false) != 0) + property bool DecapOrGibRagdoll { - if (GetSelectableBoxingBossProfileList().FindString(profile) == -1) + public get() { - // Add to the selectable boss list if it isn't there already. - GetSelectableBoxingBossProfileList().PushString(profile); + return this.GetBool("decap_or_gib_ragdoll_on_kill", false); } } - else + + property bool MultiEffectRagdoll { - int selectIndex = GetSelectableBoxingBossProfileList().FindString(profile); - if (selectIndex != -1) + public get() { - GetSelectableBoxingBossProfileList().Erase(selectIndex); + return this.GetBool("multieffect_ragdoll_on_kill", false); } } - if (kv.GetNum("enable_random_selection_renevant", false) != 0) + property bool CustomDeathFlag { - if (GetSelectableRenevantBossProfileList().FindString(profile) == -1) + public get() { - // Add to the selectable boss list if it isn't there already. - GetSelectableRenevantBossProfileList().PushString(profile); + return this.GetBool("attack_custom_deathflag_enabled", false); } } - else + + property int CustomDeathFlagType { - int selectIndex = GetSelectableRenevantBossProfileList().FindString(profile); - if (selectIndex != -1) + public get() { - GetSelectableRenevantBossProfileList().Erase(selectIndex); + return this.GetInt("attack_custom_deathflag", 0); } } - if (kv.GetNum("enable_random_selection_renevant_admin", false) != 0) + public void GetConstantSound(char[] buffer, int bufferSize) { - if (GetSelectableRenevantBossAdminProfileList().FindString(profile) == -1) + this.GetString("constant_sound", buffer, bufferSize, buffer); + this.GetString("engine_sound", buffer, bufferSize, buffer); + } + + property int ConstantSoundLevel + { + public get() { - // Add to the selectable boss list if it isn't there already. - GetSelectableRenevantBossAdminProfileList().PushString(profile); + int def = 83; + def = this.GetInt("constant_sound_level", def); + def = this.GetInt("engine_sound_level", def); + return def; } } - else + + property float ConstantSoundVolume { - int selectIndex = GetSelectableRenevantBossAdminProfileList().FindString(profile); - if (selectIndex != -1) + public get() { - GetSelectableRenevantBossAdminProfileList().Erase(selectIndex); + float def = 0.8; + def = this.GetFloat("constant_sound_volume", def); + def = this.GetFloat("engine_sound_volume", def); + return def; } } - if (kv.JumpToKey("pve")) + public float GetIdleLifeTime(int difficulty) { - profileData.IsPvEBoss = true; - kv.GoBack(); + return this.GetDifficultyFloat("idle_lifetime", difficulty, 10.0); } - else + + public BossProfileSlaughterRunData GetSlaughterRunData() { - profileData.IsPvEBoss = kv.GetNum("is_pve", profileData.IsPvEBoss) != 0; + return view_as(this.GetSection("slaughter_run")); } - if (profileData.IsPvEBoss) + public KeyMap_Array GetSpawnEffects() { - profileData.Flags = profileData.Flags & ~SFF_PROXIES; - if (kv.JumpToKey("pve")) + return this.GetArray("spawn_effects"); + } + + public ProfileInputsList GetSpawnInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) { - if (kv.JumpToKey("spawn_messages")) - { - profileData.PvESpawnMessagesArray = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - char message[256], section[64]; - for (int i = 1;; i++) - { - FormatEx(section, sizeof(section), "%d", i); - kv.GetString(section, message, sizeof(message)); - if (message[0] == '\0') - { - break; - } + return view_as(obj.GetSection("spawn")); + } + return null; + } - profileData.PvESpawnMessagesArray.PushString(message); - } - kv.GoBack(); - } - kv.GetString("spawn_message_prefix", profileData.PvESpawnMessagePrefix, sizeof(profileData.PvESpawnMessagePrefix), profileData.PvESpawnMessagePrefix); - profileData.DisplayPvEHealth = kv.GetNum("health_bar", profileData.DisplayPvEHealth) != 0; - char setProfile[SF2_MAX_PROFILE_NAME_LENGTH]; - strcopy(setProfile, sizeof(setProfile), profile); - if (kv.GetNum("selectable", 1) != 0) - { - RegisterPvESlenderBoss(setProfile); - } - profileData.PvETeleportEndTimer = kv.GetFloat("teleport_players_time", profileData.PvETeleportEndTimer); - kv.GoBack(); + public KeyMap_Array GetDespawnEffects() + { + return this.GetArray("despawn_effects"); + } + + public ProfileInputsList GetDespawnInputs() + { + ProfileObject obj = this.GetSection("inputs"); + if (obj != null) + { + return view_as(obj.GetSection("despawn")); } - else + return null; + } + + public ProfileOutputsList GetOutputs() + { + return view_as(this.GetSection("outputs")); + } + + property bool HideDespawnEffectsOnDeath + { + public get() { - if (kv.JumpToKey("pve_spawn_messages")) - { - profileData.PvESpawnMessagesArray = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - char message[256], section[64]; - for (int i = 1;; i++) - { - FormatEx(section, sizeof(section), "%d", i); - kv.GetString(section, message, sizeof(message)); - if (message[0] == '\0') - { - break; - } + return this.GetBool("hide_on_death", false); + } + } - profileData.PvESpawnMessagesArray.PushString(message); - } - kv.GoBack(); - } - kv.GetString("pve_spawn_message_prefix", profileData.PvESpawnMessagePrefix, sizeof(profileData.PvESpawnMessagePrefix), profileData.PvESpawnMessagePrefix); - profileData.DisplayPvEHealth = kv.GetNum("pve_health_bar", profileData.DisplayPvEHealth) != 0; - char setProfile[SF2_MAX_PROFILE_NAME_LENGTH]; - strcopy(setProfile, sizeof(setProfile), profile); - if (kv.GetNum("pve_selectable", 1) != 0) - { - RegisterPvESlenderBoss(setProfile); - } + property bool DiscoMode + { + public get() + { + return this.GetBool("disco_mode", false); } + } - index = GetSelectableBossProfileList().FindString(profile); - if (index != -1) + property float DiscoDistanceMin + { + public get() { - GetSelectableBossProfileList().Erase(index); + return this.GetFloat("disco_mode_rng_distance_min", 420.0); } - index = GetSelectableAdminBossProfileList().FindString(profile); - if (index != -1) + } + + property float DiscoDistanceMax + { + public get() { - GetSelectableAdminBossProfileList().Erase(index); + return this.GetFloat("disco_mode_rng_distance_max", 750.0); } - index = GetSelectableBoxingBossProfileList().FindString(profile); - if (index != -1) + } + + public void GetDiscoPos(float buffer[3]) + { + this.GetVector("disco_mode_pos", buffer); + } + + property bool FestiveLights + { + public get() { - GetSelectableBoxingBossProfileList().Erase(index); + return this.GetBool("festive_lights", false); } - index = GetSelectableRenevantBossProfileList().FindString(profile); - if (index != -1) + } + + property int FestiveLightBrightness + { + public get() { - GetSelectableRenevantBossProfileList().Erase(index); + return this.GetInt("festive_light_brightness", 0); } - index = GetSelectableRenevantBossAdminProfileList().FindString(profile); - if (index != -1) + } + + property float FestiveLightDistance + { + public get() { - GetSelectableRenevantBossAdminProfileList().Erase(index); + return this.GetFloat("festive_light_distance", 0.0); } } - ArrayList validSections = new ArrayList(ByteCountToCells(128)); + property float FestiveLightRadius + { + public get() + { + return this.GetFloat("festive_light_radius", 0.0); + } + } - if (kv.GotoFirstSubKey()) //Special thanks to Fire for modifying the code for download errors. + public void GetFestiveLightPos(float buffer[3]) { - char s2[64], s3[64], s4[PLATFORM_MAX_PATH], s5[PLATFORM_MAX_PATH]; + this.GetVector("festive_lights_pos", buffer); + } - do - { - kv.GetSectionName(s2, sizeof(s2)); + public void GetFestiveLightAng(float buffer[3]) + { + this.GetVector("festive_lights_ang", buffer); + } - if (validSections.FindString(s2) != -1) + property float TickRate + { + public get() + { + return this.GetFloat("tick_rate", 0.0); + } + } + + public ProfileMusic GetGlobalMusic(int difficulty) + { + return view_as(this.GetDifficultySection("sound_music", difficulty)); + } + + public ProfileGlobalTracks GetGlobalTracks() + { + ProfileObject obj = this.GetSection("music"); + obj = obj != null ? obj.GetSection("global") : null; + if (obj != null) + { + return view_as(obj); + } + + return null; + } + + public ProfileSound GetOutroWinSounds() + { + return view_as(this.GetSection("sound_outro_win")); + } + + public ProfileSound GetOutroLoseSounds() + { + return view_as(this.GetSection("sound_outro_lose")); + } + + public BossProfileAttributes GetAttributes() + { + return view_as(this.GetSection("attributes")); + } + + public BossProfileDescription GetDescription() + { + return view_as(this.GetSection("description")); + } + + public ProfileSound GetFootstepEventSounds(int index) + { + char formatter[64]; + FormatEx(formatter, sizeof(formatter), "sound_footsteps_event_%i", index); + return view_as(this.GetSection(formatter)); + } + + public ProfileSound GetEventSounds(int index) + { + char formatter[64]; + FormatEx(formatter, sizeof(formatter), "sound_event_%i", index); + return view_as(this.GetSection(formatter)); + } + + public BossProfileEventData GetEvents(int index) + { + char formatter[64]; + FormatEx(formatter, sizeof(formatter), "%d", index); + ProfileObject obj = this.GetSection("events"); + if (obj != null) + { + return view_as(obj.GetSection(formatter)); + } + return null; + } + + public BossKillSoundsData GetLocalKillSounds() + { + return view_as(this.GetSection("local_kill_sounds")); + } + + public BossKillSoundsData GetGlobalKillSounds() + { + return view_as(this.GetSection("global_kill_sounds")); + } + + public BossKillSoundsData GetClientKillSounds() + { + return view_as(this.GetSection("client_kill_sounds")); + } + + public ProfileObject GetMapSelectionBlacklist() + { + ProfileObject obj = this.GetSection("selection_blacklist"); + obj = obj != null ? obj.GetSection("maps") : null; + return obj; + } + + public ProfileObject GetModeSelectionBlacklist() + { + ProfileObject obj = this.GetSection("selection_blacklist"); + obj = obj != null ? obj.GetSection("modes") : null; + return obj; + } + + public BossOnPageCountChangedData GetPageCountChagedData(int count, float highest = -1.0, float& newHighest = 0.0) + { + ProfileObject obj = this.GetSection("on_page_collected"); + if (obj == null) + { + return null; + } + + float percent = float(count) / float(g_PageMax); + char highestName[64]; + for (int i = 0; i < obj.SectionLength; i++) + { + char name[64]; + obj.GetSectionNameFromIndex(i, name, sizeof(name)); + float value = StringToFloat(name); + if (value > highest && value <= percent) { - continue; + highest = value; + highestName = name; } + } - validSections.PushString(s2); + newHighest = highest; - if (StrContains(s2, "sound_") != -1) + return view_as(obj.GetSection(highestName)); + } + + public void Precache() + { + char path[PLATFORM_MAX_PATH], key[512], value[2048]; + for (int i = 0; i < Difficulty_Max; i++) + { + this.GetModel(i, path, sizeof(path)); + if (path[0] != '\0') { - bool doBack = false; - if (kv.JumpToKey("paths")) - { - doBack = true; - } - for (int i = 1;; i++) - { - FormatEx(s3, sizeof(s3), "%d", i); - kv.GetString(s3, s4, sizeof(s4)); - if (s4[0] == '\0') - { - break; - } + PrecacheModel2(path, _, _, g_FileCheckConVar.BoolValue); + } + } - TryPrecacheBossProfileSoundPath(s4, g_FileCheckConVar.BoolValue); + path[0] = '\0'; + this.GetConstantSound(path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheSound2(path, g_FileCheckConVar.BoolValue); + } - // Here comes an if else mess, I'm very sorry - if (strcmp(s2, "sound_jumpscare") == 0) - { - profileData.JumpscareSound = s4; - break; - } - else if (strcmp(s2, "sound_static") == 0) - { - profileData.StaticSound = s4; - break; - } - else if (strcmp(s2, "sound_static_loop_local") == 0) - { - profileData.StaticLocalSound = s4; - break; - } - else if (strcmp(s2, "sound_static_shake_local") == 0) - { - profileData.StaticShakeLocal = s4; - break; - } - } - if (doBack) + ProfileObject obj = this.GetSection("on_page_collected"); + if (obj != null) + { + for (int i = 0; i < obj.SectionLength; i++) + { + char name[64]; + obj.GetSectionNameFromIndex(i, name, sizeof(name)); + BossOnPageCountChangedData pageData = view_as(obj.GetSection(name)); + if (pageData == null) { - kv.GoBack(); + continue; } - profileData.SortSoundSections(kv, s2, g_FileCheckConVar.BoolValue); + + pageData.Precache(); } - else if (strcmp(s2, "download") == 0) + } + + // ======================== + // LEGACY KEY CONVERSION + // ======================== + ProfileObject newObj = null, temp = null, temp2 = null, temp3 = null, temp4 = null; + ArrayList keys = new ArrayList(ByteCountToCells(256)); + if (this.GetAnimations() != null) + { + for (int i = 0; i < this.GetAnimations().SectionLength; i++) { - for (int i = 1;; i++) + char animType[64], name[64], formatter[256]; + int size = 0; + this.GetAnimations().GetSectionNameFromIndex(i, animType, sizeof(animType)); + temp = this.GetAnimations().GetSection(animType); + for (int i2 = 0; i2 < temp.SectionLength; i2++) { - FormatEx(s3, sizeof(s3), "%d", i); - kv.GetString(s3, s4, sizeof(s4)); - if (s4[0] == '\0') + temp.GetSectionNameFromIndex(i2, name, sizeof(name)); + temp2 = temp.GetSection(name); + for (int i3 = 0; i3 < temp2.KeyLength; i3++) { - break; - } - - if (g_FileCheckConVar.BoolValue) - { - if (FileExists(s4) || FileExists(s4, true)) - { - AddFileToDownloadsTable(s4); - } - else + temp2.GetKeyNameFromIndex(i3, name, sizeof(name)); + FormatEx(formatter, sizeof(formatter), "animation_%s", animType); + if (strcmp(name, formatter) == 0) { - LogSF2Message("File %s does not exist, please fix this download or remove it from the array.", s4); + size = temp2.GetKeyValueLength(name); + } } - else - { - AddFileToDownloadsTable(s4); - } } } - else if (strcmp(s2, "mod_precache") == 0) + } + else + { + keys.PushString("animation_idle"); + keys.PushString("animation_walk"); + keys.PushString("animation_walkalert"); + keys.PushString("animation_attack"); + keys.PushString("animation_shoot"); + keys.PushString("animation_run"); + keys.PushString("animation_chaseinitial"); + keys.PushString("animation_rage"); + keys.PushString("animation_stun"); + keys.PushString("animation_death"); + keys.PushString("animation_spawn"); + keys.PushString("animation_fleestart"); + keys.PushString("animation_heal"); + keys.PushString("animation_deathcam"); + keys.PushString("animation_crawlwalk"); + keys.PushString("animation_crawlrun"); + if (this.ContainsAnyDifficultyKey(keys)) { - for (int i = 1;; i++) - { - FormatEx(s3, sizeof(s3), "%d", i); - kv.GetString(s3, s4, sizeof(s4)); - if (s4[0] == '\0') - { - break; - } + newObj = this.InsertNewSection("animations"); + this.InsertAnimationSection("idle", newObj); + this.InsertAnimationSection("walk", newObj); + this.InsertAnimationSection("walkalert", newObj); + this.InsertAnimationSection("attack", newObj); + this.InsertAnimationSection("shoot", newObj); + this.InsertAnimationSection("run", newObj); + this.InsertAnimationSection("chaseinitial", newObj); + this.InsertAnimationSection("rage", newObj); + this.InsertAnimationSection("stun", newObj); + this.InsertAnimationSection("death", newObj); + this.InsertAnimationSection("spawn", newObj); + this.InsertAnimationSection("fleestart", newObj); + this.InsertAnimationSection("heal", newObj); + this.InsertAnimationSection("deathcam", newObj); + this.InsertAnimationSection("crawlwalk", newObj); + this.InsertAnimationSection("crawlrun", newObj); + } + } - if (!PrecacheModel(s4, true)) - { - LogSF2Message("Model file %s failed to be precached, likely does not exist. This will crash the server if not fixed.", s4); - } - } + char particle[128], sound[PLATFORM_MAX_PATH]; + float offset[3]; + this.GetVector("tp_effect_origin", offset, offset); + if (this.GetBool("tp_effect_spawn")) + { + newObj = this.InsertNewSection("spawn_effects"); + temp = newObj.InsertNewSection("Legacy Spawn"); + temp2 = temp.InsertNewSection("effects"); + this.GetString("tp_effect_spawn_particle", particle, sizeof(particle)); + this.GetString("tp_effect_spawn_sound", sound, sizeof(sound)); + if (particle[0] != '\0') + { + temp3 = temp2.InsertNewSection("particle"); + temp3.SetString("type", "particle"); + temp3.SetString("particlename", particle); + temp3.SetVector("origin", offset); } - else if (strcmp(s2, "mat_download") == 0) + + if (sound[0] != '\0') { - for (int i = 1;; i++) - { - FormatEx(s3, sizeof(s3), "%d", i); - kv.GetString(s3, s4, sizeof(s4)); - if (s4[0] == '\0') - { - break; - } + float defFloat = 1.0; + defFloat = this.GetFloat("tp_effect_spawn_sound_volume", defFloat); + int defInt = 100; + defInt = this.GetInt("tp_effect_spawn_sound_pitch", defInt); + + temp3 = temp2.InsertNewSection("sound"); + temp3.SetString("type", "sound"); + temp4 = temp3.InsertNewSection("paths"); + temp4.SetString("1", sound); + temp3.SetFloat("volume", defFloat); + temp3.SetInt("pitch", defInt); + } + } - FormatEx(s5, sizeof(s5), "%s.vtf", s4); - if (g_FileCheckConVar.BoolValue) - { - if (FileExists(s5) || FileExists(s5, true)) - { - AddFileToDownloadsTable(s5); - } - else - { - LogSF2Message("Texture file %s does not exist, please fix this download or remove it from the array.", s5); - } - } - else - { - AddFileToDownloadsTable(s5); - } + if (this.GetBool("tp_effect_despawn")) + { + newObj = this.InsertNewSection("despawn_effects"); + temp = newObj.InsertNewSection("Legacy Despawn"); + temp2 = temp.InsertNewSection("effects"); + this.GetString("tp_effect_despawn_particle", particle, sizeof(particle)); + this.GetString("tp_effect_despawn_sound", sound, sizeof(sound)); + if (particle[0] != '\0') + { + temp3 = temp2.InsertNewSection("particle"); + temp3.SetString("type", "particle"); + temp3.SetString("particlename", particle); + temp3.SetVector("origin", offset); + } - FormatEx(s5, sizeof(s5), "%s.vmt", s4); - if (g_FileCheckConVar.BoolValue) - { - if (FileExists(s5) || FileExists(s5, true)) - { - AddFileToDownloadsTable(s5); - } - else - { - LogSF2Message("Material file %s does not exist, please fix this download or remove it from the array.", s5); - } - } - else - { - AddFileToDownloadsTable(s5); - } - } + if (sound[0] != '\0') + { + float defFloat = 1.0; + defFloat = this.GetFloat("tp_effect_despawn_sound_volume", defFloat); + int defInt = 100; + defInt = this.GetInt("tp_effect_despawn_sound_pitch", defInt); + + temp3 = temp2.InsertNewSection("sound"); + temp3.SetString("type", "sound"); + temp4 = temp3.InsertNewSection("paths"); + temp4.SetString("1", sound); + temp3.SetFloat("volume", defFloat); + temp3.SetInt("pitch", defInt); } - else if (strcmp(s2, "mod_download") == 0) + } + + if (this.GetBool("customizable_outlines", false)) + { + newObj = this.InsertNewSection("outline"); + int color[4]; + color[0] = this.GetInt("outline_color_r", 255); + color[1] = this.GetInt("outline_color_g", 255); + color[2] = this.GetInt("outline_color_b", 255); + color[3] = this.GetInt("outline_color_transparency", 255); + ColorToString(color, value, sizeof(value)); + newObj.SetKeyValue("color", value); + newObj.TransferKey(this, "enable_rainbow_outline", "rainbow"); + newObj.TransferKey(this, "rainbow_outline_cycle_rate", "rainbow_cycle"); + } + + if (this.GetBool("is_pve")) + { + newObj = this.InsertNewSection("pve"); + newObj.TransferKey(this, "pve_spawn_message_prefix", "spawn_message_prefix"); + newObj.TransferKey(this, "pve_health_bar", "health_bar"); + newObj.TransferKey(this, "pve_selectable", "selectable"); + if (this.GetSection("pve_spawn_messages") != null) { - for (int i = 1;; i++) - { - FormatEx(s3, sizeof(s3), "%d", i); - kv.GetString(s3, s4, sizeof(s4)); - if (s4[0] == '\0') - { - break; - } + temp = view_as(this.GetSection("pve_spawn_messages").Clone()); + temp.SetSectionName("spawn_messages"); + newObj.AddExistingSection(temp); + this.RemoveKey("pve_spawn_messages"); + } + } - PrecacheModel2(s4, _, _, g_FileCheckConVar.BoolValue); - } + if (this.GetBool("enable_random_selection_boxing")) + { + this.TransferKey(this, "enable_random_selection_boxing", "enable_random_selection"); + } + + if (this.GetBool("enable_random_selection_renevant")) + { + this.TransferKey(this, "enable_random_selection_renevant", "enable_random_selection"); + } + + for (int i = 0; i < this.SectionLength; i++) + { + this.GetSectionNameFromIndex(i, key, sizeof(key)); + if (StrContains(key, "sound_footsteps_event_", false) != -1) + { + newObj = this.InsertNewSection("events"); + temp = view_as(this.GetSection(key).Clone()); + this.RemoveKey(key); + ReplaceStringEx(key, sizeof(key), "sound_footsteps_event_", "", .caseSensitive = false); + temp.SetSectionName("sounds"); + temp2 = newObj.InsertNewSection(key); + temp2.AddExistingSection(temp); + temp2.SetKeyValue("footstep", "1"); + i--; + continue; } - else if (strcmp(s2, "overlay_player_death") == 0) + + if (StrContains(key, "sound_event_", false) != -1) { - kv.GetString("1", s4, sizeof(s4)); - profileData.OverlayPlayerDeath = s4; + newObj = this.InsertNewSection("events"); + temp = view_as(this.GetSection(key).Clone()); + this.RemoveKey(key); + ReplaceStringEx(key, sizeof(key), "sound_event_", "", .caseSensitive = false); + temp.SetSectionName("sounds"); + temp2 = newObj.InsertNewSection(key); + temp2.AddExistingSection(temp); + i--; + continue; } - else if (strcmp(s2, "overlay_jumpscare") == 0) + } + + if (this.GetSection("sound_music_outro") != null) + { + this.RenameKey("sound_music_outro", "sound_outro_lose"); + newObj = view_as(this.GetSection("sound_outro_lose").Clone()); + newObj.SetSectionName("sound_outro_win"); + this.AddExistingSection(newObj); + } + + if (this.GetBool("death_cam")) + { + this.RemoveKey("death_cam"); + newObj = this.InsertNewSection("death_cam"); + newObj.TransferKey(this, "death_cam_pos", "look_position"); + newObj.TransferKey(this, "death_cam_play_scare_sound", "scare_sound"); + newObj.SetFloat("__legacy_duration", this.GetFloat("death_cam_time_death") + this.GetFloat("death_cam_time_overlay_start")); + newObj.SetFloat("__legacy_overlay_start", this.GetFloat("death_cam_time_overlay_start")); + if (this.GetSection("overlay_player_death") != null) { - kv.GetString("1", s4, sizeof(s4)); - profileData.OverlayJumpscare = s4; + temp3 = view_as(this.GetSection("overlay_player_death").Clone()); + temp3.SetSectionName("overlays"); + newObj.AddExistingSection(temp3); } - if (StrContains(s2, "sound_footsteps_event_") != -1) + if (this.GetSection("animations") != null) { - if (profileData.FootstepEventSounds == null) + temp = this.GetSection("animations"); + temp2 = newObj.InsertNewSection("animations"); + if (temp.GetSection("deathcam") != null) { - profileData.FootstepEventSounds = new ArrayList(sizeof(SF2BossProfileSoundInfo)); + temp4 = view_as(temp.GetSection("deathcam").Clone()); + temp4.SetSectionName("start"); + temp2.AddExistingSection(temp4); } - if (profileData.FootstepEventIndexes == null) + else if (temp.GetSection("idle") != null) { - profileData.FootstepEventIndexes = new ArrayList(); - } - - SF2BossProfileSoundInfo soundInfo; - soundInfo.Init(); - soundInfo.Load(kv, g_FileCheckConVar.BoolValue); - soundInfo.PostLoad(); - if (soundInfo.Paths != null) - { - strcopy(s3, sizeof(s3), s2); - ReplaceStringEx(s3, sizeof(s3), "sound_footsteps_event_", ""); - int eventNumber = StringToInt(s3); - - profileData.FootstepEventIndexes.Push(eventNumber); - profileData.FootstepEventSounds.PushArray(soundInfo); + temp4 = view_as(temp.GetSection("idle").Clone()); + temp4.SetSectionName("start"); + temp2.AddExistingSection(temp4); } } - else if (StrContains(s2, "sound_event_") != -1) + if (this.GetBool("death_cam_public") || this.GetSection("public_death_cam") != null) { - if (profileData.EventSounds == null) - { - profileData.EventSounds = new ArrayList(sizeof(SF2BossProfileSoundInfo)); - } - if (profileData.EventIndexes == null) + temp = newObj.InsertNewSection("public"); + temp.TransferKey(this, "death_cam_acceleration", "acceleration"); + temp.TransferKey(this, "death_cam_deceleration", "deceleration"); + temp.TransferKey(this, "death_cam_speed", "speed"); + if (this.GetSection("public_death_cam") == null) { - profileData.EventIndexes = new ArrayList(); + this.GetString("death_cam_attachtment_target_point", value, sizeof(value)); + this.GetString("death_cam_attachment_target_point", value, sizeof(value), value); + temp.SetKeyValue("target_attachment", value); + this.GetString("death_cam_attachtment_point", value, sizeof(value)); + this.GetString("death_cam_attachment_point", value, sizeof(value), value); + temp.SetKeyValue("attachment", value); + temp = temp.InsertNewSection("offset"); + temp.TransferKey(this, "deathcam_death_backward_offset", "backward"); + temp.TransferKey(this, "deathcam_death_downward_offset", "downward"); } - - SF2BossProfileSoundInfo soundInfo; - soundInfo.Init(); - soundInfo.Load(kv, g_FileCheckConVar.BoolValue); - soundInfo.PostLoad(); - if (soundInfo.Paths != null) + else { - strcopy(s3, sizeof(s3), s2); - ReplaceStringEx(s3, sizeof(s3), "sound_event_", ""); - int eventNumber = StringToInt(s3); - - profileData.EventIndexes.Push(eventNumber); - profileData.EventSounds.PushArray(soundInfo); + temp2 = this.GetSection("public_death_cam"); + temp.TransferKey(temp2, "acceleration", "acceleration"); + temp.TransferKey(temp2, "deceleration", "deceleration"); + temp.TransferKey(temp2, "target_attachment", "target_attachment"); + temp.TransferKey(temp2, "attachment", "attachment"); + if (temp2.GetSection("offset") != null) + { + temp.AddExistingSection(view_as(temp2.GetSection("offset").Clone())); + } + if (temp2.GetSection("sounds") != null) + { + newObj.AddExistingSection(view_as(temp2.GetSection("sounds").Clone())); + } } } } - while (kv.GotoNextKey()); - - kv.GoBack(); - } - - delete validSections; - - if (kv.JumpToKey("companions")) - { - profileData.CompanionsArray = new ArrayList(sizeof(SF2BossProfileCompanionsInfo)); - kv.GetString("type", profileData.CompanionSpawnType, sizeof(profileData.CompanionSpawnType)); - if (kv.GotoFirstSubKey()) + bool exists = false; + for (int i = 1; i < Difficulty_Max; i++) { - do + if (this.GetDifficultySection("sound_music", i) != null) { - SF2BossProfileCompanionsInfo companions; - companions.Init(); - companions.Load(kv); - profileData.CompanionsArray.PushArray(companions); - if (lookIntoLoads) - { - char compProfile[SF2_MAX_PROFILE_NAME_LENGTH], otherProfile[SF2_MAX_PROFILE_NAME_LENGTH], dir[PLATFORM_MAX_PATH], file[PLATFORM_MAX_PATH]; - FileType fileType; - DirectoryListing directory = OpenDirectory(originalDir); - while (directory.GetNext(file, sizeof(file), fileType)) - { - if (fileType == FileType_Directory) - { - continue; - } - - FormatEx(dir, sizeof(dir), "%s/%s", originalDir, file); - - for (int i = 0; i < companions.Bosses.Length; i++) - { - companions.Bosses.GetString(i, compProfile, sizeof(compProfile)); + exists = true; + break; + } + } - KeyValues otherKeys = new KeyValues("root"); - if (!FileToKeyValues(otherKeys, dir)) - { - delete otherKeys; - continue; - } + if (exists) + { + newObj = this.InsertNewSection("music"); + newObj = newObj.InsertNewSection("global"); - otherKeys.GetSectionName(otherProfile, sizeof(otherProfile)); + for (int i = 1; i < Difficulty_Max; i++) + { + temp = this.GetDifficultySection("sound_music", i); + if (temp == null) + { + continue; + } - if (strcmp(compProfile, otherProfile) == 0) - { - if (!LoadBossProfile(otherKeys, otherProfile, loadFailReasonBuffer, loadFailReasonBufferLen)) - { - LogSF2Message("(COMPANION) %s...FAILED (reason: %s)", dir, loadFailReasonBuffer); - } - else - { - LogSF2Message("(COMPANION) %s...", otherProfile); - } - } + temp = temp.GetSection("paths"); + if (temp == null) + { + continue; + } - delete otherKeys; - } - } + temp2 = newObj.InsertDifficultySection("tracks", i); + for (int i2 = 0; i2 < temp.KeyLength; i2++) + { + temp.GetKeyNameFromIndex(i2, key, sizeof(key)); + temp.GetString(key, path, sizeof(path)); + temp3 = temp2.InsertNewSection(key); + temp3.SetKeyValue("path", path); } } - while (kv.GotoNextKey()); - kv.GoBack(); } - kv.GoBack(); - } - if (kv.JumpToKey("attributes")) - { - profileData.AttributesInfo.Load(kv); - } + this.ConvertSectionsSectionToArray("spawn_effects"); + this.ConvertSectionsSectionToArray("despawn_effects"); + this.ConvertValuesSectionToArray("overlay_jumpscare"); + this.ConvertValuesSectionToArray("overlay_red_camera"); + this.ConvertValuesSectionToArray("mat_download"); + this.ConvertValuesSectionToArray("mod_download"); + this.ConvertValuesSectionToArray("mod_precache"); + this.ConvertValuesSectionToArray("download"); + + KeyMap_Array arr = this.GetArray("mat_download"); + if (arr != null) + { + for (int i = 0; i < arr.Length; i++) + { + char asset[PLATFORM_MAX_PATH]; + arr.GetString(i, asset, sizeof(asset)); - if (kv.JumpToKey("effects")) - { - profileData.EffectsArray = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); + if (asset[0] != '\0') + { + PrecacheMaterial2(asset, g_FileCheckConVar.BoolValue); + } + } + } - if (kv.GotoFirstSubKey()) + arr = this.GetArray("mod_download"); + if (arr != null) { - do + for (int i = 0; i < arr.Length; i++) { - SF2BossProfileBaseEffectInfo effects; - effects.Init(); - effects.ModelScale = profileData.ModelScale; - effects.Load(kv, g_FileCheckConVar.BoolValue); - profileData.EffectsArray.PushArray(effects); + char asset[PLATFORM_MAX_PATH]; + arr.GetString(i, asset, sizeof(asset)); + if (asset[0] != '\0') + { + PrecacheModel2(asset, _, _, g_FileCheckConVar.BoolValue); + } } - while (kv.GotoNextKey()); - kv.GoBack(); } - kv.GoBack(); - } - if (kv.JumpToKey("spawn_effects")) - { - if (profileData.SpawnEffects == null) + arr = this.GetArray("mod_precache"); + if (arr != null) { - profileData.SpawnEffects = new StringMap(); + for (int i = 0; i < arr.Length; i++) + { + char asset[PLATFORM_MAX_PATH]; + arr.GetString(i, asset, sizeof(asset)); + if (asset[0] != '\0') + { + PrecacheModel(asset, true); + } + } } - if (kv.GotoFirstSubKey()) + arr = this.GetArray("download"); + if (arr != null) { - do + for (int i = 0; i < arr.Length; i++) { - char section[64]; - kv.GetSectionName(section, sizeof(section)); - if (kv.JumpToKey("effects")) + char asset[PLATFORM_MAX_PATH]; + arr.GetString(i, asset, sizeof(asset)); + if (asset[0] != '\0') { - ArrayList list = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); - if (kv.GotoFirstSubKey()) - { - do - { - SF2BossProfileBaseEffectInfo effects; - effects.Init(); - effects.ModelScale = profileData.ModelScale; - effects.Load(kv, g_FileCheckConVar.BoolValue); - if (effects.Type == EffectType_Particle) - { - effects.LifeTime = 0.1; - } - list.PushArray(effects); - } - while (kv.GotoNextKey()); - kv.GoBack(); - } - kv.GoBack(); - profileData.SpawnEffects.SetValue(section, list); + AddFileToDownloadsTable(asset); } } - while (kv.GotoNextKey()); - kv.GoBack(); } - kv.GoBack(); - } - if (kv.JumpToKey("despawn_effects")) - { - if (profileData.DespawnEffects == null) + arr = this.GetJumpscareOverlays(); + if (arr != null) { - profileData.DespawnEffects = new StringMap(); + for (int i = 0; i < arr.Length; i++) + { + char asset[PLATFORM_MAX_PATH]; + arr.GetString(i, asset, sizeof(asset)); + if (asset[0] != '\0') + { + PrecacheMaterial2(asset, g_FileCheckConVar.BoolValue); + } + } } - profileData.HideDespawnEffectsOnDeath = kv.GetNum("hide_on_death", profileData.HideDespawnEffectsOnDeath) != 0; - if (kv.GotoFirstSubKey()) + arr = this.GetRedCameraOverlays(); + if (arr != null) { - do + for (int i = 0; i < arr.Length; i++) { - char section[64]; - kv.GetSectionName(section, sizeof(section)); - if (kv.JumpToKey("effects")) + char asset[PLATFORM_MAX_PATH]; + arr.GetString(i, asset, sizeof(asset)); + if (asset[0] != '\0') { - ArrayList list = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); - if (kv.GotoFirstSubKey()) - { - do - { - SF2BossProfileBaseEffectInfo effects; - effects.Init(); - effects.ModelScale = profileData.ModelScale; - effects.Load(kv, g_FileCheckConVar.BoolValue); - if (effects.Type == EffectType_Particle) - { - effects.LifeTime = 0.1; - } - list.PushArray(effects); - } - while (kv.GotoNextKey()); - kv.GoBack(); - } - kv.GoBack(); - profileData.DespawnEffects.SetValue(section, list); + PrecacheMaterial2(asset, g_FileCheckConVar.BoolValue); } } - while (kv.GotoNextKey()); - kv.GoBack(); } - kv.GoBack(); - } - profileData.PostLoad(); + if (this.GetIntroSounds() != null) + { + this.GetIntroSounds().Precache(); + } - g_BossProfileData.SetArray(profile, profileData, sizeof(profileData)); + if (this.GetLocalSpawnSounds() != null) + { + this.GetLocalSpawnSounds().Precache(); + } - Call_StartForward(g_OnBossProfileLoadedFwd); - Call_PushString(profile); - Call_PushCell(kv); - Call_Finish(); + if (this.GetScareSounds() != null) + { + this.GetScareSounds().Precache(); + } - return true; -} + if (this.GetJumpscareSounds() != null) + { + this.GetJumpscareSounds().Precache(); + } -static SF2BossProfileData g_CachedProfileData; + if (this.GetClientDeathCamSounds() != null) + { + this.GetClientDeathCamSounds().Precache(); + } -int GetBossProfileSkin(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.Skin[1]; -} + if (this.GetGlobalDeathCamSounds() != null) + { + this.GetGlobalDeathCamSounds().Precache(); + } -int GetBossProfileSkinDifficulty(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.Skin[difficulty]; -} + if (this.GetLocalDeathCamSounds() != null) + { + this.GetLocalDeathCamSounds().Precache(); + } -bool GetBossProfileSkinDifficultyState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.SkinDifficultiesOn; -} + if (this.GetOverlayDeathCamSounds() != null) + { + this.GetOverlayDeathCamSounds().Precache(); + } -int GetBossProfileSkinMax(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.SkinMax; -} + if (this.GetSightSounds() != null) + { + this.GetSightSounds().Precache(); + } -int GetBossProfileBodyGroups(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.Body[1]; -} + if (this.GetSpawnEffects() != null) + { + for (int i = 0; i < this.GetSpawnEffects().Length; i++) + { + obj = view_as(this.GetSpawnEffects().GetSection(i)); + obj = obj != null ? obj.GetSection("effects") : null; + if (obj == null) + { + continue; + } + view_as(obj).Precache(); + } + } -int GetBossProfileBodyGroupsDifficulty(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.Body[difficulty]; -} + if (this.GetDespawnEffects() != null) + { + for (int i = 0; i < this.GetDespawnEffects().Length; i++) + { + obj = view_as(this.GetDespawnEffects().GetSection(i)); + obj = obj != null ? obj.GetSection("effects") : null; + if (obj == null) + { + continue; + } + view_as(obj).Precache(); + } + } -bool GetBossProfileBodyGroupsDifficultyState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.BodyDifficultiesOn; -} - -int GetBossProfileBodyGroupsMax(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.BodyMax; -} - -bool GetBossProfileRaidHitbox(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.RaidHitbox; -} + if (this.GetGlobalMusic(1) != null) + { + this.GetGlobalMusic(1).Precache(); + } -bool GetBossProfileIgnoreNavPrefer(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.IgnoreNavPrefer; -} + if (this.GetGlobalTracks() != null) + { + this.GetGlobalTracks().Precache(); + } -float GetBossProfileSoundMusicLoop(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.SoundMusicLoop[difficulty]; -} + if (this.GetOutroLoseSounds() != null) + { + this.GetOutroLoseSounds().Precache(); + } -int GetBossProfileMaxCopies(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.CopiesInfo.MaxCopies[difficulty]; -} + if (this.GetOutroWinSounds() != null) + { + this.GetOutroWinSounds().Precache(); + } -float GetBossProfileModelScale(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ModelScale; -} + if (this.GetLocalKillSounds() != null) + { + this.GetLocalKillSounds().Precache(); + } -float GetBossProfileStepSize(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.StepSize; -} + if (this.GetGlobalKillSounds() != null) + { + this.GetGlobalKillSounds().Precache(); + } -float GetBossProfileNodeDistanceLookAhead(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.NodeDistanceLookAhead; -} + if (this.GetClientKillSounds() != null) + { + this.GetClientKillSounds().Precache(); + } -void GetBossProfileRenderColor(const char[] profile, int buffer[4]) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - buffer = g_CachedProfileData.RenderColor; -} + if (this.GetDeathCamData() != null) + { + this.GetDeathCamData().Precache(); + } -int GetBossProfileRenderFX(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.RenderFX; -} + newObj = this.GetSection("events"); + if (newObj != null) + { + for (int i = 0; i < newObj.SectionLength; i++) + { + char index[64]; + newObj.GetSectionNameFromIndex(i, index, sizeof(index)); + BossProfileEventData event = view_as(newObj.GetSection(index)); + if (event == null) + { + continue; + } -int GetBossProfileRenderMode(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.RenderMode; -} + event.Precache(); + } + } -int GetBossProfileWeaponString(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.WeaponString); -} + switch (this.Type) + { + case SF2BossType_Chaser: + { + view_as(this).Precache(); + } -int GetBossProfileWeaponInt(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.WeaponInt; -} + case SF2BossType_Statue: + { + view_as(this).Precache(); + } + } -void GetBossProfileHullMins(const char[] profile, float buffer[3]) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - buffer = g_CachedProfileData.Mins; -} + delete keys; + } -void GetBossProfileHullMaxs(const char[] profile, float buffer[3]) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - buffer = g_CachedProfileData.Maxs; -} + public void InsertAnimationSection(char[] animName, ProfileObject animationSection) + { + ProfileObject temp = null; + char formatter[128], name[64]; -bool GetBossProfileDiscoModeState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DiscoMode; + for (int i = 0; i < Difficulty_Max; i++) + { + FormatEx(formatter, sizeof(formatter), "animation_%s", animName); + if (this.ContainsDifficultyKey(formatter, i)) + { + temp = animationSection.InsertNewSection(animName); + temp = temp.InsertNewSection("1"); + this.GetDifficultyString(formatter, i, name, sizeof(name)); + temp.SetDifficultyString("name", i, name); + FormatEx(formatter, sizeof(formatter), "animation_%s_playbackrate", animName); + temp.SetDifficultyFloat("playbackrate", i, this.GetDifficultyFloat(formatter, i, 1.0)); + FormatEx(formatter, sizeof(formatter), "animation_%s_footstepinterval", animName); + temp.SetDifficultyFloat("footstepinterval", i, this.GetDifficultyFloat(formatter, i, 0.0)); + } + } + } } -float GetBossProfileDiscoRadiusMin(const char[] profile) +methodmap BossProfileEventData < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DiscoDistanceMin; -} + property bool IsFootsteps + { + public get() + { + return this.GetBool("footstep"); + } + } -float GetBossProfileDiscoRadiusMax(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DiscoDistanceMax; -} + public ProfileSound GetSounds() + { + return view_as(this.GetSection("sounds")); + } -void GetBossProfileDiscoPosition(const char[] profile, float buffer[3]) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - buffer = g_CachedProfileData.DiscoPos; -} + public ProfileEffectMaster GetEffects() + { + return view_as(this.GetSection("effects")); + } -bool GetBossProfileFestiveLightState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.FestiveLights; -} + public void Precache() + { + if (this.GetSounds() != null) + { + this.GetSounds().Precache(); + } -int GetBossProfileFestiveLightBrightness(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.FestiveLightBrightness; + if (this.GetEffects() != null) + { + this.GetEffects().Precache(); + } + } } -float GetBossProfileFestiveLightDistance(const char[] profile) +methodmap BossProfilePvEData < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.FestiveLightDistance; -} + property bool IsEnabled + { + public get() + { + return this != null; + } + } -float GetBossProfileFestiveLightRadius(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.FestiveLightRadius; -} + property bool IsSelectable + { + public get() + { + return this.GetBool("selectable", true); + } + } -void GetBossProfileFestiveLightPosition(const char[] profile, float buffer[3]) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - buffer = g_CachedProfileData.FestiveLightPos; -} + public ProfileObject GetSpawnMessages() + { + return this.GetSection("spawn_messages"); + } -void GetBossProfileFestiveLightAngles(const char[] profile, float buffer[3]) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - buffer = g_CachedProfileData.FestiveLightAng; -} + public void GetSpawnMessagePrefix(char[] buffer, int bufferSize) + { + this.GetString("spawn_message_prefix", buffer, bufferSize); + } -ArrayList GetBossProfileNames(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.Names; -} + property bool DisplayHealth + { + public get() + { + this.GetBool("health_bar", true); + } + } -ArrayList GetBossProfileModels(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.Models; + property float TeleportEndTimer + { + public get() + { + return this.GetFloat("teleport_players_time", 5.0); + } + } } -int GetBossProfileType(const char[] profile) +methodmap BossProfileCopies < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.Type; -} + property bool IsLegacy + { + public get() + { + char section[64]; + this.GetSectionName(section, sizeof(section)); + return strcmp(section, "copies") != 0; + } + } -int GetBossProfileFlags(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.Flags; -} + public bool IsEnabled(int difficulty) + { + bool def = false; + if (this.IsLegacy) + { + return this.GetDifficultyBool("copy", difficulty, def); + } + return this.GetDifficultyBool("enabled", difficulty, def); + } -float GetBossProfileBlinkLookRate(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.BlinkLookRate; -} + public int GetMinCopies(int difficulty) + { + int def = 0; + if (this.IsLegacy) + { + return def; + } + return this.GetDifficultyInt("min", difficulty, def); + } -float GetBossProfileBlinkStaticRate(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.BlinkStaticRate; -} + public int GetMaxCopies(int difficulty) + { + int def = 1; + if (this.IsLegacy) + { + return this.GetDifficultyInt("copy_max", difficulty, def); + } + return this.GetDifficultyInt("max", difficulty, def); + } -bool GetBossProfileDeathCamState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DeathCam; -} + public float GetTeleportDistanceSpacing(int difficulty) + { + float def = 800.0; + if (this.IsLegacy) + { + return this.GetDifficultyFloat("copy_teleport_dist_from_others", difficulty, def); + } + return this.GetDifficultyFloat("teleport_spacing_between", difficulty, def); + } -bool GetBossProfileDeathCamScareSound(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DeathCamScareSound; + public bool GetFakes(int difficulty) + { + bool def = false; + if (this.IsLegacy) + { + return this.GetDifficultyBool("fake_copies", difficulty, def); + } + return this.GetDifficultyBool("fakes", difficulty, def); + } } -bool GetBossProfilePublicDeathCamState(const char[] profile) +methodmap BossProfileCompanions < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.PublicDeathCam; -} + public void GetSpawnType(char[] buffer, int bufferSize) + { + this.GetString("type", buffer, bufferSize, "on_spawn"); + } -float GetBossProfilePublicDeathCamSpeed(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.PublicDeathCamSpeed; -} + public bool DoesGroupExist(char[] group) + { + return this.GetSection(group) != null; + } -float GetBossProfilePublicDeathCamAcceleration(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.PublicDeathCamAcceleration; -} + public float GetWeightFromGroup(char[] group, int difficulty) + { + float def = 1.0; + if (!this.DoesGroupExist(group)) + { + return 0.0; + } + return this.GetSection(group).GetDifficultyFloat("weight", difficulty, def); + } -float GetBossProfilePublicDeathCamDeceleration(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.PublicDeathCamDeceleration; -} + public ProfileObject GetBossesFromGroup(char[] group) + { + if (!this.DoesGroupExist(group)) + { + return null; + } + ProfileObject obj = this.GetSection(group); + return obj.GetSection("bosses"); + } -float GetBossProfilePublicDeathCamBackwardOffset(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.PublicDeathCamBackwardOffset; -} + public bool DoesGroupExistEx(int group, char[] buffer, int bufferSize) + { + return this.GetSectionNameFromIndex(group, buffer, bufferSize); + } -float GetBossProfilePublicDeathCamDownwardOffset(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.PublicDeathCamDownwardOffset; -} + public float GetWeightFromGroupEx(int group, int difficulty) + { + float def = 1.0; + char key[SF2_MAX_PROFILE_NAME_LENGTH]; + if (!this.GetSectionNameFromIndex(group, key, sizeof(key))) + { + return 0.0; + } + return this.GetSection(key).GetDifficultyFloat("weight", difficulty, def); + } -bool GetBossProfileDeathCamOverlayState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DeathCamOverlay; + public ProfileObject GetBossesFromGroupEx(int group) + { + char key[SF2_MAX_PROFILE_NAME_LENGTH]; + if (!this.GetSectionNameFromIndex(group, key, sizeof(key))) + { + return null; + } + ProfileObject obj = this.GetSection(key); + return obj.GetSection("bosses"); + } } -float GetBossProfileDeathCamOverlayStartTime(const char[] profile) +methodmap BossProfileEyeData < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DeathCamOverlayStartTime; -} + property bool IsLegacy + { + public get() + { + char section[64]; + this.GetSectionName(section, sizeof(section)); + return strcmp(section, "eyes") != 0; + } + } -float GetBossProfileDeathCamTime(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DeathCamTime; -} + property int Type + { + public get() + { + if (this.IsLegacy) + { + return 0; + } -float GetBossProfileIdleLifetime(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.IdleLifeTime[difficulty]; -} + char type[64]; + this.GetString("mode", type, sizeof(type), "default"); + if (strcmp(type, "bone") == 0) + { + return 1; + } + return 0; + } + } -float GetBossProfileStaticRadius(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.StaticRadius[difficulty]; -} + public void GetOffsetPos(float buffer[3]) + { + float def[3]; + if (this.Type == 0) + { + def = { 0.0, 0.0, 45.0 }; + } + if (this.IsLegacy) + { + this.GetVector("eye_pos", buffer, def); + } + else + { + this.GetVector("offset_pos", buffer, def); + } + } -float GetBossProfileStaticRate(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.StaticRate[difficulty]; -} + public void GetOffsetAng(float buffer[3]) + { + if (this.IsLegacy) + { + this.GetVector("eye_ang_offset", buffer); + } + else + { + this.GetVector("offset_pos", buffer); + } + } -float GetBossProfileStaticRateDecay(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.StaticRateDecay[difficulty]; + public void GetBone(char[] buffer, int bufferSize) + { + this.GetString("bone", buffer, bufferSize); + } } -float GetBossProfileStaticGraceTime(const char[] profile, int difficulty) +methodmap BossProfileOutlineData < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.StaticGraceTime[difficulty]; -} + public void GetOutlineColor(int buffer[4], int difficulty) + { + this.GetDifficultyColor("color", difficulty, buffer); + } -float GetBossProfileStaticScareAmount(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.StaticScareAmount; -} + public bool GetRainbowState(int difficulty) + { + return this.GetDifficultyBool("rainbow", difficulty, false); + } -int GetBossProfileStaticShakeLocalLevel(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.StaticShakeLocalLevel; + public float GetRainbowCycle(int difficulty) + { + return this.GetDifficultyFloat("rainbow_cycle", difficulty, 1.0); + } } -float GetBossProfileStaticShakeLocalVolumeMin(const char[] profile) +methodmap BossProfileSlaughterRunData < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.StaticShakeVolumeMin; -} + public bool ShouldUseCustomMinSpeed(int difficulty) + { + if (this == null) + { + return false; + } + return this.GetDifficultyBool("custom_minimum_speed", difficulty, false); + } -float GetBossProfileStaticShakeLocalVolumeMax(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.StaticShakeVolumeMax; + public float GetCustomSpawnTime(int difficulty) + { + if (this == null) + { + return -1.0; + } + return this.GetDifficultyFloat("spawn_time", difficulty, -1.0); + } } -bool GetBossProfileTeleportAllowed(const char[] profile, int difficulty) +methodmap BossProfileAttributes < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportAllowed[difficulty]; + public float GetValue(int attribute) + { + float def = -1.0; + if (this == null) + { + return def; + } + ProfileObject obj = this.GetSection(g_AttributesList[attribute]); + if (obj == null) + { + return def; + } + return obj.GetFloat("value", def); + } } -float GetBossProfileTeleportTimeMin(const char[] profile, int difficulty) +methodmap BossProfileDescription < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportTimeMin[difficulty]; -} + property bool Hidden + { + public get() + { + if (this == null) + { + return false; + } + return this.GetBool("hidden", false); + } + } -float GetBossProfileTeleportTimeMax(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportTimeMax[difficulty]; -} + public void GetType(char[] buffer, int bufferSize, char[] def) + { + if (this == null) + { + strcopy(buffer, bufferSize, def); + return; + } + this.GetString("type", buffer, bufferSize, def); + } -float GetBossProfileTeleportTargetRestPeriod(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportRestPeriod[difficulty]; + public void GetDescription(char[] buffer, int bufferSize) + { + char def[64]; + def = "No description provided."; + if (this == null) + { + strcopy(buffer, bufferSize, def); + return; + } + this.GetString("description", buffer, bufferSize, def); + } } -float GetBossProfileTeleportTargetStressMin(const char[] profile, int difficulty) +methodmap BossProfileParticleData < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportStressMin[difficulty]; -} + public void GetName(char[] buffer, int bufferSize) + { + this.GetString("particle", buffer, bufferSize); + } -float GetBossProfileTeleportTargetStressMax(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportStressMax[difficulty]; -} + property bool AttachParticles + { + public get() + { + return this.GetBool("attach", true); + } + } -float GetBossProfileTeleportTargetPersistencyPeriod(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportPersistencyPeriod[difficulty]; -} + property bool BeamParticles + { + public get() + { + return this.GetBool("beam", false); + } + } -float GetBossProfileTeleportRangeMin(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportRangeMin[difficulty]; -} + property ParticleAttachment Type + { + public get() + { + return view_as(this.GetInt("attach_type", view_as(PATTACH_CUSTOMORIGIN))); + } + } -float GetBossProfileTeleportRangeMax(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportRangeMax[difficulty]; -} + public void GetAttachment(char[] buffer, int bufferSize) + { + this.GetString("attachment", buffer, bufferSize); + } -bool GetBossProfileTeleportIgnoreChases(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportIgnoreChases; -} + public void Apply(CBaseAnimating target, CBaseAnimating caster) + { + CBaseAnimating castTo; + float myPos[3], myAng[3], targetPos[3]; + if (!this.AttachParticles || this.BeamParticles) + { + caster.WorldSpaceCenter(myPos); + caster.GetAbsAngles(myAng); + target.WorldSpaceCenter(targetPos); + if (this.AttachParticles) + { + castTo = caster; + } + } + else + { + target.WorldSpaceCenter(myPos); + target.GetAbsAngles(myAng); + castTo = target; + } -bool GetBossProfileTeleportIgnoreVis(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportIgnoreVis; -} + int attachment = 0; + char name[128], attachmentName[64]; + this.GetAttachment(attachmentName, sizeof(attachmentName)); + this.GetName(name, sizeof(name)); + if (attachmentName[0] != '\0') + { + attachment = castTo.LookupAttachment(attachmentName); + } + if (attachment != 0) + { + castTo.GetAttachment(attachment, myPos, myAng); + } -float GetBossProfileTeleportCopyDistance(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.CopiesInfo.TeleportDistanceSpacing[difficulty]; -} + if (this.Type == PATTACH_ROOTBONE_FOLLOW) + { + myPos = NULL_VECTOR; + myAng = NULL_VECTOR; + } -float GetBossProfileJumpscareDistance(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.JumpscareDistance[difficulty]; -} + if (this.BeamParticles) + { + DispatchParticleEffectBeam(castTo.index, name, myPos, myAng, targetPos, attachment, this.Type, true); + } + else + { + if (!DispatchParticleEffect(castTo.index, name, myPos, myAng, myPos, attachment, this.Type, true)) + { + if (this.Type == PATTACH_ROOTBONE_FOLLOW) + { + castTo.WorldSpaceCenter(myPos); + castTo.GetAbsAngles(myAng); + } + CBaseEntity particleEnt = CBaseEntity(CreateEntityByName("info_particle_system")); + if (particleEnt.IsValid()) + { + particleEnt.Teleport(myPos, myAng, NULL_VECTOR); -float GetBossProfileJumpscareDuration(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.JumpscareDuration[difficulty]; + particleEnt.KeyValue("effect_name", name); + particleEnt.Spawn(); + particleEnt.Activate(); + particleEnt.AcceptInput("start"); + RemoveEntity(particleEnt.index); + } + } + } + } } -float GetBossProfileJumpscareCooldown(const char[] profile, int difficulty) +methodmap BossKillSoundsData < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.JumpscareCooldown[difficulty]; -} + public ProfileSound GetKilledAllSounds() + { + return view_as(this.GetSection("player")); + } -int GetBossProfileProxyClasses(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.ProxyClasses); -} + public ProfileSound GetKilledClassSounds(TFClassType class) + { + switch (class) + { + case TFClass_Scout: + { + return view_as(this.GetSection("scout")); + } -float GetBossProfileProxyDamageVsEnemy(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyDamageVsEnemy[difficulty]; -} + case TFClass_Soldier: + { + return view_as(this.GetSection("soldier")); + } -float GetBossProfileProxyDamageVsBackstab(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyDamageVsBackstab[difficulty]; -} + case TFClass_Pyro: + { + return view_as(this.GetSection("pyro")); + } -float GetBossProfileProxyDamageVsSelf(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyDamageVsSelf[difficulty]; -} + case TFClass_DemoMan: + { + return view_as(this.GetSection("demoman")); + } -int GetBossProfileProxyControlGainHitEnemy(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyControlGainHitEnemy[difficulty]; -} + case TFClass_Heavy: + { + return view_as(this.GetSection("heavy")); + } -int GetBossProfileProxyControlGainHitByEnemy(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyControlGainHitByEnemy[difficulty]; -} + case TFClass_Engineer: + { + return view_as(this.GetSection("engineer")); + } -float GetBossProfileProxyControlDrainRate(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyControlDrainRate[difficulty]; -} + case TFClass_Medic: + { + return view_as(this.GetSection("medic")); + } -float GetBossProfileProxySpawnChanceMin(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxySpawnChanceMin[difficulty]; -} + case TFClass_Sniper: + { + return view_as(this.GetSection("sniper")); + } -float GetBossProfileProxySpawnChanceMax(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxySpawnChaceMax[difficulty]; -} + case TFClass_Spy: + { + return view_as(this.GetSection("spy")); + } + } + return this.GetKilledAllSounds(); + } -float GetBossProfileProxySpawnChanceThreshold(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxySpawnChanceThreshold[difficulty]; -} + public ProfileSound GetKilledBuildingSounds() + { + return view_as(this.GetSection("building")); + } -int GetBossProfileProxySpawnNumberMin(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxySpawnNumMin[difficulty]; -} + public void Precache() + { + if (this.GetKilledAllSounds() != null) + { + this.GetKilledAllSounds().Precache(); + } -int GetBossProfileProxySpawnNumberMax(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxySpawnNumMax[difficulty]; -} + for (int i = 1; i <= view_as(TFClass_Engineer); i++) + { + if (this.GetKilledClassSounds(view_as(i)) != null) + { + this.GetKilledClassSounds(view_as(i)).Precache(); + } + } -float GetBossProfileProxySpawnCooldownMin(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxySpawnCooldownMin[difficulty]; + if (this.GetKilledBuildingSounds() != null) + { + this.GetKilledBuildingSounds().Precache(); + } + } } -float GetBossProfileProxySpawnCooldownMax(const char[] profile, int difficulty) +methodmap BossOnPageCountChangedData < ProfileObject { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxySpawnCooldownMax[difficulty]; -} + public float GetAddSpeed(int difficulty) + { + return this.GetDifficultyFloat("add_run_speed", difficulty, 0.0); + } -float GetBossProfileProxyTeleportRangeMin(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportRangeMin[difficulty]; -} + public float GetAddWalkSpeed(int difficulty) + { + return this.GetDifficultyFloat("add_walk_speed", difficulty, 0.0); + } -float GetBossProfileProxyTeleportRangeMax(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportRangeMax[difficulty]; -} + public float GetAddAcceleration(int difficulty) + { + return this.GetDifficultyFloat("add_acceleration", difficulty, 0.0); + } -int GetBossProfileMaxProxies(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.MaxProxies[difficulty]; -} + public ProfileSound GetLocalSounds() + { + return view_as(this.GetSection("local_sounds")); + } -bool GetBossProfileFakeCopies(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.CopiesInfo.Fakes[difficulty]; -} + public ProfileSound GetGlobalSounds() + { + return view_as(this.GetSection("global_sounds")); + } -bool GetBossProfileDrainCreditState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DrainCredits; -} + public void Precache() + { + if (this.GetLocalSounds() != null) + { + this.GetLocalSounds().Precache(); + } -int GetBossProfileDrainCreditAmount(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DrainCreditAmount[difficulty]; + if (this.GetGlobalSounds() != null) + { + this.GetGlobalSounds().Precache(); + } + } } -bool GetBossProfileProxySpawnEffectState(const char[] profile) +/** + * Loads a profile in the current KeyValues position in kv. + */ +/*bool LoadBossProfile(const char[] profile, char[] loadFailReasonBuffer, int loadFailReasonBufferLen, bool lookIntoLoads = false, const char[] originalDir = "") { - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxySpawnEffect; -} + SF2BossProfileData profileData; + profileData.Init(); -int GetBossProfileProxySpawnEffectName(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.ProxySpawnEffectName); -} + if (kv.JumpToKey("map_blacklist")) + { + char s1[4], s2[64], s3[64]; + GetCurrentMap(s3, sizeof(s3)); + for (int i = 1;; i++) + { + FormatEx(s1, sizeof(s1), "%d", i); + kv.GetString(s1, s2, sizeof(s2)); + if (s2[0] == '\0') + { + break; + } -float GetBossProfileProxySpawnEffectZOffset(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxySpawnEffectZOffset; -} + if (StrContains(s3, s2, false) != -1) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "is blacklisted on %s!", s3); + return false; + } + } -ArrayList GetBossProfileProxyDeathAnimations(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyDeathAnimations; -} + kv.GoBack(); + } -int GetBossProfileProxyDeathAnimFrames(const char[] profile, int index) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyDeathAnimFrames[index]; -} + if (lookIntoLoads) + { + // In this, we're basically just gonna go look for companion bosses and skip them here + bool skip = true; -bool GetBossProfileProxyZombiesState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyZombies; -} + if (kv.GetNum("enable_random_selection", true) != 0) + { + skip = false; + } -bool GetBossProfileProxyDifficultyModelsState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyDifficultyModels; -} + if (kv.GetNum("admin_only", false) != 0) + { + skip = false; + } -ArrayList GetBossProfileProxyModels(const char[] profile, int index, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - switch (difficulty) - { - case Difficulty_Hard: + if (kv.GetNum("enable_random_selection_boxing", false) != 0) { - return g_CachedProfileData.ProxyModelsHard[index]; + skip = false; } - case Difficulty_Insane: + + if (kv.GetNum("enable_random_selection_renevant", false) != 0) { - return g_CachedProfileData.ProxyModelsInsane[index]; + skip = false; } - case Difficulty_Nightmare: + + if (kv.GetNum("enable_random_selection_renevant_admin", false) != 0) { - return g_CachedProfileData.ProxyModelsNightmare[index]; + skip = false; } - case Difficulty_Apollyon: + + if (kv.GetNum("is_pve", false) != 0 && kv.GetNum("pve_selectable", 1) != 0) { - return g_CachedProfileData.ProxyModelsApollyon[index]; + skip = false; } - } - return g_CachedProfileData.ProxyModels[index]; -} -bool GetBossProfileProxyOverrideMaxSpeed(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyOverrideMaxSpeed; -} + if (kv.GetNum("always_load", false) != 0) + { + skip = false; + } -float GetBossProfileProxyMaxSpeed(const char[] profile, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyMaxSpeed[difficulty]; -} + if (kv.JumpToKey("pve") && kv.GetNum("selectable", 1) != 0) + { + kv.GoBack(); + skip = false; + } -void GetBossProfileEyePositionOffset(const char[] profile, float buffer[3]) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - buffer = g_CachedProfileData.EyePosOffset; -} + if (skip) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "is not selectable, skipping!"); + return false; + } + } -void GetBossProfileEyeAngleOffset(const char[] profile, float buffer[3]) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - buffer = g_CachedProfileData.EyeAngOffset; -} - -float GetBossProfileScareRadius(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ScareRadius; -} - -float GetBossProfileScareCooldown(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ScareCooldown; -} - -bool GetBossProfileSpeedBoostOnScare(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.SpeedBoostOnScare; -} - -bool GetBossProfileJumpscareOnScare(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.JumpscareOnScare; -} - -bool GetBossProfileJumpscareNoSight(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.JumpscareNoSight; -} - -float GetBossProfileScareSpeedBoostDuration(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ScareSpeedBoostDuration; -} - -bool GetBossProfileScareReactionState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ScareReaction; -} - -int GetBossProfileScareReactionType(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ScareReactionType; -} + profileData.Models = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + profileData.Names = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + SetProfileDifficultyStringArrayValues(kv, "model", profileData.Models, true); + char modelName[PLATFORM_MAX_PATH]; + for (int i = 0; i < profileData.Models.Length; i++) + { + profileData.Models.GetString(i, modelName, sizeof(modelName)); + if (modelName[0] != '\0' && strcmp(modelName, "models/", true) != 0 && strcmp(modelName, "models\\", true) != 0) + { + PrecacheModel2(modelName, _, _, g_FileCheckConVar.BoolValue); + } + } + SetProfileDifficultyStringArrayValues(kv, "name", profileData.Names); -int GetBossProfileScareReactionCustom(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.ScareReactionCustom); -} + profileData.Type = kv.GetNum("type", profileData.Type); + if (profileData.Type == SF2BossType_Unknown || profileData.Type >= SF2BossType_MaxTypes) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "boss type is unknown!"); + return false; + } -bool GetBossProfileScareReplenishState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ScareReplenishSprint; -} + profileData.ModelScale = kv.GetFloat("model_scale", profileData.ModelScale); + if (profileData.ModelScale <= 0.0) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "model_scale must be a value greater than 0!"); + return false; + } -float GetBossProfileScareReplenishAmount(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ScareReplenishSprintAmount; -} + profileData.Skin[1] = kv.GetNum("skin", profileData.Skin[1]); + if (profileData.Skin[1] < 0) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "skin must be a value that is at least 0!"); + return false; + } + GetProfileDifficultyNumValues(kv, "skin", profileData.Skin, profileData.Skin); -int GetBossProfileTeleportType(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.TeleportType; -} + profileData.SkinMax = kv.GetNum("skin_max", profileData.SkinMax); + if (profileData.SkinMax < 0) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "skin_max must be a value that is at least 0!"); + return false; + } -bool GetBossProfileCustomOutlinesState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.CustomOutlines; -} + profileData.SkinDifficultiesOn = kv.GetNum("skin_difficulty", profileData.SkinDifficultiesOn) != 0; -bool GetBossProfileRainbowOutlineState(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.RainbowOutline; -} + profileData.Body[1] = kv.GetNum("body", profileData.Body[1]); + if (profileData.Body[1] < 0) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "body must be a value that is at least 0!"); + return false; + } + GetProfileDifficultyNumValues(kv, "body", profileData.Body, profileData.Body); -bool GetBossProfileProxyWeapons(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyWeapons; -} + profileData.BodyMax = kv.GetNum("body_max", profileData.BodyMax); + if (profileData.BodyMax < 0) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "body_max must be a value that is at least 0!"); + return false; + } -ArrayList GetBossProfileProxyWeaponClassNames(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyWeaponClassNames; -} + profileData.BodyDifficultiesOn = kv.GetNum("body_difficulty", profileData.BodyDifficultiesOn) != 0; -ArrayList GetBossProfileProxyWeaponStats(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyWeaponStats; -} + profileData.RaidHitbox = kv.GetNum("use_raid_hitbox", profileData.RaidHitbox) != 0; -int GetBossProfileProxyWeaponIndexes(const char[] profile, int index) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyWeaponIndexes[index]; -} + profileData.InstantKillRadius = kv.GetFloat("kill_radius", profileData.InstantKillRadius); -int GetBossProfileProxyWeaponSlots(const char[] profile, int index) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyWeaponSlots[index]; -} + profileData.ScareRadius = kv.GetFloat("scare_radius", profileData.ScareRadius); + if (profileData.ScareRadius < 0.0) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "scare_radius must be a value that is at least 0!"); + return false; + } -bool GetBossProfileProxyAllowNormalVoices(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.ProxyAllowVoices; -} + profileData.TeleportType = kv.GetNum("teleport_type", profileData.TeleportType); + if (profileData.TeleportType < 0) + { + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "unknown teleport type!"); + return false; + } -int GetBossProfileChatDeathMessageDifficultyIndexes(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DeathMessageDifficultyIndexes; -} + FormatEx(loadFailReasonBuffer, loadFailReasonBufferLen, "unknown!"); -ArrayList GetBossProfileChatDeathMessages(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.DeathMessagesArray; -} + profileData.FOV = kv.GetFloat("fov", profileData.FOV); + if (profileData.FOV < 0.0) + { + profileData.FOV = 0.0; + } + else if (profileData.FOV > 360.0) + { + profileData.FOV = 360.0; + } -int GetBossProfileChatDeathMessagePrefix(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.DeathMessagePrefix); -} + profileData.TurnRate = kv.GetFloat("maxyawrate", profileData.TurnRate); + profileData.TurnRate = kv.GetFloat("turnrate", profileData.TurnRate); -void GetBossProfileMusicSounds(const char[] profile, SF2BossProfileSoundInfo params, int difficulty) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - switch (difficulty) + switch (profileData.Type) { - case Difficulty_Normal: - { - params = g_CachedProfileData.MusicSoundsNormal; - } - case Difficulty_Hard: - { - params = g_CachedProfileData.MusicSoundsHard; - } - case Difficulty_Insane: - { - params = g_CachedProfileData.MusicSoundsInsane; - } - case Difficulty_Nightmare: + case SF2BossType_Chaser: { - params = g_CachedProfileData.MusicSoundsNightmare; + profileData.Description.Type = "Chaser"; } - case Difficulty_Apollyon: + + case SF2BossType_Statue: { - params = g_CachedProfileData.MusicSoundsApollyon; + profileData.Description.Type = "Statue"; } } -} -void GetBossProfileScareSounds(const char[] profile, SF2BossProfileSoundInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.ScareSounds; -} - -void GetBossProfileSightSounds(const char[] profile, SF2BossProfileSoundInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.SightSounds; -} + if (kv.JumpToKey("description")) + { + profileData.Description.Load(kv); + kv.GoBack(); + } -void GetBossProfileIntroSounds(const char[] profile, SF2BossProfileSoundInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.IntroSounds; -} + profileData.ScareCooldown = kv.GetFloat("scare_cooldown", profileData.ScareCooldown); + if (profileData.ScareCooldown < 0.0) + { + // clamp value + profileData.ScareCooldown = 0.0; + } -void GetBossProfileSpawnLocalSounds(const char[] profile, SF2BossProfileSoundInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.SpawnLocalSounds; -} + kv.GetVector("mins", profileData.Mins, profileData.Mins); + kv.GetVector("maxs", profileData.Maxs, profileData.Maxs); -void GetBossProfileProxySpawnSounds(const char[] profile, SF2BossProfileSoundInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.ProxySpawnSounds; -} + profileData.StepSize = kv.GetFloat("stepsize", profileData.StepSize); -void GetBossProfileProxyIdleSounds(const char[] profile, SF2BossProfileSoundInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.ProxyIdleSounds; -} + profileData.NodeDistanceLookAhead = kv.GetFloat("search_node_dist_lookahead", profileData.NodeDistanceLookAhead); -void GetBossProfileProxyHurtSounds(const char[] profile, SF2BossProfileSoundInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.ProxyHurtSounds; -} + GetProfileColorNoBacks(kv, "effect_rendercolor", profileData.RenderColor[0], profileData.RenderColor[1], profileData.RenderColor[2], profileData.RenderColor[3], + profileData.RenderColor[0], profileData.RenderColor[1], profileData.RenderColor[2], profileData.RenderColor[3]); + profileData.RenderFX = kv.GetNum("effect_renderfx", profileData.RenderFX); + profileData.RenderMode = kv.GetNum("effect_rendermode", profileData.RenderMode); -void GetBossProfileProxyDeathSounds(const char[] profile, SF2BossProfileSoundInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.ProxyDeathSounds; -} + kv.GetString("kill_weapontype", profileData.WeaponString, sizeof(profileData.WeaponString), profileData.WeaponString); + profileData.WeaponInt = kv.GetNum("kill_weapontype", profileData.WeaponInt); -void GetBossProfileOutroMusics(const char[] profile, SF2BossProfileSoundInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.OutroMusics; -} + profileData.DiscoMode = kv.GetNum("disco_mode", profileData.DiscoMode) != 0; + if (profileData.DiscoMode) + { + profileData.DiscoDistanceMin = kv.GetFloat("disco_mode_rng_distance_min", profileData.DiscoDistanceMin); + profileData.DiscoDistanceMax = kv.GetFloat("disco_mode_rng_distance_max", profileData.DiscoDistanceMax); + kv.GetVector("disco_mode_pos", profileData.DiscoPos, profileData.DiscoPos); + } -ArrayList GetBossProfileFootstepEventSounds(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.FootstepEventSounds; -} + profileData.FestiveLights = kv.GetNum("festive_lights", profileData.FestiveLights) != 0; + if (profileData.FestiveLights) + { + profileData.FestiveLightBrightness = kv.GetNum("festive_light_brightness", profileData.FestiveLightBrightness); + profileData.FestiveLightDistance = kv.GetFloat("festive_light_distance", profileData.FestiveLightDistance); + profileData.FestiveLightRadius = kv.GetFloat("festive_light_radius", profileData.FestiveLightRadius); + kv.GetVector("festive_lights_pos", profileData.FestiveLightPos, profileData.FestiveLightPos); + kv.GetVector("festive_lights_ang", profileData.FestiveLightAng, profileData.FestiveLightAng); + } -ArrayList GetBossProfileFootstepEventIndexes(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.FootstepEventIndexes; -} + if (kv.GetNum("tp_effect_spawn", false) != 0) + { + if (profileData.SpawnEffects == null) + { + profileData.SpawnEffects = new StringMap(); + } + ArrayList listEffects = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); -ArrayList GetBossProfileEventSounds(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.EventSounds; -} + SF2BossProfileBaseEffectInfo particle; + SF2BossProfileBaseEffectInfo sound; -ArrayList GetBossProfileEventIndexes(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.EventIndexes; -} + particle.Init(); + particle.Type = EffectType_Particle; + particle.Event = EffectEvent_Constant; + kv.GetString("tp_effect_spawn_particle", particle.ParticleName, sizeof(particle.ParticleName), particle.ParticleName); + particle.LifeTime = 0.1; + kv.GetVector("tp_effect_origin", particle.Origin, particle.Origin); + particle.PostLoad(); + listEffects.PushArray(particle); -void GetBossProfileAttributesInfo(const char[] profile, SF2BossProfileAttributesInfo params) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - params = g_CachedProfileData.AttributesInfo; -} + char soundName[PLATFORM_MAX_PATH]; + sound.Init(); + sound.Type = EffectType_Sound; + sound.Event = EffectEvent_Constant; + kv.GetString("tp_effect_spawn_sound", soundName, sizeof(soundName), soundName); + TryPrecacheBossProfileSoundPath(soundName, g_FileCheckConVar.BoolValue); + sound.SoundSounds.Paths = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + sound.SoundSounds.Paths.PushString(soundName); + sound.SoundSounds.Volume = kv.GetFloat("tp_effect_spawn_sound_volume", sound.SoundSounds.Volume); + sound.SoundSounds.Pitch = kv.GetNum("tp_effect_spawn_sound_pitch", sound.SoundSounds.Pitch); + sound.SoundSounds.PostLoad(); + listEffects.PushArray(sound); -int GetBossProfileJumpscareSound(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.JumpscareSound); -} + profileData.SpawnEffects.SetValue("TPEffectSpawnBackwards", listEffects); + } -int GetBossProfileStaticSound(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.StaticSound); -} + if (kv.GetNum("tp_effect_despawn", false) != 0) + { + if (profileData.DespawnEffects == null) + { + profileData.DespawnEffects = new StringMap(); + } + ArrayList listEffects = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); -int GetBossProfileStaticLocalSound(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.StaticLocalSound); -} + SF2BossProfileBaseEffectInfo particle; + SF2BossProfileBaseEffectInfo sound; + + particle.Init(); + particle.Type = EffectType_Particle; + particle.Event = EffectEvent_Constant; + kv.GetString("tp_effect_despawn_particle", particle.ParticleName, sizeof(particle.ParticleName), particle.ParticleName); + particle.LifeTime = 0.1; + kv.GetVector("tp_effect_origin", particle.Origin, particle.Origin); + particle.PostLoad(); + listEffects.PushArray(particle); + + char soundName[PLATFORM_MAX_PATH]; + sound.Init(); + sound.Type = EffectType_Sound; + sound.Event = EffectEvent_Constant; + kv.GetString("tp_effect_despawn_sound", soundName, sizeof(soundName), soundName); + TryPrecacheBossProfileSoundPath(soundName, g_FileCheckConVar.BoolValue); + sound.SoundSounds.Paths = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + sound.SoundSounds.Paths.PushString(soundName); + sound.SoundSounds.Volume = kv.GetFloat("tp_effect_despawn_sound_volume", sound.SoundSounds.Volume); + sound.SoundSounds.Pitch = kv.GetNum("tp_effect_despawn_sound_pitch", sound.SoundSounds.Pitch); + sound.SoundSounds.PostLoad(); + listEffects.PushArray(sound); + + profileData.DespawnEffects.SetValue("TPEffectDespawnBackwards", listEffects); + } + + profileData.BlinkLookRate = kv.GetFloat("blink_look_rate_multiply", profileData.BlinkLookRate); + profileData.BlinkStaticRate = kv.GetFloat("blink_static_rate_multiply", profileData.BlinkStaticRate); + + profileData.DeathCam = kv.GetNum("death_cam", profileData.DeathCam) != 0; + if (profileData.DeathCam) + { + profileData.DeathCamScareSound = kv.GetNum("death_cam_play_scare_sound", profileData.DeathCamScareSound) != 0; + profileData.PublicDeathCam = kv.GetNum("death_cam_public", profileData.PublicDeathCam) != 0; + if (profileData.PublicDeathCam) + { + profileData.PublicDeathCamSpeed = kv.GetFloat("death_cam_speed", profileData.PublicDeathCamSpeed); + profileData.PublicDeathCamAcceleration = kv.GetFloat("death_cam_acceleration", profileData.PublicDeathCamAcceleration); + profileData.PublicDeathCamDeceleration = kv.GetFloat("death_cam_deceleration", profileData.PublicDeathCamDeceleration); + profileData.PublicDeathCamBackwardOffset = kv.GetFloat("deathcam_death_backward_offset", profileData.PublicDeathCamBackwardOffset); + profileData.PublicDeathCamDownwardOffset = kv.GetFloat("deathcam_death_downward_offset", profileData.PublicDeathCamDownwardOffset); + } + profileData.DeathCamOverlay = kv.GetNum("death_cam_overlay", profileData.DeathCamOverlay) != 0; + profileData.DeathCamOverlayStartTime = kv.GetFloat("death_cam_time_overlay_start", profileData.DeathCamOverlayStartTime); + if (profileData.DeathCamOverlayStartTime < 0.0) + { + profileData.DeathCamOverlayStartTime = 0.0; + } + profileData.DeathCamTime = kv.GetFloat("death_cam_time_death", profileData.DeathCamTime); + if (profileData.DeathCamTime < 0.0) + { + profileData.DeathCamTime = 0.0; + } + kv.GetVector("death_cam_pos", profileData.DeathCamPos, profileData.DeathCamPos); + kv.GetString("death_cam_attachtment_target_point", profileData.PublicDeathCamAttachmentTarget, sizeof(profileData.PublicDeathCamAttachmentTarget), profileData.PublicDeathCamAttachmentTarget); + kv.GetString("death_cam_attachtment_point", profileData.PublicDeathCamAttachment, sizeof(profileData.PublicDeathCamAttachment), profileData.PublicDeathCamAttachment); + } + + if (kv.JumpToKey("public_death_cam")) + { + profileData.DeathCamData.Load(kv, g_FileCheckConVar.BoolValue); + kv.GoBack(); + } + + GetProfileDifficultyFloatValues(kv, "sound_music_loop", profileData.SoundMusicLoop, profileData.SoundMusicLoop); + GetProfileDifficultyFloatValues(kv, "kill_cooldown", profileData.InstantKillCooldown, profileData.InstantKillCooldown); + + GetProfileDifficultyFloatValues(kv, "search_view_distance", profileData.SearchRange, profileData.SearchRange); + GetProfileDifficultyFloatValues(kv, "search_range", profileData.SearchRange, profileData.SearchRange); + GetProfileDifficultyFloatValues(kv, "hearing_range", profileData.SearchSoundRange, profileData.SearchSoundRange); + GetProfileDifficultyFloatValues(kv, "search_sound_range", profileData.SearchSoundRange, profileData.SearchSoundRange); + GetProfileDifficultyFloatValues(kv, "taunt_alert_range", profileData.TauntAlertRange, profileData.TauntAlertRange); + + GetProfileDifficultyBoolValues(kv, "teleport_allowed", profileData.TeleportAllowed, profileData.TeleportAllowed); + GetProfileDifficultyFloatValues(kv, "teleport_range_min", profileData.TeleportRangeMin, profileData.TeleportRangeMin); + GetProfileDifficultyFloatValues(kv, "teleport_range_max", profileData.TeleportRangeMax, profileData.TeleportRangeMax); + GetProfileDifficultyFloatValues(kv, "teleport_time_min", profileData.TeleportTimeMin, profileData.TeleportTimeMin); + GetProfileDifficultyFloatValues(kv, "teleport_time_max", profileData.TeleportTimeMax, profileData.TeleportTimeMax); + GetProfileDifficultyFloatValues(kv, "teleport_target_rest_period", profileData.TeleportRestPeriod, profileData.TeleportRestPeriod); + GetProfileDifficultyFloatValues(kv, "teleport_target_stress_min", profileData.TeleportStressMin, profileData.TeleportStressMin); + GetProfileDifficultyFloatValues(kv, "teleport_target_stress_max", profileData.TeleportStressMax, profileData.TeleportStressMax); + GetProfileDifficultyFloatValues(kv, "teleport_target_persistency_period", profileData.TeleportPersistencyPeriod, profileData.TeleportPersistencyPeriod); + profileData.TeleportIgnoreChases = kv.GetNum("teleport_target_ignore_chases", profileData.TeleportIgnoreChases) != 0; + profileData.TeleportIgnoreVis = kv.GetNum("teleport_target_ignore_visibility", profileData.TeleportIgnoreVis) != 0; + + GetProfileDifficultyFloatValues(kv, "jumpscare_distance", profileData.JumpscareDistance, profileData.JumpscareDistance); + GetProfileDifficultyFloatValues(kv, "jumpscare_duration", profileData.JumpscareDuration, profileData.JumpscareDuration); + GetProfileDifficultyFloatValues(kv, "jumpscare_cooldown", profileData.JumpscareCooldown, profileData.JumpscareCooldown); + profileData.JumpscareOnScare = kv.GetNum("jumpscare_on_scare", profileData.JumpscareOnScare) != 0; + profileData.JumpscareNoSight = kv.GetNum("jumpscare_no_sight", profileData.JumpscareNoSight) != 0; + + GetProfileDifficultyFloatValues(kv, "speed", profileData.RunSpeed, profileData.RunSpeed); + GetProfileDifficultyFloatValues(kv, "acceleration", profileData.Acceleration, profileData.Acceleration); + + GetProfileDifficultyFloatValues(kv, "idle_lifetime", profileData.IdleLifeTime, profileData.IdleLifeTime); + + profileData.CustomOutlines = kv.GetNum("customizable_outlines", profileData.CustomOutlines) != 0; + if (profileData.CustomOutlines) + { + profileData.OutlineColor[0] = kv.GetNum("outline_color_r", profileData.OutlineColor[0]); + profileData.OutlineColor[1] = kv.GetNum("outline_color_g", profileData.OutlineColor[1]); + profileData.OutlineColor[2] = kv.GetNum("outline_color_b", profileData.OutlineColor[2]); + profileData.OutlineColor[3] = kv.GetNum("outline_color_transparency", profileData.OutlineColor[3]); + profileData.RainbowOutline = !!kv.GetNum("enable_rainbow_outline", profileData.RainbowOutline); + if (profileData.RainbowOutline) + { + profileData.RainbowOutlineCycle = kv.GetFloat("rainbow_outline_cycle_rate", profileData.RainbowOutlineCycle); + if (profileData.RainbowOutlineCycle < 0.0) + { + profileData.RainbowOutlineCycle = 0.0; + } + } + } + + profileData.SpeedBoostOnScare = !!kv.GetNum("scare_player_speed_boost", profileData.SpeedBoostOnScare); + if (profileData.SpeedBoostOnScare) + { + profileData.ScareSpeedBoostDuration = kv.GetFloat("scare_player_speed_boost_duration", profileData.ScareSpeedBoostDuration); + } + + profileData.ScareReaction = !!kv.GetNum("scare_player_reaction", profileData.ScareReaction); + if (profileData.ScareReaction) + { + profileData.ScareReactionType = kv.GetNum("scare_player_reaction_type", profileData.ScareReactionType); + if (profileData.ScareReactionType < 1) + { + profileData.ScareReactionType = 1; + } + if (profileData.ScareReactionType > 3) + { + profileData.ScareReactionType = 3; + } + kv.GetString("scare_player_reaction_response_custom", profileData.ScareReactionCustom, sizeof(profileData.ScareReactionCustom), profileData.ScareReactionCustom); + } + + profileData.ScareReplenishSprint = kv.GetNum("scare_player_replenish_sprint", profileData.ScareReplenishSprint) != 0; + if (profileData.ScareReplenishSprint) + { + profileData.ScareReplenishSprintAmount = kv.GetFloat("scare_player_replenish_sprint_amount", profileData.ScareReplenishSprintAmount); + } + + GetProfileDifficultyFloatValues(kv, "static_radius", profileData.StaticRadius, profileData.StaticRadius); + GetProfileDifficultyFloatValues(kv, "static_rate", profileData.StaticRate, profileData.StaticRate); + GetProfileDifficultyFloatValues(kv, "static_rate_decay", profileData.StaticRateDecay, profileData.StaticRateDecay); + GetProfileDifficultyFloatValues(kv, "static_on_look_gracetime", profileData.StaticGraceTime, profileData.StaticGraceTime); + profileData.StaticScareAmount = kv.GetFloat("scare_static_amount", profileData.StaticScareAmount); + + profileData.StaticShakeLocalLevel = kv.GetNum("sound_static_loop_local_level", profileData.StaticShakeLocalLevel); + profileData.StaticShakeVolumeMin = kv.GetFloat("sound_static_shake_local_volume_min", profileData.StaticShakeVolumeMin); + profileData.StaticShakeVolumeMax = kv.GetFloat("sound_static_shake_local_volume_max", profileData.StaticShakeVolumeMax); + + profileData.DrainCredits = !!kv.GetNum("drain_credits_on_kill", profileData.DrainCredits); + GetProfileDifficultyNumValues(kv, "drain_credits_amount", profileData.DrainCreditAmount, profileData.DrainCreditAmount); + + profileData.Healthbar = !!kv.GetNum("healthbar", profileData.Healthbar); + + profileData.DeathMessageDifficultyIndexes = kv.GetNum("chat_message_upon_death_difficulty_indexes", profileData.DeathMessageDifficultyIndexes); + if (kv.JumpToKey("chat_message_upon_death")) + { + profileData.DeathMessagesArray = new ArrayList(ByteCountToCells(256)); + char message[256], section[64]; + for (int i = 1;; i++) + { + FormatEx(section, sizeof(section), "%d", i); + kv.GetString(section, message, sizeof(message)); + if (message[0] == '\0') + { + break; + } + + profileData.DeathMessagesArray.PushString(message); + } + kv.GoBack(); + } + kv.GetString("chat_message_upon_death_prefix", profileData.DeathMessagePrefix, sizeof(profileData.DeathMessagePrefix), profileData.DeathMessagePrefix); + + profileData.BurnRagdoll = kv.GetNum("burn_ragdoll_on_kill", profileData.BurnRagdoll) != 0; + profileData.CloakRagdoll = kv.GetNum("cloak_ragdoll_on_kill", profileData.CloakRagdoll) != 0; + profileData.DecapRagdoll = kv.GetNum("decap_ragdoll_on_kill", profileData.DecapRagdoll) != 0; + profileData.GibRagdoll = kv.GetNum("gib_ragdoll_on_kill", profileData.GibRagdoll) != 0; + profileData.IceRagdoll = kv.GetNum("ice_ragdoll_on_kill", profileData.IceRagdoll) != 0; + profileData.GoldRagdoll = kv.GetNum("gold_ragdoll_on_kill", profileData.GoldRagdoll) != 0; + profileData.ElectrocuteRagdoll = kv.GetNum("electrocute_ragdoll_on_kill", profileData.ElectrocuteRagdoll) != 0; + profileData.AshRagdoll = kv.GetNum("disintegrate_ragdoll_on_kill", profileData.AshRagdoll) != 0; + profileData.DeleteRagdoll = kv.GetNum("delete_ragdoll_on_kill", profileData.DeleteRagdoll) != 0; + profileData.PushRagdoll = kv.GetNum("push_ragdoll_on_kill", profileData.PushRagdoll) != 0; + if (profileData.PushRagdoll) + { + kv.GetVector("push_ragdoll_force", profileData.PushRagdollForce, profileData.PushRagdollForce); + } + profileData.DissolveRagdoll = kv.GetNum("dissolve_ragdoll_on_kill", profileData.DissolveRagdoll) != 0; + if (profileData.DissolveRagdoll) + { + profileData.DissolveKillType = kv.GetNum("dissolve_ragdoll_type", profileData.DissolveKillType); + } + profileData.PlasmaRagdoll = kv.GetNum("plasma_ragdoll_on_kill", profileData.PlasmaRagdoll) != 0; + profileData.ResizeRagdoll = kv.GetNum("resize_ragdoll_on_kill", profileData.ResizeRagdoll) != 0; + if (profileData.ResizeRagdoll) + { + profileData.ResizeRagdollHead = kv.GetFloat("resize_ragdoll_head", profileData.ResizeRagdollHead); + profileData.ResizeRagdollHands = kv.GetFloat("resize_ragdoll_hands", profileData.ResizeRagdollHands); + profileData.ResizeRagdollTorso = kv.GetFloat("resize_ragdoll_torso", profileData.ResizeRagdollTorso); + } + profileData.DecapOrGibRagdoll = kv.GetNum("decap_or_gib_ragdoll_on_kill", profileData.DecapOrGibRagdoll) != 0; + profileData.SilentKill = kv.GetNum("silent_kill", profileData.SilentKill) != 0; + profileData.MultiEffectRagdoll = kv.GetNum("multieffect_ragdoll_on_kill", profileData.MultiEffectRagdoll) != 0; + profileData.CustomDeathFlag = kv.GetNum("attack_custom_deathflag_enabled", profileData.CustomDeathFlag) != 0; + if (profileData.CustomDeathFlag) + { + profileData.CustomDeathFlagType = kv.GetNum("attack_custom_deathflag", profileData.CustomDeathFlagType); + } + + profileData.OutroMusic = kv.GetNum("sound_music_outro_enabled", profileData.OutroMusic) != 0; + + profileData.EngineSoundLevel = kv.GetNum("constant_sound_level", profileData.EngineSoundLevel); + profileData.EngineSoundLevel = kv.GetNum("engine_sound_level", profileData.EngineSoundLevel); + profileData.EngineSoundVolume = kv.GetFloat("constant_sound_volume", profileData.EngineSoundVolume); + profileData.EngineSoundVolume = kv.GetFloat("engine_sound_volume", profileData.EngineSoundVolume); + + kv.GetVector("eye_pos", profileData.EyePosOffset, profileData.EyePosOffset); + kv.GetVector("eye_ang_offset", profileData.EyeAngOffset, profileData.EyeAngOffset); + + if (kv.JumpToKey("eyes")) + { + profileData.EyeData.Load(kv); + kv.GoBack(); + profileData.EyePosOffset = profileData.EyeData.OffsetPos; + profileData.EyeAngOffset = profileData.EyeData.OffsetAng; + } + + if (kv.JumpToKey("slaughter_run")) + { + profileData.SlaughterRunData.Load(kv); + kv.GoBack(); + } + + // Parse through flags. + int bossFlags = 0; + if (kv.GetNum("static_on_look")) + { + bossFlags |= SFF_STATICONLOOK; + } + if (kv.GetNum("static_on_radius")) + { + bossFlags |= SFF_STATICONRADIUS; + } + if (kv.GetNum("proxies")) + { + bossFlags |= SFF_PROXIES; + } + if (kv.GetNum("jumpscare")) + { + bossFlags |= SFF_HASJUMPSCARE; + } + if (kv.GetNum("sound_static_loop_local_enabled")) + { + bossFlags |= SFF_HASSTATICLOOPLOCALSOUND; + } + if (kv.GetNum("view_shake", 1)) + { + bossFlags |= SFF_HASVIEWSHAKE; + } + if (kv.GetNum("wander_move", 1)) + { + bossFlags |= SFF_WANDERMOVE; + } + if (kv.GetNum("attack_props", 0)) + { + bossFlags |= SFF_ATTACKPROPS; + } + if (kv.GetNum("attack_weaponsenable", 0)) + { + bossFlags |= SFF_WEAPONKILLS; + } + if (kv.GetNum("kill_weaponsenable", 0)) + { + bossFlags |= SFF_WEAPONKILLSONRADIUS; + } + profileData.Flags = bossFlags; + + profileData.CopiesInfo.Load(kv); + + if (profileData.Flags & SFF_PROXIES) + { + kv.GetString("proxies_classes", profileData.ProxyClasses, sizeof(profileData.ProxyClasses), profileData.ProxyClasses); + GetProfileDifficultyFloatValues(kv, "proxies_damage_scale_vs_enemy", profileData.ProxyDamageVsEnemy, profileData.ProxyDamageVsEnemy); + GetProfileDifficultyFloatValues(kv, "proxies_damage_scale_vs_enemy_backstab", profileData.ProxyDamageVsBackstab, profileData.ProxyDamageVsBackstab); + GetProfileDifficultyFloatValues(kv, "proxies_damage_scale_vs_self", profileData.ProxyDamageVsSelf, profileData.ProxyDamageVsSelf); + GetProfileDifficultyNumValues(kv, "proxies_controlgain_hitenemy", profileData.ProxyControlGainHitEnemy, profileData.ProxyControlGainHitEnemy); + GetProfileDifficultyNumValues(kv, "proxies_controlgain_hitbyenemy", profileData.ProxyControlGainHitByEnemy, profileData.ProxyControlGainHitByEnemy); + GetProfileDifficultyFloatValues(kv, "proxies_controldrainrate", profileData.ProxyControlDrainRate, profileData.ProxyControlDrainRate); + GetProfileDifficultyNumValues(kv, "proxies_max", profileData.MaxProxies, profileData.MaxProxies); + GetProfileDifficultyFloatValues(kv, "proxies_spawn_chance_min", profileData.ProxySpawnChanceMin, profileData.ProxySpawnChanceMin); + GetProfileDifficultyFloatValues(kv, "proxies_spawn_chance_max", profileData.ProxySpawnChaceMax, profileData.ProxySpawnChaceMax); + GetProfileDifficultyFloatValues(kv, "proxies_spawn_chance_threshold", profileData.ProxySpawnChanceThreshold, profileData.ProxySpawnChanceThreshold); + GetProfileDifficultyNumValues(kv, "proxies_spawn_num_min", profileData.ProxySpawnNumMin, profileData.ProxySpawnNumMin); + GetProfileDifficultyNumValues(kv, "proxies_spawn_num_max", profileData.ProxySpawnNumMax, profileData.ProxySpawnNumMax); + GetProfileDifficultyFloatValues(kv, "proxies_spawn_cooldown_min", profileData.ProxySpawnCooldownMin, profileData.ProxySpawnCooldownMin); + GetProfileDifficultyFloatValues(kv, "proxies_spawn_cooldown_max", profileData.ProxySpawnCooldownMax, profileData.ProxySpawnCooldownMax); + GetProfileDifficultyFloatValues(kv, "proxies_teleport_range_min", profileData.ProxyTeleportRangeMin, profileData.ProxyTeleportRangeMin); + GetProfileDifficultyFloatValues(kv, "proxies_teleport_range_max", profileData.ProxyTeleportRangeMax, profileData.ProxyTeleportRangeMax); + profileData.ProxyAllowVoices = !!kv.GetNum("proxies_allownormalvoices", profileData.ProxyAllowVoices); + profileData.ProxyWeapons = !!kv.GetNum("proxies_weapon", profileData.ProxyWeapons); + profileData.ProxyDeathAnimations = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + char className[15], deathSection[64], storedAnim[PLATFORM_MAX_PATH]; + for (int i = 0; i < 10; i++) + { + TF2_GetClassName(view_as(i), className, sizeof(className)); + if (i == 0) + { + deathSection = "proxies_death_anim_all"; + } + else + { + FormatEx(deathSection, sizeof(deathSection), "proxies_death_anim_%s", className); + } + kv.GetString(deathSection, storedAnim, sizeof(storedAnim)); + profileData.ProxyDeathAnimations.PushString(storedAnim); + if (i == 0) + { + deathSection = "proxies_death_anim_frames_all"; + } + else + { + FormatEx(deathSection, sizeof(deathSection), "proxies_death_anim_frames_%s", className); + } + profileData.ProxyDeathAnimFrames[i] = kv.GetNum(deathSection); + } + if (profileData.ProxyWeapons) + { + profileData.ProxyWeaponClassNames = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + profileData.ProxyWeaponStats = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + char classKey[15], keyValue[45], arrayStringValue[PLATFORM_MAX_PATH]; + for (int i = 1; i < 10; i++) + { + TF2_GetClassName(view_as(i), classKey, sizeof(classKey)); + + FormatEx(keyValue, sizeof(keyValue), "proxies_weapon_class_%s", classKey); + kv.GetString(keyValue, arrayStringValue, sizeof(arrayStringValue)); + profileData.ProxyWeaponClassNames.PushString(arrayStringValue); + + FormatEx(keyValue, sizeof(keyValue), "proxies_weapon_stats_%s", classKey); + kv.GetString(keyValue, arrayStringValue, sizeof(arrayStringValue)); + profileData.ProxyWeaponStats.PushString(arrayStringValue); + + FormatEx(keyValue, sizeof(keyValue), "proxies_weapon_index_%s", classKey); + profileData.ProxyWeaponIndexes[i] = kv.GetNum(keyValue, profileData.ProxyWeaponIndexes[i]); + + FormatEx(keyValue, sizeof(keyValue), "proxies_weapon_slot_%s", classKey); + profileData.ProxyWeaponSlots[i] = kv.GetNum(keyValue, profileData.ProxyWeaponSlots[i]); + } + } + profileData.ProxySpawnEffect = kv.GetNum("proxies_spawn_effect_enabled", profileData.ProxySpawnEffect) != 0; + if (profileData.ProxySpawnEffect) + { + kv.GetString("proxies_spawn_effect", profileData.ProxySpawnEffectName, sizeof(profileData.ProxySpawnEffectName), profileData.ProxySpawnEffectName); + profileData.ProxySpawnEffectZOffset = kv.GetFloat("proxies_spawn_effect_z_offset", profileData.ProxySpawnEffectZOffset); + } + profileData.ProxyZombies = kv.GetNum("proxies_zombie", profileData.ProxyZombies) != 0; + profileData.ProxyRobots = kv.GetNum("proxies_robot", profileData.ProxyRobots) != 0; + profileData.ProxyDifficultyModels = kv.GetNum("proxy_difficulty_models", profileData.ProxyDifficultyModels) != 0; + + char index[64], modelDirectory[PLATFORM_MAX_PATH]; + if (profileData.ProxyDifficultyModels) + { + for (int i = 0; i < 10; i++) + { + for (int j = 1; j < Difficulty_Max; j--) + { + TF2_GetClassName(view_as(i), className, sizeof(className)); + if (i == 0) + { + switch (j) + { + case Difficulty_Normal: + { + deathSection = "mod_proxy_all"; + } + case Difficulty_Hard: + { + deathSection = "mod_proxy_all_hard"; + } + case Difficulty_Insane: + { + deathSection = "mod_proxy_all_insane"; + } + case Difficulty_Nightmare: + { + deathSection = "mod_proxy_all_nightmare"; + } + case Difficulty_Apollyon: + { + deathSection = "mod_proxy_all_apollyon"; + } + } + } + else + { + switch (j) + { + case Difficulty_Normal: + { + FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s", className); + } + case Difficulty_Hard: + { + FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s_hard", className); + } + case Difficulty_Insane: + { + FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s_insane", className); + } + case Difficulty_Nightmare: + { + FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s_nightmare", className); + } + case Difficulty_Apollyon: + { + FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s_apollyon", className); + } + } + } + if (kv.JumpToKey(deathSection)) + { + switch (j) + { + case Difficulty_Normal: + { + profileData.ProxyModels[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + } + case Difficulty_Hard: + { + profileData.ProxyModelsHard[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + } + case Difficulty_Insane: + { + profileData.ProxyModelsInsane[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + } + case Difficulty_Nightmare: + { + profileData.ProxyModelsNightmare[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + } + case Difficulty_Apollyon: + { + profileData.ProxyModelsApollyon[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + } + } + for (int i2 = 1;; i2++) + { + FormatEx(index, sizeof(index), "%d", i2); + kv.GetString(index, modelDirectory, sizeof(modelDirectory)); + if (modelDirectory[0] == '\0') + { + break; + } + + if (!PrecacheModel(modelDirectory, true)) + { + LogSF2Message("Proxy model file %s failed to be precached, this model will not be used.", modelDirectory); + } + else + { + switch (j) + { + case Difficulty_Normal: + { + profileData.ProxyModels[i].PushString(modelDirectory); + } + case Difficulty_Hard: + { + profileData.ProxyModelsHard[i].PushString(modelDirectory); + } + case Difficulty_Insane: + { + profileData.ProxyModelsInsane[i].PushString(modelDirectory); + } + case Difficulty_Nightmare: + { + profileData.ProxyModelsNightmare[i].PushString(modelDirectory); + } + case Difficulty_Apollyon: + { + profileData.ProxyModelsApollyon[i].PushString(modelDirectory); + } + } + PrecacheModel2(modelDirectory, _, _, g_FileCheckConVar.BoolValue); + } + } + kv.GoBack(); + } + } + } + } + else + { + for (int i = 0; i < 10; i++) + { + TF2_GetClassName(view_as(i), className, sizeof(className)); + if (i == 0) + { + deathSection = "mod_proxy_all"; + } + else + { + FormatEx(deathSection, sizeof(deathSection), "mod_proxy_%s", className); + } + if (kv.JumpToKey(deathSection)) + { + profileData.ProxyModels[i] = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + for (int i2 = 1;; i2++) + { + FormatEx(index, sizeof(index), "%d", i2); + kv.GetString(index, modelDirectory, sizeof(modelDirectory)); + if (modelDirectory[0] == '\0') + { + break; + } + + if (!PrecacheModel(modelDirectory, true)) + { + LogSF2Message("Proxy model file %s failed to be precached, this model will not be used.", modelDirectory); + } + else + { + profileData.ProxyModels[i].PushString(modelDirectory); + } + } + kv.GoBack(); + } + } + } + + profileData.ProxyOverrideMaxSpeed = !!kv.GetNum("proxies_override_max_speed", profileData.ProxyOverrideMaxSpeed); + if (profileData.ProxyOverrideMaxSpeed) + { + GetProfileDifficultyFloatValues(kv, "proxies_max_speed", profileData.ProxyMaxSpeed, profileData.ProxyMaxSpeed); + } + } + + UnloadBossProfile(profile); + + profileData.AnimationData.Load(kv, true); + + switch (profileData.Type) + { + case SF2BossType_Statue: + { + if (!LoadStatueBossProfile(kv, profile, loadFailReasonBuffer, loadFailReasonBufferLen, profileData)) + { + return false; + } + } + case SF2BossType_Chaser: + { + if (!LoadChaserBossProfile(kv, profile, loadFailReasonBuffer, loadFailReasonBufferLen, profileData)) + { + return false; + } + } + } + // Add the section to our config. + g_Config.Rewind(); + g_Config.JumpToKey(profile, true); + g_Config.Import(kv); + + kv.GetString("constant_sound", profileData.EngineSound, sizeof(profileData.EngineSound), profileData.EngineSound); + kv.GetString("engine_sound", profileData.EngineSound, sizeof(profileData.EngineSound), profileData.EngineSound); + + TryPrecacheBossProfileSoundPath(profileData.EngineSound, g_FileCheckConVar.BoolValue); + + int index = g_BossProfileList.FindString(profile); + if (index == -1) + { + g_BossProfileList.PushString(profile); + } + + if (kv.GetNum("enable_random_selection", true) != 0) + { + if (GetSelectableBossProfileList().FindString(profile) == -1) + { + // Add to the selectable boss list if it isn't there already. + GetSelectableBossProfileList().PushString(profile); + } + } + else + { + int selectIndex = GetSelectableBossProfileList().FindString(profile); + if (selectIndex != -1) + { + GetSelectableBossProfileList().Erase(selectIndex); + } + } + + if (kv.GetNum("admin_only", false) != 0) + { + if (GetSelectableAdminBossProfileList().FindString(profile) == -1) + { + // Add to the selectable boss list if it isn't there already. + GetSelectableAdminBossProfileList().PushString(profile); + } + } + else + { + int selectIndex = GetSelectableAdminBossProfileList().FindString(profile); + if (selectIndex != -1) + { + GetSelectableAdminBossProfileList().Erase(selectIndex); + } + } + + if (kv.GetNum("enable_random_selection_boxing", false) != 0) + { + if (GetSelectableBoxingBossProfileList().FindString(profile) == -1) + { + // Add to the selectable boss list if it isn't there already. + GetSelectableBoxingBossProfileList().PushString(profile); + } + } + else + { + int selectIndex = GetSelectableBoxingBossProfileList().FindString(profile); + if (selectIndex != -1) + { + GetSelectableBoxingBossProfileList().Erase(selectIndex); + } + } + + if (kv.GetNum("enable_random_selection_renevant", false) != 0) + { + if (GetSelectableRenevantBossProfileList().FindString(profile) == -1) + { + // Add to the selectable boss list if it isn't there already. + GetSelectableRenevantBossProfileList().PushString(profile); + } + } + else + { + int selectIndex = GetSelectableRenevantBossProfileList().FindString(profile); + if (selectIndex != -1) + { + GetSelectableRenevantBossProfileList().Erase(selectIndex); + } + } + + if (kv.GetNum("enable_random_selection_renevant_admin", false) != 0) + { + if (GetSelectableRenevantBossAdminProfileList().FindString(profile) == -1) + { + // Add to the selectable boss list if it isn't there already. + GetSelectableRenevantBossAdminProfileList().PushString(profile); + } + } + else + { + int selectIndex = GetSelectableRenevantBossAdminProfileList().FindString(profile); + if (selectIndex != -1) + { + GetSelectableRenevantBossAdminProfileList().Erase(selectIndex); + } + } + + if (kv.JumpToKey("pve")) + { + profileData.IsPvEBoss = true; + kv.GoBack(); + } + else + { + profileData.IsPvEBoss = kv.GetNum("is_pve", profileData.IsPvEBoss) != 0; + } + + if (profileData.IsPvEBoss) + { + profileData.Flags = profileData.Flags & ~SFF_PROXIES; + if (kv.JumpToKey("pve")) + { + if (kv.JumpToKey("spawn_messages")) + { + profileData.PvESpawnMessagesArray = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + char message[256], section[64]; + for (int i = 1;; i++) + { + FormatEx(section, sizeof(section), "%d", i); + kv.GetString(section, message, sizeof(message)); + if (message[0] == '\0') + { + break; + } + + profileData.PvESpawnMessagesArray.PushString(message); + } + kv.GoBack(); + } + kv.GetString("spawn_message_prefix", profileData.PvESpawnMessagePrefix, sizeof(profileData.PvESpawnMessagePrefix), profileData.PvESpawnMessagePrefix); + profileData.DisplayPvEHealth = kv.GetNum("health_bar", profileData.DisplayPvEHealth) != 0; + char setProfile[SF2_MAX_PROFILE_NAME_LENGTH]; + strcopy(setProfile, sizeof(setProfile), profile); + if (kv.GetNum("selectable", 1) != 0) + { + RegisterPvESlenderBoss(setProfile); + } + profileData.PvETeleportEndTimer = kv.GetFloat("teleport_players_time", profileData.PvETeleportEndTimer); + kv.GoBack(); + } + else + { + if (kv.JumpToKey("pve_spawn_messages")) + { + profileData.PvESpawnMessagesArray = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + char message[256], section[64]; + for (int i = 1;; i++) + { + FormatEx(section, sizeof(section), "%d", i); + kv.GetString(section, message, sizeof(message)); + if (message[0] == '\0') + { + break; + } + + profileData.PvESpawnMessagesArray.PushString(message); + } + kv.GoBack(); + } + kv.GetString("pve_spawn_message_prefix", profileData.PvESpawnMessagePrefix, sizeof(profileData.PvESpawnMessagePrefix), profileData.PvESpawnMessagePrefix); + profileData.DisplayPvEHealth = kv.GetNum("pve_health_bar", profileData.DisplayPvEHealth) != 0; + char setProfile[SF2_MAX_PROFILE_NAME_LENGTH]; + strcopy(setProfile, sizeof(setProfile), profile); + if (kv.GetNum("pve_selectable", 1) != 0) + { + RegisterPvESlenderBoss(setProfile); + } + } + + index = GetSelectableBossProfileList().FindString(profile); + if (index != -1) + { + GetSelectableBossProfileList().Erase(index); + } + index = GetSelectableAdminBossProfileList().FindString(profile); + if (index != -1) + { + GetSelectableAdminBossProfileList().Erase(index); + } + index = GetSelectableBoxingBossProfileList().FindString(profile); + if (index != -1) + { + GetSelectableBoxingBossProfileList().Erase(index); + } + index = GetSelectableRenevantBossProfileList().FindString(profile); + if (index != -1) + { + GetSelectableRenevantBossProfileList().Erase(index); + } + index = GetSelectableRenevantBossAdminProfileList().FindString(profile); + if (index != -1) + { + GetSelectableRenevantBossAdminProfileList().Erase(index); + } + } + + ArrayList validSections = new ArrayList(ByteCountToCells(128)); + + if (kv.GotoFirstSubKey()) //Special thanks to Fire for modifying the code for download errors. + { + char s2[64], s3[64], s4[PLATFORM_MAX_PATH], s5[PLATFORM_MAX_PATH]; + + do + { + kv.GetSectionName(s2, sizeof(s2)); + + if (validSections.FindString(s2) != -1) + { + continue; + } + + validSections.PushString(s2); + + if (StrContains(s2, "sound_") != -1) + { + bool doBack = false; + if (kv.JumpToKey("paths")) + { + doBack = true; + } + for (int i = 1;; i++) + { + FormatEx(s3, sizeof(s3), "%d", i); + kv.GetString(s3, s4, sizeof(s4)); + if (s4[0] == '\0') + { + break; + } + + TryPrecacheBossProfileSoundPath(s4, g_FileCheckConVar.BoolValue); + + // Here comes an if else mess, I'm very sorry + if (strcmp(s2, "sound_jumpscare") == 0) + { + profileData.JumpscareSound = s4; + break; + } + else if (strcmp(s2, "sound_static") == 0) + { + profileData.StaticSound = s4; + break; + } + else if (strcmp(s2, "sound_static_loop_local") == 0) + { + profileData.StaticLocalSound = s4; + break; + } + else if (strcmp(s2, "sound_static_shake_local") == 0) + { + profileData.StaticShakeLocal = s4; + break; + } + } + if (doBack) + { + kv.GoBack(); + } + profileData.SortSoundSections(kv, s2, g_FileCheckConVar.BoolValue); + } + else if (strcmp(s2, "download") == 0) + { + for (int i = 1;; i++) + { + FormatEx(s3, sizeof(s3), "%d", i); + kv.GetString(s3, s4, sizeof(s4)); + if (s4[0] == '\0') + { + break; + } + + if (g_FileCheckConVar.BoolValue) + { + if (FileExists(s4) || FileExists(s4, true)) + { + AddFileToDownloadsTable(s4); + } + else + { + LogSF2Message("File %s does not exist, please fix this download or remove it from the array.", s4); + } + } + else + { + AddFileToDownloadsTable(s4); + } + } + } + else if (strcmp(s2, "mod_precache") == 0) + { + for (int i = 1;; i++) + { + FormatEx(s3, sizeof(s3), "%d", i); + kv.GetString(s3, s4, sizeof(s4)); + if (s4[0] == '\0') + { + break; + } + + if (!PrecacheModel(s4, true)) + { + LogSF2Message("Model file %s failed to be precached, likely does not exist. This will crash the server if not fixed.", s4); + } + } + } + else if (strcmp(s2, "mat_download") == 0) + { + for (int i = 1;; i++) + { + FormatEx(s3, sizeof(s3), "%d", i); + kv.GetString(s3, s4, sizeof(s4)); + if (s4[0] == '\0') + { + break; + } + + FormatEx(s5, sizeof(s5), "%s.vtf", s4); + if (g_FileCheckConVar.BoolValue) + { + if (FileExists(s5) || FileExists(s5, true)) + { + AddFileToDownloadsTable(s5); + } + else + { + LogSF2Message("Texture file %s does not exist, please fix this download or remove it from the array.", s5); + } + } + else + { + AddFileToDownloadsTable(s5); + } + + FormatEx(s5, sizeof(s5), "%s.vmt", s4); + if (g_FileCheckConVar.BoolValue) + { + if (FileExists(s5) || FileExists(s5, true)) + { + AddFileToDownloadsTable(s5); + } + else + { + LogSF2Message("Material file %s does not exist, please fix this download or remove it from the array.", s5); + } + } + else + { + AddFileToDownloadsTable(s5); + } + } + } + else if (strcmp(s2, "mod_download") == 0) + { + for (int i = 1;; i++) + { + FormatEx(s3, sizeof(s3), "%d", i); + kv.GetString(s3, s4, sizeof(s4)); + if (s4[0] == '\0') + { + break; + } + + PrecacheModel2(s4, _, _, g_FileCheckConVar.BoolValue); + } + } + else if (strcmp(s2, "overlay_player_death") == 0) + { + kv.GetString("1", s4, sizeof(s4)); + profileData.OverlayPlayerDeath = s4; + } + else if (strcmp(s2, "overlay_jumpscare") == 0) + { + kv.GetString("1", s4, sizeof(s4)); + profileData.OverlayJumpscare = s4; + } + if (StrContains(s2, "sound_footsteps_event_") != -1) + { + if (profileData.FootstepEventSounds == null) + { + profileData.FootstepEventSounds = new ArrayList(sizeof(SF2BossProfileSoundInfo)); + } + if (profileData.FootstepEventIndexes == null) + { + profileData.FootstepEventIndexes = new ArrayList(); + } -int GetBossProfileStaticShakeSound(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.StaticShakeLocal); -} + SF2BossProfileSoundInfo soundInfo; + soundInfo.Init(); + soundInfo.Load(kv, g_FileCheckConVar.BoolValue); + soundInfo.PostLoad(); + if (soundInfo.Paths != null) + { + strcopy(s3, sizeof(s3), s2); + ReplaceStringEx(s3, sizeof(s3), "sound_footsteps_event_", ""); + int eventNumber = StringToInt(s3); -int GetBossProfileOverlayJumpscare(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.OverlayJumpscare); -} + profileData.FootstepEventIndexes.Push(eventNumber); + profileData.FootstepEventSounds.PushArray(soundInfo); + } + } + else if (StrContains(s2, "sound_event_") != -1) + { + if (profileData.EventSounds == null) + { + profileData.EventSounds = new ArrayList(sizeof(SF2BossProfileSoundInfo)); + } + if (profileData.EventIndexes == null) + { + profileData.EventIndexes = new ArrayList(); + } -int GetBossProfileOverlayPlayerDeath(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.OverlayPlayerDeath); -} + SF2BossProfileSoundInfo soundInfo; + soundInfo.Init(); + soundInfo.Load(kv, g_FileCheckConVar.BoolValue); + soundInfo.PostLoad(); + if (soundInfo.Paths != null) + { + strcopy(s3, sizeof(s3), s2); + ReplaceStringEx(s3, sizeof(s3), "sound_event_", ""); + int eventNumber = StringToInt(s3); -ArrayList GetBossProfileCompanions(const char[] profile) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return g_CachedProfileData.CompanionsArray; -} + profileData.EventIndexes.Push(eventNumber); + profileData.EventSounds.PushArray(soundInfo); + } + } + } + while (kv.GotoNextKey()); -int GetBossProfileCompanionsSpawnType(const char[] profile, char[] buffer, int bufferlen) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - return strcopy(buffer, bufferlen, g_CachedProfileData.CompanionSpawnType); -} + kv.GoBack(); + } -void GetBossProfileAnimationsData(const char[] profile, SF2BossProfileMasterAnimationsData data) -{ - g_BossProfileData.GetArray(profile, g_CachedProfileData, sizeof(g_CachedProfileData)); - data = g_CachedProfileData.AnimationData; -} \ No newline at end of file + delete validSections; + + if (kv.JumpToKey("companions")) + { + profileData.CompanionsArray = new ArrayList(sizeof(SF2BossProfileCompanionsInfo)); + + kv.GetString("type", profileData.CompanionSpawnType, sizeof(profileData.CompanionSpawnType)); + if (kv.GotoFirstSubKey()) + { + do + { + SF2BossProfileCompanionsInfo companions; + companions.Init(); + companions.Load(kv); + profileData.CompanionsArray.PushArray(companions); + if (lookIntoLoads) + { + char compProfile[SF2_MAX_PROFILE_NAME_LENGTH], otherProfile[SF2_MAX_PROFILE_NAME_LENGTH], dir[PLATFORM_MAX_PATH], file[PLATFORM_MAX_PATH]; + FileType fileType; + DirectoryListing directory = OpenDirectory(originalDir); + while (directory.GetNext(file, sizeof(file), fileType)) + { + if (fileType == FileType_Directory) + { + continue; + } + + FormatEx(dir, sizeof(dir), "%s/%s", originalDir, file); + + for (int i = 0; i < companions.Bosses.Length; i++) + { + companions.Bosses.GetString(i, compProfile, sizeof(compProfile)); + + KeyValues otherKeys = new KeyValues("root"); + if (!FileToKeyValues(otherKeys, dir)) + { + delete otherKeys; + continue; + } + + otherKeys.GetSectionName(otherProfile, sizeof(otherProfile)); + + if (strcmp(compProfile, otherProfile) == 0) + { + if (!LoadBossProfile(otherKeys, otherProfile, loadFailReasonBuffer, loadFailReasonBufferLen)) + { + LogSF2Message("(COMPANION) %s...FAILED (reason: %s)", dir, loadFailReasonBuffer); + } + else + { + LogSF2Message("(COMPANION) %s...", otherProfile); + } + } + + delete otherKeys; + } + } + } + } + while (kv.GotoNextKey()); + kv.GoBack(); + } + kv.GoBack(); + } + + if (kv.JumpToKey("attributes")) + { + profileData.AttributesInfo.Load(kv); + } + + if (kv.JumpToKey("effects")) + { + profileData.EffectsArray = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); + + if (kv.GotoFirstSubKey()) + { + do + { + SF2BossProfileBaseEffectInfo effects; + effects.Init(); + effects.ModelScale = profileData.ModelScale; + effects.Load(kv, g_FileCheckConVar.BoolValue); + profileData.EffectsArray.PushArray(effects); + } + while (kv.GotoNextKey()); + kv.GoBack(); + } + kv.GoBack(); + } + + if (kv.JumpToKey("spawn_effects")) + { + if (profileData.SpawnEffects == null) + { + profileData.SpawnEffects = new StringMap(); + } + + if (kv.GotoFirstSubKey()) + { + do + { + char section[64]; + kv.GetSectionName(section, sizeof(section)); + if (kv.JumpToKey("effects")) + { + ArrayList list = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); + if (kv.GotoFirstSubKey()) + { + do + { + SF2BossProfileBaseEffectInfo effects; + effects.Init(); + effects.ModelScale = profileData.ModelScale; + effects.Load(kv, g_FileCheckConVar.BoolValue); + if (effects.Type == EffectType_Particle) + { + effects.LifeTime = 0.1; + } + list.PushArray(effects); + } + while (kv.GotoNextKey()); + kv.GoBack(); + } + kv.GoBack(); + profileData.SpawnEffects.SetValue(section, list); + } + } + while (kv.GotoNextKey()); + kv.GoBack(); + } + kv.GoBack(); + } + + if (kv.JumpToKey("despawn_effects")) + { + if (profileData.DespawnEffects == null) + { + profileData.DespawnEffects = new StringMap(); + } + profileData.HideDespawnEffectsOnDeath = kv.GetNum("hide_on_death", profileData.HideDespawnEffectsOnDeath) != 0; + + if (kv.GotoFirstSubKey()) + { + do + { + char section[64]; + kv.GetSectionName(section, sizeof(section)); + if (kv.JumpToKey("effects")) + { + ArrayList list = new ArrayList(sizeof(SF2BossProfileBaseEffectInfo)); + if (kv.GotoFirstSubKey()) + { + do + { + SF2BossProfileBaseEffectInfo effects; + effects.Init(); + effects.ModelScale = profileData.ModelScale; + effects.Load(kv, g_FileCheckConVar.BoolValue); + if (effects.Type == EffectType_Particle) + { + effects.LifeTime = 0.1; + } + list.PushArray(effects); + } + while (kv.GotoNextKey()); + kv.GoBack(); + } + kv.GoBack(); + profileData.DespawnEffects.SetValue(section, list); + } + } + while (kv.GotoNextKey()); + kv.GoBack(); + } + kv.GoBack(); + } + + profileData.PostLoad(); + + g_BossProfileData.SetArray(profile, profileData, sizeof(profileData)); + + Call_StartForward(g_OnBossProfileLoadedFwd); + Call_PushString(profile); + Call_PushCell(kv); + Call_Finish(); + + return true; +}*/ diff --git a/addons/sourcemod/scripting/sf2/pve.sp b/addons/sourcemod/scripting/sf2/pve.sp index 00ef2e2b..14099d47 100644 --- a/addons/sourcemod/scripting/sf2/pve.sp +++ b/addons/sourcemod/scripting/sf2/pve.sp @@ -1,4 +1,5 @@ #pragma semicolon 1 +#pragma newdecls required static bool g_PlayerInPvE[MAXTF2PLAYERS]; static bool g_PlayerIsLeavingPvE[MAXTF2PLAYERS]; @@ -13,6 +14,8 @@ static ArrayList g_ActiveBosses; static bool g_IsPvEActive; static bool g_DoesPvEExist; static char g_PlayerCurrentPvEMusic[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; +static char g_PlayerUsedPvEMusic[MAXTF2PLAYERS][PLATFORM_MAX_PATH]; +static float g_PlayerUsedPvEMusicVolume[MAXTF2PLAYERS]; static bool g_PvEBoxingMode; static float g_TimeUntilBossSpawns; @@ -283,6 +286,18 @@ static void RoundStart() g_HasBossSpawned = false; g_ActiveBosses.Clear(); + for (int i = 1; i <= MaxClients; i++) + { + SF2_BasePlayer client = SF2_BasePlayer(i); + if (!client.IsValid) + { + continue; + } + + g_PlayerUsedPvEMusic[client.index][0] = '\0'; + g_PlayerUsedPvEMusicVolume[client.index] = 1.0; + } + int ent = -1; while ((ent = FindEntityByClassname(ent, "trigger_multiple")) != -1) { @@ -333,6 +348,9 @@ static void OnPutInServer(SF2_BasePlayer client) PvE_ForceResetPlayerPvEData(client.index); + g_PlayerUsedPvEMusic[client.index][0] = '\0'; + g_PlayerUsedPvEMusicVolume[client.index] = 1.0; + if (IsClientAuthorized(client.index)) { OnClientAuthorized(client.index, NULL_STRING); @@ -438,10 +456,9 @@ static void OnPlayerAverageUpdate(SF2_BasePlayer client) SF2_ChaserEntity chaser = SF2_ChaserEntity(EntRefToEntIndex(bosses.Get(i2))); if (chaser.IsValid() && chaser.Controller.IsValid()) { - SF2BossProfileData data; - data = view_as(chaser.Controller).GetProfileData(); - data.Names.GetString(1, name, sizeof(name)); - if (!data.DisplayPvEHealth) + ChaserBossProfile data = chaser.Controller.GetProfileData(); + data.GetName(1, name, sizeof(name)); + if (!data.GetPvEData().DisplayHealth) { continue; } @@ -658,33 +675,43 @@ void PvE_OnTriggerStartTouch(int trigger, int other) PvE_SetPlayerPvEState(other, true); if (g_IsPvEActive && g_PvETriggers.FindValue(entRef) != -1) { - if (g_CustomMusicOverride[0] != '\0') + if (g_PlayerUsedPvEMusic[other][0] != '\0') { - EmitSoundToClient(other, g_CustomMusicOverride, _, _, _, _, 0.75); - strcopy(g_PlayerCurrentPvEMusic[other], sizeof(g_PlayerCurrentPvEMusic[]), g_CustomMusicOverride); + EmitSoundToClient(other, g_PlayerUsedPvEMusic[other], _, _, _, _, g_PlayerUsedPvEMusicVolume[other]); + strcopy(g_PlayerCurrentPvEMusic[other], sizeof(g_PlayerCurrentPvEMusic[]), g_PlayerUsedPvEMusic[other]); } else { - ArrayList musics = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); - ArrayList volumes = new ArrayList(); + ArrayList usableMusics = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + ArrayList usableVolumes = new ArrayList(); + if (g_CustomMusicOverride[0] != '\0') + { + usableMusics.PushString(g_CustomMusicOverride); + usableVolumes.Push(0.75); + } + for (int i = 0; i < MAX_MUSICS; i++) { if (g_MusicFile[other][i][0] != '\0') { - musics.PushString(g_MusicFile[other][i]); - volumes.Push(g_MusicVolume[other][i]); + usableMusics.PushString(g_MusicFile[other][i]); + usableVolumes.Push(g_MusicVolume[other][i]); } } - if (musics.Length > 0) + + if (usableMusics.Length > 0) { char preferredMusic[PLATFORM_MAX_PATH]; - int random = GetRandomInt(0, musics.Length - 1); - musics.GetString(random, preferredMusic, sizeof(preferredMusic)); - EmitSoundToClient(other, preferredMusic, _, _, _, _, volumes.Get(random)); + int random = GetRandomInt(0, usableMusics.Length - 1); + usableMusics.GetString(random, preferredMusic, sizeof(preferredMusic)); + EmitSoundToClient(other, preferredMusic, _, _, _, _, usableVolumes.Get(random)); strcopy(g_PlayerCurrentPvEMusic[other], sizeof(g_PlayerCurrentPvEMusic[]), preferredMusic); + strcopy(g_PlayerUsedPvEMusic[other], sizeof(g_PlayerUsedPvEMusic[]), preferredMusic); + g_PlayerUsedPvEMusicVolume[other] = usableVolumes.Get(random); } - delete volumes; - delete musics; + + delete usableVolumes; + delete usableMusics; } } } @@ -875,13 +902,9 @@ static void SpawnPvEBoss(const char[] override = "") if (npc.IsValid()) { npc.Spawn(spawnPos); - SF2BossProfileData data; - data = npc.GetProfileData(); - SF2BossProfileSoundInfo soundInfo; - soundInfo = data.MusicSoundsNormal; - SF2ChaserBossProfileData chaserData; - chaserData = view_as(npc).GetProfileData(); - g_PvEBoxingMode = chaserData.BoxingBoss; + ChaserBossProfile data = view_as(npc).GetProfileData(); + ProfileSound soundInfo = data.GetGlobalMusic(1); + g_PvEBoxingMode = data.BoxingBoss; if (soundInfo.Paths != null && soundInfo.Paths.Length > 0) { soundInfo.Paths.GetString(GetRandomInt(0, soundInfo.Paths.Length - 1), g_CustomMusicOverride, sizeof(g_CustomMusicOverride)); @@ -900,14 +923,13 @@ static void SpawnPvEBoss(const char[] override = "") testNPC.Spawn(spawnPos); g_ActiveBosses.Push(EntIndexToEntRef(testNPC.EntIndex)); - SF2BossProfileData tempData; - tempData = testNPC.GetProfileData(); + BaseBossProfile tempData = testNPC.GetProfileData(); - if (tempData.CopiesInfo.Enabled[1]) + if (tempData.GetCopies().IsEnabled(1)) { char tempProfile[SF2_MAX_PROFILE_NAME_LENGTH]; testNPC.GetProfile(tempProfile, sizeof(tempProfile)); - for (int i2 = 0; i2 < tempData.CopiesInfo.MaxCopies[1]; i2++) + for (int i2 = 0; i2 < tempData.GetCopies().GetMaxCopies(1); i2++) { SF2NPC_BaseNPC copy = AddProfile(tempProfile, _, testNPC); if (!copy.IsValid()) @@ -921,9 +943,9 @@ static void SpawnPvEBoss(const char[] override = "") } } - if (data.CopiesInfo.Enabled[1]) + if (data.GetCopies().IsEnabled(1)) { - for (int i = 0; i < data.CopiesInfo.MaxCopies[1]; i++) + for (int i = 0; i < data.GetCopies().GetMaxCopies(1); i++) { SF2NPC_BaseNPC copy = AddProfile(profile, _, npc); if (!copy.IsValid()) @@ -989,12 +1011,14 @@ static Action Command_AddPvEMusic(int client, int args) } bool isAvailable = false; + int index = 0; for (int i = 0; i < MAX_MUSICS; i++) { if (g_MusicFile[client][i][0] == '\0') { isAvailable = true; strcopy(g_MusicFile[client][i], sizeof(g_MusicFile[][]), soundFile); + index = i; break; } @@ -1014,6 +1038,8 @@ static Action Command_AddPvEMusic(int client, int args) PrecacheSound(soundFile); CPrintToChat(client, "{royalblue}%t {default}%T", "SF2 Prefix", "SF2 PvE Added Track", client, soundFile); + UpdateMusicToDatabase(client, index); + ClientSaveCookies(client); return Plugin_Handled; @@ -1160,10 +1186,12 @@ static int PvEMenu_SelectedMusic(Menu menu, MenuAction action, int param1, int p case 1: { + int index = 0; for (int i = 0; i < MAX_MUSICS; i++) { if (strcmp(g_MusicFile[param1][i], g_MenuMusic[param1], false) == 0) { + index = i; g_MusicFile[param1][i][0] = '\0'; g_MusicVolume[param1][i] = 0.75; g_MusicLoopTime[param1][i] = 0.0; @@ -1172,6 +1200,7 @@ static int PvEMenu_SelectedMusic(Menu menu, MenuAction action, int param1, int p } CPrintToChat(param1, "{royalblue}%t {default}%T", "SF2 Prefix", "SF2 PvE Removed Music", param1, g_MenuMusic[param1]); DisplayMusicSelectionPvE(param1); + UpdateMusicToDatabase(param1, index); } } } @@ -1194,15 +1223,20 @@ static int Panel_SettingsMusicVolume(Menu menu, MenuAction action, int param1, i { volume += 0.25; } + int index = 0; for (int i = 0; i < MAX_MUSICS; i++) { if (strcmp(g_MusicFile[param1][i], g_MenuMusic[param1], false) == 0) { g_MusicVolume[param1][i] = volume; + index = i; break; } } CPrintToChat(param1, "%t", "SF2 Music Volum Changed", RoundToNearest(volume * 100.0)); + + UpdateMusicToDatabase(param1, index); + ClientSaveCookies(param1); DisplayMusicSelectionPvE(param1); @@ -1210,6 +1244,31 @@ static int Panel_SettingsMusicVolume(Menu menu, MenuAction action, int param1, i return 0; } +static void UpdateMusicToDatabase(int client, int index) +{ + int id = GetSteamAccountID(client); + if (g_MusicDataBase != null && !IsFakeClient(client) && g_IsMusicCached[client] && id != 0) + { + Transaction action = new Transaction(); + + char formatter[512]; + FormatEx(formatter, sizeof(formatter), "UPDATE music_selection_%i SET " + ... "music_file = '%s', " + ... "music_volume = %f, " + ... "music_loop_time = %f " + ... "WHERE steamid = %d;", + index, + g_MusicFile[client][index], + g_MusicVolume[client][index], + g_MusicLoopTime[client][index], + id); + + action.AddQuery(formatter); + + g_MusicDataBase.Execute(action, _, Database_Fail, _, DBPrio_High); + } +} + // These register boss entity names to choose from void RegisterPvEBoss(char[] bossName) { @@ -1225,12 +1284,12 @@ void UnregisterPveBoss(char[] bossName) } } -void RegisterPvESlenderBoss(char profile[SF2_MAX_PROFILE_NAME_LENGTH]) +void RegisterPvESlenderBoss(const char[] profile) { g_SlenderBosses.PushString(profile); } -void UnregisterPvESlenderBoss(char profile[SF2_MAX_PROFILE_NAME_LENGTH]) +void UnregisterPvESlenderBoss(const char[] profile) { int index = g_SlenderBosses.FindString(profile); if (index != -1) @@ -1259,14 +1318,20 @@ void KillPvEBoss(int boss) float time = 5.0; if (bossIndex != -1) { - SF2BossProfileData data; - data = NPCGetProfileData(bossIndex); - time = data.PvETeleportEndTimer; + time = SF2NPC_BaseNPC(bossIndex).GetProfileData().GetPvEData().TeleportEndTimer; } for (int i = 1; i <= MaxClients; i++) { SF2_BasePlayer client = SF2_BasePlayer(i); - if (!client.IsValid || !client.IsInPvE) + if (!client.IsValid) + { + continue; + } + + g_PlayerUsedPvEMusic[client.index][0] = '\0'; + g_PlayerUsedPvEMusicVolume[client.index] = 1.0; + + if (!client.IsInPvE) { continue; } @@ -1314,9 +1379,7 @@ static Action Timer_RemoveAllPvEBosses(Handle timer) continue; } - SF2BossProfileData data; - data = npc.GetProfileData(); - if (!data.IsPvEBoss) + if (!npc.GetProfileData().IsPvEBoss) { continue; } diff --git a/addons/sourcemod/scripting/sf2/pvp.sp b/addons/sourcemod/scripting/sf2/pvp.sp index 0d359e80..3e2a26e6 100644 --- a/addons/sourcemod/scripting/sf2/pvp.sp +++ b/addons/sourcemod/scripting/sf2/pvp.sp @@ -998,24 +998,6 @@ void PvP_OnClientGhostModeEnable(int client) g_PlayerPvPRespawnTimer[client] = null; } -static bool Hook_ClientPvPShouldCollide(int ent,int collisiongroup,int contentsmask, bool originalResult) -{ - if (!g_Enabled || g_PlayerProxy[ent] || !IsClientInPvP(ent) || !g_PlayerEliminated[ent]) - { - SDKUnhook(ent, SDKHook_ShouldCollide, Hook_ClientPvPShouldCollide); - if (collisiongroup == 8) - { - return false; - } - return originalResult; - } - if (IsClientInPvP(ent) && GetClientTeam(ent) == TFTeam_Blue && collisiongroup == 8) - { - return true; - } - return originalResult; -} - void PvP_OnTriggerStartTouch(int trigger, int other) { char name[64]; diff --git a/addons/sourcemod/scripting/sf2/pvp/menus.sp b/addons/sourcemod/scripting/sf2/pvp/menus.sp index 1994ccdb..bebc4cc3 100644 --- a/addons/sourcemod/scripting/sf2/pvp/menus.sp +++ b/addons/sourcemod/scripting/sf2/pvp/menus.sp @@ -5,6 +5,7 @@ #define _sf2_pvp_menus #pragma semicolon 1 +#pragma newdecls required Menu g_MenuSettingsPvP; diff --git a/addons/sourcemod/scripting/sf2/specialround.sp b/addons/sourcemod/scripting/sf2/specialround.sp index e45ad534..2c738584 100644 --- a/addons/sourcemod/scripting/sf2/specialround.sp +++ b/addons/sourcemod/scripting/sf2/specialround.sp @@ -4,6 +4,7 @@ #define _sf2_specialround_included #pragma semicolon 1 +#pragma newdecls required #define SR_CYCLELENGTH 10.0 #define SR_STARTDELAY 0.25 @@ -867,21 +868,10 @@ static ArrayList SpecialEnabledList() } } - if (!SF_IsBoxingMap()) + if (GetSelectableBossProfileList().Length > 0) { - if (GetSelectableBossProfileList().Length > 0) - { - AddSpecialRoundToList(SPECIALROUND_DOUBLETROUBLE, enabledRounds); - AddSpecialRoundToList(SPECIALROUND_SILENTSLENDER, enabledRounds); - } - } - else - { - if (GetSelectableBoxingBossProfileList().Length > 0) - { - AddSpecialRoundToList(SPECIALROUND_DOUBLETROUBLE, enabledRounds); - AddSpecialRoundToList(SPECIALROUND_SILENTSLENDER, enabledRounds); - } + AddSpecialRoundToList(SPECIALROUND_DOUBLETROUBLE, enabledRounds); + AddSpecialRoundToList(SPECIALROUND_SILENTSLENDER, enabledRounds); } if (GetActivePlayerCount() <= g_MaxPlayersConVar.IntValue * 2 && !SF_IsBoxingMap()) @@ -1010,7 +1000,6 @@ static ArrayList SpecialEnabledList() { AddSpecialRoundToList(SPECIALROUND_TRIPLEBOSSES, enabledRounds); } - if (!SF_SpecialRound(SPECIALROUND_MODBOSSES) && !SF_IsRaidMap() && !SF_IsBoxingMap() && !SF_BossesChaseEndlessly() && !SF_IsProxyMap() && !SF_SpecialRound(SPECIALROUND_VOTE) && (GetSelectableAdminBossProfileList().Length > 0 || IsProfileValid(snatcher))) { AddSpecialRoundToList(SPECIALROUND_MODBOSSES, enabledRounds); @@ -1066,27 +1055,13 @@ void SpecialRoundStart() case SPECIALROUND_DOUBLETROUBLE: { char buffer[SF2_MAX_PROFILE_NAME_LENGTH]; - ArrayList selectableBosses = GetSelectableBossProfileList().Clone(); - ArrayList selectableBoxingBosses = GetSelectableBoxingBossProfileList().Clone(); + ArrayList selectableBosses = GetSelectableBossProfileList(); - if (!SF_IsBoxingMap()) - { - if (selectableBosses.Length > 0) - { - selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); - AddProfile(buffer); - } - } - else + if (selectableBosses.Length > 0) { - if (selectableBoxingBosses.Length > 0) - { - selectableBoxingBosses.GetString(GetRandomInt(0, selectableBoxingBosses.Length - 1), buffer, sizeof(buffer)); - AddProfile(buffer); - } + selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); + AddProfile(buffer); } - delete selectableBosses; - delete selectableBoxingBosses; SF_AddSpecialRound(SPECIALROUND_DOUBLETROUBLE); } case SPECIALROUND_SILENTSLENDER: @@ -1102,8 +1077,7 @@ void SpecialRoundStart() } char buffer[SF2_MAX_PROFILE_NAME_LENGTH]; - ArrayList selectableBosses = GetSelectableBossProfileList().Clone(); - ArrayList selectableBoxingBosses = GetSelectableBoxingBossProfileList().Clone(); + ArrayList selectableBosses = GetSelectableBossProfileList(); if (selectableBosses.Length > 0) { @@ -1119,8 +1093,6 @@ void SpecialRoundStart() selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); AddProfile(buffer, _, _, _, false); } - delete selectableBosses; - delete selectableBoxingBosses; SF_AddSpecialRound(SPECIALROUND_SILENTSLENDER); } case SPECIALROUND_THANATOPHOBIA: @@ -1288,8 +1260,8 @@ void SpecialRoundStart() g_OverrideDifficulty = 3; } char buffer[SF2_MAX_PROFILE_NAME_LENGTH]; - ArrayList selectableBosses = GetSelectableBossProfileList().Clone(); - ArrayList selectableBoxingBosses = GetSelectableBossProfileList().Clone(); + ArrayList selectableBosses = GetSelectableBossProfileList(); + ArrayList selectableBoxingBosses = GetSelectableBossProfileList(); if (!SF_IsBoxingMap()) { if (selectableBosses.Length > 0) @@ -1306,8 +1278,6 @@ void SpecialRoundStart() AddProfile(buffer); } } - delete selectableBosses; - delete selectableBoxingBosses; SF_AddSpecialRound(SPECIALROUND_2DOUBLE); } case SPECIALROUND_SUPRISE: @@ -1345,10 +1315,9 @@ void SpecialRoundStart() char buffer[SF2_MAX_PROFILE_NAME_LENGTH], nightmareDisplay[256]; if (!SF_SpecialRound(SPECIALROUND_DOUBLEROULETTE) && !SF_SpecialRound(SPECIALROUND_REVOLUTION)) { - NPCStopMusic(); NPCRemoveAll(); } - ArrayList selectableBosses = GetSelectableAdminBossProfileList().Clone(); + ArrayList selectableBosses = GetSelectableAdminBossProfileList(); if (selectableBosses.Length > 0) { selectableBosses.GetString(GetRandomInt(0, selectableBosses.Length - 1), buffer, sizeof(buffer)); @@ -1525,16 +1494,14 @@ void SpecialRoundStart() g_OverrideDifficulty = randomDifficulty; } } - delete selectableBosses; SF_AddSpecialRound(SPECIALROUND_MODBOSSES); } case SPECIALROUND_TRIPLEBOSSES: { char buffer[SF2_MAX_PROFILE_NAME_LENGTH]; - int tripleBosses=0; + int tripleBosses = 0; for (int i = 0; i < MAX_BOSSES; i++) { - NPCStopMusic(); SF2NPC_BaseNPC Npc = SF2NPC_BaseNPC(i); if (!Npc.IsValid()) { diff --git a/addons/sourcemod/scripting/sf2/stocks.sp b/addons/sourcemod/scripting/sf2/stocks.sp index 71180d4d..6abe6c15 100644 --- a/addons/sourcemod/scripting/sf2/stocks.sp +++ b/addons/sourcemod/scripting/sf2/stocks.sp @@ -4,6 +4,7 @@ #define _sf2_stocks_included #pragma semicolon 1 +#pragma newdecls required #define VALID_MINIMUM_MEMORY_ADDRESS 0x10000 @@ -24,6 +25,12 @@ #define HIDEHUD_VEHICLE_CROSSHAIR ( 1<<9 ) // Hide vehicle crosshair #define HIDEHUD_INVEHICLE ( 1<<10 ) #define HIDEHUD_BONUS_PROGRESS ( 1<<11 ) // Hide bonus progress display (for bonus map challenges) +#define HIDEHUD_BUILDING_STATUS (1 << 12) +#define HIDEHUD_CLOAK_AND_FEIGN (1 << 13) +#define HIDEHUD_PIPES_AND_CHARGE (1 << 14) +#define HIDEHUD_METAL (1 << 15) +#define HIDEHUD_TARGET_ID (1 << 16) +#define HIDEHUD_MATCH_STATUS (1 << 17) #define FFADE_IN 0x0001 // Just here so we don't pass 0 into the function #define FFADE_OUT 0x0002 // Fade out (not in) @@ -349,7 +356,7 @@ bool IsSpaceOccupiedNPC(const float pos[3], const float mins[3], const float max return hit; } -void CBaseNPC_RemoveAllLayers(int entity) +void CBaseNPC_RemoveAllLayers(int entity, bool instant = true) { if (!IsValidEntity(entity)) { @@ -364,7 +371,16 @@ void CBaseNPC_RemoveAllLayers(int entity) { continue; } - overlay.KillMe(); + if (instant) + { + overlay.KillMe(); + } + else + { + overlay.m_flBlendOut = 0.2; + overlay.m_flCycle = 0.7; + overlay.AutoKill(); + } } } @@ -417,6 +433,31 @@ bool CBaseAnimating_GetSequenceVelocity(Address studioHdr, int sequence, float c return SDKCall(g_SDKSequenceVelocity, studioHdr, sequence, cycle, poseParameter, velocity); } +float GetDamageDistance(float start[3], float end[3], float baseDamage, float minRange, float maxRange, float minMultiplier, float maxMultiplier) +{ + float minDamage = baseDamage * minMultiplier; + float maxDamage = baseDamage * maxMultiplier; + float newDamage = baseDamage; + + float distance = GetVectorDistance(start, end, true); + float percent = (distance - minRange) / (maxRange - minRange); + if (percent <= 0.0) + { + percent = distance / minRange; + newDamage = LerpFloats(maxDamage, baseDamage, percent); + } + else + { + if (percent > 1.0) + { + percent = 1.0; + } + newDamage = LerpFloats(baseDamage, minDamage, percent); + } + + return newDamage; +} + // ========================================================= // GLOW FUNCTIONS // ========================================================= @@ -478,9 +519,10 @@ void KillClient(int client) { if (client != -1) { - SDKHooks_TakeDamage(client, 0, 0, 9001.0, 0x80 | DMG_PREVENT_PHYSICS_FORCE, _, { 0.0, 0.0, 0.0 }); + int health = GetEntProp(client, Prop_Send, "m_iHealth") * 1000; + SDKHooks_TakeDamage(client, 0, 0, float(health), 0x80 | DMG_PREVENT_PHYSICS_FORCE, _, { 0.0, 0.0, 0.0 }, .bypassHooks = false); ForcePlayerSuicide(client); - SetVariantInt(9001); + SetVariantInt(health); AcceptEntityInput(client, "RemoveHealth"); } } @@ -497,7 +539,7 @@ void Explode(float pos[3], float damage, float radius, int attacker, const char[ SetEntityOwner(bomb, attacker); DispatchSpawn(bomb); - SDKHooks_TakeDamage(bomb, attacker, attacker, 9001.0, DMG_BLAST); + SDKHooks_TakeDamage(bomb, attacker, attacker, 9001.0, DMG_BLAST, .bypassHooks = false); } void DestroyAllActiveWeapons(int client) @@ -850,9 +892,14 @@ void ForceTeamWin(int team) AcceptEntityInput(ent, "SetWinner"); } -Handle PrepareItemHandle(char[] classname, int index, int level, int quality, char[] att) +Handle PrepareItemHandle(char[] classname, int index, int level, int quality, char[] att, bool preserveAttributes = false) { - Handle item = TF2Items_CreateItem(OVERRIDE_ALL | FORCE_GENERATION); + int flags = OVERRIDE_ALL | FORCE_GENERATION; + if (preserveAttributes) + { + flags |= PRESERVE_ATTRIBUTES; + } + Handle item = TF2Items_CreateItem(flags); TF2Items_SetClassname(item, classname); TF2Items_SetItemIndex(item, index); TF2Items_SetLevel(item, level); @@ -1105,7 +1152,7 @@ void UTIL_ScreenShake(float center[3], float amplitude, float frequency, float d SF2_BasePlayer player = SF2_BasePlayer(i); if (player.IsValid && !player.IsBot && !player.IsInGhostMode) { - if (!airShake && command == 0 && !(player.GetFlags() && FL_ONGROUND)) + if (!airShake && command == 0 && (player.GetFlags() & FL_ONGROUND) != 0) { continue; } @@ -1165,7 +1212,7 @@ float ComputeShakeAmplitude(float center[3], float playerPos[3], float amplitude /** * Converts a given timestamp into hours, minutes, and seconds. */ -void FloatToTimeHMS(float time, int &h=0, int &m=0, int &s=0) +void FloatToTimeHMS(float time, int &h = 0, int &m = 0, int &s = 0) { s = RoundFloat(time); h = s / 3600; @@ -1237,7 +1284,7 @@ void CopyVector(const float copy[3], float dest[3]) dest[2] = copy[2]; } -/*void LerpVectors(const float a[3] , const float b[3], float c[3], float t) +void LerpVectors(const float a[3] , const float b[3], float c[3], float t) { if (t < 0.0) { @@ -1251,7 +1298,7 @@ void CopyVector(const float copy[3], float dest[3]) c[0] = a[0] + (b[0] - a[0]) * t; c[1] = a[1] + (b[1] - a[1]) * t; c[2] = a[2] + (b[2] - a[2]) * t; -}*/ +} /** * Translates and re-orients a given offset vector into world space, given a world position and angle. @@ -1470,35 +1517,22 @@ Action Timer_KillEntity(Handle timer, any entref) return Plugin_Stop; } -Action Timer_KillEdict(Handle timer, any entref) -{ - int ent = EntRefToEntIndex(entref); - if (!ent || ent == INVALID_ENT_REFERENCE) - { - return Plugin_Stop; - } - - RemoveEdict(ent); - - return Plugin_Stop; -} - // ========================================================== // SPECIAL ROUND FUNCTIONS // ========================================================== bool IsInfiniteFlashlightEnabled() { - return (g_RoundInfiniteFlashlight || (g_PlayerInfiniteFlashlightOverrideConVar.IntValue == 1) || SF_SpecialRound(SPECIALROUND_INFINITEFLASHLIGHT) || (IsNightVisionEnabled() && g_NightVisionType == 1)); + return ((g_RoundInfiniteFlashlight && g_PlayerInfiniteFlashlightOverrideConVar.IntValue != 0) || (g_PlayerInfiniteFlashlightOverrideConVar.IntValue == 1) || SF_SpecialRound(SPECIALROUND_INFINITEFLASHLIGHT) || (IsNightVisionEnabled() && g_NightVisionType == 1)); } bool IsInfiniteBlinkEnabled() { - return g_RoundInfiniteBlink || (g_PlayerInfiniteBlinkOverrideConVar.IntValue == 1); + return (g_RoundInfiniteBlink && g_PlayerInfiniteBlinkOverrideConVar.IntValue != 0) || (g_PlayerInfiniteBlinkOverrideConVar.IntValue == 1); } bool IsInfiniteSprintEnabled() { - return g_IsRoundInfiniteSprint || (g_PlayerInfiniteSprintOverrideConVar.IntValue == 1) || SF_IsSlaughterRunMap() || SF_IsBoxingMap() || SF_IsRaidMap(); + return (g_IsRoundInfiniteSprint && g_PlayerInfiniteSprintOverrideConVar.IntValue != 0) || (g_PlayerInfiniteSprintOverrideConVar.IntValue == 1) || SF_IsSlaughterRunMap() || SF_IsBoxingMap() || SF_IsRaidMap(); } bool IsNightVisionEnabled() @@ -1565,15 +1599,9 @@ int GetLocalGlobalDifficulty(int npcIndex = -1) { return g_DifficultyConVar.IntValue; } - SF2BossProfileData data; SF2NPC_BaseNPC controller = SF2NPC_BaseNPC(npcIndex); - data = controller.GetProfileData(); - SF2ChaserBossProfileData chaserData; - if (data.Type == SF2BossType_Chaser) - { - chaserData = view_as(controller).GetProfileData(); - } - if (data.IsPvEBoss || chaserData.BoxingBoss) + ChaserBossProfile data = view_as(controller.GetProfileData()); + if (data.IsPvEBoss || data.BoxingBoss) { if (NPCGetUniqueID(npcIndex) != -1) { diff --git a/addons/sourcemod/scripting/sf2/traps.sp b/addons/sourcemod/scripting/sf2/traps.sp index 56f246da..ee35af8d 100644 --- a/addons/sourcemod/scripting/sf2/traps.sp +++ b/addons/sourcemod/scripting/sf2/traps.sp @@ -4,47 +4,150 @@ #define _sf2_traps_included #pragma semicolon 1 +#pragma newdecls required -static float g_TrapDespawnTimer[2049]; -static bool g_TrapClosed[2049]; -static int g_TrapState[2049]; -static SF2NPC_Chaser g_TrapMaster[2049]; -static bool g_TrapStartedOpenAnim[2049]; -static bool g_TrapDoIdleAnim[2049]; -static bool g_TrapAnimChange[2049]; -static Handle g_TrapTimer[2049]; -//State 0 = Idle, State 1 = Closed +#include "traps/base.sp" +#include "traps/beartrap.sp" +#include "traps/sentry.sp" + +methodmap BossProfileTrapData < ProfileObject +{ + public bool IsEnabled(int difficulty) + { + if (this == null) + { + return false; + } + return this.GetDifficultyBool("enabled", difficulty, true); + } + + public float GetSpawnCooldown(int difficulty) + { + return this.GetDifficultyFloat("spawn_cooldown", difficulty, 8.0); + } + + public bool CanPlaceOnState(int difficulty, int state) + { + char states[128]; + this.GetDifficultyString("place_on_states", difficulty, states, sizeof(states), "idle alert"); + if (state == STATE_IDLE && StrContains(states, "idle") != -1) + { + return true; + } + + if (state == STATE_ALERT && StrContains(states, "alert") != -1) + { + return true; + } + + if (state == STATE_CHASE && StrContains(states, "chase") != -1) + { + return true; + } + return false; + } + + public ProfileObject GetTrapTypes() + { + return this.GetSection("types"); + } + + public BossProfileBaseTrap GetTrapFromName(const char[] name) + { + ProfileObject obj = this.GetTrapTypes(); + if (obj == null) + { + return null; + } + return view_as(obj.GetSection(name)); + } + + public void Precache() + { + for (int i = 0; i < this.GetTrapTypes().SectionLength; i++) + { + char name[64]; + this.GetTrapTypes().GetSectionNameFromIndex(i, name, sizeof(name)); + BossProfileBaseTrap trap = this.GetTrapFromName(name); + if (trap == null) + { + continue; + } + trap.Precache(); + } + } +} void SetupTraps() { + g_OnPlayerPutInServerPFwd.AddFunction(null, OnPutInServer); + g_OnPlayerSpawnPFwd.AddFunction(null, OnPlayerSpawn); g_OnPlayerJumpPFwd.AddFunction(null, OnJump); + SF2_BaseTrap.Initialize(); + SF2_BearTrap.Initialize(); + SF2_SentryTrap.Initialize(); } -static void OnJump(SF2_BasePlayer client) +static void OnPutInServer(SF2_BasePlayer client) +{ + SDKHook(client.index, SDKHook_PreThink, ClientPreThink); +} + +static void OnPlayerSpawn(SF2_BasePlayer client) { - if (client.IsEliminated || IsRoundEnding() || IsRoundInWarmup() || client.HasEscaped) + if (client.IsTrapped) { - return; + TF2Attrib_RemoveByName(client.index, "increased jump height"); + TF2Attrib_RemoveByName(client.index, "move speed bonus"); + client.UpdateSpeed(); } + client.IsTrapped = false; + client.TrapCount = 0; +} - if (client.IsTrapped) +static void OnJump(SF2_BasePlayer client) +{ + if (!client.IsTrapped) { - client.TrapCount -= 1; + return; } - if (client.IsTrapped && client.TrapCount <= 1) + + client.TrapCount -= 1; + if (client.TrapCount <= 0) { client.IsTrapped = false; client.TrapCount = 0; + TF2Attrib_RemoveByName(client.index, "increased jump height"); + TF2Attrib_RemoveByName(client.index, "move speed bonus"); + client.UpdateSpeed(); + } +} + +static void ClientPreThink(int entity) +{ + SF2_BasePlayer client = SF2_BasePlayer(entity); + if ((client.IsTrapped || client.IsLatched) && !client.IsInGhostMode) + { + if (client.InCondition(TFCond_Taunting)) + { + client.ChangeCondition(TFCond_Taunting, true); + } } } void Trap_SpawnTrap(float position[3], float direction[3], SF2NPC_Chaser controller) { + if (g_TrapEntityCount > MAX_TRAPS) + { + return; + } + int slender = controller.EntIndex; if (!slender || slender == INVALID_ENT_REFERENCE) { return; } + SF2_ChaserEntity chaser = SF2_ChaserEntity(slender); float newPos[3], zPos[3], newAngles[3], tempAngles[3], product1[3], product2[3], cross[3]; CopyVector(position, zPos); zPos[2] -= 99999.9; @@ -64,261 +167,85 @@ void Trap_SpawnTrap(float position[3], float direction[3], SF2NPC_Chaser control float yaw = GetAngleBetweenVectors(product2, cross, tempAngles); RotateYaw(newAngles, yaw - 90.0); - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); + ChaserBossProfile data = controller.GetProfileData(); int difficulty = controller.Difficulty; + BossProfileTrapData trapData = data.GetTrapData(); + char name[64]; + trapData.GetTrapTypes().GetSectionNameFromIndex(GetRandomInt(0, trapData.GetTrapTypes().SectionLength - 1), name, sizeof(name)); + BossProfileBaseTrap baseTrap = trapData.GetTrapFromName(name); + char buffer[PLATFORM_MAX_PATH]; - switch (data.TrapType[difficulty]) + switch (baseTrap.GetTrapType()) { - case SF2BossTrapType_BearTrap: + case SF2BossTrap_BearTrap: { - int trapEntity = CreateEntityByName("prop_dynamic"); - if (trapEntity != -1) - { - TeleportEntity(trapEntity, newPos, newAngles, NULL_VECTOR); - SetEntityModel(trapEntity, data.TrapModel); - DispatchSpawn(trapEntity); - ActivateEntity(trapEntity); - - SetEntProp(trapEntity, Prop_Send, "m_usSolidFlags", FSOLID_TRIGGER_TOUCH_DEBRIS|FSOLID_TRIGGER|FSOLID_NOT_SOLID|FSOLID_CUSTOMBOXTEST); - SetEntProp(trapEntity, Prop_Data, "m_nSolidType", SOLID_BBOX); - SetEntityCollisionGroup(trapEntity, COLLISION_GROUP_DEBRIS_TRIGGER); - - float mins[3], maxs[3]; - mins[0] = -25.0; - mins[1] = -25.0; - mins[2] = 0.0; - maxs[0] = 25.0; - maxs[1] = 25.0; - maxs[2] = 25.0; - - SetEntPropVector(trapEntity, Prop_Send, "m_vecMins", mins); - SetEntPropVector(trapEntity, Prop_Send, "m_vecMaxs", maxs); - SetEntPropVector(trapEntity, Prop_Send, "m_vecMinsPreScaled", mins); - SetEntPropVector(trapEntity, Prop_Send, "m_vecMaxsPreScaled", maxs); - - AcceptEntityInput(trapEntity, "EnableCollision"); - - g_TrapMaster[trapEntity] = controller; - - g_TrapClosed[trapEntity] = false; - g_TrapAnimChange[trapEntity] = true; - SetEntProp(trapEntity, Prop_Data, "m_bSequenceLoops", false); - if (data.TrapAnimOpen[0] != '\0') - { - SetVariantString(data.TrapAnimOpen); - AcceptEntityInput(trapEntity, "SetAnimation"); - HookSingleEntityOutput(trapEntity, "OnAnimationDone", OnTrapOpenComplete, false); - g_TrapStartedOpenAnim[trapEntity] = true; - } - else - { - SetVariantString(data.TrapAnimIdle); - AcceptEntityInput(trapEntity, "SetAnimation"); - } - - g_TrapEntityCount++; - - g_TrapDespawnTimer[trapEntity] = GetGameTime() + GetRandomFloat(20.0, 40.0); - g_TrapState[trapEntity] = 0; - - EmitSoundToAll(data.TrapDeploySound, trapEntity, SNDCHAN_AUTO, SNDLEVEL_SCREAMING, _, 1.0); - - SDKHook(trapEntity, SDKHook_OnTakeDamage, Hook_TrapOnTakeDamage); - g_TrapTimer[trapEntity] = CreateTimer(0.1, Timer_TrapThink, EntIndexToEntRef(trapEntity), TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); - } + SF2_BearTrap trap = SF2_BearTrap(CreateEntityByName("sf2_trap_bear_trap")); + trap.Teleport(newPos, newAngles); + baseTrap.GetModel(difficulty, buffer, sizeof(buffer)); + trap.SetModel(buffer); + trap.Controller = controller; + trap.SetName(name); + trap.TimeUntilRemove = baseTrap.GetTimeUntilRemoved(difficulty) + GetGameTime(); + trap.Spawn(); + trap.Activate(); + + baseTrap.GetSpawnSounds().EmitSound(.entity = trap.index); } - } -} - -static Action Timer_TrapThink(Handle timer, any entref) -{ - int trapEntity = EntRefToEntIndex(entref); - if (!trapEntity || trapEntity == INVALID_ENT_REFERENCE) - { - return Plugin_Stop; - } - - if (timer != g_TrapTimer[trapEntity]) - { - return Plugin_Stop; - } - - SF2NPC_Chaser controller = g_TrapMaster[trapEntity]; - if (GetGameTime() >= g_TrapDespawnTimer[trapEntity] || !controller.IsValid()) - { - Trap_Despawn(trapEntity); - } - if (!g_TrapClosed[trapEntity]) - { - for (int i = 1; i <= MaxClients; i++) + case SF2BossTrap_Sentry: { - SF2_BasePlayer player = SF2_BasePlayer(i); - if (!player.IsValid) - { - continue; - } - - if (!player.IsAlive || - player.IsInDeathCam || - player.IsInGhostMode || - player.HasEscaped || - player.InCondition(view_as(130)) || - player.Team == TFTeam_Spectator) - { - continue; - } - - if (!g_Enabled) + SF2_SentryTrap trap = SF2_SentryTrap(CreateEntityByName("sf2_trap_sentry")); + BossProfileSentryTrap sentry = view_as(baseTrap); + chaser.GetAbsAngles(newAngles); + trap.Teleport(newPos, newAngles); + trap.Controller = controller; + trap.SetName(name); + trap.TimeUntilRemove = baseTrap.GetTimeUntilRemoved(difficulty) + GetGameTime(); + int desiredTeam = chaser.Team; + if (desiredTeam <= TFTeam_Spectator) { - if (player.GetProp(Prop_Data, "m_iTeamNum") == controller.DefaultTeam) - { - continue; - } + desiredTeam = GetRandomInt(TFTeam_Red, TFTeam_Blue); } - - float entPos[3], otherPos[3]; - player.GetAbsOrigin(otherPos); - GetEntPropVector(trapEntity, Prop_Data, "m_vecAbsOrigin", entPos); - float zPos = otherPos[2] - entPos[2]; - float distance = GetVectorSquareMagnitude(otherPos, entPos); - if (distance <= SquareFloat(50.0) && (zPos <= 25.0 && zPos >= -25.0)) + if (desiredTeam >= 4) { - TFClassType classType = player.Class; - int classToInt = view_as(classType); - - if (!IsClassConfigsValid()) - { - if (classType != TFClass_Heavy) - { - player.IsTrapped = true; - player.TrapCount = GetRandomInt(2, 4); - } - } - else - { - if (!g_ClassInvulnerableToTraps[classToInt]) - { - player.IsTrapped = true; - player.TrapCount = GetRandomInt(2, 4); - } - } - if (!player.HasHint(PlayerHint_Trap)) - { - player.ShowHint(PlayerHint_Trap); - } - player.TakeDamage(true, _, _, 10.0, 128); - g_TrapState[trapEntity] = 1; - g_TrapAnimChange[trapEntity] = true; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - EmitSoundToAll(data.TrapCatchSound, trapEntity, SNDCHAN_AUTO, SNDLEVEL_SCREAMING, _, 1.0); - SetVariantString(data.TrapAnimClose); - AcceptEntityInput(trapEntity, "SetAnimation"); - AcceptEntityInput(trapEntity, "DisableCollision"); - g_TrapClosed[trapEntity] = true; - TrapUpdateAnimation(trapEntity); + desiredTeam = TFTeam_Blue; } - } - } - - return Plugin_Continue; -} + SetVariantInt(desiredTeam); + trap.AcceptInput("SetTeam"); -static void TrapUpdateAnimation(int trapEntity) -{ - if (!IsValidEntity(trapEntity)) - { - return; - } - int state = g_TrapState[trapEntity]; - SF2NPC_Chaser controller = g_TrapMaster[trapEntity]; - if (!controller.IsValid()) - { - return; - } - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - switch (state) - { - case 0: - { - if (!g_TrapStartedOpenAnim[trapEntity] && data.TrapAnimOpen[0] != '\0') + int level = sentry.GetLevel(difficulty); + if (level == 0) { - SetVariantString(data.TrapAnimOpen); - AcceptEntityInput(trapEntity, "SetAnimation"); - HookSingleEntityOutput(trapEntity, "OnAnimationDone", OnTrapOpenComplete, true); - g_TrapStartedOpenAnim[trapEntity] = true; + trap.SetProp(Prop_Send, "m_bMiniBuilding", 1); + trap.SetProp(Prop_Send, "m_iUpgradeLevel", 1); + trap.SetProp(Prop_Send, "m_iHighestUpgradeLevel", 1); + trap.SetProp(Prop_Send, "m_nSkin", desiredTeam); } - else if (g_TrapDoIdleAnim[trapEntity]) + else { - SetVariantString(data.TrapAnimIdle); - AcceptEntityInput(trapEntity, "SetAnimation"); + trap.SetProp(Prop_Send, "m_iUpgradeLevel", level); + trap.SetProp(Prop_Send, "m_iHighestUpgradeLevel", level); + trap.SetProp(Prop_Send, "m_nSkin", desiredTeam - 2); } - } - case 1: - { - SetVariantString(data.TrapAnimClose); - AcceptEntityInput(trapEntity, "SetAnimation"); - AcceptEntityInput(trapEntity, "DisableCollision"); - } - } - SetEntProp(trapEntity, Prop_Data, "m_bSequenceLoops", false); - g_TrapAnimChange[trapEntity] = false; -} + trap.SetProp(Prop_Send, "m_bBuilding", 1); -static void OnTrapOpenComplete(const char[] output, int caller, int activator, float delay) -{ - if (IsValidEntity(caller)) - { - g_TrapAnimChange[caller] = true; - g_TrapDoIdleAnim[caller] = true; - } -} + trap.Spawn(); + trap.Activate(); + trap.SetProp(Prop_Send, "m_nSkin", level == 0 ? desiredTeam : desiredTeam - 2); -static Action Hook_TrapOnTakeDamage(int trapEntity, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom) -{ - if (!g_Enabled) - { - return Plugin_Continue; - } + SetVariantInt(sentry.GetHealth(difficulty)); + trap.AcceptInput("SetHealth"); + trap.Health = sentry.GetHealth(difficulty); - if (IsValidClient(attacker)) - { - if (!g_PlayerEliminated[attacker]) - { - if (damagetype & 0x80 && !g_TrapClosed[trapEntity] && GetClientTeam(attacker) == TFTeam_Red) // 0x80 == melee damage + float minsMini[3] = {-15.0, -15.0, 0.0}, maxsMini[3] = {15.0, 15.0, 49.5}; + if (level == 0) { - g_TrapClosed[trapEntity] = true; - g_TrapState[trapEntity] = 1; - g_TrapAnimChange[trapEntity] = true; - SF2NPC_Chaser controller = g_TrapMaster[trapEntity]; - SF2ChaserBossProfileData data; - data = controller.GetProfileData(); - EmitSoundToAll(data.TrapMissSound, trapEntity, SNDCHAN_AUTO, SNDLEVEL_SCREAMING, _, 1.0); - SetVariantString(data.TrapAnimClose); - AcceptEntityInput(trapEntity, "SetAnimation"); - AcceptEntityInput(trapEntity, "DisableCollision"); - if (g_TrapDespawnTimer[trapEntity] > 5.0) - { - g_TrapDespawnTimer[trapEntity] = GetGameTime()+5.0; - } - TrapUpdateAnimation(trapEntity); + trap.SetPropFloat(Prop_Send, "m_flModelScale", 0.75); + trap.SetPropVector(Prop_Send, "m_vecMins", minsMini); + trap.SetPropVector(Prop_Send, "m_vecMaxs", maxsMini); } + + baseTrap.GetSpawnSounds().EmitSound(.entity = trap.index); } } - - return Plugin_Continue; } - -static void Trap_Despawn(int trapEntity) -{ - if (g_TrapTimer[trapEntity] != null) - { - KillTimer(g_TrapTimer[trapEntity]); - } - SDKUnhook(trapEntity, SDKHook_OnTakeDamage, Hook_TrapOnTakeDamage); - g_TrapEntityCount--; - RemoveEntity(trapEntity); -} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/traps/base.sp b/addons/sourcemod/scripting/sf2/traps/base.sp new file mode 100644 index 00000000..87feeaa6 --- /dev/null +++ b/addons/sourcemod/scripting/sf2/traps/base.sp @@ -0,0 +1,274 @@ +#pragma semicolon 1 +#pragma newdecls required + +static CEntityFactory g_Factory; + +enum SF2BossTrapType +{ + SF2BossTrap_Invalid = -1, + SF2BossTrap_BearTrap = 0, + SF2BossTrap_Explosive = 1, + SF2BossTrap_Sentry = 2, + SF2BossTrap_Custom, + SF2BossTrap_Max +}; + +methodmap BossProfileBaseTrap < ProfileObject +{ + public SF2BossTrapType GetTrapType() + { + char type[64]; + this.GetString("type", type, sizeof(type)); + + if (strcmp(type, "bear_trap", false) == 0) + { + return SF2BossTrap_BearTrap; + } + else if (strcmp(type, "explosive_trap", false) == 0) + { + return SF2BossTrap_Explosive; + } + else if (strcmp(type, "sentry", false) == 0) + { + return SF2BossTrap_Sentry; + } + + return SF2BossTrap_Invalid; + } + + public float GetWeight(int difficulty) + { + return this.GetDifficultyFloat("weight", difficulty, 1.0); + } + + public float GetTimeUntilRemoved(int difficulty) + { + return this.GetDifficultyFloat("time_until_removed", difficulty, 30.0); + } + + public void GetModel(int difficulty, char[] buffer, int bufferSize) + { + this.GetDifficultyString("model", difficulty, buffer, bufferSize); + ReplaceString(buffer, bufferSize, "\\", "/", false); + } + + public ProfileSound GetSpawnSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + return view_as(obj.GetSection("spawn")); + } + return null; + } + + public float GetDamage(int difficulty) + { + float def = 10.0; + switch (this.GetTrapType()) + { + case SF2BossTrap_BearTrap: + { + def = 10.0; + } + + case SF2BossTrap_Explosive: + { + def = 50.0; + } + + case SF2BossTrap_Sentry: + { + def = 8.0; + } + } + + return this.GetDifficultyFloat("damage", difficulty, def); + } + + public KeyMap_Array GetDamageEffects() + { + return this.GetArray("apply_conditions"); + } + + public void ApplyDamageEffects(CBaseCombatCharacter player, int difficulty, CBaseAnimating trap = view_as(-1), SF2NPC_Chaser parentController = SF2_INVALID_NPC_CHASER) + { + if (this.GetDamageEffects() == null) + { + return; + } + + if (!player.IsValid()) + { + return; + } + + for (int i = 0; i < this.GetDamageEffects().Length; i++) + { + KeyMap obj = this.GetDamageEffects().GetSection(i); + if (obj == null) + { + continue; + } + BossProfileDamageEffect effect = view_as(obj); + effect.Apply(player, difficulty, trap, parentController); + } + } + + public void Precache() + { + this.ConvertSectionsSectionToArray("apply_conditions"); + + char path[PLATFORM_MAX_PATH]; + for (int i = 0; i < Difficulty_Max; i++) + { + this.GetModel(i, path, sizeof(path)); + if (path[0] != '\0') + { + PrecacheModel2(path, _, _, g_FileCheckConVar.BoolValue); + } + } + + if (this.GetSpawnSounds() != null) + { + this.GetSpawnSounds().Precache(); + } + + if (this.GetDamageEffects() != null) + { + for (int i = 0; i < this.GetDamageEffects().Length; i++) + { + BossProfileDamageEffect effect = view_as(this.GetDamageEffects().GetSection(i)); + if (effect != null) + { + effect.Precache(); + } + } + } + + switch (this.GetTrapType()) + { + case SF2BossTrap_BearTrap: + { + view_as(this).Precache(); + } + } + } +} + +methodmap SF2_BaseTrap < CBaseAnimating +{ + public SF2_BaseTrap(int entity) + { + return view_as(entity); + } + + public bool IsValid() + { + if (!CBaseAnimating(this.index).IsValid()) + { + return false; + } + + return CEntityFactory.GetFactoryOfEntity(this.index) == g_Factory; + } + + public static void Initialize() + { + g_Factory = new CEntityFactory("sf2_trap_base", OnCreate, OnRemove); + g_Factory.IsAbstract = true; + g_Factory.DeriveFromClass("prop_dynamic_override"); + g_Factory.BeginDataMapDesc() + .DefineIntField("m_Type") + .DefineStringField("m_Name") + .DefineIntField("m_BossUniqueID") + .DefineFloatField("m_TimeUntilRemove") + .EndDataMapDesc(); + g_Factory.Install(); + } + + public static CEntityFactory GetFactory() + { + return g_Factory; + } + + property SF2BossTrapType Type + { + public get() + { + return view_as(this.GetProp(Prop_Data, "m_Type")); + } + + public set(SF2BossTrapType value) + { + this.SetProp(Prop_Data, "m_Type", value); + } + } + + public char[] GetName() + { + char name[64]; + this.GetPropString(Prop_Data, "m_Name", name, sizeof(name)); + return name; + } + + public void SetName(const char[] name) + { + this.SetPropString(Prop_Data, "m_Name", name); + } + + property SF2NPC_Chaser Controller + { + public get() + { + return SF2NPC_Chaser(NPCGetFromUniqueID(this.GetProp(Prop_Data, "m_BossUniqueID"))); + } + + public set(SF2NPC_Chaser value) + { + this.SetProp(Prop_Data, "m_BossUniqueID", value.UniqueID); + } + } + + property float TimeUntilRemove + { + public get() + { + return this.GetPropFloat(Prop_Data, "m_TimeUntilRemove"); + } + + public set(float value) + { + this.SetPropFloat(Prop_Data, "m_TimeUntilRemove", value); + } + } +} + +static void OnCreate(SF2_BaseTrap entity) +{ + g_TrapEntityCount++; + CreateTimer(0.1, Timer_Think, EntIndexToEntRef(entity.index), TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); +} + +static void OnRemove(SF2_BaseTrap entity) +{ + g_TrapEntityCount--; +} + +static Action Timer_Think(Handle timer, int ref) +{ + int entity = EntRefToEntIndex(ref); + if (!entity || entity == INVALID_ENT_REFERENCE) + { + return Plugin_Stop; + } + + float gameTime = GetGameTime(); + SF2_BaseTrap trap = SF2_BaseTrap(entity); + if (trap.TimeUntilRemove < gameTime || !trap.Controller.IsValid()) + { + RemoveEntity(trap.index); + return Plugin_Stop; + } + return Plugin_Continue; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/traps/beartrap.sp b/addons/sourcemod/scripting/sf2/traps/beartrap.sp new file mode 100644 index 00000000..380707a0 --- /dev/null +++ b/addons/sourcemod/scripting/sf2/traps/beartrap.sp @@ -0,0 +1,319 @@ +#pragma semicolon 1 +#pragma newdecls required + +methodmap BossProfileBearTrap < BossProfileBaseTrap +{ + public int GetDamageType(int difficulty) + { + return this.GetDifficultyInt("damagetype", difficulty, 128); + } + + public ProfileMasterAnimations GetAnimations() + { + return view_as(this.GetSection("animations")); + } + + public ProfileSound GetCatchSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + return view_as(obj.GetSection("catch")); + } + return null; + } + + public ProfileSound GetMissSounds() + { + ProfileObject obj = this.GetSection("sounds"); + if (obj != null) + { + return view_as(obj.GetSection("miss")); + } + return null; + } + + public void Precache() + { + if (this.GetCatchSounds() != null) + { + this.GetCatchSounds().Precache(); + } + + if (this.GetMissSounds() != null) + { + this.GetMissSounds().Precache(); + } + } +} + +static CEntityFactory g_Factory; + +methodmap SF2_BearTrap < SF2_BaseTrap +{ + public SF2_BearTrap(int entity) + { + return view_as(entity); + } + + public bool IsValid() + { + if (!CBaseAnimating(this.index).IsValid()) + { + return false; + } + + return CEntityFactory.GetFactoryOfEntity(this.index) == g_Factory; + } + + public static void Initialize() + { + g_Factory = new CEntityFactory("sf2_trap_bear_trap", OnCreate); + g_Factory.DeriveFromFactory(SF2_BaseTrap.GetFactory()); + g_Factory.BeginDataMapDesc() + .DefineBoolField("m_Triggered") + .DefineEntityField("m_Target") + .EndDataMapDesc(); + g_Factory.Install(); + } + + property bool Triggered + { + public get() + { + return this.GetProp(Prop_Data, "m_Triggered") != 0; + } + + public set(bool value) + { + this.SetProp(Prop_Data, "m_Triggered", value); + } + } + + property CBaseEntity Target + { + public get() + { + return CBaseEntity(EntRefToEntIndex(this.GetPropEnt(Prop_Data, "m_Target"))); + } + + public set(CBaseEntity entity) + { + this.SetPropEnt(Prop_Data, "m_Target", EnsureEntRef(entity.index)); + } + } + + public bool UpdateAnimation(const char[] section, bool def = false) + { + if (!this.Controller.IsValid()) + { + return false; + } + int difficulty = this.Controller.Difficulty; + + char animName[64]; + + BossProfileBearTrap data = view_as(this.Controller.GetProfileData().GetTrapData().GetTrapFromName(this.GetName())); + ProfileMasterAnimations animations = data.GetAnimations(); + if (animations == null) + { + return false; + } + ProfileAnimation animation = animations.GetAnimation(section); + if (animation == null) + { + return false; + } + animation.GetAnimationName(difficulty, animName, sizeof(animName)); + SetVariantString(animName); + if (def) + { + this.AcceptInput("SetDefaultAnimation"); + } + else + { + this.AcceptInput("SetAnimation"); + } + this.SetPropFloat(Prop_Data, "m_flPlaybackRate", animation.GetAnimationPlaybackRate(difficulty)); + this.SetProp(Prop_Data, "m_bSequenceLoops", false); + return true; + } + + public void Close(bool miss = false) + { + BossProfileBearTrap data = view_as(this.Controller.GetProfileData().GetTrapData().GetTrapFromName(this.GetName())); + if (miss) + { + data.GetMissSounds().EmitSound(.entity = this.index); + } + else + { + data.GetCatchSounds().EmitSound(.entity = this.index); + } + bool state = this.UpdateAnimation("close"); + if (!state) + { + this.UpdateAnimation("closed"); + } + this.UpdateAnimation("closed", true); + this.AcceptInput("DisableCollision"); + this.Triggered = true; + } +} + +static void OnCreate(SF2_BearTrap entity) +{ + bool state = entity.UpdateAnimation("open"); + if (!state) + { + entity.UpdateAnimation("opened"); + } + entity.UpdateAnimation("opened", true); + entity.SetProp(Prop_Send, "m_usSolidFlags", FSOLID_TRIGGER_TOUCH_DEBRIS |FSOLID_TRIGGER | FSOLID_NOT_SOLID | FSOLID_CUSTOMBOXTEST); + entity.SetProp(Prop_Data, "m_nSolidType", SOLID_BBOX); + SetEntityCollisionGroup(entity.index, COLLISION_GROUP_DEBRIS_TRIGGER); + + float mins[3], maxs[3]; + mins[0] = -25.0; + mins[1] = -25.0; + mins[2] = 0.0; + maxs[0] = 25.0; + maxs[1] = 25.0; + maxs[2] = 25.0; + + entity.SetPropVector(Prop_Send, "m_vecMins", mins); + entity.SetPropVector(Prop_Send, "m_vecMaxs", maxs); + entity.SetPropVector(Prop_Send, "m_vecMinsPreScaled", mins); + entity.SetPropVector(Prop_Send, "m_vecMaxsPreScaled", maxs); + + entity.AcceptInput("EnableCollision"); + + CreateTimer(0.1, Timer_Think, EntIndexToEntRef(entity.index), TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); + SDKHook(entity.index, SDKHook_OnTakeDamage, OnTakeDamage); +} + +static Action Timer_Think(Handle timer, int ref) +{ + int entity = EntRefToEntIndex(ref); + if (!entity || entity == INVALID_ENT_REFERENCE) + { + return Plugin_Stop; + } + + SF2_BearTrap trap = SF2_BearTrap(entity); + SF2NPC_Chaser controller = trap.Controller; + if (!controller.IsValid()) + { + RemoveEntity(trap.index); + return Plugin_Stop; + } + + float gameTime = GetGameTime(); + int difficulty = controller.Difficulty; + if (trap.Triggered) + { + if (trap.Target.IsValid()) + { + trap.TimeUntilRemove = 5.0 + gameTime; + } + return Plugin_Continue; + } + + BossProfileBearTrap data = view_as(controller.GetProfileData().GetTrapData().GetTrapFromName(trap.GetName())); + for (int i = 1; i <= MaxClients; i++) + { + SF2_BasePlayer player = SF2_BasePlayer(i); + if (!player.IsValid) + { + continue; + } + + if (!player.IsAlive || + player.IsInDeathCam || + player.IsInGhostMode || + player.HasEscaped || + player.InCondition(view_as(130)) || + player.Team == TFTeam_Spectator) + { + continue; + } + + /*if ((controller.Flags & SFF_ATTACKWAITERS) == 0 && player.IsEliminated) + { + continue; + }*/ + + if (!g_Enabled) + { + if (player.GetProp(Prop_Data, "m_iTeamNum") == controller.DefaultTeam) + { + continue; + } + } + + float entPos[3], otherPos[3]; + player.GetAbsOrigin(otherPos); + trap.GetAbsOrigin(entPos); + float zPos = otherPos[2] - entPos[2]; + float distance = GetVectorSquareMagnitude(otherPos, entPos); + if (distance <= SquareFloat(50.0) && (zPos <= 25.0 && zPos >= -25.0)) + { + TFClassType classType = player.Class; + int classToInt = view_as(classType); + + if (!IsClassConfigsValid()) + { + if (classType != TFClass_Heavy) + { + player.IsTrapped = true; + player.TrapCount = GetRandomInt(2, 4); + } + } + else + { + if (!g_ClassInvulnerableToTraps[classToInt]) + { + player.IsTrapped = true; + player.TrapCount = GetRandomInt(2, 4); + } + } + if (!player.HasHint(PlayerHint_Trap)) + { + player.ShowHint(PlayerHint_Trap); + } + player.TakeDamage(true, _, _, data.GetDamage(difficulty), data.GetDamageType(difficulty)); + data.ApplyDamageEffects(player, difficulty, trap, controller); + trap.Close(); + trap.Target = player; + TF2Attrib_SetByName(player.index, "increased jump height", 0.0); + TF2Attrib_SetByName(player.index, "move speed bonus", 0.0001); + player.UpdateSpeed(); + break; + } + } + return Plugin_Continue; +} + +static Action OnTakeDamage(int entity, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom) +{ + SF2_BearTrap trap = SF2_BearTrap(entity); + if (trap.Triggered || !IsValidClient(attacker)) + { + return Plugin_Continue; + } + + if (g_Enabled/* && g_PlayerEliminated[attacker]*/) + { + return Plugin_Continue; + } + + if ((damagetype & 0x80) == 0) + { + return Plugin_Continue; + } + + trap.Close(true); + trap.TimeUntilRemove = 5.0; + + return Plugin_Continue; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/sf2/traps/explosive.sp b/addons/sourcemod/scripting/sf2/traps/explosive.sp new file mode 100644 index 00000000..e69de29b diff --git a/addons/sourcemod/scripting/sf2/traps/sentry.sp b/addons/sourcemod/scripting/sf2/traps/sentry.sp new file mode 100644 index 00000000..6cdbf1ee --- /dev/null +++ b/addons/sourcemod/scripting/sf2/traps/sentry.sp @@ -0,0 +1,224 @@ +#pragma semicolon 1 +#pragma newdecls required + +methodmap BossProfileSentryTrap < BossProfileBaseTrap +{ + public int GetLevel(int difficulty) + { + int val = this.GetDifficultyInt("level", difficulty, 0); + if (val < 0) + { + val = 0; + } + if (val > 3) + { + val = 3; + } + return val; + } + + public int GetHealth(int difficulty) + { + int def = 100; + switch (this.GetLevel(difficulty)) + { + case 0: + { + def = 100; + } + + case 1: + { + def = 150; + } + + case 2: + { + def = 180; + } + + case 3: + { + def = 216; + } + } + return this.GetDifficultyInt("health", difficulty, def); + } +} + +static CEntityFactory g_Factory; + +methodmap SF2_SentryTrap < CBaseCombatCharacter +{ + public SF2_SentryTrap(int entity) + { + return view_as(entity); + } + + public bool IsValid() + { + if (!CBaseCombatCharacter(this.index).IsValid()) + { + return false; + } + + return CEntityFactory.GetFactoryOfEntity(this.index) == g_Factory; + } + + public static void Initialize() + { + g_Factory = new CEntityFactory("sf2_trap_sentry", OnCreate, OnRemove); + g_Factory.DeriveFromClass("obj_sentrygun"); + g_Factory.BeginDataMapDesc() + .DefineIntField("m_Type") + .DefineStringField("m_Name") + .DefineIntField("m_BossUniqueID") + .DefineFloatField("m_TimeUntilRemove") + .DefineIntField("m_OldState") + .EndDataMapDesc(); + g_Factory.Install(); + } + + property SF2BossTrapType Type + { + public get() + { + return view_as(this.GetProp(Prop_Data, "m_Type")); + } + + public set(SF2BossTrapType value) + { + this.SetProp(Prop_Data, "m_Type", value); + } + } + + public char[] GetName() + { + char name[64]; + this.GetPropString(Prop_Data, "m_Name", name, sizeof(name)); + return name; + } + + public void SetName(const char[] name) + { + this.SetPropString(Prop_Data, "m_Name", name); + } + + property SF2NPC_Chaser Controller + { + public get() + { + return SF2NPC_Chaser(NPCGetFromUniqueID(this.GetProp(Prop_Data, "m_BossUniqueID"))); + } + + public set(SF2NPC_Chaser value) + { + this.SetProp(Prop_Data, "m_BossUniqueID", value.UniqueID); + } + } + + property float TimeUntilRemove + { + public get() + { + return this.GetPropFloat(Prop_Data, "m_TimeUntilRemove"); + } + + public set(float value) + { + this.SetPropFloat(Prop_Data, "m_TimeUntilRemove", value); + } + } + + property int Health + { + public get() + { + return this.GetProp(Prop_Data, "m_iHealth"); + } + + public set(int value) + { + this.SetProp(Prop_Data, "m_iHealth", value); + } + } + + property int MaxHealth + { + public get() + { + return this.GetProp(Prop_Data, "m_iMaxHealth"); + } + + public set(int value) + { + this.SetProp(Prop_Data, "m_iMaxHealth", value); + } + } + + property int State + { + public get() + { + return this.GetProp(Prop_Send, "m_iState"); + } + + public set(int value) + { + this.SetProp(Prop_Send, "m_iState", value); + } + } + + property int OldState + { + public get() + { + return this.GetProp(Prop_Data, "m_OldState"); + } + + public set(int value) + { + this.SetProp(Prop_Data, "m_OldState", value); + } + } +} + +static void OnCreate(SF2_SentryTrap entity) +{ + g_TrapEntityCount++; + CreateTimer(0.1, Timer_Think, EntIndexToEntRef(entity.index), TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); +} + +static void OnRemove(SF2_SentryTrap entity) +{ + g_TrapEntityCount--; +} + +static Action Timer_Think(Handle timer, int ref) +{ + int entity = EntRefToEntIndex(ref); + if (!entity || entity == INVALID_ENT_REFERENCE) + { + return Plugin_Stop; + } + + float gameTime = GetGameTime(); + SF2_SentryTrap trap = SF2_SentryTrap(entity); + SF2NPC_Chaser controller = trap.Controller; + int difficulty = controller.Difficulty; + if (trap.TimeUntilRemove < gameTime || !controller.IsValid()) + { + SDKHooks_TakeDamage(trap.index, 0, 0, trap.Health * 4.0, .bypassHooks = false); + return Plugin_Stop; + } + + if (trap.OldState != trap.State && trap.State == 3) + { + BossProfileSentryTrap data = view_as(controller.GetProfileData().GetTrapData().GetTrapFromName(trap.GetName())); + SetVariantInt(data.GetHealth(difficulty)); + trap.AcceptInput("SetHealth"); + } + + trap.OldState = trap.State; + return Plugin_Continue; +} \ No newline at end of file diff --git a/addons/sourcemod/translations/sf2.phrases.txt b/addons/sourcemod/translations/sf2.phrases.txt index 3c0617d3..3224f6ec 100644 --- a/addons/sourcemod/translations/sf2.phrases.txt +++ b/addons/sourcemod/translations/sf2.phrases.txt @@ -706,6 +706,16 @@ "ko" "HUD 버전 설정" } + "SF2 Settings Hud New" + { + "en" "New" + } + + "SF2 Settings Hud Legacy" + { + "en" "Legacy" + } + "SF2 Settings Music Volume Title" { "en" "Set music volume" @@ -732,6 +742,11 @@ "ko" "{lightblue}음악 볼륨이 {1}%% 으로 설정 되었습니다." } + "SF2 Settings Projected Flashlight Title" + { + "en" "Projected flashlight" + } + "SF2 Settings Flashlight Temperature Title" { "en" "Set flashlight temperature" @@ -1161,6 +1176,23 @@ "ko" "현재 보스 재정의 : {1}" } + "SF2 Disabled Boss Pack" + { + "en" "{red}Boss Pack vote is disabled on this server." + } + + "SF2 Boss Pack Next" + { + "#format" "{1:s}" + "en" "{dodgerblue}Next boss pack: {lightblue}{1}{default}." + } + + "SF2 Current Boss Pack" + { + "#format" "{1:s}" + "en" "{dodgerblue}Current boss pack: {lightblue}{1}{default}." + } + "SF2 Admin Menu Player Set Play State" { "en" "Force player in/out of game" diff --git a/materials/editor/sf2/sf2_boss_maker.vmt b/materials/editor/sf2/sf2_boss_maker.vmt new file mode 100644 index 00000000..4c82a259 --- /dev/null +++ b/materials/editor/sf2/sf2_boss_maker.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_boss_maker" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_boss_maker.vtf b/materials/editor/sf2/sf2_boss_maker.vtf new file mode 100644 index 00000000..c47a9036 Binary files /dev/null and b/materials/editor/sf2/sf2_boss_maker.vtf differ diff --git a/materials/editor/sf2/sf2_game_text.vmt b/materials/editor/sf2/sf2_game_text.vmt new file mode 100644 index 00000000..2e561aad --- /dev/null +++ b/materials/editor/sf2/sf2_game_text.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_game_text" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_game_text.vtf b/materials/editor/sf2/sf2_game_text.vtf new file mode 100644 index 00000000..522665f0 Binary files /dev/null and b/materials/editor/sf2/sf2_game_text.vtf differ diff --git a/materials/editor/sf2/sf2_gamerules.vmt b/materials/editor/sf2/sf2_gamerules.vmt new file mode 100644 index 00000000..682cb499 --- /dev/null +++ b/materials/editor/sf2/sf2_gamerules.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_gamerules" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_gamerules.vtf b/materials/editor/sf2/sf2_gamerules.vtf new file mode 100644 index 00000000..8a122172 Binary files /dev/null and b/materials/editor/sf2/sf2_gamerules.vtf differ diff --git a/materials/editor/sf2/sf2_info_jumprestrict.vmt b/materials/editor/sf2/sf2_info_jumprestrict.vmt new file mode 100644 index 00000000..eea88ca9 --- /dev/null +++ b/materials/editor/sf2/sf2_info_jumprestrict.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_info_jumprestrict" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_info_jumprestrict.vtf b/materials/editor/sf2/sf2_info_jumprestrict.vtf new file mode 100644 index 00000000..4f69fdeb Binary files /dev/null and b/materials/editor/sf2/sf2_info_jumprestrict.vtf differ diff --git a/materials/editor/sf2/sf2_info_page_music.vmt b/materials/editor/sf2/sf2_info_page_music.vmt new file mode 100644 index 00000000..0c24a0a9 --- /dev/null +++ b/materials/editor/sf2/sf2_info_page_music.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_info_page_music" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_info_page_music.vtf b/materials/editor/sf2/sf2_info_page_music.vtf new file mode 100644 index 00000000..abaea1be Binary files /dev/null and b/materials/editor/sf2/sf2_info_page_music.vtf differ diff --git a/materials/editor/sf2/sf2_logic_arena.vmt b/materials/editor/sf2/sf2_logic_arena.vmt new file mode 100644 index 00000000..98e45181 --- /dev/null +++ b/materials/editor/sf2/sf2_logic_arena.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_logic_arena" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_logic_arena.vtf b/materials/editor/sf2/sf2_logic_arena.vtf new file mode 100644 index 00000000..4614ffdc Binary files /dev/null and b/materials/editor/sf2/sf2_logic_arena.vtf differ diff --git a/materials/editor/sf2/sf2_logic_boxing.vmt b/materials/editor/sf2/sf2_logic_boxing.vmt new file mode 100644 index 00000000..c279b3c8 --- /dev/null +++ b/materials/editor/sf2/sf2_logic_boxing.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_logic_boxing" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_logic_boxing.vtf b/materials/editor/sf2/sf2_logic_boxing.vtf new file mode 100644 index 00000000..8cd28caf Binary files /dev/null and b/materials/editor/sf2/sf2_logic_boxing.vtf differ diff --git a/materials/editor/sf2/sf2_logic_elimination.vmt b/materials/editor/sf2/sf2_logic_elimination.vmt new file mode 100644 index 00000000..97dcf7b9 --- /dev/null +++ b/materials/editor/sf2/sf2_logic_elimination.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_logic_elimination" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_logic_elimination.vtf b/materials/editor/sf2/sf2_logic_elimination.vtf new file mode 100644 index 00000000..b1059ce0 Binary files /dev/null and b/materials/editor/sf2/sf2_logic_elimination.vtf differ diff --git a/materials/editor/sf2/sf2_logic_proxy.vmt b/materials/editor/sf2/sf2_logic_proxy.vmt new file mode 100644 index 00000000..73876d09 --- /dev/null +++ b/materials/editor/sf2/sf2_logic_proxy.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_logic_proxy" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_logic_proxy.vtf b/materials/editor/sf2/sf2_logic_proxy.vtf new file mode 100644 index 00000000..b8476bd0 Binary files /dev/null and b/materials/editor/sf2/sf2_logic_proxy.vtf differ diff --git a/materials/editor/sf2/sf2_logic_raid.vmt b/materials/editor/sf2/sf2_logic_raid.vmt new file mode 100644 index 00000000..df6550e0 --- /dev/null +++ b/materials/editor/sf2/sf2_logic_raid.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_logic_raid" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_logic_raid.vtf b/materials/editor/sf2/sf2_logic_raid.vtf new file mode 100644 index 00000000..669c28f3 Binary files /dev/null and b/materials/editor/sf2/sf2_logic_raid.vtf differ diff --git a/materials/editor/sf2/sf2_logic_slaughter.vmt b/materials/editor/sf2/sf2_logic_slaughter.vmt new file mode 100644 index 00000000..d3e0d0eb --- /dev/null +++ b/materials/editor/sf2/sf2_logic_slaughter.vmt @@ -0,0 +1,7 @@ +"Sprite" +{ + "$spriteorientation" "vp_parallel" + "$spriteorigin" "[ 0.50 0.50 ]" + "$basetexture" "editor/sf2/sf2_logic_slaughter" + "$no_fullbright" "1" +} \ No newline at end of file diff --git a/materials/editor/sf2/sf2_logic_slaughter.vtf b/materials/editor/sf2/sf2_logic_slaughter.vtf new file mode 100644 index 00000000..316b88bc Binary files /dev/null and b/materials/editor/sf2/sf2_logic_slaughter.vtf differ diff --git a/materials/models/editor/sf2/dev_engy_body_blu.vmt b/materials/models/editor/sf2/dev_engy_body_blu.vmt new file mode 100644 index 00000000..0fcba12c --- /dev/null +++ b/materials/models/editor/sf2/dev_engy_body_blu.vmt @@ -0,0 +1,4 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/editor/sf2/dev_engy_body_blu" +} \ No newline at end of file diff --git a/materials/models/editor/sf2/dev_engy_body_blu.vtf b/materials/models/editor/sf2/dev_engy_body_blu.vtf new file mode 100644 index 00000000..b9cdb1bb Binary files /dev/null and b/materials/models/editor/sf2/dev_engy_body_blu.vtf differ diff --git a/materials/models/editor/sf2/dev_engy_body_red.vmt b/materials/models/editor/sf2/dev_engy_body_red.vmt new file mode 100644 index 00000000..5f02bca2 --- /dev/null +++ b/materials/models/editor/sf2/dev_engy_body_red.vmt @@ -0,0 +1,4 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/editor/sf2/dev_engy_body_red" +} \ No newline at end of file diff --git a/materials/models/editor/sf2/dev_engy_body_red.vtf b/materials/models/editor/sf2/dev_engy_body_red.vtf new file mode 100644 index 00000000..6c02f7cf Binary files /dev/null and b/materials/models/editor/sf2/dev_engy_body_red.vtf differ diff --git a/materials/models/editor/sf2/dev_engy_head.vmt b/materials/models/editor/sf2/dev_engy_head.vmt new file mode 100644 index 00000000..08528bee --- /dev/null +++ b/materials/models/editor/sf2/dev_engy_head.vmt @@ -0,0 +1,4 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/editor/sf2/dev_engy_head" +} \ No newline at end of file diff --git a/materials/models/editor/sf2/dev_engy_head.vtf b/materials/models/editor/sf2/dev_engy_head.vtf new file mode 100644 index 00000000..857e0f54 Binary files /dev/null and b/materials/models/editor/sf2/dev_engy_head.vtf differ diff --git a/materials/models/editor/sf2/masky.vmt b/materials/models/editor/sf2/masky.vmt new file mode 100644 index 00000000..3aad48e6 --- /dev/null +++ b/materials/models/editor/sf2/masky.vmt @@ -0,0 +1,4 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/editor/sf2/masky" +} \ No newline at end of file diff --git a/materials/models/editor/sf2/masky.vtf b/materials/models/editor/sf2/masky.vtf new file mode 100644 index 00000000..7265d2b9 Binary files /dev/null and b/materials/models/editor/sf2/masky.vtf differ diff --git a/models/editor/sf2/escapespawn.dx80.vtx b/models/editor/sf2/escapespawn.dx80.vtx new file mode 100644 index 00000000..7f6bac64 Binary files /dev/null and b/models/editor/sf2/escapespawn.dx80.vtx differ diff --git a/models/editor/sf2/escapespawn.dx90.vtx b/models/editor/sf2/escapespawn.dx90.vtx new file mode 100644 index 00000000..88ff2897 Binary files /dev/null and b/models/editor/sf2/escapespawn.dx90.vtx differ diff --git a/models/editor/sf2/escapespawn.mdl b/models/editor/sf2/escapespawn.mdl new file mode 100644 index 00000000..e1a6f752 Binary files /dev/null and b/models/editor/sf2/escapespawn.mdl differ diff --git a/models/editor/sf2/escapespawn.sw.vtx b/models/editor/sf2/escapespawn.sw.vtx new file mode 100644 index 00000000..3bc1766c Binary files /dev/null and b/models/editor/sf2/escapespawn.sw.vtx differ diff --git a/models/editor/sf2/escapespawn.vvd b/models/editor/sf2/escapespawn.vvd new file mode 100644 index 00000000..07d41be5 Binary files /dev/null and b/models/editor/sf2/escapespawn.vvd differ diff --git a/models/editor/sf2/proxyspawn.dx80.vtx b/models/editor/sf2/proxyspawn.dx80.vtx new file mode 100644 index 00000000..62533f31 Binary files /dev/null and b/models/editor/sf2/proxyspawn.dx80.vtx differ diff --git a/models/editor/sf2/proxyspawn.dx90.vtx b/models/editor/sf2/proxyspawn.dx90.vtx new file mode 100644 index 00000000..da58a8bc Binary files /dev/null and b/models/editor/sf2/proxyspawn.dx90.vtx differ diff --git a/models/editor/sf2/proxyspawn.mdl b/models/editor/sf2/proxyspawn.mdl new file mode 100644 index 00000000..e1d841c2 Binary files /dev/null and b/models/editor/sf2/proxyspawn.mdl differ diff --git a/models/editor/sf2/proxyspawn.sw.vtx b/models/editor/sf2/proxyspawn.sw.vtx new file mode 100644 index 00000000..e28eadec Binary files /dev/null and b/models/editor/sf2/proxyspawn.sw.vtx differ diff --git a/models/editor/sf2/proxyspawn.vvd b/models/editor/sf2/proxyspawn.vvd new file mode 100644 index 00000000..b488e229 Binary files /dev/null and b/models/editor/sf2/proxyspawn.vvd differ diff --git a/models/editor/sf2/pvpspawn.dx80.vtx b/models/editor/sf2/pvpspawn.dx80.vtx new file mode 100644 index 00000000..c07bb5a9 Binary files /dev/null and b/models/editor/sf2/pvpspawn.dx80.vtx differ diff --git a/models/editor/sf2/pvpspawn.dx90.vtx b/models/editor/sf2/pvpspawn.dx90.vtx new file mode 100644 index 00000000..4949fa06 Binary files /dev/null and b/models/editor/sf2/pvpspawn.dx90.vtx differ diff --git a/models/editor/sf2/pvpspawn.mdl b/models/editor/sf2/pvpspawn.mdl new file mode 100644 index 00000000..97ee2156 Binary files /dev/null and b/models/editor/sf2/pvpspawn.mdl differ diff --git a/models/editor/sf2/pvpspawn.sw.vtx b/models/editor/sf2/pvpspawn.sw.vtx new file mode 100644 index 00000000..7193f495 Binary files /dev/null and b/models/editor/sf2/pvpspawn.sw.vtx differ diff --git a/models/editor/sf2/pvpspawn.vvd b/models/editor/sf2/pvpspawn.vvd new file mode 100644 index 00000000..df114334 Binary files /dev/null and b/models/editor/sf2/pvpspawn.vvd differ diff --git a/sf2.fgd b/sf2.fgd index 1eaeeb04..783b1528 100644 --- a/sf2.fgd +++ b/sf2.fgd @@ -405,4 +405,8 @@ 0 : "Disabled" 1 : "Enabled" ] +] + +@SolidClass base(trigger_multiple) = sf2_trigger_weapons : "(Modified only) A trigger brush that when touched by a player will automatically give them all weapons but cannot damage other players. The substitude for sf2_trigger_pve" +[ ] \ No newline at end of file