namespace Shop { const int MAX_UPGRADES_PER_PAGE = 7; const int MAX_ITEMS_PER_PAGE = 3; array UpgradeNames = { "Damage Upgrade", "Speed Upgrade", "Cooldown Reduction", "Max HP", "Max Speed", "Golden Hook", "Hook Curl", "Agility", "Will to Win" }; array UpgradeDescriptions = { "Increases hook dmg by 5.", "Increases hook speed & pull by 50.", "Reduces hook cooldown by 0.1s.", "Increases max health by 10.", "Increases max speed by 5.", "Increases gold earned by 1.", "Increases hook line curl by 1.\n", "Chance to reduce dmg by 5-80%, up to 40% chance.", "Increases dmg by 10% + 2% per level when health is below 50%, up to max of 30%.\n" }; array UpgradeCosts = { 10, 30, 20, 15, 20, 50, 15, 40, 25 }; array itemNames = { "Grappler", "Bloodthirster", "Techies Scopes", "Pullbane", "Barbed Wire", "Wooden Barricade", "Explosive Hook", "Chainmail", "Heal Hook", "Touch of Undeath", "Grafters Charm", "Wooden Bridge", "Aggressive Bait", "Adrenaline Rush", "Bracelet of Instability" }; array itemDescriptions = { "Allows you to grapple towards a target or location using the secondary attack.", "Heals you by 20% of damage dealt with each successful hook.", "Provides detailed hook stats, including travel distance, hook speed, damage dealt, and hooked player's remaining health. Can be enabled or disabled by typing /on and /off.\n", "Increases damage based on hook travel distance, up to 60, plus 10% of the victim's max HP.", "Adds a barbed wire to your hook line dealing periodic damage to enemies who stand between them. Damage scales with Hook Curl upgrades.", "300 HP barricade. Press the R key twice to display and confirm the placement. Has 5 max charges.\n", "10% chance to cause an explosion on impact.", "Provides you with 25 armor.", "Heals an ally player by 50% of your maximum damage.\n", "Reduces the enemy player's movement speed by 40% and damage output by 45% for 5 seconds after being hooked.", "Grants 10 Max HP upon a successful kill.", "5000 hp bridge. Press the R key twice to display and confirm the placement. Has 3 max charges.\n", "Spawns a Gonome in front of you. Will attack anything on sight. Press R to spawn.", "Increases max movement speed by 35% for 3 seconds after a player kill.", "Randomly buffs max health by 50-250 HP or damage by 25-100 for 7 seconds after a player kill. (Can't be recovered after reconnecting).\n" }; array itemCosts = { 140, 80, 100, 120, 125, 30, 200, 100, 150, 250, 220, 300, 50, 150, 250 }; dictionary PlayerUpgrades; dictionary PlayerMenus; dictionary VisualIndicators; dictionary LastKeyPressTime; dictionary PlayerBarricades; dictionary PlayerBridges; dictionary PlayerGonomeCount; class CShopMenuItem { string Name; int Cost; string Description; int Index; CShopMenuItem() {} CShopMenuItem(string name, int cost, string description, int index) { Name = name; Cost = cost; Description = description; Index = index; } void Display(CTextMenu@ menu) { string itemCommand = "upgrade_" + Index; string itemText = Name + " - " + Cost + " gold\n " + Description; menu.AddItem(itemText, any(itemCommand)); } void Execute(CBasePlayer@ pPlayer) { if (BuyUpgrade(pPlayer, Index)) { g_Scheduler.SetTimeout("RefreshShopMenu", 0.0, EHandle(pPlayer)); } } } class CShopItem { string Name; int Cost; string Description; int Index; CShopItem() {} CShopItem(string name, int cost, string description, int index) { Name = name; Cost = cost; Description = description; Index = index; } void Display(CTextMenu@ menu) { string itemCommand = "item_" + Index; string itemText = Name + " - " + Cost + " gold\n " + Description; menu.AddItem(itemText, any(itemCommand)); } void Execute(CBasePlayer@ pPlayer) { if (BuyItem(pPlayer, Index)) { g_Scheduler.SetTimeout("RefreshShopMenu", 0.0, EHandle(pPlayer)); } } } array ShopMenuItems; array ShopItems; void InitializeMenuItems() { ShopMenuItems.resize(UpgradeNames.length()); for (uint i = 0; i < UpgradeNames.length(); i++) { ShopMenuItems[i] = CShopMenuItem(UpgradeNames[i], UpgradeCosts[i], UpgradeDescriptions[i], int(i)); } ShopItems.resize(itemNames.length()); for (uint i = 0; i < itemNames.length(); i++) { ShopItems[i] = CShopItem(itemNames[i], itemCosts[i], itemDescriptions[i], int(i)); } } void BuyBarricade(CBasePlayer@ pPlayer) { CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int playerGold = pCustom.GetKeyvalue("$i_gold").GetInteger(); int barricadeCost = 30; if (playerGold >= barricadeCost) { pCustom.SetKeyvalue("$i_gold", playerGold - barricadeCost); pCustom.SetKeyvalue("$i_barricadecount", pCustom.GetKeyvalue("$i_barricadecount").GetInteger() + 1); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You have bought a barricade. Press R to place it.\n"); } else { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You do not have enough gold to buy a barricade.\n"); } } void BuyBridge(CBasePlayer@ pPlayer) { CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int playerGold = pCustom.GetKeyvalue("$i_gold").GetInteger(); int bridgeCount = pCustom.GetKeyvalue("$i_bridgecount").GetInteger(); int bridgeCost = 1000; if (bridgeCount >= 1) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You already have a bridge. You can't buy another one.\n"); return; } if (playerGold >= bridgeCost) { pCustom.SetKeyvalue("$i_gold", playerGold - bridgeCost); pCustom.SetKeyvalue("$i_bridgecount", 1); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You have bought a bridge. Press R to place it.\n"); } else { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You do not have enough gold to buy a bridge.\n"); } } HookReturnCode ShopClientSay(SayParameters@ pParams) { CBasePlayer@ pPlayer = pParams.GetPlayer(); const CCommand@ args = pParams.GetArguments(); if (args.ArgC() >= 1) { const string command = args.Arg(0).ToLowercase(); if (command == "/shop") { CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int playerGold = pCustom.GetKeyvalue("$i_gold").GetInteger(); // Display the player's total gold g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You have " + playerGold + " gold.\n"); // Show the upgrade menu ShowUpgradeMenu(pPlayer, 0); pParams.ShouldHide = true; return HOOK_CONTINUE; } // Handle /stats command if (command == "/stats") { ShowPlayerStats(pPlayer); pParams.ShouldHide = true; return HOOK_CONTINUE; } } return HOOK_CONTINUE; } HookReturnCode PlayerPostThink(CBasePlayer@ pPlayer) { if (pPlayer is null || !pPlayer.IsConnected()) { return HOOK_CONTINUE; } // Continuously update the visual indicator position UpdateVisualIndicator(pPlayer); // Check for E + R key combination to switch placement mode if ((pPlayer.pev.button & IN_USE) != 0 && (pPlayer.pev.button & IN_RELOAD) != 0) { HandleSwitchPlacementMode(pPlayer); } // Handle key press for placing barricades or bridges if ((pPlayer.pev.button & IN_RELOAD) != 0) { HandleKeyPress(pPlayer); } return HOOK_CONTINUE; } HookReturnCode PlayerSpawn(CBasePlayer@ pPlayer) { CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); // Restore armor value if chainmail is active if (pCustom.GetKeyvalue("$i_chainmail").GetInteger() == 1) { pPlayer.pev.armorvalue = Math.max(pPlayer.pev.armorvalue, 25); } // Reset extra damage to base value from Damage Upgrades string PlayerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int damageUpgradeLevel = 0; string damageUpgradeKey = PlayerId + "_0"; // Index 0 is Damage Upgrade if (PlayerUpgrades.exists(damageUpgradeKey)) { PlayerUpgrades.get(damageUpgradeKey, damageUpgradeLevel); } int baseExtraDamage = damageUpgradeLevel * 5; pCustom.SetKeyvalue("$i_extradmg", baseExtraDamage); // Restore max health to default (100) plus upgrades, ignoring temporary buffs int additionalMaxHealth = pCustom.GetKeyvalue("$i_maxhp").GetInteger(); pPlayer.pev.max_health = 100 + additionalMaxHealth; // Adjust 100 if default is different pPlayer.pev.health = pPlayer.pev.max_health; // Restore maximum speed int additionalMaxSpeed = pCustom.GetKeyvalue("$i_maxspeed").GetInteger(); pPlayer.SetMaxSpeed(270 + additionalMaxSpeed); // Clear the buff state on respawn PlayerBuffState[pPlayer.entindex()] = false; return HOOK_CONTINUE; } dictionary PlayerBuffState; HookReturnCode PlayerKilled(CBasePlayer@ pVictim, CBaseEntity@ pAttacker, int iGib) { if (pAttacker !is null && pAttacker.IsPlayer() && pVictim.entindex() != pAttacker.entindex()) { CBasePlayer@ pKiller = cast(pAttacker); if (pKiller !is null) { CustomKeyvalues@ pCustom = pKiller.GetCustomKeyvalues(); // Check for Grafters Charm upgrade if (pCustom.GetKeyvalue("$i_grafterscharm").GetInteger() == 1) { int additionalHealth = 10; pKiller.pev.max_health += additionalHealth; pKiller.pev.health = Math.min(pKiller.pev.health + additionalHealth, pKiller.pev.max_health); int currentMaxHealth = pCustom.GetKeyvalue("$i_maxhp").GetInteger(); pCustom.SetKeyvalue("$i_maxhp", currentMaxHealth + additionalHealth); } // Check for Adrenaline Boost item if (pCustom.GetKeyvalue("$i_adrenalineboost").GetInteger() == 1) { ApplyAdrenalineBoost(pKiller); } // Check if the player is already buffed by the bracelet if (PlayerBuffState.exists(pKiller.entindex()) && bool(PlayerBuffState[pKiller.entindex()])) { g_PlayerFuncs.ClientPrint(pKiller, HUD_PRINTTALK, "The bracelet's power is still active.\n"); return HOOK_CONTINUE; } // Check for Bracelet of Instability if (pCustom.GetKeyvalue("$i_instabilitybracelet").GetInteger() == 1) { ApplyInstabilityBracelet(pKiller); } } } // Clear the buff state of the victim PlayerBuffState[pVictim.entindex()] = false; return HOOK_CONTINUE; } void ApplyInstabilityBracelet(CBasePlayer@ pPlayer) { if (!pPlayer.IsAlive()) return; // Check if the player is alive int randomBuff = Math.RandomLong(0, 1); // 0 for max health, 1 for damage if (randomBuff == 0) { // Temporarily increase max health by a random number between 50 and 250 int additionalHealth = Math.RandomLong(50, 250); float originalMaxHealth = pPlayer.pev.max_health; float buffedMaxHealth = originalMaxHealth + additionalHealth; pPlayer.pev.max_health = buffedMaxHealth; pPlayer.pev.health = Math.min(pPlayer.pev.health + additionalHealth, buffedMaxHealth); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Bracelet of Instability: Max health increased by " + additionalHealth + " for 7 seconds.\n"); g_Scheduler.SetTimeout("ResetMaxHealth", 7.0, EHandle(pPlayer), originalMaxHealth); } else { // Temporarily increase damage by a random number between 25 and 100 int additionalDamage = Math.RandomLong(25, 100); CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int originalDamage = pCustom.GetKeyvalue("$i_extradmg").GetInteger(); int buffedDamage = originalDamage + additionalDamage; pCustom.SetKeyvalue("$i_extradmg", buffedDamage); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Bracelet of Instability: Damage increased by " + additionalDamage + " for 7 seconds.\n"); g_Scheduler.SetTimeout("ResetDamage", 7.0, EHandle(pPlayer), originalDamage); } // Mark the player as currently buffed PlayerBuffState[pPlayer.entindex()] = true; } void ResetMaxHealth(EHandle hPlayer, float originalMaxHealth) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer !is null && pPlayer.IsAlive()) { pPlayer.pev.max_health = originalMaxHealth; pPlayer.pev.health = Math.min(pPlayer.pev.health, originalMaxHealth); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Bracelet of Instability: Max health buff ended.\n"); } // Clear the buff state PlayerBuffState[pPlayer.entindex()] = false; } void ResetDamage(EHandle hPlayer, int originalDamage) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); if (pPlayer !is null && pPlayer.IsAlive()) { pCustom.SetKeyvalue("$i_extradmg", originalDamage); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Bracelet of Instability: Damage buff ended.\n"); } // Clear the buff state PlayerBuffState[pPlayer.entindex()] = false; } void ApplyAdrenalineBoost(CBasePlayer@ pPlayer) { if (pPlayer !is null && pPlayer.IsAlive()) { int originalMaxSpeed = int(pPlayer.pev.maxspeed); int newMaxSpeed = int(Math.Ceil(originalMaxSpeed * 1.35)); if (newMaxSpeed > 4000) { newMaxSpeed = 4000; g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Can't go beyond 4k max speed.\n"); } // Apply max speed boost (capped at 4000) pPlayer.SetMaxSpeed(newMaxSpeed); // Add yellow glow effect pPlayer.pev.renderfx = kRenderFxGlowShell; pPlayer.pev.rendercolor = Vector(255, 0, 0); // Yellow color pPlayer.pev.renderamt = 8; // Glow intensity // Schedule reset of effects after 3 seconds g_Scheduler.SetTimeout("ResetSpeedAndGlow", 3.0, EHandle(pPlayer), originalMaxSpeed); } } void ResetSpeedAndGlow(EHandle hPlayer, int originalMaxSpeed) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer !is null && pPlayer.IsAlive()) { // Reset max speed to original value pPlayer.SetMaxSpeed(originalMaxSpeed); // Remove glow effect pPlayer.pev.renderfx = kRenderFxNone; pPlayer.pev.rendercolor = Vector(0, 0, 0); pPlayer.pev.renderamt = 0; } } void HandleKeyPress(CBasePlayer@ pPlayer) { int playerIndex = pPlayer.entindex(); float currentTime = g_Engine.time; string playerId = "" + playerIndex; // Cooldown of 0.5 second between presses if (LastKeyPressTime.exists(playerId)) { float lastKeyPressTime; LastKeyPressTime.get(playerId, lastKeyPressTime); if (currentTime - lastKeyPressTime < 0.5) { return; // Still in cooldown period } } // Record the current time as the last key press time LastKeyPressTime[playerId] = currentTime; CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int currentMode = pCustom.GetKeyvalue("$i_placement_mode").GetInteger(); if (currentMode == 0) { // Place bait instantly PlaceBaitAt(pPlayer); } else if (currentMode == 1) { // Handle barricade placement if (VisualIndicators.exists(playerIndex)) { // Confirm placement if the indicators are already present ConfirmPlacement(pPlayer); } else { // Create the visual indicators if it's not present CreateBarricadeVisualIndicator(pPlayer); } } else if (currentMode == 2) { if (VisualIndicators.exists(playerIndex)) { ConfirmBridgePlacement(pPlayer); } else { CreateBridgeVisualIndicator(pPlayer); } } } void HandleSwitchPlacementMode(CBasePlayer@ pPlayer) { float currentTime = g_Engine.time; string playerId = "" + pPlayer.entindex(); // Cooldown of 0.5 seconds before switching if (LastKeyPressTime.exists(playerId)) { float lastKeyPressTime; LastKeyPressTime.get(playerId, lastKeyPressTime); if (currentTime - lastKeyPressTime < 0.5) { return; // Still in cooldown period } } // Record the current time as the last key press time LastKeyPressTime[playerId] = currentTime; CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int currentMode = pCustom.GetKeyvalue("$i_placement_mode").GetInteger(); int nextMode = (currentMode + 1) % 3; // Cycle through 0, 1, 2 pCustom.SetKeyvalue("$i_placement_mode", nextMode); pCustom.SetKeyvalue("$i_placing_bait", nextMode == 0 ? 1 : 0); pCustom.SetKeyvalue("$i_placing_barricade", nextMode == 1 ? 1 : 0); string modeMessage = nextMode == 0 ? "Bait" : (nextMode == 1 ? "Barricades" : "Bridge"); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Switched to " + modeMessage + " placement mode.\n"); // Remove any existing visual indicator when switching modes RemoveVisualIndicators(pPlayer); } void RemoveVisualIndicators(CBasePlayer@ pPlayer) { RemoveBridgeVisualIndicator(pPlayer); RemoveBarricadeVisualIndicator(pPlayer); } void PlaceBaitAt(CBasePlayer@ pPlayer) { if (!pPlayer.IsAlive()) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You need to be alive to place the bait.\n"); return; } if ((pPlayer.pev.flags & FL_ONGROUND) == 0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You need to be on the ground to place the bait.\n"); return; } CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int baitCount = pCustom.GetKeyvalue("$i_baitcount").GetInteger(); int freshStart = pCustom.GetKeyvalue("$i_freshstart").GetInteger(); if (freshStart == 1 && baitCount >= 1) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Nice try.\n"); return; } if (baitCount <= 0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You do not have any baits to place. (Hold E + R Keys to switch placement mode).\n"); return; } string playerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int gonomesPlaced = 0; if (PlayerGonomeCount.exists(playerId)) { PlayerGonomeCount.get(playerId, gonomesPlaced); } if (gonomesPlaced >= 20) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You have reached the maximum of 20 placed gonomes. What a mess!\n"); return; } dictionary keyvalues; keyvalues["origin"] = (pPlayer.pev.origin + g_Engine.v_forward * 112).ToString(); keyvalues["angles"] = Vector(0, pPlayer.pev.angles.y, 0).ToString(); keyvalues["health"] = "300"; keyvalues["displayname"] = "Aggressive Bait"; keyvalues["spawnflags"] = "4"; CBaseEntity@ newBait = g_EntityFuncs.CreateEntity("monster_gonome", keyvalues); if (newBait is null) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Failed to create a new bait.\n"); return; } // Increase the player's gonome count gonomesPlaced += 1; PlayerGonomeCount[playerId] = gonomesPlaced; // Decrease the player's bait count pCustom.SetKeyvalue("$i_baitcount", baitCount - 1); // Monitor the gonome if it died g_Scheduler.SetTimeout("MonitorGonome", 0.5, EHandle(pPlayer), EHandle(newBait)); } void MonitorGonome(EHandle hPlayer, EHandle hGonome) { if (!hGonome.IsValid()) { DecreaseGonomeCountFromHandle(hPlayer); return; } CBaseEntity@ pGonome = hGonome.GetEntity(); if (pGonome.pev.health <= 0) { DecreaseGonomeCountFromHandle(hPlayer); g_EntityFuncs.Remove(pGonome); return; } g_Scheduler.SetTimeout("MonitorGonome", 0.5, hPlayer, hGonome); } void DecreaseGonomeCountFromHandle(EHandle hPlayer) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer is null) return; string playerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int gonomesPlaced = 0; if (PlayerGonomeCount.exists(playerId)) { PlayerGonomeCount.get(playerId, gonomesPlaced); gonomesPlaced -= 1; if (gonomesPlaced < 0) gonomesPlaced = 0; PlayerGonomeCount[playerId] = gonomesPlaced; } } void HandleBarricadePlacement(CBasePlayer@ pPlayer) { int playerIndex = pPlayer.entindex(); float currentTime = g_Engine.time; string playerId = "" + playerIndex; // Cooldown of 0.5 second between presses if (LastKeyPressTime.exists(playerId)) { float lastKeyPressTime; LastKeyPressTime.get(playerId, lastKeyPressTime); if (currentTime - lastKeyPressTime < 0.5) { return; // Still in cooldown period } } // Record the current time as the last key press time LastKeyPressTime[playerId] = currentTime; CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int barricadeCount = pCustom.GetKeyvalue("$i_barricadecount").GetInteger(); if (barricadeCount <= 0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You do not have any barricades to place. (Hold E + R Keys to switch placement mode).\n"); return; } if (VisualIndicators.exists(playerIndex)) { // Confirm placement if the indicator is already present ConfirmPlacement(pPlayer); } else { // Create the visual indicator if it's not present CreateBarricadeVisualIndicator(pPlayer); } } void HandleBridgePlacement(CBasePlayer@ pPlayer) { int playerIndex = pPlayer.entindex(); float currentTime = g_Engine.time; string playerId = "" + playerIndex; // Cooldown of 0.5 second between presses if (LastKeyPressTime.exists(playerId)) { float lastKeyPressTime; LastKeyPressTime.get(playerId, lastKeyPressTime); if (currentTime - lastKeyPressTime < 0.5) { return; // Still in cooldown period } } // Record the current time as the last key press time LastKeyPressTime[playerId] = currentTime; CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int bridgeCount = pCustom.GetKeyvalue("$i_bridgecount").GetInteger(); if (bridgeCount <= 0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You do not have any bridges to place. (Hold E + R Keys to switch placement mode).\n"); return; } string playerIdAuth = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int bridgesPlaced = 0; if (PlayerBridges.exists(playerIdAuth)) { PlayerBridges.get(playerIdAuth, bridgesPlaced); } if (bridgesPlaced >= 3) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You have reached the maximum of 3 placed bridges.\n"); return; } if (VisualIndicators.exists(playerIndex)) { ConfirmBridgePlacement(pPlayer); } else { CreateBridgeVisualIndicator(pPlayer); } } void CreateBarricadeVisualIndicator(CBasePlayer@ pPlayer) { if (!pPlayer.IsAlive()) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You need to be alive to place a barricade.\n"); return; } CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int barricadeInventory = pCustom.GetKeyvalue("$i_barricadecount").GetInteger(); if (barricadeInventory <= 0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You do not have any barricades to place. (Hold E + R Keys to switch placement mode).\n"); return; } string playerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int barricadesPlaced = 0; if (PlayerBarricades.exists(playerId)) { PlayerBarricades.get(playerId, barricadesPlaced); } if (barricadesPlaced >= 5) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You have reached the maximum of 5 placed barricades.\n"); return; } // Ensure previous indicator is removed RemoveBarricadeVisualIndicator(pPlayer); CBaseEntity@ hookBlockBrush = g_EntityFuncs.FindEntityByTargetname(null, "hook_block_brush"); if (hookBlockBrush is null) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Could not find hook_block_brush entity.\n"); return; } dictionary keyvalues; keyvalues["model"] = string(hookBlockBrush.pev.model); keyvalues["rendermode"] = "2"; // Render as transparent keyvalues["renderamt"] = "128"; // Half-transparent keyvalues["renderfx"] = "1"; Vector angles(0, pPlayer.pev.angles.y, 0); g_EngineFuncs.MakeVectors(angles); Vector origin = pPlayer.pev.origin + g_Engine.v_forward * 112; if (origin.z < pPlayer.pev.origin.z) { origin.z = pPlayer.pev.origin.z; // Prevent it from going underground } keyvalues["origin"] = origin.ToString(); keyvalues["angles"] = angles.ToString(); CBaseEntity@ visualIndicator = g_EntityFuncs.CreateEntity("func_illusionary", keyvalues); if (visualIndicator !is null) { // Store the visual indicator in a global dictionary using the player's index VisualIndicators[pPlayer.entindex()] = EHandle(visualIndicator); } } void CreateBridgeVisualIndicator(CBasePlayer@ pPlayer) { if (!pPlayer.IsAlive()) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You need to be alive to place a bridge.\n"); return; } CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int bridgeInventory = pCustom.GetKeyvalue("$i_bridgecount").GetInteger(); if (bridgeInventory <= 0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You do not have any bridges to place. (Hold E + R Keys to switch placement mode).\n"); return; } string playerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int bridgesPlaced = 0; if (PlayerBridges.exists(playerId)) { PlayerBridges.get(playerId, bridgesPlaced); } if (bridgesPlaced >= 3) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You have reached the maximum of 3 placed bridges.\n"); return; } RemoveBridgeVisualIndicator(pPlayer); CBaseEntity@ hookBridgeBrush = g_EntityFuncs.FindEntityByTargetname(null, "hook_bridge_brush"); if (hookBridgeBrush is null) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Could not find hook_bridge_brush entity.\n"); return; } dictionary keyvalues; keyvalues["model"] = string(hookBridgeBrush.pev.model); keyvalues["rendermode"] = "2"; // Render as transparent keyvalues["renderamt"] = "128"; // Half-transparent keyvalues["renderfx"] = "1"; Vector angles(0, pPlayer.pev.angles.y, 0); g_EngineFuncs.MakeVectors(angles); Vector origin = pPlayer.pev.origin + g_Engine.v_forward * 112; if (origin.z < pPlayer.pev.origin.z) { origin.z = pPlayer.pev.origin.z; // Prevent it from going underground } keyvalues["origin"] = origin.ToString(); keyvalues["angles"] = angles.ToString(); CBaseEntity@ visualIndicator = g_EntityFuncs.CreateEntity("func_illusionary", keyvalues); if (visualIndicator !is null) { // Store the visual indicator in a global dictionary using the player's index VisualIndicators[pPlayer.entindex()] = EHandle(visualIndicator); } } void RemoveBarricadeVisualIndicator(CBasePlayer@ pPlayer) { int playerIndex = pPlayer.entindex(); if (VisualIndicators.exists(playerIndex)) { EHandle hVisualIndicator; VisualIndicators.get(playerIndex, hVisualIndicator); if (hVisualIndicator.IsValid()) { g_EntityFuncs.Remove(hVisualIndicator.GetEntity()); } VisualIndicators.delete(playerIndex); } } void RemoveBridgeVisualIndicator(CBasePlayer@ pPlayer) { int playerIndex = pPlayer.entindex(); if (VisualIndicators.exists(playerIndex)) { EHandle hVisualIndicator; VisualIndicators.get(playerIndex, hVisualIndicator); if (hVisualIndicator.IsValid()) { g_EntityFuncs.Remove(hVisualIndicator.GetEntity()); } VisualIndicators.delete(playerIndex); } } void UpdateVisualIndicator(CBasePlayer@ pPlayer) { int playerIndex = pPlayer.entindex(); if (VisualIndicators.exists(playerIndex)) { EHandle hVisualIndicator; VisualIndicators.get(playerIndex, hVisualIndicator); if (hVisualIndicator.IsValid()) { CBaseEntity@ visualIndicator = hVisualIndicator.GetEntity(); Vector angles(0, pPlayer.pev.angles.y, 0); g_EngineFuncs.MakeVectors(angles); CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); bool isPlacingBarricade = pCustom.GetKeyvalue("$i_placing_barricade").GetInteger() == 1; Vector origin; if (isPlacingBarricade) { origin = pPlayer.pev.origin + g_Engine.v_forward * 112; } else { origin = pPlayer.pev.origin + g_Engine.v_forward * 412; } if (origin.z < pPlayer.pev.origin.z) { origin.z = pPlayer.pev.origin.z; // Prevent it from going underground } visualIndicator.pev.origin = origin; visualIndicator.pev.angles = angles; } } } void ConfirmPlacement(CBasePlayer@ pPlayer) { int playerIndex = pPlayer.entindex(); if (VisualIndicators.exists(playerIndex)) { EHandle hVisualIndicator; VisualIndicators.get(playerIndex, hVisualIndicator); if (hVisualIndicator.IsValid()) { CBaseEntity@ visualIndicator = hVisualIndicator.GetEntity(); Vector origin = visualIndicator.pev.origin; Vector angles = visualIndicator.pev.angles; // Place the barricade PlaceBarricadeAt(pPlayer, origin, angles); // Remove the visual indicator g_EntityFuncs.Remove(visualIndicator); VisualIndicators.delete(playerIndex); } } } void ConfirmBridgePlacement(CBasePlayer@ pPlayer) { int playerIndex = pPlayer.entindex(); if (VisualIndicators.exists(playerIndex)) { EHandle hVisualIndicator; VisualIndicators.get(playerIndex, hVisualIndicator); if (hVisualIndicator.IsValid()) { CBaseEntity@ visualIndicator = hVisualIndicator.GetEntity(); Vector origin = visualIndicator.pev.origin; Vector angles = visualIndicator.pev.angles; // Place the bridge PlaceBridgeAt(pPlayer, origin, angles); // Remove the visual indicator g_EntityFuncs.Remove(visualIndicator); VisualIndicators.delete(playerIndex); } } } void PlaceBarricadeAt(CBasePlayer@ pPlayer, const Vector& in origin, const Vector& in angles) { if (!pPlayer.IsAlive()) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You need to be alive to place a barricade.\n"); return; } CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int barricadeInventory = pCustom.GetKeyvalue("$i_barricadecount").GetInteger(); int freshStart = pCustom.GetKeyvalue("$i_freshstart").GetInteger(); if (freshStart == 1 && barricadeInventory >= 1) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Nice try.\n"); return; } if (barricadeInventory <= 0) { return; } // Ensure the player is on the ground if ((pPlayer.pev.flags & FL_ONGROUND) == 0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You need to be on the ground to place a barricade.\n"); return; } // Perform a trace downwards to check for solid ground TraceResult tr; Vector groundPosition = origin; groundPosition.z -= 48; // Trace downwards 48 units g_Utility.TraceLine(origin, groundPosition, ignore_monsters, pPlayer.edict(), tr); // Check if the trace hit solid ground if (tr.flFraction == 1.0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You cannot place a barricade in the air.\n"); return; } // Proceed to create the barricade dictionary keyvalues; keyvalues["model"] = string(g_EntityFuncs.FindEntityByTargetname(null, "hook_block_brush").pev.model); keyvalues["origin"] = origin.ToString(); keyvalues["angles"] = angles.ToString(); keyvalues["health"] = "300"; keyvalues["material"] = "1"; keyvalues["spawnflags"] = "32"; keyvalues["displayname"] = "Wooden Barricade"; keyvalues["effects"] = "2048"; CBaseEntity@ newBarricade = g_EntityFuncs.CreateEntity("func_breakable", keyvalues); if (newBarricade is null) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Failed to create a new barricade.\n"); return; } // Ensure the barricade matches the player's yaw newBarricade.pev.angles = angles; // Decrease the player's barricade inventory pCustom.SetKeyvalue("$i_barricadecount", barricadeInventory - 1); // Increase the player's placed barricade count string playerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int barricadesPlaced = 0; if (PlayerBarricades.exists(playerId)) { PlayerBarricades.get(playerId, barricadesPlaced); } barricadesPlaced += 1; PlayerBarricades[playerId] = barricadesPlaced; // Associate the barricade with the player newBarricade.pev.impulse = pPlayer.entindex(); // Monitor the barricade for destruction g_Scheduler.SetTimeout("MonitorBarricade", 0.5, EHandle(pPlayer), EHandle(newBarricade)); // Play the barricade placement sound g_SoundSystem.EmitSound(pPlayer.edict(), CHAN_ITEM, "hookwars/placed.ogg", 1.0, ATTN_NORM); } void PlaceBridgeAt(CBasePlayer@ pPlayer, const Vector& in origin, const Vector& in angles) { if (!pPlayer.IsAlive()) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You need to be alive to place a bridge.\n"); return; } CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int bridgeInventory = pCustom.GetKeyvalue("$i_bridgecount").GetInteger(); int freshStart = pCustom.GetKeyvalue("$i_freshstart").GetInteger(); // Check if the player has both a fresh start and a bridge count of 1 or more if (freshStart == 1 && bridgeInventory >= 1) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Nice try.\n"); return; } if (bridgeInventory <= 0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You do not have any bridges to place. (Hold E + R Keys to switch placement mode).\n"); return; } // Ensure the player is on the ground if ((pPlayer.pev.flags & FL_ONGROUND) == 0) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You need to be on the ground to place a bridge.\n"); return; } // Proceed to create the bridge dictionary keyvalues; keyvalues["model"] = string(g_EntityFuncs.FindEntityByTargetname(null, "hook_bridge_brush").pev.model); keyvalues["origin"] = origin.ToString(); keyvalues["angles"] = angles.ToString(); keyvalues["targetname"] = "hook_bridge_brush_copy"; keyvalues["health"] = "5000"; keyvalues["material"] = "1"; keyvalues["spawnflags"] = "32"; keyvalues["displayname"] = "Wooden Bridge"; keyvalues["effects"] = "2048"; CBaseEntity@ newBridge = g_EntityFuncs.CreateEntity("func_breakable", keyvalues); if (newBridge is null) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Failed to create a new bridge.\n"); return; } // Ensure the bridge matches the player's yaw newBridge.pev.angles = angles; // Decrease the player's bridge inventory pCustom.SetKeyvalue("$i_bridgecount", bridgeInventory - 3); // Increase the player's placed bridge count string playerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int bridgesPlaced = 0; if (PlayerBridges.exists(playerId)) { PlayerBridges.get(playerId, bridgesPlaced); } bridgesPlaced += 1; PlayerBridges[playerId] = bridgesPlaced; // Associate the bridge with the player newBridge.pev.impulse = pPlayer.entindex(); // Monitor the bridge for destruction g_Scheduler.SetTimeout("MonitorBridge", 0.5, EHandle(pPlayer), EHandle(newBridge)); // Play the bridge placement sound g_SoundSystem.EmitSound(pPlayer.edict(), CHAN_ITEM, "hookwars/placed.ogg", 1.0, ATTN_NORM); } void MonitorBarricade(EHandle hPlayer, EHandle hBarricade) { if (!hBarricade.IsValid()) { // Barricade entity no longer exists DecreaseBarricadeCountFromHandle(hPlayer); return; } CBaseEntity@ pBarricade = hBarricade.GetEntity(); if (pBarricade.pev.health <= 0) { // Barricade has been destroyed DecreaseBarricadeCountFromHandle(hPlayer); // Remove the entity to prevent further checks g_EntityFuncs.Remove(pBarricade); return; } // Schedule the next check g_Scheduler.SetTimeout("MonitorBarricade", 0.5, hPlayer, hBarricade); } void MonitorBridge(EHandle hPlayer, EHandle hBridge) { if (!hBridge.IsValid()) { // Bridge entity no longer exists DecreaseBridgeCountFromHandle(hPlayer); return; } CBaseEntity@ pBridge = hBridge.GetEntity(); if (pBridge.pev.health <= 0) { // Bridge has been destroyed DecreaseBridgeCountFromHandle(hPlayer); // Remove the entity to prevent further checks g_EntityFuncs.Remove(pBridge); return; } // Schedule the next check g_Scheduler.SetTimeout("MonitorBridge", 0.5, hPlayer, hBridge); } void DecreaseBarricadeCountFromHandle(EHandle hPlayer) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer is null) return; string playerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int barricadesPlaced = 0; if (PlayerBarricades.exists(playerId)) { PlayerBarricades.get(playerId, barricadesPlaced); barricadesPlaced -= 1; if (barricadesPlaced < 0) barricadesPlaced = 0; PlayerBarricades[playerId] = barricadesPlaced; // Debugging // g_PlayerFuncs.ClientPrintAll(HUD_PRINTCONSOLE, "[DEBUG] Decreased barricadesPlaced to " + barricadesPlaced + " for playerId " + playerId + ".\n"); } } void DecreaseBridgeCountFromHandle(EHandle hPlayer) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer is null) return; string playerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int bridgesPlaced = 0; if (PlayerBridges.exists(playerId)) { PlayerBridges.get(playerId, bridgesPlaced); bridgesPlaced -= 1; if (bridgesPlaced < 0) bridgesPlaced = 0; PlayerBridges[playerId] = bridgesPlaced; } } void CloseExistingMenu(CBasePlayer@ pPlayer) { string PlayerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); if (PlayerMenus.exists(PlayerId)) { CTextMenu@ menu; PlayerMenus.get(PlayerId, @menu); if (menu !is null) { menu.Unregister(); } PlayerMenus.delete(PlayerId); } } void ShowUpgradeMenu(EHandle hPlayer, int page) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer is null) return; const int totalUpgrades = int(ShopMenuItems.length()); if (page < 0 || (page * MAX_UPGRADES_PER_PAGE) >= totalUpgrades) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Invalid page number.\n"); return; } int start = page * MAX_UPGRADES_PER_PAGE; int end = start + MAX_UPGRADES_PER_PAGE; CloseExistingMenu(pPlayer); CTextMenu@ menu = CTextMenu(@ShopMenuCallback); menu.SetTitle("Upgrades Menu - Page " + (page + 1) + "\n"); int itemsAdded = 0; for (int i = start; i < totalUpgrades && itemsAdded < MAX_UPGRADES_PER_PAGE; ++i, ++itemsAdded) { string description = ShopMenuItems[i].Description; array wrappedLines = WrapText(description, 50); string itemText = "\\g" + ShopMenuItems[i].Name + "\\w - \\o" + ShopMenuItems[i].Cost + " Gold\\w"; for (uint j = 0; j < wrappedLines.length(); ++j) { itemText += "\n" + wrappedLines[j]; } menu.AddItem(itemText, any(string("upgrade_" + i))); } if (end < totalUpgrades) menu.AddItem("Next Page", any("nextpage")); if (page > 0) menu.AddItem("Previous Page", any("prevpage")); menu.AddItem("Switch to Items", any("switch_to_items")); menu.Register(); menu.Open(255, 0, pPlayer); string PlayerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); PlayerMenus[PlayerId] = @menu; CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); pCustom.SetKeyvalue("$i_shop_page", page); pCustom.SetKeyvalue("show_upgrades", 1); UpdateHUDMessage(pPlayer, page, true); } void ShowItemMenu(CBasePlayer@ pPlayer, int page) { const int totalItems = int(ShopItems.length()); if (page < 0 || (page * MAX_ITEMS_PER_PAGE) >= totalItems) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Invalid page number.\n"); return; } CloseExistingMenu(pPlayer); int start = page * MAX_ITEMS_PER_PAGE; int end = start + MAX_ITEMS_PER_PAGE; CTextMenu@ menu = CTextMenu(@ShopMenuCallback); menu.SetTitle("Item Menu - Page " + (page + 1) + "\n"); for (int i = start; i < end && i < totalItems; ++i) { string description = ShopItems[i].Description; array wrappedLines = WrapText(description, 50); string itemText = "\\m" + ShopItems[i].Name + "\\w - \\o" + ShopItems[i].Cost + " Gold\\w"; for (uint j = 0; j < wrappedLines.length(); ++j) { itemText += "\n" + wrappedLines[j]; } menu.AddItem(itemText, any(string("item_" + i))); } if (end < totalItems) menu.AddItem("Next Page", any("nextpage")); if (page > 0) menu.AddItem("Previous Page", any("prevpage")); menu.AddItem("Switch to Upgrades", any("switch_to_upgrades")); menu.Register(); menu.Open(255, 0, pPlayer); string PlayerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); PlayerMenus[PlayerId] = @menu; CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); pCustom.SetKeyvalue("$i_shop_page", page); pCustom.SetKeyvalue("show_upgrades", 0); UpdateHUDMessage(pPlayer, page, false); } void NextPage(EHandle hPlayer) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer is null) return; CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int currentPage = pCustom.GetKeyvalue("$i_shop_page").GetInteger(); bool showUpgrades = pCustom.GetKeyvalue("show_upgrades").GetInteger() != 0; if (showUpgrades) { int totalPages = Math.Ceil(float(ShopMenuItems.length()) / MAX_UPGRADES_PER_PAGE); if (currentPage + 1 < totalPages) { ShowUpgradeMenu(pPlayer, currentPage + 1); } else { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You are on the last page.\n"); } } else { int totalPages = Math.Ceil(float(ShopItems.length()) / MAX_ITEMS_PER_PAGE); if (currentPage + 1 < totalPages) { ShowItemMenu(pPlayer, currentPage + 1); } else { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You are on the last page.\n"); } } } void PrevPage(EHandle hPlayer) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer is null) return; CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int currentPage = pCustom.GetKeyvalue("$i_shop_page").GetInteger(); bool showUpgrades = pCustom.GetKeyvalue("show_upgrades").GetInteger() != 0; if (currentPage > 0) { if (showUpgrades) { ShowUpgradeMenu(pPlayer, currentPage - 1); } else { ShowItemMenu(pPlayer, currentPage - 1); } } else { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You are on the first page.\n"); } } void ShopMenuCallback(CTextMenu@ menu, CBasePlayer@ pPlayer, int iSlot, const CTextMenuItem@ pItem) { if (pPlayer is null || pItem is null) { return; } any@ data = pItem.m_pUserData; if (data is null) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Menu data is null.\n"); return; } string command; data.retrieve(command); if (command.StartsWith("upgrade_")) { int upgradeIndex = atoi(command.SubString(8)); if (BuyUpgrade(pPlayer, upgradeIndex)) { // Refresh the menu if the purchase was successful g_Scheduler.SetTimeout("ShowUpgradeMenu", 0.0, EHandle(pPlayer), 0); } } else if (command.StartsWith("item_")) { int itemIndex = atoi(command.SubString(5)); ShopItems[itemIndex].Execute(pPlayer); } else if (command == "nextpage") { g_Scheduler.SetTimeout("NextPage", 0.0, EHandle(pPlayer)); } else if (command == "prevpage") { g_Scheduler.SetTimeout("PrevPage", 0.0, EHandle(pPlayer)); } else if (command == "switch_to_items") { g_Scheduler.SetTimeout("SwitchToItems", 0.0, EHandle(pPlayer)); } else if (command == "switch_to_upgrades") { g_Scheduler.SetTimeout("SwitchToUpgrades", 0.0, EHandle(pPlayer)); } else if (command == "exit") { CloseExistingMenu(pPlayer); } } void UpdateHUDMessage(CBasePlayer@ pPlayer, int page, bool showUpgrades) { HUDTextParams params; params.x = -1.0; params.y = 0.8; params.effect = 0; params.r1 = 255; params.g1 = 255; params.b1 = 255; params.a1 = 255; params.r2 = 255; params.g2 = 255; params.b2 = 255; params.a2 = 255; params.fadeinTime = 0.0; params.fadeoutTime = 0.0; params.holdTime = 1.0; params.fxTime = 0.5; params.channel = 4; string message = "Page: " + (page + 1) + " - " + (showUpgrades ? "Upgrades" : "Items"); g_PlayerFuncs.HudMessage(pPlayer, params, message); } bool BuyUpgrade(CBasePlayer@ pPlayer, int upgradeIndex) { CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int playerGold = pCustom.GetKeyvalue("$i_gold").GetInteger(); int upgradeCost = UpgradeCosts[upgradeIndex]; // Check for max values before deducting gold if (upgradeIndex == 2 && pCustom.GetKeyvalue("show_upgrades").GetInteger() != 0) { // Cooldown upgrade int currentCooldown = pCustom.GetKeyvalue("$i_hookcooldown").GetInteger(); if (currentCooldown >= 20) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You've maxed out your cooldown reduction.\n"); return false; } } if (upgradeIndex == 8) { // Will to Win upgrade int currentWillToWin = pCustom.GetKeyvalue("$i_willtowin").GetInteger(); if (currentWillToWin >= 10) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You've maxed out your Will to Win charges.\n"); return false; } } if (upgradeIndex == 1) { // Hookspeed upgrade int currentHookSpeed = pCustom.GetKeyvalue("$i_hookspeedupgrade").GetInteger(); if (currentHookSpeed >= 10) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You've maxed out your hook speed.\n"); return false; } } if (upgradeIndex == 7) { // Agility upgrade int currentAgility = pCustom.GetKeyvalue("$i_agility").GetInteger(); if (currentAgility >= 8) { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You've maxed out your agility.\n"); return false; } } if (playerGold >= upgradeCost) { playerGold -= upgradeCost; pCustom.SetKeyvalue("$i_gold", playerGold); string PlayerId = g_EngineFuncs.GetPlayerAuthId(pPlayer.edict()); int upgradeLevel = 0; string upgradeKey = PlayerId + "_" + upgradeIndex; if (PlayerUpgrades.exists(upgradeKey)) { PlayerUpgrades.get(upgradeKey, upgradeLevel); } upgradeLevel++; PlayerUpgrades[upgradeKey] = upgradeLevel; switch (upgradeIndex) { case 0: pCustom.SetKeyvalue("$i_extradmg", pCustom.GetKeyvalue("$i_extradmg").GetInteger() + 5); break; case 1: { int currentHookSpeed = pCustom.GetKeyvalue("$i_hookspeedupgrade").GetInteger(); int currentPullSpeed = pCustom.GetKeyvalue("$i_pullspeedupgrade").GetInteger(); if (currentHookSpeed < 10) { pCustom.SetKeyvalue("$i_hookspeedupgrade", currentHookSpeed + 1); pCustom.SetKeyvalue("$i_pullspeedupgrade", currentPullSpeed + 1); } } break; case 2: { if (pCustom.GetKeyvalue("show_upgrades").GetInteger() != 0) { // Cooldown upgrade pCustom.SetKeyvalue("$i_hookcooldown", pCustom.GetKeyvalue("$i_hookcooldown").GetInteger() + 1); } } break; case 3: { pCustom.SetKeyvalue("$i_maxhp", pCustom.GetKeyvalue("$i_maxhp").GetInteger() + 10); pPlayer.pev.max_health += 10; pPlayer.pev.health = Math.min(pPlayer.pev.health + 10, pPlayer.pev.max_health); // Adjust health accordingly } break; case 4: { pCustom.SetKeyvalue("$i_maxspeed", pCustom.GetKeyvalue("$i_maxspeed").GetInteger() + 5); int newMaxSpeed = 270 + pCustom.GetKeyvalue("$i_maxspeed").GetInteger(); pPlayer.SetMaxSpeed(newMaxSpeed); // Adjust maximum speed } break; case 5: { pCustom.SetKeyvalue("$i_coinhook", pCustom.GetKeyvalue("$i_coinhook").GetInteger() + 1); } break; case 6: { pCustom.SetKeyvalue("$i_twirlupgrade", pCustom.GetKeyvalue("$i_twirlupgrade").GetInteger() + 1); } break; case 7: { int currentAgility = pCustom.GetKeyvalue("$i_agility").GetInteger(); if (currentAgility < 8) { pCustom.SetKeyvalue("$i_agility", currentAgility + 1); } } break; case 8: { int currentWillToWin = pCustom.GetKeyvalue("$i_willtowin").GetInteger(); if (currentWillToWin < 10) { pCustom.SetKeyvalue("$i_willtowin", currentWillToWin + 1); } } break; default: g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "[DEBUG] Unknown upgrade index.\n"); break; } // Trigger audio files in the map g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Purchased " + UpgradeNames[upgradeIndex] + ".\n"); g_EntityFuncs.FireTargets("purchased_sound", pPlayer, pPlayer, USE_ON, 0.0); return true; // Purchase successful } else { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Insufficient gold.\n"); g_EntityFuncs.FireTargets("insufficient_gold_sound", pPlayer, pPlayer, USE_ON, 0.0); return false; // Purchase failed } } bool BuyItem(CBasePlayer@ pPlayer, int itemIndex) { CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int playerGold = pCustom.GetKeyvalue("$i_gold").GetInteger(); int itemCost = itemCosts[itemIndex]; // Check if the player already owns the items bool alreadyOwned = false; switch (itemIndex) { case 0: alreadyOwned = pCustom.GetKeyvalue("$i_grappler").GetInteger() == 1; break; case 1: alreadyOwned = pCustom.GetKeyvalue("$i_lifestealhookitem").GetInteger() == 1; break; case 2: alreadyOwned = pCustom.GetKeyvalue("$i_techiesscope").GetInteger() == 1; break; case 3: alreadyOwned = pCustom.GetKeyvalue("$i_pulldmgitem").GetInteger() == 1; break; case 4: alreadyOwned = pCustom.GetKeyvalue("$i_barbedwire").GetInteger() == 1; break; case 5: alreadyOwned = false; break; // Barricade can be bought multiple times case 6: alreadyOwned = pCustom.GetKeyvalue("$i_explosivehook").GetInteger() == 1; break; case 7: alreadyOwned = pCustom.GetKeyvalue("$i_chainmail").GetInteger() == 1; break; case 8: alreadyOwned = pCustom.GetKeyvalue("$i_healhook").GetInteger() == 1; break; case 9: alreadyOwned = pCustom.GetKeyvalue("$i_touchofundeath").GetInteger() == 1; break; case 10: alreadyOwned = pCustom.GetKeyvalue("$i_grafterscharm").GetInteger() == 1; break; case 11: alreadyOwned = pCustom.GetKeyvalue("$i_bridgecount").GetInteger() == 1; break; case 12: alreadyOwned = false; break; // Allow multiple purchases of bait case 13: alreadyOwned = pCustom.GetKeyvalue("$i_adrenalineboost").GetInteger() == 1; break; case 14: alreadyOwned = pCustom.GetKeyvalue("$i_instabilitybracelet").GetInteger() == 1; break; } if (alreadyOwned && itemIndex != 5 && itemIndex != 12) { // Allow multiple purchases of barricades and baits g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "You already own the " + itemNames[itemIndex] + ".\n"); return false; } if (playerGold >= itemCost) { playerGold -= itemCost; pCustom.SetKeyvalue("$i_gold", playerGold); if (itemIndex == 5) { // Barricade int barricadeCount = pCustom.GetKeyvalue("$i_barricadecount").GetInteger(); pCustom.SetKeyvalue("$i_barricadecount", barricadeCount + 1); } else if (itemIndex == 12) { // Aggressive Bait int baitCount = pCustom.GetKeyvalue("$i_baitcount").GetInteger(); pCustom.SetKeyvalue("$i_baitcount", baitCount + 1); } else { switch (itemIndex) { case 0: pCustom.SetKeyvalue("$i_grappler", 1); break; case 1: pCustom.SetKeyvalue("$i_lifestealhookitem", 1); break; case 2: pCustom.SetKeyvalue("$i_techiesscope", 1); break; case 3: pCustom.SetKeyvalue("$i_pulldmgitem", 1); break; case 4: pCustom.SetKeyvalue("$i_barbedwire", 1); break; case 6: pCustom.SetKeyvalue("$i_explosivehook", 1); break; case 7: pCustom.SetKeyvalue("$i_chainmail", 1); pPlayer.pev.armorvalue += 25; // Give the player 25 armor break; case 8: pCustom.SetKeyvalue("$i_healhook", 1); break; case 9: pCustom.SetKeyvalue("$i_touchofundeath", 1); break; case 10: pCustom.SetKeyvalue("$i_grafterscharm", 1); break; case 11: pCustom.SetKeyvalue("$i_bridgecount", 1); break; case 13: pCustom.SetKeyvalue("$i_adrenalineboost", 1); break; case 14: pCustom.SetKeyvalue("$i_instabilitybracelet", 1); break; default: g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "[DEBUG] Unknown item index.\n"); break; } } // Trigger audio files g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Purchased " + itemNames[itemIndex] + ".\n"); g_EntityFuncs.FireTargets("purchased_sound", pPlayer, pPlayer, USE_ON, 0.0); return true; } else { g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Insufficient gold.\n"); g_EntityFuncs.FireTargets("insufficient_gold_sound", pPlayer, pPlayer, USE_ON, 0.0); return false; // Purchase failed } } void RefreshShopMenu(EHandle hPlayer) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer is null) return; CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); int showUpgrades = pCustom.GetKeyvalue("show_upgrades").GetInteger(); int currentPage = pCustom.GetKeyvalue("$i_shop_page").GetInteger(); if (showUpgrades != 0) { ShowUpgradeMenu(pPlayer, currentPage); } else { ShowItemMenu(pPlayer, currentPage); } } array WrapText(const string& in text, int lineLength) { array words = text.Split(" "); array wrappedLines; string currentLine = ""; for (uint i = 0; i < words.length(); i++) { if (currentLine.Length() + words[i].Length() + 1 > lineLength) { wrappedLines.insertLast(currentLine); currentLine = words[i]; } else { if (currentLine.Length() > 0) { currentLine += " "; } currentLine += words[i]; } } if (currentLine.Length() > 0) { wrappedLines.insertLast(currentLine); } return wrappedLines; } void SwitchToItems(EHandle hPlayer) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer is null) return; CloseExistingMenu(pPlayer); // Close existing menu ShowItemMenu(pPlayer, 0); } void SwitchToUpgrades(EHandle hPlayer) { if (!hPlayer.IsValid()) return; CBasePlayer@ pPlayer = cast(hPlayer.GetEntity()); if (pPlayer is null) return; CloseExistingMenu(pPlayer); // Close existing menu ShowUpgradeMenu(pPlayer, 0); } void ShowPlayerStats(CBasePlayer@ pPlayer) { CustomKeyvalues@ pCustom = pPlayer.GetCustomKeyvalues(); // Get player stats int hookSpeedUpgrade = pCustom.GetKeyvalue("$i_hookspeedupgrade").GetInteger(); int minHookSpeed = 1800 + hookSpeedUpgrade * 50; int maxHookSpeed = Math.min(2500 + hookSpeedUpgrade * 50, 3500); int pullSpeedUpgrade = pCustom.GetKeyvalue("$i_pullspeedupgrade").GetInteger(); int pullSpeed = 1800 + pullSpeedUpgrade * 50; int extraDamage = pCustom.GetKeyvalue("$i_extradmg").GetInteger(); int hookCooldown = pCustom.GetKeyvalue("$i_hookcooldown").GetInteger(); float currentCooldown = Math.max(3.0 - hookCooldown * 0.1, 1.0); // Calculate current cooldown, cap at 1 second int gold = pCustom.GetKeyvalue("$i_gold").GetInteger(); int moveSpeed = int(pPlayer.pev.maxspeed); int coinHook = pCustom.GetKeyvalue("$i_coinhook").GetInteger(); int agility = pCustom.GetKeyvalue("$i_agility").GetInteger(); int willToWin = pCustom.GetKeyvalue("$i_willtowin").GetInteger(); // Calculate damage negation percentage int agilityNegationChance = Math.min(agility * 5, 40); // Cap at 40% int damageReductionPercentage = 30 + (agilityNegationChance / 5) * 6.25; // 30% initial, 6.25% per point, up to 80% // Will to Win bonus damage percentage int willToWinBonus = 10 + (willToWin * 2); // 10% initial, 2% per point, up to 30% // Notify the player that their stats have been printed in the console g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTTALK, "Your stats have been printed in your console.\n"); // Print the stats in the player's console g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "\n"); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Your Stats:\n"); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Base Hook Speed: " + minHookSpeed + " - " + maxHookSpeed + " (+" + hookSpeedUpgrade * 50 + ")\n"); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Pull Speed: " + pullSpeed + " (+" + pullSpeedUpgrade * 50 + ")\n"); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Base Attack: " + (50 + extraDamage) + " (+" + extraDamage + ")\n"); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Hook Cooldown: " + formatFloat(currentCooldown, "", 1, 1) + " seconds (-" + hookCooldown * 0.1 + ")\n"); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Move Speed: " + moveSpeed + "\n"); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Gold: " + gold + "\n"); g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Extra Gold Per Hook: +" + coinHook + "\n"); // Include the gold upgrade in the stats g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Damage Negation: " + damageReductionPercentage + "%% (with " + agilityNegationChance + "%% chance)\n"); // Display damage negation percentage and chance g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "Will to Win Bonus Damage: " + willToWinBonus + "%% (when health < 50%%)\n"); // Display Will to Win bonus damage g_PlayerFuncs.ClientPrint(pPlayer, HUD_PRINTCONSOLE, "\n"); } }