Files
garrysmod-coop/gamemode/progression_shared.lua

638 lines
18 KiB
Lua

-- Shared, server-authoritative progression configuration.
-- Prices and rewards are only acted upon by sv_progression.lua. Clients receive
-- this file so the shop can render the same catalogue without duplicating it.
CustomCoopProgression = CustomCoopProgression or {}
local Progression = CustomCoopProgression
Progression.KillReward = 1
Progression.DeathCreditCost = 5
Progression.PlaytimeUpdateInterval = 10
Progression.PlaytimeRewardInterval = 600
Progression.PlaytimeCreditsPerInterval = 5
Progression.DefaultModelID = "kleiner"
Progression.DefaultWeaponID = "none"
Progression.DefaultTrailID = "none"
Progression.MaximumShopPrice = 100000000
Progression.MusicCooldownPadding = 10
Progression.AdminShopTestingBypass = true
Progression.HoverpackHoldDelay = 0.3
Progression.HoverpackRiseSpeed = 24
Progression.HoverpackDrainPerSecond = 16
Progression.HoverpackRechargeDelay = 1.25
Progression.HoverpackRechargePerSecond = 14
Progression.HoverpackWalkingRechargePerSecond = 22
Progression.HoverpackSound = "PhysicsCannister.ThrusterLoop"
Progression.HoverpackSoundVolume = 0.55
Progression.HoverpackSoundPitch = 115
Progression.ShopSpawnDistance = 500
Progression.PurchasedEntityLimit = 12
Progression.PurchasedHelperLimit = 6
Progression.Ranks = {
{ score = 0, name = "Newbie" },
{ score = 10, name = "Survivor" },
{ score = 50, name = "Hunter" },
{ score = 150, name = "Slayer" },
{ score = 500, name = "Veteran" },
{ score = 1500, name = "Exterminator" },
{ score = 5000, name = "Legend" }
}
Progression.Models = {
{
id = "kleiner",
name = "Dr. Kleiner",
model = "models/player/kleiner.mdl",
price = 0,
description = "The free default model."
},
{
id = "male_citizen",
name = "Male Citizen",
model = "models/player/group01/male_07.mdl",
price = 5,
description = "A survivor from City 17."
},
{
id = "female_citizen",
name = "Female Citizen",
model = "models/player/group01/female_04.mdl",
price = 5,
description = "A survivor from City 17."
},
{
id = "barney",
name = "Barney Calhoun",
model = "models/player/barney.mdl",
price = 10,
description = "Civil Protection's least loyal officer."
},
{
id = "alyx",
name = "Alyx Vance",
model = "models/player/alyx.mdl",
price = 15,
description = "Resistance fighter and engineer."
},
{
id = "monk",
name = "Father Grigori",
model = "models/player/monk.mdl",
price = 25,
description = "A familiar face from Ravenholm."
},
{
id = "combine",
name = "Combine Soldier",
model = "models/player/combine_soldier.mdl",
price = 50,
description = "Standard Combine infantry."
},
{
id = "combine_elite",
name = "Combine Elite",
model = "models/player/combine_super_soldier.mdl",
price = 100,
description = "An elite Overwatch soldier."
}
}
Progression.Weapons = {
{
id = "none",
name = "No Bonus Weapon",
weaponClass = "",
price = 0,
description = "Use only weapons supplied by the map."
},
{
id = "glock",
name = "Glock",
weaponClass = "coop_glock",
price = 15,
description = "A reliable permanent sidearm unlock."
},
{
id = "p228",
name = "P228",
weaponClass = "coop_p228",
price = 20,
description = "A permanent accurate 13-round sidearm unlock."
},
{
id = "usp",
name = "USP",
weaponClass = "coop_usp",
price = 25,
description = "A permanent tactical sidearm unlock with a detachable suppressor."
},
{
id = "fiveseven",
name = "Five-Seven",
weaponClass = "coop_fiveseven",
price = 30,
description = "A permanent fast-firing 20-round sidearm unlock."
},
{
id = "mac10",
name = "MAC-10",
weaponClass = "coop_mac10",
price = 35,
description = "A permanent compact automatic weapon unlock with a very high fire rate."
},
{
id = "mp5",
name = "MP5",
weaponClass = "coop_mp5",
price = 40,
description = "A permanent compact SMG unlock."
},
{
id = "pumpshotgun",
name = "Pump Shotgun",
weaponClass = "coop_pumpshotgun",
price = 75,
description = "A permanent close-range weapon unlock."
},
{
id = "famas",
name = "FAMAS",
weaponClass = "coop_famas",
price = 100,
description = "A permanent accurate, controllable automatic rifle unlock."
},
{
id = "ak47",
name = "AK-47",
weaponClass = "coop_ak47",
price = 110,
description = "A permanent hard-hitting rifle unlock with substantial recoil."
},
{
id = "m4",
name = "M4",
weaponClass = "coop_m4",
price = 125,
description = "A permanent rifle unlock."
},
{
id = "deagle",
name = "Desert Eagle",
weaponClass = "coop_deagle",
price = 150,
description = "A permanent seven-round hand cannon unlock with very high damage."
},
{
id = "autoshotgun",
name = "Automatic Shotgun",
weaponClass = "coop_autoshotgun",
price = 225,
description = "A permanent rapid-fire combat shotgun unlock."
},
{
id = "m249",
name = "M249",
weaponClass = "coop_para",
price = 300,
description = "A permanent light machine gun unlock with a 100-round magazine."
},
{
id = "nyan_gun",
name = "Nyan Gun",
weaponClass = "weapon_nyangun",
price = 1000,
description = "Rainbows, music and extremely colourful destruction."
},
{
id = "lightsaber",
name = "Lightsaber",
weaponClass = "weapon_lightsaber",
price = 1000,
description = "A permanent weapon from a galaxy far, far away."
},
{
id = "orbital_friendship_cannon",
name = "Orbital Friendship Cannon",
weaponClass = "orbital_friendship_cannon",
price = 5000,
description = "Deliver friendship from orbit."
},
{
id = "sexyness",
name = "Sexyness (2-bit)",
weaponClass = "pist_weagon",
price = 100000,
description = "The absurdly powerful Ultimate Admin Gun."
}
}
Progression.Music = {
{
id = "trololo",
name = "Trolololo",
sound = "custom_coop_music/trololo.mp3",
price = 250,
duration = 162,
spatial = true,
minimumDistance = 120,
maximumDistance = 2200,
description = "Your character plays this song as moving, positional audio. Unlocks permanently."
}
}
Progression.Behaviours = {
{
id = "hoverpack",
name = "Hoverpack",
price = 500,
description = "Hold Space after jumping to hover and rise slowly. Toggle this behaviour on or off."
},
{
id = "godmode_30",
name = "God Mode — 30 Seconds",
price = 400,
duration = 30,
consumable = true,
godmode = true,
description = "Activate 30 seconds of complete damage immunity. Each use is purchased separately."
},
{
id = "godmode_90",
name = "God Mode — 90 Seconds",
price = 1000,
duration = 90,
consumable = true,
godmode = true,
description = "Activate 90 seconds of complete damage immunity. Each use is purchased separately."
}
}
Progression.Trails = {
{
id = "none",
name = "No Trail",
price = 0,
material = "",
description = "Turn off your currently equipped player trail."
},
{
id = "lol",
name = "LOLOLOL Trail",
price = 75,
material = "trails/lol",
color = Color(255, 255, 255),
description = "Leave a stream of Garry's Mod's classic LOL material behind you."
},
{
id = "love",
name = "Love Trail",
price = 100,
material = "trails/love",
color = Color(255, 90, 180),
description = "A bright pink trail made from the built-in love material."
},
{
id = "rainbow",
name = "Rainbow Plasma",
price = 150,
material = "trails/plasma",
color = Color(255, 255, 255),
rainbow = true,
description = "A plasma trail that continuously cycles through rainbow colours."
},
{
id = "electric",
name = "Electric Blue",
price = 125,
material = "trails/electric",
color = Color(70, 150, 255),
additive = true,
description = "A glowing blue electrical trail."
},
{
id = "laser",
name = "Green Laser",
price = 125,
material = "trails/laser",
color = Color(80, 255, 100),
additive = true,
description = "A sharp green laser trail."
},
{
id = "smoke",
name = "Smoke Trail",
price = 50,
material = "trails/smoke",
color = Color(190, 190, 190),
description = "A subtle grey smoke trail."
}
}
-- These are consumable purchases: every successful purchase spawns one
-- server-controlled entity at the surface the player is aiming at.
Progression.Entities = {
{
id = "bouncy_ball",
name = "Bouncy Ball",
class = "sent_ball",
model = "models/Combine_Helicopter/helicopter_bomb01.mdl",
price = 10,
spawnOffset = 24,
ballSize = 24,
description = "A colourful edible ball that bounces and restores 5 health when used."
},
{
id = "glowstick",
name = "Cyan Glowstick",
class = "gmod_light",
model = "models/maxofs2d/light_tubular.mdl",
price = 20,
spawnOffset = 10,
color = Color(60, 220, 255),
brightness = 2,
lightSize = 320,
description = "A portable physics light for dark corners. It remains on until removed or cleaned up."
},
{
id = "healthkit",
name = "Health Kit",
class = "item_healthkit",
model = "models/items/healthkit.mdl",
price = 25,
spawnOffset = 10,
description = "Drop a Half-Life 2 health kit at the aimed location."
},
{
id = "suit_battery",
name = "Suit Battery",
class = "item_battery",
model = "models/items/battery.mdl",
price = 35,
spawnOffset = 10,
description = "Drop a suit battery for armour and AUX-equipped survivors."
},
{
id = "explosive_barrel",
name = "Explosive Barrel",
class = "prop_physics",
model = "models/props_c17/oildrum001_explosive.mdl",
price = 60,
spawnOffset = 48,
description = "A physics barrel that explodes when sufficiently damaged. Keep your distance."
},
{
id = "tnt",
name = "Timed TNT",
class = "gmod_dynamite",
model = "models/dynamite/dynamite.mdl",
price = 125,
spawnOffset = 8,
fuse = 5,
damage = 250,
description = "A 250-damage TNT charge that detonates five seconds after purchase."
},
{
id = "red_tnt",
name = "Red TNT — Shoot to Detonate",
class = "sent_tnt",
model = "models/dav0r/tnt/tnt.mdl",
price = 150,
spawnOffset = 16,
description = "The red Nuke Pack TNT. Damage it to detonate it; nearby charges can chain-react."
}
}
-- Helpers are also consumable purchases. They join the Resistance squad, but
-- their kills never award progression points to avoid an automatic score farm.
Progression.Helpers = {
{
id = "rebel",
name = "Rebel",
class = "npc_citizen",
model = "models/Humans/Group03/Male_07.mdl",
weapon = "weapon_smg1",
citizenType = 3,
price = 175,
description = "An armed Resistance fighter with an SMG."
},
{
id = "rebel_medic",
name = "Rebel Paramedic",
class = "npc_citizen",
model = "models/Humans/Group03m/Male_07.mdl",
weapon = "weapon_smg1",
citizenType = 3,
medic = true,
price = 275,
description = "An armed Resistance medic who can heal nearby players and allies."
},
{
id = "barney",
name = "Barney Calhoun",
class = "npc_barney",
model = "models/barney.mdl",
weapon = "weapon_smg1",
price = 325,
description = "A dependable Resistance companion armed with an SMG."
},
{
id = "alyx",
name = "Alyx Vance",
class = "npc_alyx",
model = "models/alyx.mdl",
weapon = "weapon_alyxgun",
price = 350,
description = "A fast and capable Resistance fighter with her signature weapon."
},
{
id = "father_grigori",
name = "Father Grigori",
class = "npc_monk",
model = "models/monk.mdl",
weapon = "weapon_annabelle",
price = 400,
description = "Ravenholm's fearless guardian, armed with Annabelle."
},
{
id = "rebel_turret",
name = "Rebel Floor Turret",
class = "npc_turret_floor",
model = "models/Combine_turrets/Floor_turret.mdl",
friendlyTurret = true,
price = 500,
spawnOffset = 4,
description = "A stationary floor turret configured to fight for the Resistance."
}
}
-- Only these combat NPC classes award points. The numeric value is the Score
-- and Credit reward, so bosses and other dangerous enemies are worth more.
-- Keeping an allow-list avoids farming friendly or player-spawned NPCs.
Progression.MonsterClasses = {
npc_advisor = 25,
npc_antlion = 1,
npc_antlionguard = 12,
npc_antlion_worker = 2,
npc_barnacle = 2,
npc_clawscanner = 1,
npc_combine_s = 3,
npc_combinedropship = 15,
npc_combinegunship = 20,
npc_cscanner = 1,
npc_fastzombie = 3,
npc_fastzombie_torso = 1,
npc_headcrab = 1,
npc_headcrab_black = 2,
npc_headcrab_fast = 1,
npc_headcrab_poison = 2,
npc_helicopter = 20,
npc_hunter = 8,
npc_manhack = 1,
npc_metropolice = 2,
npc_poisonzombie = 6,
npc_rollermine = 2,
npc_sniper = 4,
npc_stalker = 2,
npc_strider = 25,
npc_turret_ceiling = 2,
npc_turret_floor = 2,
npc_turret_ground = 2,
npc_vortigaunt = 4,
npc_zombie = 2,
npc_zombie_torso = 1,
npc_zombine = 4,
monster_alien_controller = 5,
monster_alien_grunt = 3,
monster_alien_slave = 2,
monster_apache = 20,
monster_barnacle = 2,
monster_bigmomma = 25,
monster_bullchicken = 3,
monster_gargantua = 25,
monster_headcrab = 1,
monster_houndeye = 2,
monster_human_assassin = 5,
monster_human_grunt = 3,
monster_ichthyosaur = 6,
monster_leech = 1,
monster_nihilanth = 40,
monster_osprey = 20,
monster_snark = 1,
monster_tentacle = 8,
monster_zombie = 2
}
local function FindByID(items, id)
for _, item in ipairs(items) do
if item.id == id then return item end
end
end
function Progression.GetModel(id)
return FindByID(Progression.Models, id)
end
function Progression.GetWeapon(id)
return FindByID(Progression.Weapons, id)
end
function Progression.GetMusic(id)
return FindByID(Progression.Music, id)
end
function Progression.GetBehaviour(id)
return FindByID(Progression.Behaviours, id)
end
function Progression.GetTrail(id)
return FindByID(Progression.Trails, id)
end
function Progression.GetEntity(id)
return FindByID(Progression.Entities, id)
end
function Progression.GetHelper(id)
return FindByID(Progression.Helpers, id)
end
function Progression.GetRank(score)
local result = Progression.Ranks[1].name
score = math.max(tonumber(score) or 0, 0)
for _, rank in ipairs(Progression.Ranks) do
if score < rank.score then break end
result = rank.name
end
return result
end
function Progression.GetMonsterReward(className)
local configured = Progression.MonsterClasses[className]
if configured == nil or configured == false then return end
if configured == true then configured = Progression.KillReward end
return math.max(math.floor(tonumber(configured) or Progression.KillReward), 1)
end
function Progression.FormatPlaytime(totalSeconds)
local totalMinutes = math.max(math.floor((tonumber(totalSeconds) or 0) / 60), 0)
local days = math.floor(totalMinutes / 1440)
local hours = math.floor(totalMinutes % 1440 / 60)
local minutes = totalMinutes % 60
if days > 0 then return string.format("%dd %dh", days, hours) end
if hours > 0 then return string.format("%dh %02dm", hours, minutes) end
return string.format("%dm", minutes)
end
local function SetHoverpackThrusting(ply, thrusting)
if SERVER and Progression.SetHoverpackThrusting then
Progression.SetHoverpackThrusting(ply, thrusting)
end
end
hook.Add("SetupMove", "CustomCoopProgressionHoverpack", function(ply, moveData)
local jumpHeld = moveData:KeyDown(IN_JUMP)
local suitPower = ply.GetSuitPower and ply:GetSuitPower() or 100
local canHover = ply:GetNW2Bool("CustomCoopHoverpackEnabled", false)
and ply:Alive()
and ply:GetMoveType() == MOVETYPE_WALK
and ply:WaterLevel() < 2
and not ply:InVehicle()
and suitPower > 0.01
if not jumpHeld or not canHover then
ply.CustomCoopHoverHoldStarted = nil
ply.CustomCoopHoverJumpWasHeld = false
SetHoverpackThrusting(ply, false)
return
end
if not ply.CustomCoopHoverJumpWasHeld then
ply.CustomCoopHoverHoldStarted = CurTime()
end
ply.CustomCoopHoverJumpWasHeld = true
-- Continually reset while grounded. A normal jump therefore happens first,
-- and hovering only starts after Space remains held in the air.
if ply:OnGround() then
ply.CustomCoopHoverHoldStarted = CurTime()
SetHoverpackThrusting(ply, false)
return
end
ply.CustomCoopHoverHoldStarted = ply.CustomCoopHoverHoldStarted or CurTime()
if CurTime() - ply.CustomCoopHoverHoldStarted < Progression.HoverpackHoldDelay then
SetHoverpackThrusting(ply, false)
return
end
local velocity = moveData:GetVelocity()
velocity.z = Progression.HoverpackRiseSpeed
moveData:SetVelocity(velocity)
SetHoverpackThrusting(ply, true)
end)