folkarps / F3

ArmA 3 mission development framework customized for Folk ARPS
http://www.folkarps.com/
12 stars 5 forks source link

Assign Gear: Config Class Refactor #94

Open SamLex opened 6 years ago

SamLex commented 6 years ago

I propose a refactor of the assignGear component to use config classes (via '#include' into description.ext) to define the gear classes. I have created a prototype that demonstrates the basic idea. It can be viewed here: https://gist.github.com/SamLex/d9e0089ce2f232d309e6c3289160d667. 'gear_nato.hpp' is probably the most interesting file as it defines the actual gear.

Advantages

Disadvantages

shadow-fa commented 6 years ago

I created an adaption of Lexer's idea and filled it out for all our classes and all nato factions): https://gist.github.com/shadow-fa/bc3f0a226ebc9edab194a853fa8a7b5c

Key differences:

With all those changes I wanted to minimize the need for custom code for every special case, since everything can be defined in the config file. But this is just a prototype and I'm open for any suggestions. Maybe we get some new ideas from this and can find something better together.

SamLex commented 6 years ago

Many of the design decesions I made in the prototype were to make best use of the inheritance that config classes provides. To this end, I also thing the gear classes themselves should be put into a clear hierarchy.

"num" variables

I'm unsure why the ammo type of these would ever need to change. As far as I am aware, there is only one white smoke grenade used by all factions and this is the case for all the "num" variables.

These variables are also broken out instead of just being one "magazines" to allow for easy changes with inheritance. For example, if I have class A with numWhiteSmoke = 2, and I want class B to be identical except with only one smoke, class B can just inherit from class A and set numWhiteSmoke = 1. With a magazines array, class B must copy the full array from class A and change it to only have one smoke. These means that is class A's magazines change in future class B will not automatically reflect these changes.

"has" variables (minus hasNVGs)

Again, not sure why you would want to change these, they are global.

hasNVGs

I had envisioned the SQF code using faction to determine which NVGs a unit should be given, but this could also be changed to a variable similar to "helmet" (is a string).

Defines

No, just no. Was trying to get away from defines and includes as they are horrible. All of those uses can be achieved with a carefully created class hierarchy and the meta classes. E.g. a Pacific NATO man can inherit from NATO man and just override the Pacific specific equipment.

Meta classes

One of the reasons for meta classes was so that there was a clear distinction between single valued variables (backpack, primaryWeapon, etc.) and multi-valued variables (primaryWeaponAdditionalAmmo). With everything being an array, this distinction is completely lost. It also brings a nice degree of abstraction: should the Man class 'know' that its equipment is randomised? Does it need to know? I feel that meta mapping allows for simpler code and easier reasoning about the gear classes. This also comes back to the maximising of inheritance from earlier. Incidently, the equipment defines (RIFLE et al.) could easily be single value meta classes, giving a central place to edit without the horror of defines.

"NumMag"

These were done to make changing the type of a weapon an easier task for missionmakers (and better inheritance, seeing a pattern here :P). If a missionmaker wants to change an MX to a TRG, they can just change the primaryWeapon without having to look up the class name of the ammo as well ("primaryWeaponAdditionalAmmo" aside, if I could work out a way to do the same with it, I would). Breaking the ammo types out into primary, secondary, and tertiary (though these should be renamed primary, launcher, and handgun) makes it clear what ammo belongs to which gun. This means that is a missionmaker wants to remove the handgun for a gear class, they just need to clear the tertiary variables and all the handgun related ammo will be gone, without even having to know what ammo a given handgun uses.

As for vehicles, this again can be solved with single value meta classes.

Traits

I was planning for these to be in the form 'isA': e.g. "isMedic", "isEngineer", etc. This would be from the reasons of inheritance again. E.g. Class E is a medic and engineer, class F can remove the medic while keeping the engineer with just isMedic = 0.

Attachments

I like the idea of breaking these out into types of attachments, but again would prefer them to be single value with meta classes (where a single value makes sense, like a scope).

Display Name

Good idea!

Items and backpack items

While I think and items variable will be required for edge cases, I was planning to have as many "num" and "has" variables as possible for faction-independent equipment: e.g. "hasMedkit", "numExplosiveSachels", "hasToolkit", etc. These have the triple advantages of better inheritance, missionmakers not needing to know or lookup class names, and better readability.

shadow-fa commented 6 years ago

"num" variables

