Dynamic Weapon Spawns (Advanced)
WIP. Check discussion first please --gia 14:08, 19 Feb 2006 (PST)
Introduction
Here we will expand on Draco's tutorial Dynamic_Weapon_Spawns. The mechanic used was to have a model entity that spawned a set of weapons depending on the values of six ConVars, all of this at the beginning of the round.
Each ConVar would represent an slot of a Weapon Set. Weapon Sets may not just spawn weapons but other items like ammo boxes, armor or health.
Objectives
This tutorial works for HL2MP, you will have to modify some steps to get it to work on another game. We will try to accomplish the following:
- Use a resource file with the contents of Weapon Sets: weaponsets.txt
- Ability to modify, switch, load or save Weapon Sets at any time during the round using ConCommands with their respective AutoComplete functions.
- Use a Point_Entity CWeaponSetEnt to spawn other entities. Weapons or other Items like armor or health.
- Inputs/Ouputs as in any Weapon and working. ie. must have OnPlayerPickUp and it must work when the player picks up the spawned weapon.
- Ability to set the ammo for the items to spawn (amount of health or armor to recover in the other cases).
- Ability to force the spawning of a specific item regardless what the weapon set says.
- Inclusion of 4 special spawn rules:
- <none> which will spawn nothing
- <random>
- <allweaps-old> will divide the items available in tiers, like High, Medium and Lower tier Weapons. And only spawn certain tier depending of the slot number.
- <allweaps-new> will use the same tier division but will spawn more than one tier per slot, each tier has a different chance of being spawned at each slot.
- If the Weapon Set doesn't have enough slots defined to cover the needs of a map then the values will wrap around the Weapon Set. (ie. A set with only 2 slots and a map entity that requires slot 7, after wrapping 7 around 2 it would end up using slot 1).
The Resource File weaponsets.txt
The file could look like this.
"WeaponSets" { "WeaponSet" { "Name" "Pistols" "Slots" { "1" "357" "2" "ammo_pistol_large" "3" "pistol" "4" "pistol" "5" "ammo_pistol" } } "WeaponSet" { "Slots" { "1" "357" //only 1 slot } "Name" "357s" //Name is last } "WeaponSet" { "Name" "Automatics" "Slots" { "1" "ar2" "2" "ammo_ar2_alt" "5" "ammo_ar2" //there's no 4 "3" "smg1" //3 is after 5 "6" "ammo_smg1" "7" "<none>" //same effect as not listing it "8" "ammo_smg1_grenade" } } }
The Entity FGD Definition
Let's define the FGD entry to use as a base for the coding.
@include "hl2mp.fgd" @PointClass base(Weapon) studio("models/items/357ammo.mdl") = item_weaponset : "A weapon spawn point that uses weapon sets" [ spawnitem(choices) : "Spawn what item?" : -20 : "What item should spawn?" = [ -20: "Use Weapon Set" //only this option uses weaponsets.txt -3: "All Weapons Classic" //this and the following are forced values -2: "All Weapons Modern" -1: "Spawn Random Items" 0: "Spawn Nothing" 1: "weapon_ar2" 2: "weapon_pistol" 3: "weapon_smg1" 4: "weapon_357" 5: "weapon_xbow" 6: "weapon_shotgun" 7: "weapon_ml" 8: "weapon_stunstick" 9: "item_grenade (1)" 10: "weapon_slam (1)" 11: "item_ammo_ar2 (20)" 12: "item_ammo_ar2_secondary (1)" 13: "item_ammo_pistol (20)" 14: "item_ammo_smg1 (45)" 15: "item_ammo_357 (6)" 16: "item_xbow_bolt (1)" 17: "item_box_buckshot (20)" 18: "item_ml_grenade (1)" 19: "item_smg1_grenade (1)" 20: "item_ammo_pistol_large (100)" 21: "item_ammo_smg1_large (225)" 22: "item_ammo_ar2_large (100)" 23: "item_ammo_357_large (20)" 24: "item_health (1)" 25: "item_battery (100)" ] weaponsetslot(integer) : "For what weapon slot? [1,n]" : 1 : "If using Weapon Set what slot to use? [1,n]" ammotogive(choices) : "How will ammo to give be calculated?" : 0 : "Consider health, armor or whatever the item gives as ammo." = [ 0: "0-Use Default for item" 1: "1-Use AmmoValues as Absolute Value" 2: "2-Use AmmoValues as %Max-Ammo allowed by Recipient" 3: "3-Take values from Custom Ammo String" ] ammovalue(string) : "Amount of Ammo" : "" : "Read as float, only affects the first type of ammo given by item." "The only one usually." customammostring(string) : "Custom Ammo String" : "" : "For items giving two types of ammo, weapons, with different values." + " <GiveIndex>,<value>;<GiveIndex>,<value>... ie. Primary set as Default" + " and Secondary set as Relative 0.5: '0,0;2,0.5'" //spawnflags(Flags) = [ ] the base class weapon has a flag Start constrained ]
base(Weapon)
By using Weapon as the base then this entity inherits the inputs and ouputs and Name and Orientation parameters. It also gets the Start Constrained flag.
ammotogive(choices) and ammovalue(string)
These are so level designers can use this entity without having to deal with the Custom Ammo String, however they are not neccessary as the custom ammo string can do the same and more.
- 0: Default - If Box of Rounds always gives 10 ammo then do the same... This option doesnt care about AmmoValues
- 1: Absolute - If AmmoValues says to give 1 ammo give 1 ammo despite the default 10
- 2: %Max-Ammo - If the pistol that will receive the ammo takes up to 500 rounds and 50 in mag and Ammovalues says 0.5 then give 500+50 = 550 * 0.5 = 275 rounds. In the case of health we can say the recipient is the player, so he would get 50% health recovered. Same for Armor, and other items.
customammostring(string)
It could work for items that give 'n' types of ammo, provided you modify the code to accept that. When using index 0 for Default the ammo value used doesn't really matter, it could be blank, but it would be better to use another 0 there.
The Code
The whole thing is here?. Now onto the main sections of it.
Main structures
class CWeaponSetEnt; //Map Entity, cant belong to a namespace because of macros //defining their own inside namespace WeaponSetH { //Namespace, just to keep things in order struct ItemDef; //Holds constants to make things easier to read struct Item; //Holds data about items class CWeaponSet; //Static class that does all the coordination struct AllWeapChance; //Holds data of slots and tiers associated to them //to be used by <allweaps-old> and <allweaps-new> };