I liked those variables, but I fear they add addtional complexity to the code. In addition due to the number of different grenades/smoke-colors/explosive-types/etc either the number of "num" variables will explode, or custom magazine definitions would be necesassary anyway.

Another reason against those variables is that some rifles have 30 rounds magazines while others have only 20, this can become a problem when the missionmaker changes the weapon without realizing that they have now lost a third of their bullets. This is because the number of rounds per magazine is not visible from the config file.

"has" variables (minus hasNVGs)

I'm fine with those, but again feel that there's a lot of special cases like NVGs which makes it more complicated.

Meta classes

They're a great idea and might remove the need for the defines in my prototype at the cost of more code.

To summarize I like the overall approach of config files, but I feel the number of all those is*, has*, num* variables will explode or the will not cover everything and we will still have a lot of special cases not only in the config files but also in the code.

SamLex commented 6 years ago

While I agree that the number of variables might explode, I'm not sure that is totally a bad thing. The only place all of them will be visible is in the "template" Man class, everywhere else can just override/use what they need.

As for code complexity, I'm not too concerned about this. Ideally a missionmaker would never need to modify the SQF as the gear config could cover all cases, so complexity would only be visible to FA3 devs.

Good point about the rounds per mag, I had not considered this. It's something I would assume missionmakers would notice when they tested their mission, but could still be a potential problem. Do the maginzine class names always have the number of rounds in their name?

When I used 'hasNVGs', I forgot that there was faction specific NVGs. So that one should probably be a class name string like 'vest' (so 'nvgs'). Same with anything that has faction specific versions or other dependencies (like magazines depend on the gun, whereas smoke grenades depend on nothing and are faction independent).

If/when this seems to be the way the assignGear refactor is going (instead of #49 or others), I will fully implement one of the factions and see how many special cases exist and how complex the resulting code is.

SamLex commented 6 years ago

For later reference, Aquarius' gear class analysis: https://docs.google.com/spreadsheets/d/1nXuIqx7klxwct3DTkumAi6JZuv-UjIAZEh9xgVEiRVE

NikkoJT commented 6 years ago

I get the feeling this will make it much easier to implement one-off new classes, which is nice. A couple of concerns, though:

shadow-fa commented 6 years ago

As for executing code, I have suggested this: https://gist.github.com/shadow-fa/bc3f0a226ebc9edab194a853fa8a7b5c#file-nato-hpp-L134-L137

Weapon selection via parameter could be done through a new type of meta-class.

SamLex commented 6 years ago

I personally like to avoid an executeCode field for arbitrary code, but we'll see.

For custom textures, I was thinking of rolling in #13 and have a customTextures field, in a format something like: customTextures[] = {{0, "texture_file_name.paa"}, {1, "other_texture_file_name.paa"}}

For parameters, I'm not too sure, I'll have to have a think. Ideally it would be possible to change the whole gear class based on a parameter so that changes weren't just restricted to single items.

I haven't quite decided how the loadout system (light vs standard) is going to work. I'll probably try to make that system general enough that extra loadouts (e.g. MX or Mk200, with scopes, without) can be added with minimal SQF code changes (can't promise none).

NikkoJT commented 6 years ago

I've been discussing the parameters thing with shado and it's come down to this particular idea:

I think ideally, I would like to have the option for a parameter to override a specific class, with a different class - so for example every unit using the "ftl" class could be treated as if it was using the "ftl_nvs" class

This would allow a lot of options for customisation through parameters, from single item changes like giving a unit a different gun or scope, to deciding whether fireteams have RATs or Grenadiers.

SamLex commented 6 years ago

To help explain my current thinking on this, I thought I'd employ my old friend, a class diagram: assigngear

The basic current idea is to start abstract and only get more specific if need, using placeholders otherwise, and to use the 'Sets' to abstract the specifics of actual ingame object classes.

'Man' This is the top level classes for all the gear classes, specifying things that every unit has (i.e. everyone has a uniform, and maybe a FAK). It also acts sort of like a template for what other attributes are available to its children classes.

'R', 'AR', ... These inherit from 'Man' and correspond to the existing gear classes. These would build on 'Man' and create a 'stereotypical' example of a gear class. For example, an 'R' would represent the gear a typical rifleman would get, in a faction-independent way. The reasoning here is that, in general, an AAF rifleman and a NATO rifleman will have similar gear other than specifics (i.e. they will both have a rifle and 8 mags).

'Set' To support this idea of faction independence I will skip over to the other side of the diagram and the 'Set'. A 'Set' will be used to encompass all the items available to a particular faction; effectively being a mapping between a 'friendly' name and the ingame class name. For example, the 'NatoSet' might have a 'backpack' attribute of 'B_AssaultPack_mcamo'.

The way these would aid faction-independant specification is that the generic gear classes would use the '[faction]' placeholder to cause the correct ingame item class to get looked up for the faction of the unit being assigned gear to. For example, 'AR' could have an attribute 'backpack' of '[faction].backpack'. When gear was being assigned to a unit of the Nato faction, this would look up 'backpack' in the 'NatoSet'.

'Weapon' As a convenient way of specifying a weapon and its ammo types together, a 'Weapon' will be used in 'Sets' to make it easy to specify a particular weapon. For example, a 'NatoMX' class inheriting from 'Weapon' would contain the ingame object class for an MX gun and the magazine classes it accepts. The 'NatoSet' could then have an attribute 'rifle' of 'NatoMX'. In this way the information about a particular weapon is grouped together and makes switching a factions weapons easier (don't need to remember to change the magazine classes when you change what rifle a faction uses).

'LightR', 'HeavyR', 'NightR' Back to the right side of the diagram, these are 'variant' classes inheriting from the 'standard' gear classes. The idea is to allow a general system for supporting loadouts and variants. The selection of these would probably be pretty directly wired up mission parameters, possibly on a per gear class basis (e.g. a f_param_r_variant parameter to change the rifleman variant, etc.). These ones are just examples of what could be added, either in future or by missionmakers themselves. Again the reasoning is that, in general, a light rifleman is going to be quite similar to a rifleman and quite faction-independent.

'NatoR', NatoPacificR', 'AAFR', 'NatoLightR' I don't see many of these being needed in the default template, but to allow missionmakers the ability to change gear loadouts for a particular faction without affecting the other, these faction-specific classes would inherit from the gear classes they are specialising.

Examples I know this is a bit abstract, so I'll have a concrete example of what these ideas would look like as actual 'hpp' files in the near future.

SamLex commented 6 years ago

Here is the beginnings the concrete hpp for what I was rambling about in the above comment: https://gist.github.com/SamLex/2358cfb36f82721464c5732fa55b10a6

I think it is relatively clear where I am going with this, but I will continue to expand it to cover the whole of Nato as it is just now, and maybe another faction as well

shadow-fa commented 6 years ago

I have a few thoughts and opinions about this.

The first would be to swap out the whole gear file/class for a side. E.g. depending on the f_param_loadouts, we could take the ${faction}Normal or the ${faction}Light class. It should be easy for missionmakers to add additional classes.

The second approach is to only swap out parts of a gear file. For example to swap out the scopes based on a parameter, we could have the following config:

/*
Here we simply take the NatoPrimaryWeapon class and instead of a concrete scope, 
we refer to our ParameterMetaClass `SpecialScopes`.
*/
class NatoPrimaryWeapon : Weapon
{
    primaryScope = "SpecialScopes";
    //primaryScope = "optic_Holosight";
    //...
};
/*
This class selects a subclass based on the parameter that is defined in `parameterName`,
and uses its value instead.
*/
class SpecialScopes : ParameterMetaClass
{
    parameterName = "f_param_scopes";
    class 0
    {
        value      = "optic_Holosight";
    };
    class 1
    {
        value      = "optic_SOS";
    };
};

This has the advantage, that it makes swapping out some gear items quick and easy for missionmakers. There can also be multiple levels of indirection if there's a need to select gear based on two or more parameters, although this quickly gets very complicated due to the explosion of nubmer of possibilities.

(Alternatively we could also write primaryWeaponAttachments[] = {"SpecialScopes"}; and instead of concrete classes we refer to "primaryScope" and "highScope" (which are defined in the weapon class) instead. Although in this case we have to make sure that this is generic-ish and works with all types of items.)

SamLex commented 5 years ago

This is really a note to future @SamLex, but here is my current thinking on this. A 'slot'-based class with a top-down inheritance tree and tag-based conditional inheritance.

assignGear would be called with the object to be geared (vehicle, container, or man) and a number of string tags: faction name, assignGear class (i.e. "r") and an custom MM tags. Custom tags would be determined by calling a MM defined function (in init.sqf, e.g. f_fnc_assignGearDetermineAdditionalTags) with the set of standard tags. This would allow mission makers to easily define tags derived parameters, potentially based on the faction and class, without assignGear itself knowing anything about parameters.

There would be two top-level classes Container and Man that look something like this:

class Container
{
    inventory[] = {};
};

class Man
{
    uniform = "";
    headgear = "";
    vest = "";
    glasses = "";
    nvg = "";
    backpack = "";
    primaryWeapon = "";
    primaryWeaponAmmo[] = {};
    launcherWeapon = "";
    launcherWeaponAmmo[] = {};
    secondaryWeapon = "";
    secondaryWeaponAmmo[] = {};
    inventory[] = {};
    hasMap = false;
    hasCompass = false;
    hasRadio = false;
    hasWatch = false;
    hasGPS = false;
    numFAKs = 0;
};

The key here is the attributes for Man are based around the "slots" visible in the player inventory, with a few 'virtual' slots like ammo and number of FAKs for common items to separate them from the catch-all inventory. (the backpack, vest, and uniform would be filled from the inventory list; I'm not sure there is a need to specify what goes where and allows for potentially more efficient packing).

assignGear would pick which base class to look at based on the type of object: vehicle or container = Container, man = Man. It would then look for a child of that class that contained all the tags passed to it. If it found none, it would stop and construct the gear set from the hierarchy it found so far. If it found more than one, it'd throw an error (so the class hierarchy can't fork for a given tag set). Here's an example:

class Soldier : Man
{
    inheritOnTags[] = {};
    uniform="TaggedUniform";
    headgear="TaggedHeadgear";
    nvg = "NVGoggles";
    numFAKs = 1;
};

class NatoSoldier : Soldier
{
    inheritOnTags[] = {"nato"};
    primaryWeapon = "arifle_MX_F";
    primaryWeaponAmmo[] = {{3, "30Rnd_65x39_caseless_mag"}, {2, "30Rnd_65x39_caseless_mag_Tracer"}};
};

class CsatSoldier : Soldier
{
    inheritOnTags[] = {"csat"};
    primaryWeapon = "SMG_01_F";
    primaryWeaponAmmo[] = {{3, "30Rnd_45ACP_Mag_SMG_01"}, {2, "30Rnd_45ACP_Mag_SMG_01"}};
};

class NatoR : NatoSoldier
{
    inheritOnTags[] = {"nato", "r"};
    numFAKs = 20;
    primaryWeaponAmmo[] += {{3, "30Rnd_65x39_caseless_mag"}, {-1, "30Rnd_65x39_caseless_mag_Tracer"}};
};

So for a NATO man with class 'r', it would start a Man, find Soldier and accept (no tags always match), look at CsatSoldier and reject it since "csat" is not in the tag set, find and accept NatoSoldier ("nato" in set), and then find and accept NatoR ("nato" and "r" both in set). So final merged class (virtual, by the game) would look like:

class NatoR
{
    <snip empty from Man>
    inheritOnTags[] = {"nato", "r"};
    primaryWeapon = "arifle_MX_F";
    primaryWeaponAmmo[] = {{3, "30Rnd_65x39_caseless_mag"}, {2, "30Rnd_65x39_caseless_mag_Tracer"}, {3, "30Rnd_65x39_caseless_mag"}, {-1, "30Rnd_65x39_caseless_mag_Tracer"}};
    uniform="TaggedUniform";
    headgear="TaggedHeadgear";
    nvg = "NVGoggles";
    numFAKs = 20;
};

This would construct this gear set: TaggedUniform, TaggedHeadgear, NVGoggles, arifle_MX_F, 6 30Rnd_65x39_caseless_mag, 1 30Rnd_65x39_caseless_mag_Tracer, and 20 FAKs.

So what is TaggedUniform and TaggedHeadgear? These are special alternative to just specifying the BIS classes that allow gear that depends on tags as well without have to create new classes in the Man hierarchy for each. For example:

class Tagged
{
    tags[] = {};
};
class TaggedUniform : Tagged
{
    uniform = "";
};
class NatoTaggedUniform : TaggedUniform
{
    tags[] = {"nato"};
    uniform = "U_B_CombatUniform_mcam";
};
class CsatTaggedUniform : TaggedUniform
{
    tags[] = {"csat"};
    uniform = "U_B_Wetsuit";
};

These would be resolved in the same way. This would allow a generic Soldier class to assign faction-specific gear without having to have one for each faction (the example does, but ignore that. NatoSoldier could be used for much more specific customisations actual usage).

There is a special case for these Tagged gears and that is weapons. You will notice that there is no way to add a scope to a rifle in the above, and this is delibate. For that, a class inheriting from TaggedWeapon would need to be used. This is because weapons have there own 'slots' and so should be handled separately.

class TaggedWeapon : Tagged
{
    primaryWeapon = "";
    silencer = "";
    scope = "";
    bipod = "";
    flashlight = "";
};

class NatoRifle : TaggedWeapon
{
    tags[] = {"nato"};
    primaryWeapon = "arifle_MX_F";
    scope = "optic_MRCO";
};

This would allow things like scopes and silencers to easily be changed with custom tags (from parameters) without having to touch the main hierarchy.

So this was something of a brain dump from the last few hours of my thinking. Might be a few rough edges but I think this mets quite a few of the requirements. When I get time, and if people don't think this sounds like a terrible idea, I might put together a prototype and have a play around with it.

shadow-fa commented 5 years ago

A few thoughts:

SamLex commented 5 years ago

I'll have a think about how to handle vehicles. Will probably mix in some idea from my previous prototype and see how it goes. Will need to investigate the '+=' thing

SamLex commented 5 years ago

Another note to future @SamLex, merge the Set and Weapon concept of 2-up with the tagging concept and general layout of 1-up.

Sets are tagged, Weapons aren't (just convenient abstraction for weapons). So can make broad changes with parameter tags. I.e. inherit from NatoSet with additional tag "spar" that overrides the rifle attribute with the SPAR Weapon class.

Weapons have at least type (BIS class) and primary ammo (BIS class). Then any number of "friendly names" for various ammo types (tracer, he, ap, etc.). Primary ammo attribute can be a reference to one of the friendly names.

Man classes mostly make references to Set attributes that are then resolved by tag. Generic classes with flexibility of tag system. Weapons are referenced piecemeal but can be inter-references (e.g. primaryWeapon="\@\Set->rifle"; primaryWeaponMags=8; primaryWeaponExtraMags[] = {{3, "\@primaryWeapon->tracerAmmo"}}; primaryWeaponScope = "\@\Set->acog").

Vehicles/Containers can references Sets to get faction appropriate gear. Example: inventory[] = {{8, "\@\Set->rifle"}, {20, "\@\Set->rifle->primaryAmmo"}} They get passed the same sort of tags as units (i.e. faction tag, assignGear class tag, any tags from callback (e.g. parameter tags)).

(any syntax shown is just an example, not thought out)

SamLex commented 5 years ago

I have looked into += and it appears you are correct; it doesn't work with lists of lists. I don't think this is a major problem though. I see at least two ways to make it work while still using +=:

There are other options as well, but my point is I think we can get += to work well enough for our purposes.

shadow-fa commented 5 years ago

Is the order of these lists always consistent? Otherwise the flattening will not work. Additionally I don't think using a macro (e.g. LIST_2("someclass") would be any worse than the above.

SamLex commented 5 years ago

I didn't mean programmatic flattening, I meant they would be entered in their flattened form. So the order would always (need to) be consistent. (It's also a bit of an odd question because we are defining how things work, so the order will always be consistent if we require it to be so)

I would like to avoid macros if possible because it then means there is two ways of doing things: the macro way and the expanded way. These two ways can get very confusing to people who don't know that they end up being the same. They also don't solve the problem of what the underlying representation actually is.

shadow-fa commented 5 years ago

Yes, I know what you meant. However, I was questioning if the order of the list when queried using sqf is always consistent with the order of the list that is written in the file.

SamLex commented 5 years ago

From what I've seen, yes. I think it's safe to assume it will be, at least at this stage.

shadow-fa commented 5 years ago

Do the maginzine class names always have the number of rounds in their name?

For normal weapons yes, some types like missiles/cannons don't have it. But all CfgMagazines entries have a count variable for the number of rounds.

Is there a reason why missionmakers would rather assign magazine counts instead of bullet counts? Otherwise, I think using rounds is much less error prone. (E.g. when switching to different weapons that have different bullets per magazine)

SamLex commented 5 years ago

I think it's a tradeoff: with round counts, it clearer exactly how much ammo everyone has and switching weapons is easier (if you want to maintain a constant quantity of rounds). But it also might have inventory space issues (mags fit for one weapon, not another) and might end up with unintentional partials.

shadow-fa commented 5 years ago

We could display an error message in case something does not fit.

What do you mean by unintentional partials?

SamLex commented 5 years ago

100 rounds for the NATO MX AR -> one magazine 100 rounds for the NATO MX rifle -> 3.33... magazines

And I think it will be rare for MMs to want non-integer numbers of magazines

shadow-fa commented 5 years ago

We'd have to specify if it's "up to this many rounds" or "at least this many rounds"? However the latter makes more sense to me. (And then warn if there's fraction and it had to round up/down)

SamLex commented 5 years ago

Another note to future @SamLex based on my discussion with @shadow-fa about 'modules' tonight: most matching tags used to choose which set of modules is used.

Two main types of classes: GearClass and Layer.

GearClass is just a marker inherited by the various gear classes. A subclass has two fields: "layers" and "activationTags". "activationTags" follow a similar idea to above: most matching tags for a given unit is used for assignment. So a NATO_R (name unimportant) GearClass might have the tags "r" and "nato" that would be used when assignGear was called with "r" and a NATO unit. But if a missionmaker defined the tag "moresmoke" (via mission parameters, exact mechanism not important atm) and another GearClass NATO_Party_R (name unimportant) with the tags "r", "nato" and "moresmoke", then this would be used instead in the above case.

"layers" would be a list of Layer subclasses that would be 'overlayed' left to right to construct the final gear set. Layer itself would be similar to Man above; listing all possible equipment 'slots'. So a Base Layer might define a number of FAKs everyone should have, and a BaseNato Layer basic uniform and such. These effectively form a dynamic class hierarchy.

Two questions that immediately come to mind:

  1. How to handle overlaying the same field? Naive destruction overriding would probably reduce the flexibility (e.g. can't add FAKs, always have to set exact amount). But naive additive would have the opposite problem (though this might be solvable with many, small layers but then there would likely be duplication).
  2. How to handle vehicles and crates in a nice way? I.e. so they can automatically have the correct ammo and such
SamLex commented 5 years ago

Another note to future @SamLex: This is probably where we want to go with this https://discordapp.com/channels/101479298812121088/543510355327057955/548878451117916160

One issue is that is kinda mandates a 'ManGearClass' per assignGear class; it doesn't really allow for a 'prototype' assignGear class. For example, a generic R. One way to resolve this would be 'tagged modules': a special kind of module that is effectively a lookup table between the most-matching-tag-set and the actual module name. This would allow generic assignGear classes but would be another level of indirection.

SamLex commented 4 years ago

I think this needs restructured and reassessed since it kinda got stuck. I think we need a phased approach:

Aqarius90 commented 5 months ago

Thread Necro

I've been trawling through gear files again with WS/RF adoption, and finally found the time to jot down some thoughts. So, once more, with feeling:

ENGINE LIMITATIONS:

1) Multiple inheritance is not a thing, leaving:     some very creative inheritance - hard to pull off readably/concisely (see 2) )     reference storage - may lead to variable field bloat     * some sort of tag system, a la #49 2) Inheritance is, uh, static:

Root{
    parent{foo=1};
    child:parent{};
};
newRoot:Root{
    parent{foo=2};
};
//newRoot.child.foo is 1

3) Arrays:     the += command, as WontFix, reliably works on string arrays and not much else*. This points to either flat structure to be parsed later, or references. References are simpler.

OBSERVATIONS

class 3IFB:Baseline{
    class gear:gear{/* */};
    class clothesDefault:clothes{/* */};
    class clothesDesert:clothes{
        base:base{
            helmet[]={
                Headgear_lxWS_H_cloth_5_A,
                Headgear_lxWS_H_cloth_5_B,
                Headgear_lxWS_H_cloth_5_C
            };
        };
    };
    class backpacks:backpacks{/* */}
    class weaponsDefault:weapons{
        Rifle[] = {_AK_12,_AKM,_AKS};
        GLRifle[] = {_AK_12_GL};
        /* */
    };
    class weaponsFullRifle:weapons{
        Rifle[] = {_SLR,_SLR_wood, SLR_camo};
        GLRifle[] = {_SLR_GL};
        /* */
    };
    //=================
    /* */
};

This is not a complete fix, for example it's hard to give a separate choice of primary, secondary, and launcher. This could be fixed by organizing the gear differently, the layout I used is kinda arbitrary.