diff --git a/ChapterMaster.yyp b/ChapterMaster.yyp index df4dcf6035..d94c38ee1a 100644 --- a/ChapterMaster.yyp +++ b/ChapterMaster.yyp @@ -451,6 +451,7 @@ {"$GMIncludedFile":"","%Name":"formation15.png","CopyToMask":-1,"filePath":"datafiles/images/ui","name":"formation15.png","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"formation16.png","CopyToMask":-1,"filePath":"datafiles/images/ui","name":"formation16.png","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"formation17.png","CopyToMask":-1,"filePath":"datafiles/images/ui","name":"formation17.png","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, + {"$GMIncludedFile":"","%Name":"formation18.png","CopyToMask":-1,"filePath":"datafiles/images/ui","name":"formation18.png","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"formation2.png","CopyToMask":-1,"filePath":"datafiles/images/ui","name":"formation2.png","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"formation3.png","CopyToMask":-1,"filePath":"datafiles/images/ui","name":"formation3.png","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"formation4.png","CopyToMask":-1,"filePath":"datafiles/images/ui","name":"formation4.png","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, @@ -567,6 +568,7 @@ {"$GMIncludedFile":"","%Name":"equal_scouts.json","CopyToMask":-1,"filePath":"datafiles/main/squads","name":"equal_scouts.json","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"equal_specialists.json","CopyToMask":-1,"filePath":"datafiles/main/squads","name":"equal_specialists.json","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"equal_spescout.json","CopyToMask":-1,"filePath":"datafiles/main/squads","name":"equal_spescout.json","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, + {"$GMIncludedFile":"","%Name":"lightning_warriors.json","CopyToMask":-1,"filePath":"datafiles/main/squads","name":"lightning_warriors.json","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"version.json","CopyToMask":-1,"filePath":"datafiles/main","name":"version.json","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"data.json","CopyToMask":-1,"filePath":"datafiles/main/visual_sets/Company_marks_roman_right_knee","name":"data.json","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, {"$GMIncludedFile":"","%Name":"12.png","CopyToMask":-1,"filePath":"datafiles/main/visual_sets/Company_marks_roman_right_knee/one","name":"12.png","resourceType":"GMIncludedFile","resourceVersion":"2.0",}, diff --git a/datafiles/data/mobility.json b/datafiles/data/mobility.json index 2bfe3862cc..4b6f2b5887 100644 --- a/datafiles/data/mobility.json +++ b/datafiles/data/mobility.json @@ -2,22 +2,65 @@ "Bike": { "abbreviation": "Bike", "damage_resistance_mod": { - "artifact": 10, - "master_crafted": 10, - "standard": 5 + "artifact": 40, + "master_crafted": 30, + "standard": 20 }, "description": "A robust bike that can propel an Astartes at very high speeds. Boasts highly responsive controls that allow for fluid movement on the battlefield and respectable Twin-Linked Bolters for offensive action.", "hp_mod": { "artifact": 35, - "master_crafted": 25, + "master_crafted": 30, "standard": 25 }, + "ranged_hands": -0.5, + "melee_mod":{ + "artifact": 30, + "master_crafted": 25, + "standard": 20 + }, + "special_properties":[ + "Speed Force" + ], + "tags":[ + "bike" + ], "second_profiles": [ "Twin Linked Bolters" ], "value": 35, "requires_to_forge": ["combi_1"] }, + "Attack Bike": { + "abbreviation": "At Bike", + "damage_resistance_mod": { + "artifact": 40, + "master_crafted": 30, + "standard": 25 + }, + "description": "A robust bike that can propel an Astartes at very high speeds. Boasts highly responsive controls that allow for fluid movement on the battlefield and respectable Twin-Linked Bolters for offensive action. Sports an additional sidecar with a heavy weapon of choice.", + "hp_mod": { + "artifact": 150, + "master_crafted": 120, + "standard": 100 + }, + "ranged_hands": 1, + "melee_mod":{ + "artifact": 20, + "master_crafted": 15, + "standard": 10 + }, + "special_properties": [ + "Speed Force (Ranged)" + ], + "second_profiles": [ + "Twin Linked Bolters" + ], + "tags":[ + "bike", "sf_ranged" + ], + "value": 95, + "requires_to_forge": ["combi_1"] + }, "Conversion Beamer Pack": { "abbreviation": "CnvBmr", "buyable": false, diff --git a/datafiles/images/ui/formation18.png b/datafiles/images/ui/formation18.png new file mode 100644 index 0000000000..d1a2d2d1f8 Binary files /dev/null and b/datafiles/images/ui/formation18.png differ diff --git a/datafiles/main/chapters/1.JSON b/datafiles/main/chapters/1.JSON index 96f484826a..a4aba58ac6 100644 --- a/datafiles/main/chapters/1.JSON +++ b/datafiles/main/chapters/1.JSON @@ -1705,7 +1705,7 @@ }, "option": { "wep2": [ - [["Combiplasma", "Plasma Gun", "Meltagun"], 1] + [["Hand Flamer", "Grav-Pistol", "Infernus Pistol", "Plasma Pistol", "Volkite Serpenta"], 1] ] } } @@ -1722,7 +1722,7 @@ }, "option": { "wep2": [ - [["Flamer", "Grav-Gun", "Meltagun", "Plasma Gun", "Plasma Pistol"], 2] + [["Hand Flamer", "Grav-Pistol", "Infernus Pistol", "Plasma Pistol"], 2] ] } } @@ -1771,6 +1771,7 @@ "role": "Ravenwing Ancient", "loadout": { "required": { + "wep2": ["Power Axe",1], "mobi": ["Bike", 1] } } @@ -1781,6 +1782,7 @@ "role": "Librarian", "loadout": { "required": { + "wep2": ["", 0], "mobi": ["Bike", 1] } } @@ -1802,7 +1804,7 @@ "loadout": { "required": { "wep1": ["Relic Blade", 1], - "wep2": ["Plasma Pistol", 1], + "wep2": ["", 0], "gear": ["Iron Halo", 1], "armour": ["Artificer Armour", 1], "mobi": ["Bike", 1] @@ -1815,13 +1817,13 @@ "role": "Black Knight", "loadout": { "required": { - "wep1": ["Chainsword", 4], + "wep1": ["Power Sword", 4], "wep2": ["Bolt Pistol", 2], "mobi": ["Bike", 4] }, "option": { "wep2": [ - [["Flamer", "Grav-Gun", "Meltagun", "Plasma Gun", "Plasma Pistol"], 2] + [["Hand Flamer", "Grav-Pistol", "Infernus Pistol", "Plasma Pistol", "Volkite Serpenta"], 2] ] } } diff --git a/datafiles/main/chapters/34.json b/datafiles/main/chapters/34.json index 2dc7a424f8..b8d075f747 100644 --- a/datafiles/main/chapters/34.json +++ b/datafiles/main/chapters/34.json @@ -1,5 +1,6 @@ { "chapter": { + "id": 34, "name": "Flesh Tearers", "mutations": { "betchers": 0.0, @@ -38,7 +39,6 @@ "", "" ], - "id": 34, "chapter_master": { "ranged": 1.0, "specialty": 2.0, diff --git a/datafiles/main/chapters/template.JSON b/datafiles/main/chapters/template.JSON index 7c64b377f4..17d46d21c4 100644 --- a/datafiles/main/chapters/template.JSON +++ b/datafiles/main/chapters/template.JSON @@ -1,309 +1,1489 @@ { - "chapter": { - "id": 1, - "name": "Chapter Name", - "flavor": "Chapter Lore", - "origin": 3, // 1 - Founding, 2 - Successor, 3 - Other/non-canon/fanmade, 4 - Custom - "points": 150, - "founding": 0, // The id of the founding chapter, 0 for unknown or none, 10 for random - "splash": 1, // in \images\creation\chapters\splash folder, the img number, 1 being Dark Angels etc. - "icon_name": "unknown", - "fleet_type": 1, // 1= Homeworld, 2 = Fleet based, 3 = Penitent - "strength": 5, // 1-10 - "purity": 5, // 1-10 - "stability": 50, // 1-99 - "cooperation": 5, // 1-10 - "homeworld_exists": 1, // 1 = true - "recruiting_exists": 1, // 1 = true - "homeworld_rule": 1, // 1 = Govenor, 2 = Countries, 3 = Personal Rule - "homeworld": "Hive", // one of "Lava" "Desert" "Forge" "Hive" "Death" "Agri" "Feudal" "Temperate" "Ice" "Dead" "Shrine" - "homeworld_name": "", // Homeworld Planet name, leave blank to autogenerate - "recruiting": "Death", // one of "Lava" "Desert" "Forge" "Hive" "Death" "Agri" "Feudal" "Temperate" "Ice" "Dead" "Shrine" - "recruiting_name": "", // Recruiting Planet name, leave blank to autogenerate - "discipline": "librarius", // one of 'default' 'biomancy' 'pyromancy' 'telekinesis' 'rune_magic' - "aspirant_trial": "SURVIVAL", // one of "BLOODDUEL" "HUNTING" "SURVIVAL" "EXPOSURE" "KNOWLEDGE" "CHALLENGE" "APPRENTICESHIP" - "advantages": [ - "", // leave the first entry blank for now - "", - "", - "", - "", - "", - "", - "", - "" + "chapter": + { + "id": 0, //number for your JSON file to be referenced in game files + "name": "", //name of the chapter + "flavor": "", //the short description that appears on popup when you hover over a chapter image + "battle_cry": "", //this is where you cry. I mean this is where you say what are you crying about. I mean... fuck it just type shit here to scream at people + "origin": "", //[1] Founding [2] Successor [3] Other [4] Custom + "points": 0, //creation points, feel free to cheat here for more wild stuff in custom if you don't like doing it via files + "founding": 0, //ID of the progenitor chapter, as per fiels in datafiles/main/chapters + "successors": 0, //write how many successors your chapter has. Go bonkers. Show them you got the seed to have 666 successors + "splash": 0, //the more artsy image on the left side of selection screen. Highly advised to use the same number as chapter ID + "icon_name": "", //name for the chapter icon you'll be using. Standing + "fleet_type": 0, //[1] Homeworld [2] Fleetbased [3] Penitent + "strength": 0, //1-10 + "purity": 0, //1-10 + "stability": 0, //1-99 + "cooperation": 0, //1-10 + "homeworld_exists": 1, //only is there for an obscure bit of code, but to avoid bugs and crashes keep it as is until we fix it + "recruiting_exists": 1, //same here + "home_warp": 1, //[0] low/no connections [1] connected [2] warp hub + "home_planets": 1, //how many planets in home system, [0]=1 [3]=4 + "homeworld": "Forge", //Homeworld planet type "Lava" "Desert" "Forge" "Hive" "Death" "Agri" "Feudal" "Temperate" "Ice" "Dead" "Shrine" + "homeworld_rule": 1, //[1] Governor [2] Semi-independent [3] Direct Rule + "home_spawn_loc": 1, //unsure which is it, probably 0=fringe, 1=central + "homeworld_name": "", //name of the homeworld + "monastery_name": "", //name of the F-M structure on the homeplanet. + "recruiting": "", //Recruiting world planet type "Lava" "Desert" "Forge" "Hive" "Death" "Agri" "Feudal" "Temperate" "Ice" "Dead" "Shrine" + "recruiting_name": "", //recruiting world name + "recruit_home_relationship": 1, //[0] same planet [1] same system [2] different system + "discipline": "librarius", //one of 'default' 'biomancy' 'pyromancy' 'telekinesis' 'rune_magic' + "aspirant_trial": 5, //[0]"BLOODDUEL" [1]"HUNTING" [2]"SURVIVAL" [3]"EXPOSURE" [4]"KNOWLEDGE" [5]"CHALLENGE" [6]"APPRENTICESHIP" + "advantages": //Self-Explanatory + [ + "", //first blank cause reasons + "", + "", + "", + "", + "", + "", + "", + "" + ], + "disadvantages": //S-E + [ + "", //first blank cause reasons + "", + "", + "", + "", + "", + "", + "", + "" + ], + "mutations": //defines which mutations are present + { + "mucranoid":0, + "zygote":0, + "secretions":0, + "doomed":0, + "occulobe":0, + "membrane":0, + "lyman":0, + "betchers":0, + "ossmodula":0, + "omophagea":0, + "voice":0, + "catalepsean":0, + "preomnor":0 + }, + "disposition": + [ + 0, //nothing, probably self + 0, //Progenitor + 0, //Imperium + 0, //Admech + 0, //=][= + 0, //Ecclesiarchy + 0, //Astartes + 0 //Nothing + ], + "colors": //main or default colours for your chapter. Check a pasted table or scr_colors_initialize.gml for the color array + { + "special": 0, // 0 - normal, 1 - Breastplate, 2 - Vertical, 3 - Quadrant + "main": "Dark Red", + "weapon": "Dark Red", + "pauldron_l": "Dark Red", + "pauldron_r": "Dark Red", + "trim": "Dark Gold", + "secondary": "Dark Gold", + "lens": "Red" + }, + "culture_styles": //write in the styles you want + [ + "" + ], + "chapter_master": + { + "name": "", //name for your chapter master, leave blank for random + "traits": //currently kinda not working, he's guaranteed to have Lead by Example though + [ + "" ], - "disadvantages": [ - "", // leave the first entry blank for now - "", - "", - "", - "", - "", - "", - "", - "" - ], - "colors": { - "main": "Black", - "secondary": "Black", - "pauldron_r": "Black", - "pauldron_l": "Black", - "trim": "Silver", - "lens": "Red", - "weapon": "Dark Red", - "special": 0, // 0 - normal, 1 - Breastplate, 2 - Vertical, 3 - Quadrant - //"trim_on": 0 // 0 no, 1 yes for pauldron trim colours. Trim colour will still be used for certain complex livery items - }, - "names": { - //Chapter Staff - "hchaplain": "", // Head Chaplain - "clibrarian": "", // Chief Librarian - "fmaster": "", // Forge Master - "hapothecary": "", // Head Apothecary - //Company Captains 1 - 10 - "honorcapt": "", - "watchmaster": "", - "arsenalmaster": "", - "admiral": "", - "marchmaster": "", - "ritesmaster": "", - "victualler": "", - "lordexec": "", - "relmaster": "", - "recruiter": "" - }, - "battle_cry": "For the Emperor!", - "squad_distribution": 0, // 0 if no, 1 if yes. If yes, will distribute specialist roles like Assaults and Devastators equally between companies. Otherwise all Assaults go in Company 8 and all Devastators in Company 9 - "load_to_ships": { - "escort_load": 2, // 0 no, 2 yes, 1 doesnt do anything :) - "split_scouts": 0, // 0 no, 1 yes. If yes, splits scouts between ships equally. Otherwise all scouts are kept on the homeworld. - "split_vets": 0 // 0 no, 1 yes. If yes, all veterans are distrubuted equally between ships. Otherwise all veterans are kept in the flagship - }, - "successors": 0, //total number of successor chapters - "mutations": { - "preomnor": 0, - "voice": 0, - "doomed": 0, - "lyman": 0, - "omophagea": 0, - "ossmodula": 0, - "membrane": 0, - "zygote": 0, - "betchers": 0, - "catalepsean": 0, - "secretions": 0, - "occulobe": 0, - "mucranoid": 0 - }, - "disposition": [ - 0, // nothing - 0, // Progenitor faction - 50, // Imperium - 50, // Admech - 50, //Inquisition - 50, // Ecclesiarchy - 50, // Astartes - 0 // nothing - ], - "chapter_master": { - "name": "", - "specialty": 1, //1 Leader, 2 Champion, 3 Psyker, - "melee": 1, // 1 twin power fists ... 8 force staff - "ranged": 1, // 1 boltstorm gauntlets ... 7 storm shield - // All chapter masters have the trait `Lead by Example` by default - "traits": [ - "" - ], - "gear": "", - "mobi": "", - "armour": "" // default is Artificer armour, only needed to set if changing to something else. - }, - "artifact": [ + "specialty": 1, //1 Leader, 2 Champion, 3 Psyker + "melee": 1, //[1]twin power fists [2]twin lighting claws [3]Relic blade [4]Thunder Hammer [5]Power Sword [6]Power axe [7]Eviscerator [8]Force staff + "ranged": 1, //[1]boltstorm gauntlet [2]Infernus pistol [3]Plasma pistol [4]Plasma gun [5]MC Heavy Bolter [6]Mc Meltagun [7]Storm Shield + "armour": "", //default is Artificer, dont' need to put anything unless you want something different + "gear": "" //default is Iron Halo, same as above + }, + "artifact": //copy and paste the template below as many times as you like + [ + { + "name": "Artifact Name", //the most holy name of your long schlong weapon + "description": "Artefact Lore", //write fun stuff + "base_weapon_type": "Power Sword", //doesn't matter if it's armour, gear, mobi or anything else, you always use base_weapon_type to define it + "slot": "wep1" //leave empty if you want it to be stored in the Librarium without assignment + } + ], + "names": + { + //Chapter Staff + "hchaplain": "", // Head Chaplain + "clibrarian": "", // Chief Librarian + "fmaster": "", // Forge Master + "hapothecary": "", // Head Apothecary + //Company Captains 1 - 10 + "honorcapt": "", + "watchmaster": "", + "arsenalmaster": "", + "admiral": "", + "marchmaster": "", + "ritesmaster": "", + "victualler": "", + "lordexec": "", + "relmaster": "", + "recruiter": "" + }, + "company_titles": + [ + "", //leave blank cause reasons + "", //1st Company + "", //2nd + "", //3rd + "", //4th + "", //5th + "", //6th + "", //7th + "", //8th + "", //9th + "" //10th + ], + "equal_specialists": 0,// 0 if no, 1 if yes. If yes, will distribute specialist roles like Assaults and Devastators equally between companies. Otherwise all Assaults go in Company 8 and all Devastators in Company 9 + "flagship_name": "Flagship Name", // leave blank to autogenerate + "load_to_ships": + { + "escort_load": 2, // 0 no, 2 yes, 1 doesnt do anything :) + "split_scouts": 0, // 0 no, 1 yes. If yes, splits scouts between ships equally. Otherwise all scouts are kept on the homeworld. + "split_vets": 0 // 0 no, 1 yes. If yes, all veterans are distrubuted equally between ships. Otherwise all veterans are kept in the flagship + }, + "full_liveries": + [ + {//[1] Chapter Master + "weapon_primary":11, + "metallic_trim":11, + "weapon_secondary":20, + "is_changed":true, + "left_arm":20, + "left_backpack":20, + "left_chest":20, + "right_arm":20, + "right_backpack":20, + "right_chest":20, + "left_hand":11, + "left_head":20, + "left_leg_knee":20, + "right_hand":11, + "eye_lense":9, + "right_head":20, + "left_leg_lower":20, + "left_leg_upper":20, + "left_muzzle":20, + "right_leg_knee":20, + "left_pauldron":11, + "right_leg_lower":20, + "right_leg_upper":20, + "right_muzzle":20, + "right_pauldron":11, + "left_thorax":20, + "left_trim":20, + "right_thorax":20, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[2] Brown dude, so far unknown what is this defining + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":14, + "left_backpack":14, + "left_chest":14, + "right_arm":14, + "right_backpack":14, + "right_chest":14, + "left_hand":14, + "left_head":14, + "left_leg_knee":14, + "right_hand":14, + "eye_lense":22, + "right_head":14, + "left_leg_lower":14, + "left_leg_upper":14, + "left_muzzle":14, + "right_leg_knee":14, + "left_pauldron":14, + "right_leg_lower":14, + "right_leg_upper":14, + "right_muzzle":14, + "right_pauldron":14, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[3] Honour Guard + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":20, + "left_backpack":20, + "left_chest":20, + "right_arm":20, + "right_backpack":20, + "right_chest":20, + "left_hand":20, + "left_head":20, + "left_leg_knee":20, + "right_hand":20, + "eye_lense":9, + "right_head":20, + "left_leg_lower":20, + "left_leg_upper":20, + "left_muzzle":20, + "right_leg_knee":20, + "left_pauldron":20, + "right_leg_lower":20, + "right_leg_upper":20, + "right_muzzle":20, + "right_pauldron":20, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[4] Veteran + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":20, + "left_backpack":11, + "left_chest":8, + "right_arm":20, + "right_backpack":11, + "right_chest":8, + "left_hand":11, + "left_head":20, + "left_leg_knee":20, + "right_hand":11, + "eye_lense":9, + "right_head":20, + "left_leg_lower":11, + "left_leg_upper":11, + "left_muzzle":20, + "right_leg_knee":20, + "left_pauldron":11, + "right_leg_lower":11, + "right_leg_upper":11, + "right_muzzle":20, + "right_pauldron":11, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[5] Terminator + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":11, + "left_backpack":11, + "left_chest":11, + "right_arm":11, + "right_backpack":11, + "right_chest":11, + "left_hand":20, + "left_head":8, + "left_leg_knee":11, + "right_hand":20, + "eye_lense":9, + "right_head":8, + "left_leg_lower":11, + "left_leg_upper":11, + "left_muzzle":20, + "right_leg_knee":20, + "left_pauldron":20, + "right_leg_lower":11, + "right_leg_upper":11, + "right_muzzle":20, + "right_pauldron":20, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[6] Captain + "weapon_primary":11, + "metallic_trim":11, + "weapon_secondary":20, + "is_changed":true, + "left_arm":20, + "left_backpack":20, + "left_chest":20, + "right_arm":20, + "right_backpack":20, + "right_chest":20, + "left_hand":20, + "left_head":20, + "left_leg_knee":11, + "right_hand":20, + "eye_lense":9, + "right_head":20, + "left_leg_lower":20, + "left_leg_upper":20, + "left_muzzle":20, + "right_leg_knee":11, + "left_pauldron":20, + "right_leg_lower":20, + "right_leg_upper":20, + "right_muzzle":20, + "right_pauldron":20, + "left_thorax":0, + "left_trim":11, + "right_thorax":0, + "right_trim":11, + "company_marks":0, + "company_marks_loc":0 + }, + {//[7] Dreadnought + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":20, + "left_backpack":20, + "left_chest":20, + "right_arm":20, + "right_backpack":20, + "right_chest":20, + "left_hand":20, + "left_head":20, + "left_leg_knee":20, + "right_hand":20, + "eye_lense":9, + "right_head":20, + "left_leg_lower":20, + "left_leg_upper":20, + "left_muzzle":20, + "right_leg_knee":20, + "left_pauldron":20, + "right_leg_lower":20, + "right_leg_upper":20, + "right_muzzle":20, + "right_pauldron":20, + "left_thorax":20, + "left_trim":20, + "right_thorax":20, + "right_trim":20, + "company_marks":20, + "company_marks_loc":0 + }, + {//[8] Champion + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":20, + "left_backpack":20, + "left_chest":11, + "right_arm":20, + "right_backpack":20, + "right_chest":11, + "left_hand":20, + "left_head":11, + "left_leg_knee":20, + "right_hand":20, + "eye_lense":22, + "right_head":11, + "left_leg_lower":11, + "left_leg_upper":11, + "left_muzzle":11, + "right_leg_knee":20, + "left_pauldron":20, + "right_leg_lower":11, + "right_leg_upper":11, + "right_muzzle":11, + "right_pauldron":20, + "left_thorax":0, + "left_trim":11, + "right_thorax":0, + "right_trim":11, + "company_marks":0, + "company_marks_loc":0 + }, + {//[9] Tactical + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":11, + "left_backpack":11, + "left_chest":11, + "right_arm":11, + "right_backpack":11, + "right_chest":11, + "left_hand":11, + "left_head":11, + "left_leg_knee":11, + "right_hand":11, + "eye_lense":22, + "right_head":11, + "left_leg_lower":11, + "left_leg_upper":11, + "left_muzzle":11, + "right_leg_knee":11, + "left_pauldron":11, + "right_leg_lower":11, + "right_leg_upper":11, + "right_muzzle":11, + "right_pauldron":11, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[10] Devastator + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":11, + "left_backpack":11, + "left_chest":11, + "right_arm":11, + "right_backpack":11, + "right_chest":11, + "left_hand":11, + "left_head":20, + "left_leg_knee":11, + "right_hand":11, + "eye_lense":22, + "right_head":20, + "left_leg_lower":11, + "left_leg_upper":11, + "left_muzzle":11, + "right_leg_knee":11, + "left_pauldron":11, + "right_leg_lower":11, + "right_leg_upper":11, + "right_muzzle":11, + "right_pauldron":11, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[11] Assault + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":11, + "left_backpack":11, + "left_chest":11, + "right_arm":11, + "right_backpack":11, + "right_chest":11, + "left_hand":11, + "left_head":11, + "left_leg_knee":11, + "right_hand":11, + "eye_lense":22, + "right_head":11, + "left_leg_lower":11, + "left_leg_upper":11, + "left_muzzle":20, + "right_leg_knee":11, + "left_pauldron":11, + "right_leg_lower":11, + "right_leg_upper":11, + "right_muzzle":20, + "right_pauldron":11, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[12] Ancient + "weapon_primary":20, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":11, + "left_backpack":20, + "left_chest":8, + "right_arm":11, + "right_backpack":20, + "right_chest":8, + "left_hand":20, + "left_head":11, + "left_leg_knee":20, + "right_hand":20, + "eye_lense":9, + "right_head":11, + "left_leg_lower":11, + "left_leg_upper":11, + "left_muzzle":8, + "right_leg_knee":20, + "left_pauldron":11, + "right_leg_lower":11, + "right_leg_upper":11, + "right_muzzle":8, + "right_pauldron":11, + "left_thorax":0, + "left_trim":8, + "right_thorax":0, + "right_trim":8, + "company_marks":0, + "company_marks_loc":0 + }, + {//[13] Scout + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":11, + "left_backpack":11, + "left_chest":11, + "right_arm":11, + "right_backpack":11, + "right_chest":11, + "left_hand":11, + "left_head":11, + "left_leg_knee":11, + "right_hand":11, + "eye_lense":22, + "right_head":11, + "left_leg_lower":11, + "left_leg_upper":11, + "left_muzzle":11, + "right_leg_knee":11, + "left_pauldron":11, + "right_leg_lower":11, + "right_leg_upper":11, + "right_muzzle":11, + "right_pauldron":11, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[14] Green dude + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":23, + "left_backpack":23, + "left_chest":23, + "right_arm":23, + "right_backpack":23, + "right_chest":23, + "left_hand":23, + "left_head":23, + "left_leg_knee":23, + "right_hand":23, + "eye_lense":22, + "right_head":23, + "left_leg_lower":23, + "left_leg_upper":23, + "left_muzzle":23, + "right_leg_knee":23, + "left_pauldron":23, + "right_leg_lower":23, + "right_leg_upper":23, + "right_muzzle":23, + "right_pauldron":23, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[15] Chaplain + "weapon_primary":8, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":8, + "left_backpack":8, + "left_chest":8, + "right_arm":8, + "right_backpack":8, + "right_chest":8, + "left_hand":8, + "left_head":8, + "left_leg_knee":8, + "right_hand":8, + "eye_lense":9, + "right_head":8, + "left_leg_lower":8, + "left_leg_upper":8, + "left_muzzle":8, + "right_leg_knee":8, + "left_pauldron":11, + "right_leg_lower":8, + "right_leg_upper":8, + "right_muzzle":8, + "right_pauldron":8, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[16] Apothecary + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":18, + "left_backpack":18, + "left_chest":18, + "right_arm":18, + "right_backpack":18, + "right_chest":18, + "left_hand":18, + "left_head":18, + "left_leg_knee":18, + "right_hand":18, + "eye_lense":9, + "right_head":18, + "left_leg_lower":18, + "left_leg_upper":18, + "left_muzzle":18, + "right_leg_knee":18, + "left_pauldron":11, + "right_leg_lower":18, + "right_leg_upper":18, + "right_muzzle":18, + "right_pauldron":18, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[17] Techmarine + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":9, + "left_backpack":9, + "left_chest":9, + "right_arm":9, + "right_backpack":9, + "right_chest":9, + "left_hand":9, + "left_head":9, + "left_leg_knee":9, + "right_hand":9, + "eye_lense":22, + "right_head":9, + "left_leg_lower":9, + "left_leg_upper":9, + "left_muzzle":9, + "right_leg_knee":9, + "left_pauldron":11, + "right_leg_lower":9, + "right_leg_upper":9, + "right_muzzle":9, + "right_pauldron":9, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[18] Librarian + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":34, + "left_backpack":34, + "left_chest":34, + "right_arm":34, + "right_backpack":34, + "right_chest":34, + "left_hand":34, + "left_head":34, + "left_leg_knee":34, + "right_hand":34, + "eye_lense":22, + "right_head":34, + "left_leg_lower":34, + "left_leg_upper":34, + "left_muzzle":34, + "right_leg_knee":34, + "left_pauldron":1, + "right_leg_lower":34, + "right_leg_upper":34, + "right_muzzle":34, + "right_pauldron":34, + "left_thorax":0, + "left_trim":0, + "right_thorax":0, + "right_trim":0, + "company_marks":0, + "company_marks_loc":0 + }, + {//[19] Sergeant + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":11, + "left_backpack":11, + "left_chest":11, + "right_arm":11, + "right_backpack":11, + "right_chest":11, + "left_hand":11, + "left_head":11, + "left_leg_knee":11, + "right_hand":11, + "eye_lense":22, + "right_head":11, + "left_leg_lower":11, + "left_leg_upper":11, + "left_muzzle":11, + "right_leg_knee":20, + "left_pauldron":20, + "right_leg_lower":11, + "right_leg_upper":11, + "right_muzzle":11, + "right_pauldron":20, + "left_thorax":0, + "left_trim":11, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[20] Veteran Sergeant + "weapon_primary":8, + "metallic_trim":20, + "weapon_secondary":11, + "is_changed":true, + "left_arm":20, + "left_backpack":8, + "left_chest":8, + "right_arm":20, + "right_backpack":8, + "right_chest":8, + "left_hand":20, + "left_head":11, + "left_leg_knee":20, + "right_hand":20, + "eye_lense":22, + "right_head":11, + "left_leg_lower":8, + "left_leg_upper":8, + "left_muzzle":11, + "right_leg_knee":20, + "left_pauldron":11, + "right_leg_lower":8, + "right_leg_upper":8, + "right_muzzle":11, + "right_pauldron":20, + "left_thorax":0, + "left_trim":11, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + }, + {//[21] Blue Chest + "weapon_primary":11, + "metallic_trim":20, + "weapon_secondary":20, + "is_changed":true, + "left_arm":31, + "left_backpack":31, + "left_chest":31, + "right_arm":31, + "right_backpack":31, + "right_chest":31, + "left_hand":31, + "left_head":31, + "left_leg_knee":31, + "right_hand":31, + "eye_lense":22, + "right_head":31, + "left_leg_lower":31, + "left_leg_upper":31, + "left_muzzle":31, + "right_leg_knee":31, + "left_pauldron":31, + "right_leg_lower":31, + "right_leg_upper":31, + "right_muzzle":31, + "right_pauldron":31, + "left_thorax":0, + "left_trim":20, + "right_thorax":0, + "right_trim":20, + "company_marks":0, + "company_marks_loc":0 + } + ], + "complex_livery_data": //helm_pattern - paint setup, [0] Full colour primary [1] Stripe in the middle secondary [2] Upper main lower second [3] same as 1 for some reason + { + "sgt": + { + "helm_detail":20, + "helm_lens":9, + "helm_pattern":0, + "helm_primary":20, + "helm_secondary":0 + }, + "veteran": + { + "helm_detail":20, + "helm_lens":22, + "helm_pattern":1, + "helm_primary":11, + "helm_secondary":8 + }, + "captain": + { + "helm_detail":20, + "helm_lens":9, + "helm_pattern":1, + "helm_primary":20, + "helm_secondary":11 + }, + "vet_sgt": + { + "helm_detail":20, + "helm_lens":9, + "helm_pattern":1, + "helm_primary":8, + "helm_secondary":11 + } + }, + "company_liveries"://if you put -1 as value that means there is no change from the base colour scheme. Beware that compnay markings overwrite specialists as well + [ + {// HQ + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 1 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 2 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 3 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 4 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 5 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 6 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 7 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 8 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 9 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + }, + {// 10 + "weapon_primary":-1, + "metallic_trim":-1, + "weapon_secondary":-1, + "is_changed":true, + "left_arm":-1, + "left_backpack":-1, + "left_chest":-1, + "right_arm":-1, + "right_backpack":-1, + "right_chest":-1, + "left_hand":-1, + "left_head":-1, + "left_leg_knee":-1, + "right_hand":-1, + "eye_lense":-1, + "right_head":-1, + "left_leg_lower":-1, + "left_leg_upper":-1, + "left_muzzle":-1, + "right_leg_knee":-1, + "left_pauldron":-1, + "right_leg_lower":-1, + "right_leg_upper":-1, + "right_muzzle":-1, + "right_pauldron":-1, + "left_thorax":-1, + "left_trim":-1, + "right_thorax":-1, + "right_trim":-1, + "company_marks":-1, + "company_marks_loc":-1 + } + ], + "custom_roles": + { + "chapter_master"://feel free to add specific equipment, name and whatnot + { + "name": "" + } + /*"template": + { + "wep1":"", + "wep2":"", + "gear":"", + "armour":"", + "name":"template", + "mobi":"" + },*/ + }, + "extra_equipment": + [ + // ["Bolter", 10] is an example of how this is called + ], + "extra_marines": //default is 100 marines per company, the first company works via adding veterans or terminators + {//beware, only adds Tacticals + "second": 0, + "third": 0, + "fourth": 0, + "fifth": 0, + "sixth": 0, + "seventh": 0, + "eighth": 0, + "ninth": 0, + "tenth": 0 + }, + "extra_ships": + /** + * * Default fleet composition + * * Homeworld + * - 2 Battle Barges, 8 Strike cruisers, 7 Gladius, 3 Hunters + * * Fleet based and Penitent + * - 4 Battle Barges, 3 Strike Cruisers, 7 Gladius, 3 Hunters + * + * use negative numbers to subtract ships + * * Stacks with advantages/disadvantages + */ + { + "gloriana": 0, + "battle_barges": 0, + "strike_cruisers": 0, + "gladius": 0, + "hunters": 0 + }, + /** + * * Default HQ Layout (Does not include company specialists) + * - 8 Chaplains, 8 Techmarines, 8 Apothecary, 2 Epistolary (librarian), + * - 2 Codiciery, 4 Lexicanum + * * Default Company specialists (divided based on `load_to_ships.split_vets` setting) + * - 20 Terminators, 85 Veterans, 20 Devastators, 20 Assault + * Use negative numbers to subtract + * Stacks with advantages/disadvantages + */ + "extra_specialists": + { + "chaplains": 0,//adds them to the Reclusiam + "chaplains_per_company": 0,//adds them to company, X per company + "techmarines": 0, + "techmarines_per_company": 0, + "apothecary": 0, + "apothecary_per_company": 0, + "epistolary": 0, + "epistolary_per_company": 0, + "codiciery": 0, + "lexicanum": 0, + "terminator": 0, + "assault": 0, + "veteran": 0, + "devastator": 0, + "dreadnought": 0 + }, + "extra_vehicles": + { + "rhino": 0, + "predator": 0, + "land_speeder": 0, + "whirlwind": 0, + "land_raider": 0 + }, + "companies":// you may use this to override default company manpower and vehicles on start. + { + "first": "", + "second":"", + "third":"", + "fourth":"", + "fifth":"", + "sixth":"", + "seventh":"", + "eighth":"", + "ninth":"", + "tenth":"" + }, + "custom_advisors"://here you define what image [by typing the png name] are your custom advisors using. If you add nothing here, the game uses defaults + {/* + "forge_master": , + "apothecary": , + "librarian": , + "chaplain": , + "admiral": , + "recruiter": + */}, + "squad_name": "Squad", //how will any squad be referenced + "custom_squads": + {/*Example how this works, showcasing the Deathwing Terminator Squads + "deathwing_squad": + [ + // Terminator Sergeant + [ + "Veteran Sergeant", + { + "max": 1, + "min": 1, + "role": "Deathwing Sergeant", + "loadout": { - "name": "Artifact Name", - "description": "Artefact Lore", - "base_weapon_type": "Power Sword", - "slot": "wep1" + "required": + { + "wep1": + [ + "Power Sword", + 1 + ] + } } + } ], - "company_titles": [ - "", - "1st Company", - "2nd Company", - "3rd Company", - "4th Company", - "5th Company", - "6th Company", - "7th Company", - "8th Company", - "9th Company", - "10th Company" - ], - "flagship_name": "Flagship Name", // leave blank to autogenerate - /** - * * Default fleet composition - * * Homeworld - * - 2 Battle Barges, 8 Strike cruisers, 7 Gladius, 3 Hunters - * * Fleet based and Penitent - * - 4 Battle Barges, 3 Strike Cruisers, 7 Gladius, 3 Hunters - * - * use negative numbers to subtract ships - * * Stacks with advantages/disadvantages - */ - "extra_ships": { - "battle_barges": 0, - "gladius": 0, - "strike_cruisers": 0, - "hunters": 0 - }, - /** - * * Default HQ Layout (Does not include company specialists) - * - 8 Chaplains, 8 Techmarines, 8 Apothecary, 2 Epistolary (librarian), - * - 2 Codiciery, 4 Lexicanum - * * Default Company specialists (divided based on `load_to_ships.split_vets` setting) - * - 20 Terminators, 85 Veterans, 20 Devastators, 20 Assault - * Use negative numbers to subtract - * * Stacks with advantages/disadvantages - */ - "extra_specialists": { - "chaplains": 0, - "chaplains_per_company": 0, - "techmarines": 0, - "techmarines_per_company": 0, - "apothecary": 0, - "apothecary_per_company": 0, - "epistolary": 0, - "epistolary_per_company": 0, - "codiciery": 0, - "lexicanum": 0, - "terminator": 0, - "assault": 0, - "veteran": 0, - "devastator": 0, - "dreadnought": 0 - }, - /** - * * Default Marine strength - * - 100 marines per company - * use negative numbers to subtract - * * Stacks with strength for non-founding chapters only - */ - "extra_marines": { - "second": 0, - "third": 0, - "fourth": 0, - "fifth": 0, - "sixth": 0, - "seventh": 0, - "eighth": 0, - "ninth": 0, - "tenth": 0 - }, - /** - * All vehicles are added to the 10th company at the start of the game - */ - "extra_vehicles": { - "rhino": 0, - "whirlwind": 0, - "predator": 0, - "land_raider": 0, - "land_speeder": 0 - }, - /** Add extra starting items ["Item Name", Number to add] */ - "extra_equipment": [ - // [ - // "Bolter", - // 20 - // ] + // Terminator + [ + "Terminator", + { + "max": 4, + "min": 2, + "role": "Deathwing Terminator", + "loadout": + { + "required": + { + "wep1": + [ + "", + 0 + ], + "wep2": + [ + "Storm Bolter", + 3 + ] + }, + "option": + { + "wep1": + [ + [ + [ + "Power Fist", + "Chainfist" + ], + 4 + ] + ], + "wep2": + [ + [ + [ + "Heavy Flamer", + "Heavy Flamer", + "Heavy Flamer", + "Assault Cannon", + "Assault Cannon", + "Plasma Cannon" + ], + 1 + ] + ] + } + } + } ], - /** - * Provide a place to change the default name and equipment preferences of roles for this chapter - * `custom_roles` should be used for specialist/leadership type roles, - * To affect an entire squad, see `custom_squads` below - */ - "custom_roles": { - // "honour_guard": { - // "name": "Honour Guard", - // "wep1": "Power Sword", - // "wep2": "Bolter" - // }, - // "veteran": { - // "name": "Veteran", - // "wep1": "Chainaxe" - // }, - // "captain": { - // "name": "Captain", - // "wep1": "Power Sword" - // }, - // "tactical": { - // "name": "Tactical" - // }, - // "devastator": { - // "name": "Devastator" - // }, - // "assault": { - // "name": "Assault", - // "wep1": "Chainsword" - // }, - // "scout": { - // "name": "Scout", - // "wep1": "Sniper Rifle" - // }, - // "chaplain": { - // "name": "Chaplain", - // }, - // "techmarine": { - // "name": "Techmarine", - // "wep1": "Power Axe" - // }, - // "apothecary": { - // "name": "Apothecary", - // "wep1": "Power Axe" - // }, - // "librarian": { - // "name": "Librarian", - // "wep1": "Force Staff" - // }, - // "sergeant": { - // "name": "Sergeant", - // "wep1": "Chainaxe" - // }, - // "veteran_sergeant": { - // "name": "Veteran Sergeant", - // "wep1": "Chainaxe" - // } - }, - /** - * * Custom squad roles, loadouts and formations - * When companies are made, squads are formed based on these rules: - * - squad name: one of captain, terminator, terminator_assault, sternguard_veteran, - vanguard_veteran, devastator, tactical, assault, scout, scout_sniper - * - squad array layout [Role, Role, ...Role, type_data] - * - each element of the array is a default Role, and their settings. - - if you changed the role using `custom_roles` you need to reference the role with this new name - - for non-leader roles it is better to change the name of the role in the squad layout instead - * - unit layout [Role Name, Settings Struct] - * - settings struct: - * - max: The most amount of this unit is allowed per squad - * - min: The squad can't be formed unless at least 1 of this unit is in it - * - role: The name of the unit when it is a member of the squad. This is where you rename roles e.g. - "Terminator" > "Deathwing Terminator" - ** - loadout: Struct containing Required and Optional weaponry this unit can equip - a required loadout always follows this syntax :[,] - so "wep1":["Bolter",4], will mean four marines are always equipped with 4 bolters in the wep1 slot - * option loadouts follow the following syntax :[[],] - for example [["Flamer", "Meltagun"],1], means the role can have a max of one flamer or meltagun - [["Plasma Pistol","Bolt Pistol"], 4] means the role can have a mix of 4 plasma pistols and bolt pistols on top - of all required loadout options - - wep1: right hand weapon - - wep2: left hand weapon - - mobi: Mobility item, e.g. Jump Packs. - - armour: required armour - - gear: special equipment needed for certain roles, like a Roasrius or Narthecium - * - type_data: names the squad, allows certain formations - */ - "squad_name": "Squad", - "custom_squads": {} + [ + "type_data", + { + "display_data": "Deathwing Terminator Squad", + "formation_options": + [ + "terminator", + "veteran", + "assault", + "devastator", + "scout", + "tactical" + ], + "allowed_companies": [1] //this is specifically for when squads are supposed to spawn only in specific companies. Ignore for chapter-wide changes + } + ] + ]*/ } +/* * Custom squad roles, loadouts and formations + * When companies are made, squads are formed based on these rules: + * - squad name: one of captain, terminator, terminator_assault, sternguard_veteran, + vanguard_veteran, devastator, tactical, assault, scout, scout_sniper + * - squad array layout [Role, Role, ...Role, type_data] + * - each element of the array is a default Role, and their settings. + - if you changed the role using `custom_roles` you need to reference the role with this new name + - for non-leader roles it is better to change the name of the role in the squad layout instead + * - unit layout [Role Name, Settings Struct] + * - settings struct: + * - max: The most amount of this unit is allowed per squad + * - min: The squad can't be formed unless at least 1 of this unit is in it + * - role: The name of the unit when it is a member of the squad. This is where you rename roles e.g. + "Terminator" > "Deathwing Terminator" + ** - loadout: Struct containing Required and Optional weaponry this unit can equip + a required loadout always follows this syntax :[,] + so "wep1":["Bolter",4], will mean four marines are always equipped with 4 bolters in the wep1 slot + * option loadouts follow the following syntax :[[],] + for example [["Flamer", "Meltagun"],1], means the role can have a max of one flamer or meltagun + [["Plasma Pistol","Bolt Pistol"], 4] means the role can have a mix of 4 plasma pistols and bolt pistols on top + of all required loadout options + - wep1: right hand weapon + - wep2: left hand weapon + - mobi: Mobility item, e.g. Jump Packs. + - armour: required armour + - gear: special equipment needed for certain roles, like a Roasrius or Narthecium + * - type_data: names the squad, allows certain formations ? idk what that does yet*/ + } } \ No newline at end of file diff --git a/datafiles/main/squads/base_squads.json b/datafiles/main/squads/base_squads.json index 7163ec86e5..076374a477 100644 --- a/datafiles/main/squads/base_squads.json +++ b/datafiles/main/squads/base_squads.json @@ -7,27 +7,32 @@ "Champion": { "max": 1, "min": 0, - "role": "Company Champion" + "role": "Company Champion", + "alternative_roles": ["Champion"] }, "Apothecary": { "max": 1, "min": 0, - "role": "Company Apothecary" + "role": "Company Apothecary", + "alternative_roles": ["Apothecary"] }, "Chaplain": { "max": 1, "min": 0, - "role": "Company Chaplain" + "role": "Company Chaplain", + "alternative_roles": ["Chaplain"] }, "Ancient": { "max": 1, "min": 1, - "role": "Company Ancient" + "role": "Company Ancient", + "alternative_roles": ["Ancient"] }, "Veteran": { "max": 5, "min": 0, "role": "Company Veteran", + "alternative_roles": ["Veteran"], "loadout": { "required": { "wep1": ["", 0], @@ -43,12 +48,14 @@ "Techmarine": { "max": 2, "min": 0, - "role": "Company Techmarine" + "role": "Company Techmarine", + "alternative_roles": ["Techmarine"] }, "Librarian": { "max": 1, "min": 0, - "role": "Company Librarian" + "role": "Company Librarian", + "alternative_roles": ["Librarian"] }, "type_data": { "display_data": "Command {squad_name}", @@ -95,13 +102,20 @@ }, "option": { "wep1": [ - [["Power Fist", "Chainfist"], 4] - ], - "wep2": [ - ["{WEAPON_LIST_RANGED_COMBI}", 3], - ["{WEAPON_LIST_RANGED_HEAVY_TERMINATOR}", 1] + [["Power Fist", "Chainfist"], 7, {"wep2": "Storm Bolter"}] //if option group 3rd element is struct random pick ignores it ] - } + }, + "random_pick": [ + { + "wep1": ["Power Fist", "Chainfist"], + "wep2": "Storm Bolter", + "mobi": "Cyclone Missile System" + }, + { + "wep1": ["Power Fist", "Chainfist"], + "wep2": ["Heavy Flamer", "Assault Cannon"] + } + ] } }, "type_data": { @@ -136,14 +150,19 @@ "role": "Assault Terminator", "loadout": { "required": { - "wep1": ["Thunder Hammer", 1], - "wep2": ["Storm Shield", 1] + "wep1": ["", 0], + "wep2": ["", 0] }, - "option": { - "wep1": [ - [["Lightning Claw"], 3, { "wep2": "Lightning Claw" }] - ] - } + "random_pick": [ + { + "wep1": ["Thunder Hammer"], + "wep2": ["Storm Shield"] + }, + { + "wep1": ["Lightning Claw"], + "wep2": ["Lightning Claw"] + } + ] } }, "type_data": { @@ -210,14 +229,14 @@ }, "devastator_squad": { - "Devastator": { + "Devastator Marine": { "max": 9, "min": 4, + "role": "Devastator", "loadout": { "required": { "wep1": ["Bolter", 5], - "wep2": ["Combat Knife", 9], - "mobi": ["", 5] + "wep2": ["Combat Knife", 9] }, "option": { "wep1": [ @@ -255,9 +274,10 @@ }, "tactical_squad": { - "Tactical": { + "Tactical Marine": { "max": 9, "min": 4, + "role": "Tactical", "loadout": { "required": { "wep1": ["wep1[8]", 7], @@ -304,9 +324,10 @@ }, "assault_squad": { - "Assault": { + "Assault Marine": { "max": 9, "min": 4, + "role": "Assault", "loadout": { "required": { "wep1": ["wep1[10]", 5], @@ -358,6 +379,7 @@ "Scout": { "max": 9, "min": 4, + "role": "Scout", "loadout": { "required": { "wep1": ["", 0], @@ -390,6 +412,7 @@ "type_data": { "display_data": "Scout {squad_name}", "class": ["scout"], + "base": "scout", "formation_options": [ "scout", "tactical", @@ -398,39 +421,9 @@ ] } }, - "bike_squad": { - "Assault": { - "max": 9, - "min": 4, - "role": "Biker", - "loadout": { - "required": { - "wep1": ["", "max"], - "wep2": ["Chainsword", "max"], - "mobi": ["Bike", "max"] - } - } - }, - "Sergeant": { - "max": 1, - "min": 1, - "role": "Biker Sergeant", - "loadout": { - "required": { - "wep1": ["", "max"], - "wep2": ["Chainsword", "max"], - "mobi": ["Bike", 1] - } - } - }, - "type_data": { - "display_data": "Bike {squad_name}", - "class": ["bike"], - "formation_options": ["assault", "tactical"] - } - }, + "breacher_squad": { - "Tactical": { + "Tactical Marine": { "max": 9, "min": 4, "role": "Breacher", @@ -471,5 +464,89 @@ "display_data": "Breacher {squad_name}", "formation_options": ["tactical", "assault", "devastator", "scout"] } - } + }, + + "bike_squad": { + "Tactical": { + "max": 7, + "min": 1, + "role": "Biker", + "alternative_roles": ["Assault","Devastator","Tactical"], + "loadout": { + "required": { + "wep1": ["", 0], + "wep2": ["", 0], + "mobi": ["Bike", 7] + }, + "option": { + "wep1": [[["Chainsword","Chainaxe"],7,{"wep2":"Bolt Pistol"}]] + } + } + }, + "Sergeant": { + "max": 1, + "min": 1, + "role": "Biker Sergeant", + "loadout": { + "required": { + "wep1": ["", 0], + "wep2": ["", 0], + "mobi": ["Bike", 1] + }, + "option": { + "wep1": [[["Power Sword", "Power Spear", "Power Axe", "Power Fist", "Chainaxe", "Chainsword"],1]], + "wep2": [[["Volkite Serpenta", "Plasma Pistol", "Bolt Pistol", "Phobos Bolt Pistol", "Grav-Pistol", "Hand Flamer", "Infernus Pistol", "Ryza Plasma Pistol"],1]] + } + } + }, + "type_data": { + "display_data": "Bike {squad_name}", + "class": ["bike"], + "formation_options": ["biker","assault", "tactical","devastator","scout"], + "base": "biker" + } + }, + + + "attack_bike_squad": { + "Tactical": { + "max": 2, + "min": 1, + "role": "Attack Biker", + "alternative_roles": ["Devastator", "Assault", "Tactical"], + "loadout": { + "required": { + "wep1": ["", 0], + "wep2": ["", 0], + "mobi": ["Attack Bike", "max"] + }, + "option": { + "wep1": [[["Multi-Melta", "Heavy Bolter"], 2]], + "wep2": [[["Chainsword", "Chainaxe", "Combat Knife"],2]] + } + } + }, + "Sergeant": { + "max": 1, + "min": 1, + "role": "Attack Bike Sergeant", + "loadout": { + "required": { + "wep1": ["", 0], + "wep2": ["", 0], + "mobi": ["Attack Bike", 1] + }, + "option": { + "wep1": [[["Multi-Melta", "Plasma Cannon", "Lascannon"],1]], + "wep2": [[["Chainsword", "Chainaxe", "Power Sword", "Power Spear", "Power Axe"],1]] + } + } + }, + "type_data": { + "display_data": "Attack Bike {squad_name}", + "class": ["bike"], + "formation_options": ["biker","assault", "tactical", "devastator", "scout"], + "base": "biker" + } +} } \ No newline at end of file diff --git a/datafiles/main/squads/company_squad_builds.json b/datafiles/main/squads/company_squad_builds.json index 705b36f420..e0501fd9e7 100644 --- a/datafiles/main/squads/company_squad_builds.json +++ b/datafiles/main/squads/company_squad_builds.json @@ -4,207 +4,123 @@ "company": 1, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true + "require": true, + "squad": "command_squad" }, { - "squad": "terminator_squad", - "proportion": 2 + "proportion": 2, + "squad": "terminator_squad" }, { - "squad": "terminator_assault_squad", - "proportion": 2 + "proportion": 2, + "squad": "terminator_assault_squad" }, { - "squad": "veteran_squad", - "proportion": 1 + "proportion": 1, + "squad": "veteran_squad" } ] }, { - "company": 2, + "company": 6, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "breacher_squad", - "proportion": 0 + "require": true, + "squad": "command_squad" }, { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 + "proportion": 1, + "squad": "tactical_squad" } ] }, { - "company": 3, + "company": 7, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true + "require": true, + "squad": "command_squad" }, { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "breacher_squad", - "proportion": 0 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 + "proportion": 1, + "squad": "tactical_squad" } ] }, { - "company": 4, + "company": 8, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 + "require": true, + "squad": "command_squad" }, { - "squad": "breacher_squad", - "proportion": 0 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 + "proportion": 1, + "squad": "assault_squad" } ] }, { - "company": 5, + "company": 9, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "breacher_squad", - "proportion": 0 + "require": true, + "squad": "command_squad" }, { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 + "proportion": 1, + "squad": "devastator_squad" } ] }, { - "company": 6, + "company": 10, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true + "require": true, + "squad": "command_squad" }, { - "squad": "tactical_squad", - "proportion": 1 + "proportion": 1, + "squad": "scout_squad" } ] + } + ], + "default_squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" }, { - "company": 7, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 1 - } - ] + "proportion": 6, + "squad": "tactical_squad" }, { - "company": 8, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "assault_squad", - "proportion": 1 - } - ] + "proportion": 0, + "squad": "breacher_squad" }, { - "company": 9, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "devastator_squad", - "proportion": 1 - } - ] + "proportion": 2, + "squad": "devastator_squad" }, { - "company": 10, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "scout_squad", - "proportion": 1 - } - ] + "proportion": 2, + "squad": "assault_squad" } ] } \ No newline at end of file diff --git a/datafiles/main/squads/equal_scouts.json b/datafiles/main/squads/equal_scouts.json index 770df43fa8..65ebb607da 100644 --- a/datafiles/main/squads/equal_scouts.json +++ b/datafiles/main/squads/equal_scouts.json @@ -4,211 +4,127 @@ "company": 1, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true + "require": true, + "squad": "command_squad" }, { - "squad": "terminator_squad", - "proportion": 2 + "proportion": 2, + "squad": "terminator_squad" }, { - "squad": "terminator_assault_squad", - "proportion": 2 + "proportion": 2, + "squad": "terminator_assault_squad" }, { - "squad": "veteran_squad", - "proportion": 1 + "proportion": 1, + "squad": "veteran_squad" } ] }, { - "company": 2, + "company": 6, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 4 - }, - { - "squad": "devastator_squad", - "proportion": 2 + "require": true, + "squad": "command_squad" }, { - "squad": "assault_squad", - "proportion": 2 - }, - { - "squad": "scout_squad", - "proportion": 2 + "proportion": 1, + "squad": "tactical_squad" } ] }, { - "company": 3, + "company": 7, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 4 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 + "require": true, + "squad": "command_squad" }, { - "squad": "scout_squad", - "proportion": 2 + "proportion": 1, + "squad": "tactical_squad" } ] }, { - "company": 4, + "company": 8, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 4 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 + "require": true, + "squad": "command_squad" }, { - "squad": "scout_squad", - "proportion": 2 + "proportion": 1, + "squad": "assault_squad" } ] }, { - "company": 5, + "company": 9, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 4 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 + "require": true, + "squad": "command_squad" }, { - "squad": "scout_squad", - "proportion": 2 + "proportion": 1, + "squad": "devastator_squad" } ] }, { - "company": 6, + "company": 10, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true + "require": true, + "squad": "command_squad" }, { - "squad": "tactical_squad", - "proportion": 1 - } - ] - }, - { - "company": 7, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true + "proportion": 5, + "squad": "scout_squad" }, { - "squad": "tactical_squad", - "proportion": 1 + "proportion": 4, + "squad": "tactical_squad" } ] + } + ], + "default_squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" }, { - "company": 8, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "assault_squad", - "proportion": 1 - } - ] + "proportion": 4, + "squad": "tactical_squad" }, { - "company": 9, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "devastator_squad", - "proportion": 1 - } - ] + "proportion": 2, + "squad": "devastator_squad" }, { - "company": 10, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "scout_squad", - "proportion": 5 - }, - { - "squad": "tactical_squad", - "proportion": 4 - } - ] + "proportion": 2, + "squad": "assault_squad" + }, + { + "proportion": 2, + "squad": "scout_squad" } ] } \ No newline at end of file diff --git a/datafiles/main/squads/equal_specialists.json b/datafiles/main/squads/equal_specialists.json index fb8f1c55eb..abf21e358e 100644 --- a/datafiles/main/squads/equal_specialists.json +++ b/datafiles/main/squads/equal_specialists.json @@ -4,223 +4,59 @@ "company": 1, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true + "require": true, + "squad": "command_squad" }, { - "squad": "terminator_squad", - "proportion": 2 + "proportion": 2, + "squad": "terminator_squad" }, { - "squad": "terminator_assault_squad", - "proportion": 2 + "proportion": 2, + "squad": "terminator_assault_squad" }, { - "squad": "veteran_squad", - "proportion": 1 + "proportion": 1, + "squad": "veteran_squad" } ] }, { - "company": 2, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - } - ] - }, - { - "company": 3, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - } - ] - }, - { - "company": 4, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - } - ] - }, - { - "company": 5, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - } - ] - }, - { - "company": 6, + "company": 10, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "devastator_squad", - "proportion": 2 + "require": true, + "squad": "command_squad" }, { - "squad": "assault_squad", - "proportion": 2 + "proportion": 1, + "squad": "scout_squad" } ] - }, + } + ], + "default_squads": [ { - "company": 7, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - } - ] + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" }, { - "company": 8, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - } - ] + "proportion": 6, + "squad": "tactical_squad" }, { - "company": 9, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 6 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - } - ] + "proportion": 2, + "squad": "devastator_squad" }, { - "company": 10, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "scout_squad", - "proportion": 1 - } - ] + "proportion": 2, + "squad": "assault_squad" } ] } \ No newline at end of file diff --git a/datafiles/main/squads/equal_spescout.json b/datafiles/main/squads/equal_spescout.json index e95d1eb94f..7ec5c963e5 100644 --- a/datafiles/main/squads/equal_spescout.json +++ b/datafiles/main/squads/equal_spescout.json @@ -4,259 +4,67 @@ "company": 1, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true + "require": true, + "squad": "command_squad" }, { - "squad": "terminator_squad", - "proportion": 2 + "proportion": 2, + "squad": "terminator_squad" }, { - "squad": "terminator_assault_squad", - "proportion": 2 + "proportion": 2, + "squad": "terminator_assault_squad" }, { - "squad": "veteran_squad", - "proportion": 1 + "proportion": 1, + "squad": "veteran_squad" } ] }, { - "company": 2, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 5 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - }, - { - "squad": "scout_squad", - "proportion": 1 - } - ] - }, - { - "company": 3, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 5 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - }, - { - "squad": "scout_squad", - "proportion": 1 - } - ] - }, - { - "company": 4, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 5 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - }, - { - "squad": "scout_squad", - "proportion": 1 - } - ] - }, - { - "company": 5, + "company": 10, "squads": [ { - "squad": "command_squad", "max_count": 1, "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 5 - }, - { - "squad": "devastator_squad", - "proportion": 2 + "require": true, + "squad": "command_squad" }, { - "squad": "assault_squad", - "proportion": 2 + "proportion": 5, + "squad": "scout_squad" }, { - "squad": "scout_squad", - "proportion": 1 + "proportion": 4, + "squad": "tactical_squad" } ] - }, + } + ], + "default_squads": [ { - "company": 6, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 5 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - }, - { - "squad": "scout_squad", - "proportion": 1 - } - ] + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" }, { - "company": 7, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 5 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - }, - { - "squad": "scout_squad", - "proportion": 1 - } - ] + "proportion": 5, + "squad": "tactical_squad" }, { - "company": 8, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 5 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - }, - { - "squad": "scout_squad", - "proportion": 1 - } - ] + "proportion": 2, + "squad": "devastator_squad" }, { - "company": 9, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "tactical_squad", - "proportion": 5 - }, - { - "squad": "devastator_squad", - "proportion": 2 - }, - { - "squad": "assault_squad", - "proportion": 2 - }, - { - "squad": "scout_squad", - "proportion": 1 - } - ] + "proportion": 2, + "squad": "assault_squad" }, { - "company": 10, - "squads": [ - { - "squad": "command_squad", - "max_count": 1, - "min_count": 1, - "require": true - }, - { - "squad": "scout_squad", - "proportion": 5 - }, - { - "squad": "tactical_squad", - "proportion": 4 - } - ] + "proportion": 1, + "squad": "scout_squad" } ] } \ No newline at end of file diff --git a/datafiles/main/squads/lightning_warriors.json b/datafiles/main/squads/lightning_warriors.json new file mode 100644 index 0000000000..f33db3ebb5 --- /dev/null +++ b/datafiles/main/squads/lightning_warriors.json @@ -0,0 +1,586 @@ +{ + "companies": [ + { + "company": 1, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 2, + "squad": "terminator_squad" + }, + { + "proportion": 2, + "squad": "terminator_assault_squad" + }, + { + "proportion": 1, + "squad": "veteran_squad" + } + ] + }, + { + "company": 6, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "tactical_squad" + } + ] + }, + { + "company": 7, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "tactical_squad" + } + ] + }, + { + "company": 8, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + } + ] + }, + { + "company": 9, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + } + ] + }, + { + "company": 10, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "scout_squad" + } + ] + } + ], + "default_squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 0, + "squad": "tactical_squad" + }, + { + "proportion": 9, + "squad": "bike_squad" + }, + { + "proportion": 6, + "squad": "attack_bike_squad" + }, + { + "proportion": 0, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + } + ], + "distribution_overrides": { + "equal_scouts": { + "companies": [ + { + "company": 6, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 6, + "squad": "tactical_squad" + } + ] + }, + { + "company": 7, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 6, + "squad": "tactical_squad" + } + ] + }, + { + "company": 8, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + } + ] + }, + { + "company": 9, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + } + ] + }, + { + "company": 10, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "scout_squad" + }, + { + "proportion": 4, + "squad": "bike_squad" + } + ] + } + ], + "default_squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 6, + "squad": "attack_bike_squad" + }, + { + "proportion": 8, + "squad": "bike_squad" + }, + { + "proportion": 2, + "squad": "scout_squad" + } + ] + }, + "equal_specialists": { + "companies": [ + { + "company": 6, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 2, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + }, + { + "company": 7, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 2, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + }, + { + "company": 8, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 2, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + }, + { + "company": 9, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 2, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + } + ], + "default_squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 2, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + }, + "equal_spescout": { + "companies": [ + { + "company": 6, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 1, + "squad": "scout_squad" + }, + { + "proportion": 1, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + }, + { + "company": 7, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 1, + "squad": "scout_squad" + }, + { + "proportion": 1, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + }, + { + "company": 8, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 1, + "squad": "scout_squad" + }, + { + "proportion": 1, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + }, + { + "company": 9, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 1, + "squad": "scout_squad" + }, + { + "proportion": 1, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + }, + { + "company": 10, + "squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "scout_squad" + }, + { + "proportion": 4, + "squad": "bike_squad" + } + ] + } + ], + "default_squads": [ + { + "max_count": 1, + "min_count": 1, + "require": true, + "squad": "command_squad" + }, + { + "proportion": 1, + "squad": "devastator_squad" + }, + { + "proportion": 1, + "squad": "assault_squad" + }, + { + "proportion": 1, + "squad": "scout_squad" + }, + { + "proportion": 1, + "squad": "tactical_squad" + }, + { + "proportion": 4, + "squad": "attack_bike_squad" + }, + { + "proportion": 6, + "squad": "bike_squad" + } + ] + } + } +} \ No newline at end of file diff --git a/objects/obj_controller/Create_0.gml b/objects/obj_controller/Create_0.gml index c50a540b35..e2bfb416e2 100644 --- a/objects/obj_controller/Create_0.gml +++ b/objects/obj_controller/Create_0.gml @@ -808,6 +808,7 @@ bat_formation = array_create(_count, ""); bat_formation_type = array_create(_count, 0); bat_deva_for = array_create(_count, 1); bat_assa_for = array_create(_count, 4); +bat_bike_for = array_create(_count, 4); bat_tact_for = array_create(_count, 2); bat_vete_for = array_create(_count, 2); bat_hire_for = array_create(_count, 3); @@ -833,6 +834,7 @@ default_bat_formation(); bat_devastator_column = 1; bat_assault_column = 4; +bat_bike_column = 4; bat_tactical_column = 2; bat_veteran_column = 2; bat_hire_column = 3; diff --git a/objects/obj_creation/Create_0.gml b/objects/obj_creation/Create_0.gml index 0ecaca9b9d..7a72eb5f49 100644 --- a/objects/obj_creation/Create_0.gml +++ b/objects/obj_creation/Create_0.gml @@ -466,13 +466,14 @@ load_default_gear(eROLE.DEVASTATOR, "Devastator", "", "Combat Knife", STR_ANY_PO load_default_gear(eROLE.ASSAULT, "Assault", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "Jump Pack", ""); load_default_gear(eROLE.ANCIENT, "Ancient", "Company Standard", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", ""); load_default_gear(eROLE.SCOUT, "Scout", "Bolter", "Combat Knife", "Scout Armour", "", ""); +load_default_gear(eROLE.BIKER, "Biker", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "Bike", ""); load_default_gear(eROLE.CHAPLAIN, "Chaplain", "Crozius Arcanum", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", "Rosarius"); load_default_gear(eROLE.APOTHECARY, "Apothecary", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", "Narthecium"); load_default_gear(eROLE.TECHMARINE, "Techmarine", "Power Axe", "Bolt Pistol", "Artificer Armour", "Servo-arm", ""); load_default_gear(eROLE.LIBRARIAN, "Librarian", "Force Staff", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", "Psychic Hood"); load_default_gear(eROLE.SERGEANT, "Sergeant", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", ""); load_default_gear(eROLE.VETERANSERGEANT, "Veteran Sergeant", "Chainsword", "Plasma Pistol", STR_ANY_POWER_ARMOUR, "", ""); - +load_default_gear(eROLE.ATTACK_BIKER, "Attack Biker", "Heavy Bolter", "Chainsword", STR_ANY_POWER_ARMOUR, "Attack Bike", ""); if (global.restart > 0) { fade_in = -1; slate1 = -1; diff --git a/objects/obj_popup/Create_0.gml b/objects/obj_popup/Create_0.gml index 633e6523b4..72e4b3db88 100644 --- a/objects/obj_popup/Create_0.gml +++ b/objects/obj_popup/Create_0.gml @@ -191,27 +191,30 @@ get_unit_promotion_options = function() { i += 1; role_name[i] = obj_ini.role[100][8]; //tacts role_exp[i] = company_promote_data[target_comp].exp; - if (obj_controller.command_set[2] == 0) { - role_exp[i] = 0; - } } if (array_contains([2, 3, 4, 5, 8], target_comp)) { i += 1; role_name[i] = obj_ini.role[100][10]; //assualts role_exp[i] = company_promote_data[target_comp].exp; - if (obj_controller.command_set[2] == 0) { - role_exp[i] = 0; - } } if (array_contains([2, 3, 4, 5, 9], target_comp)) { i += 1; role_name[i] = obj_ini.role[100][9]; //devs role_exp[i] = company_promote_data[target_comp].exp; - if (obj_controller.command_set[2] == 0) { - role_exp[i] = 0; - } + } + + if (array_contains([2, 3, 4, 5], target_comp)) { + i += 1; + role_name[i] = obj_ini.role[100][13]; //bikers + role_exp[i] = company_promote_data[target_comp].exp; + } + + if (array_contains([2, 3, 4, 5], target_comp)) { + i += 1; + role_name[i] = obj_ini.role[100][20]; //attack bikers + role_exp[i] = company_promote_data[target_comp].exp; } if (target_comp == 1) { @@ -224,9 +227,6 @@ get_unit_promotion_options = function() { i += 1; role_name[i] = obj_ini.role[100][12]; //scouts role_exp[i] = company_promote_data[target_comp].exp; - if (obj_controller.command_set[2] == 0) { - role_exp[i] = 0; - } } if (target_comp == 1) { @@ -255,6 +255,14 @@ get_unit_promotion_options = function() { role_name[i] = obj_ini.role[100][12]; //scouts role_exp[i] = 0; + i += 1; + role_name[i] = obj_ini.role[100][13]; //bikers + role_exp[i] = 0; + + i += 1; + role_name[i] = obj_ini.role[100][20]; //attack bikers + role_exp[i] = 0; + i += 1; role_name[i] = obj_ini.role[100][3]; //veterans role_exp[i] = 0; diff --git a/scripts/is_specialist/is_specialist.gml b/scripts/is_specialist/is_specialist.gml index 0581a0c37b..9b818a1534 100644 --- a/scripts/is_specialist/is_specialist.gml +++ b/scripts/is_specialist/is_specialist.gml @@ -172,6 +172,8 @@ function role_groups(group, include_trainee = false, include_heads = true) { _roles[eROLE.TACTICAL], _roles[eROLE.DEVASTATOR], _roles[eROLE.ASSAULT], + _roles[eROLE.BIKER], + _roles[eROLE.ATTACK_BIKER], _roles[eROLE.SCOUT] ]; break; diff --git a/scripts/macros/macros.gml b/scripts/macros/macros.gml index f293b5c665..3ac5fdaca9 100644 --- a/scripts/macros/macros.gml +++ b/scripts/macros/macros.gml @@ -73,12 +73,14 @@ enum eROLE { ASSAULT = 10, ANCIENT = 11, SCOUT = 12, + BIKER = 13, CHAPLAIN = 14, APOTHECARY = 15, TECHMARINE = 16, LIBRARIAN = 17, SERGEANT = 18, VETERANSERGEANT = 19, + ATTACK_BIKER = 20, LANDRAIDER = 50, RHINO = 51, PREDATOR = 52, diff --git a/scripts/scr_UnitGroup/scr_UnitGroup.gml b/scripts/scr_UnitGroup/scr_UnitGroup.gml index e3ccf4ef9b..ebd399c620 100644 --- a/scripts/scr_UnitGroup/scr_UnitGroup.gml +++ b/scripts/scr_UnitGroup/scr_UnitGroup.gml @@ -199,10 +199,11 @@ function UnitGroup(units) constructor { var _roles = active_roles(); - static sgt_types = role_groups(SPECIALISTS_SQUAD_LEADERS); + static sgt_types = []; static create_squad = function(squad_type, squad_loadout = true, squad_uid = "", game_start = false) { - // LOGGER.info($"sgts : ${sgt_types}"); + //LOGGER.info($"sgts : ${sgt_types}"); + sgt_types = role_groups(SPECIALISTS_SQUAD_LEADERS); var roles = active_roles(); @@ -222,20 +223,74 @@ function UnitGroup(units) constructor { var _fill_squad = obj_ini.squad_types[$ squad_type]; var _fulfilled = false; + //adding multiple role sources on gen within a single squad + var _all_roles_to_fetch = array_create(0); + for (var r = 0; r < array_length(squad_unit_types); r++) { + var _primary_role = squad_unit_types[r]; + var _primary_role_def = _fill_squad[$ _primary_role]; + var _primary_role_name = struct_exists(_primary_role_def, "role") ? _primary_role_def.role : _primary_role; + if (!array_contains(_all_roles_to_fetch, _primary_role_name)) { + array_push(_all_roles_to_fetch, _primary_role_name); + } + // Always include the JSON key itself as a source — units carry their base role + // (e.g. "Terminator") before being renamed to the squad-specific role ("Deathwing Terminator") + if (!array_contains(_all_roles_to_fetch, _primary_role)) { + array_push(_all_roles_to_fetch, _primary_role); + } - var _squadless = get_from({squadless: true, roles: squad_unit_types}); + //add alternative source roles to fetch list if this squad role defines them + if (struct_exists(_primary_role_def, "alternative_roles")) { + var _alternatives = _primary_role_def.alternative_roles; + for (var a = 0; a < array_length(_alternatives); a++) { + if (!array_contains(_all_roles_to_fetch, _alternatives[a])) { + array_push(_all_roles_to_fetch, _alternatives[a]); + } + } + } + } + //ensure generic sergeant role names are always included in the pool + //JSON overrides with specific names "Tactical Sergeant" + for (var s = 0; s < 2; s++) { + if (!array_contains(_all_roles_to_fetch, sgt_types[s])) { + array_push(_all_roles_to_fetch, sgt_types[s]); + } + } + + var _squadless = get_from({squadless: true, roles: _all_roles_to_fetch}); for (var s = 0; s < 2; s++) { var _sgt_type = sgt_types[s]; - var _available_sgt = _squadless.get_from({role: _sgt_type, max_wanted: 1}); + var _available_sgt = _squadless.get_from({role: _sgt_type, max_wanted: 1}, true, true); if (_available_sgt.number() == 0) { continue; } var _sgt = _available_sgt.units[0]; - squad.add_member(_sgt); - squad_fulfilment[$ _sgt_type]++; + // Confirm this squad actually has a slot for this sergeant role BEFORE adding the + // candidate or marking sergeant_found. Otherwise a sergeant whose role the squad has + // no slot for would be added anyway and incorrectly suppress the promotion flow. + var _sgt_group = ""; + for (var r = 0; r < array_length(squad_unit_types); r++) { + var _role_name = squad_unit_types[r]; + var _role_def = _fill_squad[$ _role_name]; + var _primary_role_name = struct_exists(_role_def, "role") ? _role_def.role : _role_name; + if (_primary_role_name == _sgt_type || (_role_name == "Sergeant" && _sgt_type == sgt_types[0]) || (_role_name == "Veteran Sergeant" && _sgt_type == sgt_types[1])) { + _sgt_group = _role_name; + break; + } + } + if (_sgt_group == "") { + continue; + } + squad.add_member(_sgt.company, _sgt.marine_number); + squad_fulfilment[$ _sgt_group]++; + // Rename pre-existing sergeant to squad-specific role if needed + var _sgt_slot_def = _fill_squad[$ _sgt_group]; + var _target_sgt_role = struct_exists(_sgt_slot_def, "role") ? _sgt_slot_def.role : _sgt_type; + if (_target_sgt_role != _sgt.role()) { + _sgt.update_role(_target_sgt_role); + } sergeant_found = true; } @@ -252,8 +307,17 @@ function UnitGroup(units) constructor { var _has_sgt_requirements = false; for (var s = 0; s < 2; s++) { var _sgt_type = sgt_types[s]; - if (array_contains(squad_unit_types, _sgt_type)) { - _has_sgt_requirements = true; + for (var r = 0; r < array_length(squad_unit_types); r++) { + var _role_name = squad_unit_types[r]; + var _role_def = _fill_squad[$ _role_name]; + var _primary_role_name = struct_exists(_role_def, "role") ? _role_def.role : _role_name; + if (_primary_role_name == _sgt_type || _role_name == "Sergeant" && _sgt_type == sgt_types[0] || _role_name == "Veteran Sergeant" && _sgt_type == sgt_types[1]) { + _has_sgt_requirements = true; + break; + } + } + if (_has_sgt_requirements) { + break; } } @@ -262,15 +326,61 @@ function UnitGroup(units) constructor { } //clone or else keeps pushing up number - var _max = variable_clone(_fill_squad[$ _unit.role()][$ "max"]); + //EXPERIMENTAL Determine which role GROUP this marine belongs to + var _target_role = _unit.role(); + var _role_group = ""; // default to the marine's own role + + // Search through defined role groups to find which one this marine belongs to + for (var r = 0; r < array_length(squad_unit_types); r++) { + var _role_name = squad_unit_types[r]; + var _role_def = _fill_squad[$ _role_name]; + var _primary_role_name = struct_exists(_role_def, "role") ? _role_def.role : _role_name; + + + // Check if marine matches this primary role (squad-specific rename target) + if (_target_role == _primary_role_name) { + _role_group = _role_name; + break; + } + // Check if marine matches the JSON key itself (base role before squad rename) + if (_target_role == _role_name) { + _role_group = _role_name; + break; + } + + // Check if marine matches any alternative roles for this group + if (struct_exists(_role_def, "alternative_roles")) { + var _alts = _role_def.alternative_roles; + if (array_contains(_alts, _target_role)) { + _role_group = _role_name; + break; + } + } + } + + if (_role_group == "") { + continue; + } + + // NEW: Check max capacity for the ROLE GROUP (not the source role) + var _max = variable_clone(_fill_squad[$ _role_group][$ "max"]); if (_has_sgt_requirements) { _max += 1; } - if (squad_fulfilment[$ _unit.role()] < _max) { + // NEW: Track fulfillment by role GROUP, allowing alternatives to fill the same slot + if (squad_fulfilment[$ _role_group] < _max) { //if sergeants not required - squad_fulfilment[$ _unit.role()]++; + squad_fulfilment[$ _role_group]++; squad.add_member(_unit.company, _unit.marine_number); + // Rename unit to the squad-specific role only for rank-and-file marines + // (Tactical/Assault/Devastator/Scout → Biker, Breacher, etc.) + // Specialists (Chaplain, Ancient, Champion, Veteran, etc.) keep their existing role + var _slot_def = _fill_squad[$ _role_group]; + if (struct_exists(_slot_def, "role") && _slot_def.role != _unit.role() + && _unit.IsSpecialist(SPECIALISTS_RANK_AND_FILE)) { + _unit.update_role(_slot_def.role); + } } } @@ -279,16 +389,51 @@ function UnitGroup(units) constructor { /*and ((squad_fulfilment[$ obj_ini.role[100][8]] > 4)or (squad_fulfilment[$ obj_ini.role[100][10]] > 4) or (squad_fulfilment[$ obj_ini.role[100][9]] > 4)or (squad_fulfilment[$ obj_ini.role[100][3]] > 4) )*/ var _members = squad.get_members(true); - var _exp_unit = 0; if (!bool(_members.number())) { return [false, squad.uid]; } + // Select would-be sergeant candidates and reserve their fulfilment slot, but defer the + // actual role mutation (update_role / add_trait) until squad viability is confirmed below. + // update_role permanently rewrites obj_ini.role and applies stat/command changes, so + // promoting here — before _fulfilled is known — would leave marines mutated even when the + // squad creation attempt ultimately fails, causing persistent state drift. + var _pending_promotions = []; + var _promoted_units = []; // marines already queued for a leader slot, so a 2nd slot picks a distinct one for (var s = 0; s < 2; s++) { var _sgt_type = sgt_types[s]; - if (struct_exists(squad_fulfilment, _sgt_type) && (!sergeant_found)) { - _exp_unit = _members.highest_exp(); - - squad_fulfilment[$ _sgt_type]++; + var _sgt_group = ""; + for (var r = 0; r < array_length(squad_unit_types); r++) { + var _role_name = squad_unit_types[r]; + var _role_def = _fill_squad[$ _role_name]; + var _primary_role_name = struct_exists(_role_def, "role") ? _role_def.role : _role_name; + if (_primary_role_name == _sgt_type || (_role_name == "Sergeant" && _sgt_type == sgt_types[0]) || (_role_name == "Veteran Sergeant" && _sgt_type == sgt_types[1])) { + _sgt_group = _role_name; + break; + } + } + if (_sgt_group != "" && struct_exists(squad_fulfilment, _sgt_group) && (!sergeant_found)) { + // Highest-experience member not already queued for another leader slot, so two + // leader slots (e.g. Sergeant + Veteran Sergeant) never promote the same marine. + var _candidate = undefined; + var _candidate_exp = -1; + for (var _mi = 0; _mi < _members.number(); _mi++) { + var _m = _members.units[_mi]; + if (array_contains(_promoted_units, _m)) { + continue; + } + if (_candidate == undefined || _m.experience > _candidate_exp) { + _candidate = _m; + _candidate_exp = _m.experience; + } + } + if (_candidate == undefined) { + continue; // no distinct member left for this leader slot + } + array_push(_promoted_units, _candidate); + var _sgt_role_def = _fill_squad[$ _sgt_group]; + var _actual_sgt_role = struct_exists(_sgt_role_def, "role") ? _sgt_role_def.role : _sgt_type; + squad_fulfilment[$ _sgt_group]++; + array_push(_pending_promotions, { unit: _candidate, role: _actual_sgt_role }); } } @@ -303,12 +448,12 @@ function UnitGroup(units) constructor { } } if (_fulfilled) { - for (var s = 0; s < 2; s++) { - if (struct_exists(squad_fulfilment, sgt_types[s]) && (sergeant_found == false)) { - _exp_unit.update_role(sgt_types[s]); //if squad is viable promote marine to sergeant - if (game_start && irandom(1) == 0) { - _exp_unit.add_trait("lead_example"); - } + // Squad is viable — now apply the deferred sergeant promotions. + for (var p = 0; p < array_length(_pending_promotions); p++) { + var _promo = _pending_promotions[p]; + _promo.unit.update_role(_promo.role); //if squad is viable promote marine to sergeant + if (game_start && irandom(1) == 0) { + _promo.unit.add_trait("lead_example"); } } //update units squad marker diff --git a/scripts/scr_chapter_new/scr_chapter_new.gml b/scripts/scr_chapter_new/scr_chapter_new.gml index 132106b837..907c57727e 100644 --- a/scripts/scr_chapter_new/scr_chapter_new.gml +++ b/scripts/scr_chapter_new/scr_chapter_new.gml @@ -231,12 +231,14 @@ function scr_chapter_new(chapter_identifier) { load_default_gear(eROLE.ASSAULT, "Assault", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "Jump Pack", ""); load_default_gear(eROLE.ANCIENT, "Ancient", "Company Standard", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", ""); load_default_gear(eROLE.SCOUT, "Scout", "Bolter", "Combat Knife", "Scout Armour", "", ""); + load_default_gear(eROLE.BIKER, "Biker", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR,"Bike",""); load_default_gear(eROLE.CHAPLAIN, "Chaplain", "Crozius Arcanum", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", "Rosarius"); load_default_gear(eROLE.APOTHECARY, "Apothecary", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", "Narthecium"); load_default_gear(eROLE.TECHMARINE, "Techmarine", "Power Axe", "Bolt Pistol", "Artificer Armour", "Servo-arm", ""); load_default_gear(eROLE.LIBRARIAN, "Librarian", "Force Staff", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", "Psychic Hood"); load_default_gear(eROLE.SERGEANT, "Sergeant", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", ""); load_default_gear(eROLE.VETERANSERGEANT, "Veteran Sergeant", "Chainsword", "Plasma Pistol", STR_ANY_POWER_ARMOUR, "", ""); + load_default_gear(eROLE.ATTACK_BIKER, "Attack Biker", "Heavy Bolter", "Chainsword", STR_ANY_POWER_ARMOUR, "Attack Bike", ""); for (var c = 0; c < array_length(obj_creation.all_chapters); c++) { if (chapter_identifier == obj_creation.all_chapters[c].name && obj_creation.all_chapters[c].json == true) { diff --git a/scripts/scr_civil_roster/scr_civil_roster.gml b/scripts/scr_civil_roster/scr_civil_roster.gml index 9c3e538896..967cda5a6b 100644 --- a/scripts/scr_civil_roster/scr_civil_roster.gml +++ b/scripts/scr_civil_roster/scr_civil_roster.gml @@ -27,6 +27,7 @@ function scr_civil_roster(_unit_location, _target_location, _is_planet) { obj_controller.bat_devastator_column = obj_controller.bat_deva_for[new_combat.formation_set]; obj_controller.bat_assault_column = obj_controller.bat_assa_for[new_combat.formation_set]; + obj_controller.bat_bike_column = obj_controller.bat_bike_for[new_combat.formation_set]; obj_controller.bat_tactical_column = obj_controller.bat_tact_for[new_combat.formation_set]; obj_controller.bat_veteran_column = obj_controller.bat_vete_for[new_combat.formation_set]; obj_controller.bat_hire_column = obj_controller.bat_hire_for[new_combat.formation_set]; diff --git a/scripts/scr_company_order/scr_company_order.gml b/scripts/scr_company_order/scr_company_order.gml index b7996fc3cc..93b62e164a 100644 --- a/scripts/scr_company_order/scr_company_order.gml +++ b/scripts/scr_company_order/scr_company_order.gml @@ -70,18 +70,8 @@ function scr_company_order(company) { if (_squadless.number() > 3) { var _squad_index = _company_marines.index_squads(); - var _data_match = false; - var _data; - if (struct_exists(obj_ini.chapter_squad_arrangement, "companies")) { - var _comp_datas = obj_ini.chapter_squad_arrangement.companies; - for (var i = 0; i < array_length(_comp_datas); i++) { - if (_comp_datas[i].company == co) { - _data_match = true; - _data = _comp_datas[i]; - } - } - } - if (_data_match) { + var _data = resolve_company_arrangement(obj_ini.chapter_squad_arrangement, co); + if (_data != undefined) { _squadless.organise_by_template(_data, _squad_index, _empty_index, false); } @@ -194,5 +184,79 @@ function role_hierarchy() { "Ork Sniper" ]; + // Dynamically collect squad-specific sergeant role variants (e.g. "Biker Sergeant", "Tactical Sergeant") + // so they sort immediately after the generic sergeant, keeping them at the top of their squads + var _sgt_base = _roles[eROLE.SERGEANT]; + var _vsgt_base = _roles[eROLE.VETERANSERGEANT]; + var _squad_type_names = struct_get_names(obj_ini.squad_types); + for (var _si = 0; _si < array_length(_squad_type_names); _si++) { + var _sq_data = obj_ini.squad_types[$ _squad_type_names[_si]]; + var _sq_keys = struct_get_names(_sq_data); + for (var _ki = 0; _ki < array_length(_sq_keys); _ki++) { + var _k = _sq_keys[_ki]; + if (_k == "type_data") continue; + var _role_def = _sq_data[$ _k]; + if (!struct_exists(_role_def, "role")) continue; + var _specific_role = _role_def.role; + if (!array_contains(hierarchy, _specific_role)) { + // Classify by the slot's JSON key (_k), not the renamed role string. Veteran-sergeant + // variants are keyed "Veteran Sergeant" but get renamed to names like "Deathwing + // Sergeant" / "Proteus Watch Sergeant" that contain "Sergeant" but NOT the exact + // substring "Veteran Sergeant" — so a role-string match would mis-rank them as + // regular sergeants. Fall back to string matching only when the key isn't a sergeant. + var _is_vsgt = (_k == _vsgt_base) || (string_count(_vsgt_base, _specific_role) > 0); + var _is_sgt = (_k == _sgt_base) || (string_count(_sgt_base, _specific_role) > 0); + if (_is_vsgt) { + // Veteran-sergeant variant — insert just before _vsgt_base position + var _vpos = array_get_index(hierarchy, _vsgt_base); + array_insert(hierarchy, max(0, _vpos), _specific_role); + } else if (_is_sgt) { + // Regular sergeant variant — insert just after _sgt_base position + var _spos = array_get_index(hierarchy, _sgt_base); + array_insert(hierarchy, _spos + 1, _specific_role); + } + } + } + } + + // Also add non-sergeant squad-specific role variants so they appear after their base role + var _base_roles = [ + _roles[eROLE.TERMINATOR], _roles[eROLE.VETERAN], + _roles[eROLE.TACTICAL], _roles[eROLE.ASSAULT], + _roles[eROLE.DEVASTATOR], _roles[eROLE.SCOUT], + _roles[eROLE.ANCIENT], _roles[eROLE.CHAMPION], + _roles[eROLE.CHAPLAIN], _roles[eROLE.APOTHECARY], + _roles[eROLE.TECHMARINE], _roles[eROLE.LIBRARIAN] + ]; + for (var _si = 0; _si < array_length(_squad_type_names); _si++) { + var _sq_data = obj_ini.squad_types[$ _squad_type_names[_si]]; + var _sq_keys = struct_get_names(_sq_data); + for (var _ki = 0; _ki < array_length(_sq_keys); _ki++) { + var _k = _sq_keys[_ki]; + if (_k == "type_data") continue; + var _role_def = _sq_data[$ _k]; + if (!struct_exists(_role_def, "role")) continue; + var _specific_role = _role_def.role; + if (array_contains(hierarchy, _specific_role)) continue; + // Skip sergeant variants (already handled above) + if (string_count(_sgt_base, _specific_role) > 0) continue; + // Find the closest matching base role and insert after it + for (var _bi = 0; _bi < array_length(_base_roles); _bi++) { + if (struct_exists(_role_def, "alternative_roles") && + array_contains(_role_def.alternative_roles, _base_roles[_bi])) { + var _bpos = array_get_index(hierarchy, _base_roles[_bi]); + if (_bpos >= 0) { + array_insert(hierarchy, _bpos + 1, _specific_role); + break; + } + } + } + // If not inserted via alternative_roles, just append before rank-and-file + if (!array_contains(hierarchy, _specific_role)) { + array_push(hierarchy, _specific_role); + } + } + } + return hierarchy; } diff --git a/scripts/scr_creation/scr_creation.gml b/scripts/scr_creation/scr_creation.gml index 8f0add29e8..fbccefd23f 100644 --- a/scripts/scr_creation/scr_creation.gml +++ b/scripts/scr_creation/scr_creation.gml @@ -39,7 +39,7 @@ function set_complex_livery_buttons() { function update_creation_roles_radio(start_role = 1) { var _role_data = []; - for (var i = start_role; i <= 19; i++) { + for (var i = start_role; i <= 20; i++) { if (race[100][i] != 0 && role[100][i] != "") { array_push(_role_data, {str1: role[100][i], font: fnt_40k_14b, role_id: i}); } diff --git a/scripts/scr_initialize_custom/scr_initialize_custom.gml b/scripts/scr_initialize_custom/scr_initialize_custom.gml index 7bee2adb51..303784081c 100644 --- a/scripts/scr_initialize_custom/scr_initialize_custom.gml +++ b/scripts/scr_initialize_custom/scr_initialize_custom.gml @@ -1482,12 +1482,14 @@ function scr_initialize_custom() { load_default_gear(eROLE.ASSAULT, "Assault", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "Jump Pack", ""); load_default_gear(eROLE.ANCIENT, "Ancient", "Company Standard", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", ""); load_default_gear(eROLE.SCOUT, "Scout", "Bolter", "Combat Knife", "Scout Armour", "", ""); + load_default_gear(eROLE.BIKER, "Biker", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "Bike", ""); load_default_gear(eROLE.CHAPLAIN, "Chaplain", "Crozius Arcanum", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", "Rosarius"); load_default_gear(eROLE.APOTHECARY, "Apothecary", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", "Narthecium"); load_default_gear(eROLE.TECHMARINE, "Techmarine", "Omnissian Axe", "Bolt Pistol", _hi_qual_armour, "Servo-arm", ""); load_default_gear(eROLE.LIBRARIAN, "Librarian", "Force Staff", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", "Psychic Hood"); load_default_gear(eROLE.SERGEANT, "Sergeant", "Chainsword", "Bolt Pistol", STR_ANY_POWER_ARMOUR, "", ""); load_default_gear(eROLE.VETERANSERGEANT, "Veteran Sergeant", "Chainsword", "Plasma Pistol", STR_ANY_POWER_ARMOUR, "", ""); + load_default_gear(eROLE.ATTACK_BIKER, "Attack Biker", "Heavy Bolter", "Chainsword", STR_ANY_POWER_ARMOUR, "Attack Bike", ""); obj_ini.role[101] = obj_ini.role[100]; if (scr_has_disadv("Psyker Intolerant")) { race[defaults_slot][eROLE.LIBRARIAN] = 0; @@ -1543,6 +1545,14 @@ function scr_initialize_custom() { "scout", eROLE.SCOUT ], + [ + "biker", + eROLE.BIKER + ], + [ + "attack_biker", + eROLE.ATTACK_BIKER + ], [ "chaplain", eROLE.CHAPLAIN @@ -1639,23 +1649,41 @@ function scr_initialize_custom() { #endregion #region Squad Loadouts - switch (obj_creation.squad_distribution) { - case 1: // equal specialists only - obj_ini.chapter_squad_arrangement = json_to_gamemaker( - working_directory + $"main/squads/equal_specialists.json", json_parse); - break; - case 2: // equal scouts only - obj_ini.chapter_squad_arrangement = json_to_gamemaker( - working_directory + $"main/squads/equal_scouts.json", json_parse); - break; - case 3: // equal specialists and equal scouts - obj_ini.chapter_squad_arrangement = json_to_gamemaker( - working_directory + $"main/squads/equal_spescout.json", json_parse); - break; - default: // 0 = standard - obj_ini.chapter_squad_arrangement = json_to_gamemaker( - working_directory + $"main/squads/company_squad_builds.json", json_parse); - break; + if (scr_has_adv("Lightning Warriors")) { + obj_ini.chapter_squad_arrangement = json_to_gamemaker( + working_directory + $"main\\squads\\lightning_warriors.json", json_parse); + var _dist_key = ""; + switch (obj_creation.squad_distribution) { + case 1: _dist_key = "equal_specialists"; break; + case 2: _dist_key = "equal_scouts"; break; + case 3: _dist_key = "equal_spescout"; break; + } + if (_dist_key != "" + && struct_exists(obj_ini.chapter_squad_arrangement, "distribution_overrides") + && struct_exists(obj_ini.chapter_squad_arrangement.distribution_overrides, _dist_key)) { + apply_squad_distribution_override( + obj_ini.chapter_squad_arrangement, + obj_ini.chapter_squad_arrangement.distribution_overrides[$ _dist_key]); + } + } else { + switch (obj_creation.squad_distribution) { + case 1: // equal specialists only + obj_ini.chapter_squad_arrangement = json_to_gamemaker( + working_directory + $"main\\squads\\equal_specialists.json", json_parse); + break; + case 2: // equal scouts only + obj_ini.chapter_squad_arrangement = json_to_gamemaker( + working_directory + $"main\\squads\\equal_scouts.json", json_parse); + break; + case 3: // equal specialists and equal scouts + obj_ini.chapter_squad_arrangement = json_to_gamemaker( + working_directory + $"main\\squads\\equal_spescout.json", json_parse); + break; + default: // 0 = standard + obj_ini.chapter_squad_arrangement = json_to_gamemaker( + working_directory + $"main\\squads\\company_squad_builds.json", json_parse); + break; + } } var _squad_name = "Squad"; @@ -1749,7 +1777,8 @@ function scr_initialize_custom() { ]; var _roles_player = obj_ini.role[100]; var _default_player = obj_ini.role[101]; - for (var i = 1; i < 20; i++) { + var i; + for (i = 1; i < 21; i++) { if (_roles_player[i] == "") { continue; } @@ -1763,7 +1792,7 @@ function scr_initialize_custom() { array_push(_swaps, _set); } - for (var i = 1; i < 20; i++) { + for (i = 1; i < 21; i++) { var _set = {}; var _key = $"wep1[{i}]"; var _val = obj_ini.wep1[100][i]; @@ -1777,15 +1806,26 @@ function scr_initialize_custom() { array_push(_swaps, _set); } + // LOGGER.debug($"squads object for chapter {chapter_name}"); + // LOGGER.debug($"{custom_squads}"); + if (variable_instance_exists(obj_creation, "squad_builder")) { + if (!struct_exists(obj_ini.chapter_squad_arrangement, "companies")) { + obj_ini.chapter_squad_arrangement.companies = []; + } for (var s = 0; s < array_length(obj_creation.squad_builder); s++) { var _custom_build = obj_creation.squad_builder[s]; + var _found = false; for (var i = 0; i < array_length(obj_ini.chapter_squad_arrangement.companies); i++) { - var _default_build = obj_ini.chapter_squad_arrangement.companies[i]; - if (_custom_build.company == _default_build.company) { + if (obj_ini.chapter_squad_arrangement.companies[i].company == _custom_build.company) { obj_ini.chapter_squad_arrangement.companies[i] = _custom_build; + _found = true; + break; } } + if (!_found) { + array_push(obj_ini.chapter_squad_arrangement.companies, _custom_build); + } } } @@ -1830,40 +1870,7 @@ function scr_initialize_custom() { }; } - /*if (scr_has_adv("Lightning Warriors")) { - variable_struct_set( - custom_squads, - "bikers", - [ - [ - roles.assault, - { - "max": 9, - "min": 4, - "loadout": { - //tactical marine - "required": {"wep1": ["", "max"], "wep2": ["Chainsword", "max"], "mobi": ["Bike", "max"]}, - }, - "role": $"Biker", - } - ], - [ - roles.sergeant, - { - "max": 1, - "min": 1, - "loadout": { - //sergeant - "required": {"wep1": ["", "max"], "wep2": ["Chainsword", "max"], "mobi": ["Bike", 1]}, - }, - "role": $"Biker {roles.sergeant}", - } - ], - ["type_data", {"display_data": $"Bike {_squad_name}", "class": ["bike"], "formation_options": ["assault", "tactical"]}] - ] - ); - } - + /* if (scr_has_adv("Boarders")) { variable_struct_set( custom_squads, @@ -2303,6 +2310,7 @@ function scr_initialize_custom() { var equal_scouts = (squad_distribution == 2 || squad_distribution == 3); obj_ini.equal_scouts = equal_scouts; // for use in squad creation later + var _lw = scr_has_adv("Lightning Warriors"); var _moved_scouts = 0; var _coys = struct_get_names(companies); @@ -2361,6 +2369,15 @@ function scr_initialize_custom() { /// comp 10: tac 40: scout 50; if (squad_distribution == 1 || squad_distribution == 3) { if (_coy.coy >= 2 && _coy.coy <= 9) { + // Scout distribution logic for equal_spescout (sd==3). + // + // 10 scouts are moved from the 10th company bank into each battle company (2-9) + // so that the JSON template's scout_squad proportion can fill at game start. + // This applies regardless of whether Lightning Warriors is active; LW+equal_spescout + // distributes 10 scouts per company just like the non-LW case. + // + // Note: for LW + equal_scouts (sd==2) this branch is not reached at all because + // sd==2 does not satisfy (sd==1 || sd==3), so it falls to the else block below. if (equal_scouts) { if (companies.tenth.scouts > 10) { //theoretically this keeps track of moving scouts from the bank of them in 10th @@ -2378,6 +2395,9 @@ function scr_initialize_custom() { _coy.assaults = assault; _coy.devastators = devastator; } + // Replace the scouts that were moved out of 10th company with an equivalent number + // of tacticals so the 10th's total marine count stays consistent. + // _moved_scouts tracks the cumulative scouts transferred to other companies above. if (equal_scouts && _coy.coy == 10) { // theoretically this swaps moved scouts with tacticals _coy.tacticals = _moved_scouts; @@ -2387,11 +2407,15 @@ function scr_initialize_custom() { /// and the assaults go into the 8th and devastators into the 9th if (_coy.coy >= 2 && _coy.coy <= 5) { if (equal_scouts) { - if (companies.tenth.scouts > 10) { - _coy.scouts = 10; + // LW needs 20 scouts per company to fill proportion:2 scout squads. + // Non-LW equal_scouts uses 10 (proportion:1). Guard against the amount + // actually subtracted so the 10th's bank never goes negative. + var _coy_scout_draw = _lw ? 20 : 10; + if (companies.tenth.scouts >= _coy_scout_draw) { + _coy.scouts = _coy_scout_draw; _moved_scouts += _coy.scouts; _coy.tacticals = max(0, (_coy.total - (assault + devastator + _coy.scouts))); - companies.tenth.scouts -= _moved_scouts; + companies.tenth.scouts -= _coy.scouts; // fix: subtract this company's amount, not the cumulative total } else { // if 10th is run out somehow, revert to normal behaviour _coy.tacticals = max(0, (_coy.total - (assault + devastator))); @@ -2403,23 +2427,27 @@ function scr_initialize_custom() { _coy.devastators = devastator; } + // Companies 6-7 are tactical-only reserves for every distribution. Both + // company_squad_builds/equal_scouts.json and lightning_warriors.json's equal_scouts + // override define companies 6 and 7 as tactical_squad-only, with no scout_squad entry + // at all - so handing them scout marines here (under any distribution) would leave + // stray squadless scouts the template can never organise into squads. Scouts stay + // confined to the battle companies 2-5 (which fall through to default_squads' + // scout_squad) and the 10th; the scouts not moved here simply remain in the 10th's bank. if (real(_coy.coy) >= 6 && real(_coy.coy) <= 7) { - if (equal_scouts) { - if (companies.tenth.scouts > 10) { - _coy.scouts = 10; - _moved_scouts += _coy.scouts; - _coy.tacticals = _coy.total - _coy.scouts; - companies.tenth.scouts -= _coy.scouts; - } else { - // if 10th is run out somehow, revert to normal behaviour - _coy.tacticals = _coy.total; - } - } else { - _coy.tacticals = _coy.total; - } + _coy.tacticals = _coy.total; _coy.assaults = 0; _coy.devastators = 0; } + // Company 8 and 9: always pure assault / devastator reserves for equal_scouts + // (sd==2), regardless of Lightning Warriors. Both company_squad_builds/ + // equal_scouts.json AND lightning_warriors.json's equal_scouts override define + // company 8 as assault_squad-only and company 9 as devastator_squad-only - neither + // lists a scout_squad. The previous `_lw && equal_scouts` branches incorrectly + // handed these companies scout marines that the LW override's templates have no + // scout_squad to absorb, producing stray squadless scouts (the "all companies get + // scouts" symptom). Scouts for equal_scouts + LW must stay confined to companies + // 2-5, matching the override's default_squads scout_squad proportion. if (real(_coy.coy) == 8) { _coy.tacticals = 0; _coy.assaults = _coy.total; @@ -2432,7 +2460,8 @@ function scr_initialize_custom() { } if (real(_coy.coy) == 10 && equal_scouts) { _coy.tacticals = _moved_scouts; - _coy.scouts = _coy.scouts - _coy.tacticals; + // _coy.scouts is already the correct bank remainder after per-company + // deductions above — do not subtract tacticals again or it double-counts. } } diff --git a/scripts/scr_management/scr_management.gml b/scripts/scr_management/scr_management.gml index 4a5befcff6..55c3a20747 100644 --- a/scripts/scr_management/scr_management.gml +++ b/scripts/scr_management/scr_management.gml @@ -6,6 +6,11 @@ function scr_management(argument0) { var chapter_name = global.chapter_name; if (argument0 == 1) { + // Ensure TTRPG display data is up to date before drawing the overview + with (obj_ini) { + sort_all_companies(); + } + with (obj_managment_panel) { instance_destroy(); } @@ -85,36 +90,29 @@ function scr_management(argument0) { pane.header = 1; pane.title = t; - var _company_group = collect_company(company).index_roles(); - - pane.line = array_join(pane.line, _company_group.create_plural_strings_array()); + // Populate company panel — same pattern as the HQ specialty panels above + var _co_units = collect_company(company).index_roles(); + pane.line = array_join(pane.line, _co_units.create_plural_strings_array()); - var num = array_create(5, 0); - var nam = [ - "Land Raider", - "Predator", - "Rhino", - "Land Speeder", - "Whirlwind" - ]; - // Vehicles + // collect_company() only indexes TTRPG marines, so vehicles must be counted + // separately from obj_ini.veh_role and appended after the role strings. + var _veh_names = ["Land Raider", "Predator", "Rhino", "Land Speeder", "Whirlwind"]; + var _veh_count = array_create(array_length(_veh_names), 0); for (var i = 0; i < array_length(obj_ini.veh_role[company]); i++) { - for (var s = 0; s < array_length(nam); s++) { - if (obj_ini.veh_role[company][i] == nam[s]) { - num[s]++; + for (var s = 0; s < array_length(_veh_names); s++) { + if (obj_ini.veh_role[company][i] == _veh_names[s]) { + _veh_count[s]++; } } } - - for (var d = 0; d < 5; d++) { - if (num[d] > 0) { - if (d == 1) { - array_push(pane.line, {str1: nam[d], bold: true, italic: false}); - } else { - array_push(pane.line, nam[d], string_plural_count(nam[d], num[d], false)); - } + for (var d = 0; d < array_length(_veh_names); d++) { + if (_veh_count[d] == 1) { + array_push(pane.line, {str1: _veh_names[d], bold: true, italic: false}); + } else if (_veh_count[d] > 1) { + array_push(pane.line, string_plural_count(_veh_names[d], _veh_count[d], false)); } } + xx += 156; } } diff --git a/scripts/scr_marine_struct/scr_marine_struct.gml b/scripts/scr_marine_struct/scr_marine_struct.gml index b09d0696a3..ae2324da63 100644 --- a/scripts/scr_marine_struct/scr_marine_struct.gml +++ b/scripts/scr_marine_struct/scr_marine_struct.gml @@ -440,16 +440,18 @@ function TTRPG_stats(faction, comp, mar, class = "marine", other_spawn_data = {} stat_boosts({strength: 4, constitution: 4, dexterity: 4}); //will decide on if these are needed } } - if (!is_specialist(role())) { - //logs changes too and from specialist status - if (is_specialist(new_role)) { - obj_controller.marines -= 1; - obj_controller.command += 1; - } - } else { - if (!is_specialist(new_role)) { - obj_controller.marines += 1; - obj_controller.command -= 1; + if (instance_exists(obj_controller)) { + if (!is_specialist(role())) { + //logs changes too and from specialist status + if (is_specialist(new_role)) { + obj_controller.marines -= 1; + obj_controller.command += 1; + } + } else { + if (!is_specialist(new_role)) { + obj_controller.marines += 1; + obj_controller.command -= 1; + } } } } @@ -1662,6 +1664,25 @@ function TTRPG_stats(faction, comp, mar, class = "marine", other_spawn_data = {} return wrath; }; + static speed_force = function(_ranged = false) { + if (_ranged) { + // Attack Bike: scales off the "sidecar's" ranged weapon - single firepower profile, no melee option. + var _attack = ranged_damage_data[0]; + var _weapon = ranged_damage_data[3]; + return new EquipmentStruct({attack: _attack * 2, name: "Speed Force (Ranged)", range: 14, ammo: 12, spli: _weapon.spli, arp: _weapon.arp}, "weapon"); + } + // Standard Bike: scales off melee, dominant melee (M) profile while engaged in front. + var _melee_attack = melee_damage_data[0]; + var _melee_weapon = melee_damage_data[3]; + + var speedf = new EquipmentStruct({attack: _melee_attack * 2, name: "Speed Force", range: 14, ammo: 12, spli: _melee_weapon.spli, arp: _melee_weapon.arp}, "weapon"); + + var speedf_melee = new EquipmentStruct({attack: _melee_attack * 4, name: "Speed Force(M)", range: 1, ammo: 16, spli: _melee_weapon.spli, arp: _melee_weapon.arp}, "weapon"); + + speedf.second_profiles = [speedf_melee]; + + return speedf; + }; static armour_calc = function() { armour_rating = 0; diff --git a/scripts/scr_popup_functions/scr_popup_functions.gml b/scripts/scr_popup_functions/scr_popup_functions.gml index 6fa2266ffc..309e5ee264 100644 --- a/scripts/scr_popup_functions/scr_popup_functions.gml +++ b/scripts/scr_popup_functions/scr_popup_functions.gml @@ -303,7 +303,7 @@ function calculate_equipment_needs() { req_wep2 = "Company Standard"; req_wep2_num = units; } else { - for (var i = 2; i < 20; i++) { + for (var i = 2; i < 21; i++) { if (obj_ini.role[100][i] == rall) { req_armour = obj_ini.armour[100][i]; req_armour_num = units; diff --git a/scripts/scr_promote/scr_promote.gml b/scripts/scr_promote/scr_promote.gml index 5c050be414..45d05372db 100644 --- a/scripts/scr_promote/scr_promote.gml +++ b/scripts/scr_promote/scr_promote.gml @@ -45,7 +45,9 @@ function setup_promotion_popup() { variable_struct_set(role_squad_equivilances, obj_ini.role[100][8], "tactical_squad"); variable_struct_set(role_squad_equivilances, obj_ini.role[100][9], "devastator_squad"); variable_struct_set(role_squad_equivilances, obj_ini.role[100][10], "assault_squad"); + variable_struct_set(role_squad_equivilances, obj_ini.role[100][13], "bike_squad"); variable_struct_set(role_squad_equivilances, obj_ini.role[100][12], "scout_squad"); + variable_struct_set(role_squad_equivilances, obj_ini.role[100][20], "attack_bike_squad"); variable_struct_set(role_squad_equivilances, obj_ini.role[100][3], "veteran_squad"); variable_struct_set(role_squad_equivilances, obj_ini.role[100][4], "terminator_squad"); @@ -120,6 +122,14 @@ function setup_promotion_popup() { } // End that [i] } // End repeat + if (struct_exists(role_squad_equivilances, role_name[target_role])) { + var _grp = collect_company(target_comp); + var _result = [true]; + while (_result[0]) { + _result = _grp.create_squad(role_squad_equivilances[$ role_name[target_role]]); + } + } + with (obj_controller) { scr_management(1); } diff --git a/scripts/scr_roster/scr_roster.gml b/scripts/scr_roster/scr_roster.gml index 881512e1fb..32a41e8242 100644 --- a/scripts/scr_roster/scr_roster.gml +++ b/scripts/scr_roster/scr_roster.gml @@ -478,11 +478,15 @@ function PurgeButton(purge_image, xx, yy, purge_type) constructor { }; } +/// @desc Resolves the chosen formation (formation_set, e.g. Attack/Defend/Raid) into concrete +/// per-role battle columns on obj_controller, which add_unit_to_battle/add_vehicle_to_battle +/// then use to place individual units. function setup_battle_formations() { // Formation here var new_combat = obj_ncombat; obj_controller.bat_devastator_column = obj_controller.bat_deva_for[new_combat.formation_set]; obj_controller.bat_assault_column = obj_controller.bat_assa_for[new_combat.formation_set]; + obj_controller.bat_bike_column = obj_controller.bat_bike_for[new_combat.formation_set]; obj_controller.bat_tactical_column = obj_controller.bat_tact_for[new_combat.formation_set]; obj_controller.bat_veteran_column = obj_controller.bat_vete_for[new_combat.formation_set]; obj_controller.bat_hire_column = obj_controller.bat_hire_for[new_combat.formation_set]; @@ -500,6 +504,17 @@ function setup_battle_formations() { obj_controller.bat_scout_column = obj_controller.bat_scou_for[new_combat.formation_set]; } +/// @desc Determines which formation column (block) a single marine is placed into for the +/// current battle and adds them to the nearest obj_pnunit at that column. +/// Resolution order: 1) the unit's individual role (sergeant variants, Scout, Tactical, +/// Veteran, Devastator, Assault, Librarian/Techmarine specialists, Honour Guard, +/// Dreadnought, Terminator, Chapter Master/heads, Death Company), then 2) if the unit +/// belongs to a squad, the squad's formation_place overrides the role-based column +/// (assault/veteran/tactical/devastator/terminator/command/scout), and finally +/// 3) anything still unresolved (col == 0) defaults to the hireling column. +/// @param {Id.Instance} unit The marine instance being added to the battle. +/// @param {Bool} meeting Whether this is a meeting/temp roster (uses obj_temp_meeting lookups) rather than a normal company roster. +/// @param {Bool} is_local Whether this unit belongs to the local (player-controlled) side. function add_unit_to_battle(unit, meeting, is_local) { var new_combat = obj_ncombat; var man_size = 1; @@ -555,7 +570,12 @@ function add_unit_to_battle(unit, meeting, is_local) { //assualt marines col = obj_controller.bat_assault_column; new_combat.assaults++; - + } else if (_unit_role == _role[13]) { + // bikers + col = obj_controller.bat_bike_column; + } else if (_unit_role == _role[20]) { + //assbikers + col = obj_controller.bat_bike_column; //librarium roles } else if (unit.IsSpecialist(SPECIALISTS_LIBRARIANS, true)) { col = obj_controller.bat_librarian_column; //librarium @@ -685,6 +705,12 @@ function add_unit_to_battle(unit, meeting, is_local) { case "command": col = obj_controller.bat_command_column; break; + case "scout": + col = obj_controller.bat_scout_column; + break; + case "biker": + col = obj_controller.bat_bike_column; + break; } } if (col == 0) { diff --git a/scripts/scr_squads/scr_squads.gml b/scripts/scr_squads/scr_squads.gml index 7cabad2454..a84702be03 100644 --- a/scripts/scr_squads/scr_squads.gml +++ b/scripts/scr_squads/scr_squads.gml @@ -45,15 +45,68 @@ function SquadEquipmentSorting(squad, from_armoury = true, to_armoury = true) co squad_type = target_squad.type; squad_unit_types = squad.find_squad_unit_types(); full_squad_data = obj_ini.squad_types[$ squad_type]; + role_key_to_actual = {}; + for (var _i = 0; _i < array_length(squad_unit_types); _i++) { + var _key = squad_unit_types[_i]; + var _def = full_squad_data[$ _key]; + role_key_to_actual[$ _key] = struct_exists(_def, "role") ? _def.role : _key; + } unit_role = ""; members_UnitGroup = squad.get_members(true); members_UnitGroup.shuffle(); optional_load = undefined; + optional_fill_counts = {}; // flat struct: "slot_groupIndex" -> filled count, lazily populated required_load = undefined; + required_fill_counts = {}; + required_targets = {}; target_squad.update_fulfilment(); static sort = function() { + // For each role, clear every loadout slot that role actively manages + // (required + option + random_pick). Slots not mentioned in a role's own loadout + // are left untouched — e.g. a role that only defines armour won't have wep1/wep2 cleared. + // random_pick can manage mobi/gear/armour as well as weapons, so all managed slots are + // cleared before re-equipping to avoid stale equipment persisting after a reroll. + for (var _ri = 0; _ri < array_length(squad_unit_types); _ri++) { + var _role_key = squad_unit_types[_ri]; + var _role_data = full_squad_data[$ _role_key]; + if (!struct_exists(_role_data, "loadout")) continue; + + var _managed_slots = {}; + var _ld = _role_data.loadout; + if (struct_exists(_ld, "required")) { + var _slots = struct_get_names(_ld.required); + for (var _s = 0; _s < array_length(_slots); _s++) + _managed_slots[$ _slots[_s]] = true; + } + if (struct_exists(_ld, "option")) { + var _slots = struct_get_names(_ld.option); + for (var _s = 0; _s < array_length(_slots); _s++) + _managed_slots[$ _slots[_s]] = true; + } + if (struct_exists(_ld, "random_pick")) { + var _picks = _ld.random_pick; + for (var _p = 0; _p < array_length(_picks); _p++) { + var _slots = struct_get_names(_picks[_p]); + for (var _s = 0; _s < array_length(_slots); _s++) + _managed_slots[$ _slots[_s]] = true; + } + } + + var _managed_slot_names = struct_get_names(_managed_slots); + var _role_members = members_UnitGroup.get_from({roles: [_role_key, role_key_to_actual[$ _role_key]]}); + while (_role_members.number() > 0) { + var _u = _role_members.pop(); + for (var _s = 0; _s < array_length(_managed_slot_names); _s++) { + var _clear = {}; + _clear[$ _managed_slot_names[_s]] = ""; + // Clear via the squad's own from/to_armoury so items don't get destroyed + _u.alter_equipment(_clear, from_armoury, to_armoury); + } + } + } + for (var i = 0; i < array_length(squad_unit_types); i++) { unit_role = squad_unit_types[i]; role_squad_loadout(); @@ -69,44 +122,34 @@ function SquadEquipmentSorting(squad, from_armoury = true, to_armoury = true) co "mobi" ]; - static structure_role_optional_loadout = function(optional_data) { - optional_load = variable_clone(optional_data); //create a fulfillment object for optional loadouts - - var _optional_loadout_slots = struct_get_names(optional_load); - - for (var slot = 0; slot < array_length(_optional_loadout_slots); slot++) { - var _load_out_slot = _optional_loadout_slots[slot]; - for (var i = 0; i < array_length(optional_load[$ _load_out_slot]); i++) { - array_insert(optional_load[$ _load_out_slot][i], 2, 0); - } - } + static structure_role_optional_loadout = function(optional_data){ + //read-only alias into squad_types + optional_load = optional_data; + //mutable state through slot_groupIndex + optional_fill_counts = {}; }; static structure_role_required_loadout = function(required_data) { - //find out if the _unit type for the squad has required equipment thresholds - - required_load = variable_clone(required_data); - required_loadout_slots = struct_get_names(required_load); - for (var i = 0; i < array_length(required_loadout_slots); i++) { - var _current_load_slot = required_loadout_slots[i]; - var _equip_slot = required_load[$ _current_load_slot]; - if (is_string(required_load[$ _current_load_slot][1])) { - if (required_load[$ _current_load_slot][1] == "max") { - required_load[$ _current_load_slot][1] = target_squad.squad_fulfilment[$ unit_role]; - } - } - array_insert(required_load[$ _current_load_slot], 2, 0); + required_load = required_data; + required_fill_counts = {}; + required_targets = {}; + var _slots = struct_get_names(required_data); + for (var i = 0; i < array_length(_slots); i++) { + var _slot = _slots[i]; + var _target = required_data[$ _slot][1]; + required_targets[$ _slot] = + (_target == "max") ? target_squad.squad_fulfilment[$ unit_role] : _target; + required_fill_counts[$ _slot] = 0; } }; static equip_required_for_role = function(_unit) { - if (required_load[$ current_load_slot][2] < required_load[$ current_load_slot][1]) { + if (required_fill_counts[$ current_load_slot] < required_targets[$ current_load_slot]) { //if the required amount of equipment is not in the squad already equip this marine with equipment - var _item_to_add = required_load[$ current_load_slot][0]; - var required_load_set = {}; - required_load_set[$ current_load_slot] = _item_to_add; - _unit.alter_equipment(required_load_set, from_armoury, to_armoury); - required_load[$ current_load_slot][2]++; + var _required_load_set = {}; + _required_load_set[$ current_load_slot] = required_load[$ current_load_slot][0]; + _unit.alter_equipment(_required_load_set, from_armoury, to_armoury); + required_fill_counts[$ current_load_slot]++; return true; } //if all required equipment is included in the squad start adding optional equipment return false; @@ -117,13 +160,13 @@ function SquadEquipmentSorting(squad, from_armoury = true, to_armoury = true) co var _optional_groups = optional_load[$ current_load_slot]; for (var i = 0; i < array_length(_optional_groups); i++) { - var _optional_load_data = _optional_groups[i]; - var _optionals_filled = _optional_load_data[2]; - var _optionals_max_allowed = _optional_load_data[1]; - var _optionals_equipment = _optional_load_data[0]; + var _count_key = current_load_slot + "_" + string(i); + var _optionals_filled = optional_fill_counts[$ _count_key] ?? 0; + var _optionals_max_allowed = _optional_groups[i][1]; + var _optionals_equipment = _optional_groups[i][0]; var _item_to_add; if (_optionals_filled < _optionals_max_allowed) { - var _is_equipment_set = array_length(_optional_load_data) > 3; + var _is_equipment_set = array_length(_optional_groups[i]) > 2; if (is_array(_optionals_equipment)) { //if the array items are varibale e.g a struct @@ -155,9 +198,9 @@ function SquadEquipmentSorting(squad, from_armoury = true, to_armoury = true) co var _opt_load_out = {}; _opt_load_out[$ current_load_slot] = _item_to_add; _unit.alter_equipment(_opt_load_out, from_armoury, to_armoury); - _optional_load_data[1]++; + optional_fill_counts[$ _count_key] = _optionals_filled + 1; if (_is_equipment_set) { - var _equip_set_data = _optional_load_data[3]; + var _equip_set_data = _optional_groups[i][2]; if (is_struct(_equip_set_data)) { _unit.alter_equipment(_equip_set_data, from_armoury, to_armoury); array_push(ignore_units, _unit.uid); @@ -168,8 +211,9 @@ function SquadEquipmentSorting(squad, from_armoury = true, to_armoury = true) co } }; - static equip_loudouts_specific_equip_slot = function() { - var _members_with_role = members_UnitGroup.get_from({role: unit_role}); + static equip_loudouts_specific_equip_slot = function(){ + var _actual_role = role_key_to_actual[$ unit_role]; + var _members_with_role = members_UnitGroup.get_from({roles: [unit_role, _actual_role]}); if (!struct_exists(current_unit_squad_data, "loadout")) { return; } @@ -177,13 +221,11 @@ function SquadEquipmentSorting(squad, from_armoury = true, to_armoury = true) co var _loudouts = current_unit_squad_data[$ "loadout"]; while (_members_with_role.number() > 0) { _unit = _members_with_role.pop(); - if (array_contains(ignore_units, _unit.uid)) { - continue; - } - if (_unit.role() != unit_role) { + if (_unit.role() != unit_role && _unit.role() != _actual_role) { continue; } + // Required loadout is always applied — ignore_units only gates optional extras if (required_load != undefined && struct_exists(required_load, current_load_slot)) { var _needed_required = equip_required_for_role(_unit); if (_needed_required) { @@ -191,12 +233,49 @@ function SquadEquipmentSorting(squad, from_armoury = true, to_armoury = true) co } } + // Optional loadout respects ignore_units (units that already got a full equipment set) + if (array_contains(ignore_units, _unit.uid)) { + continue; + } + if (optional_load != undefined && struct_exists(optional_load, current_load_slot)) { equip_optional_for_role(_unit); } } }; + // Picks ONE entry (loadout category) at random, then resolves each slot: + // string values are used directly; array values get one item picked at random. + // Any slot omitted from an entry is left unchanged on the unit. + // + // JSON example: + // "random_pick": [ + // { "wep1": ["Sword","Axe","Mace"], "wep2": ["Pistol","Plasma","Volkite"] }, + // { "wep1": "Lightning Claw", "wep2": "Lightning Claw" } + // ] + static equip_random_pick_for_role = function(pick_options) { + var _actual_role = role_key_to_actual[$ unit_role]; + var _members_with_role = members_UnitGroup.get_from({roles: [unit_role, _actual_role]}); + while (_members_with_role.number() > 0) { + var _unit = _members_with_role.pop(); + if (array_contains(ignore_units, _unit.uid)) continue; + if (_unit.role() != unit_role && _unit.role() != _actual_role) continue; + + // Pick a random loadout category + var _chosen = pick_options[irandom(array_length(pick_options) - 1)]; + + // Resolve slots: array values → random element; strings → used as-is + var _resolved = {}; + var _slots = struct_get_names(_chosen); + for (var _s = 0; _s < array_length(_slots); _s++) { + var _slot = _slots[_s]; + var _value = _chosen[$ _slot]; + _resolved[$ _slot] = is_array(_value) ? array_random_element(_value) : _value; + } + _unit.alter_equipment(_resolved, from_armoury, to_armoury); + } + }; + static role_squad_loadout = function() { required_load = undefined; optional_load = undefined; @@ -222,6 +301,11 @@ function SquadEquipmentSorting(squad, from_armoury = true, to_armoury = true) co current_load_slot = load_out_areas[i]; equip_loudouts_specific_equip_slot(); } + + // random_pick runs after required/option — picks one complete loadout at random + if (struct_exists(_loudout_data, "random_pick")) { + equip_random_pick_for_role(_loudout_data[$ "random_pick"]); + } }; } @@ -320,10 +404,10 @@ function UnitSquad(squad_type = undefined, company = 0) constructor { }; // for creating a new sergeant from existing squad members - static new_sergeant = function(veteran = false) { + static new_sergeant = function(veteran = false, target_role = undefined) { var exp_unit = ""; var _unit; - var highest_exp = 0; + var highest_exp = -1; var member_length = array_length(members); for (var i = 0; i < member_length; i++) { _unit = fetch_unit(members[i]); @@ -333,7 +417,7 @@ function UnitSquad(squad_type = undefined, company = 0) constructor { i--; continue; } - if (_unit.experience > highest_exp) { + if (exp_unit == "" || _unit.experience > highest_exp) { highest_exp = _unit.experience; exp_unit = _unit; } @@ -341,7 +425,9 @@ function UnitSquad(squad_type = undefined, company = 0) constructor { if ((array_length(members) > 0) && is_struct(exp_unit)) { if (exp_unit.name() != "") { var new_role; - if (veteran == true) { + if (target_role != undefined) { + new_role = target_role; + } else if (veteran == true) { new_role = obj_ini.role[100][19]; } else { new_role = obj_ini.role[100][18]; @@ -374,6 +460,8 @@ function UnitSquad(squad_type = undefined, company = 0) constructor { var squad_unit_types = struct_get_names(fill_squad); //find out what type of units squad consists of var unit_type_count = array_length(squad_unit_types); + // build actual_role → json_key map to handle slots with a "role" override + var _actual_to_key = {}; for (var i = unit_type_count - 1; i >= 0; i--) { var _wanted_unit_role = squad_unit_types[i]; if (_wanted_unit_role == "type_data") { @@ -381,6 +469,9 @@ function UnitSquad(squad_type = undefined, company = 0) constructor { continue; } squad_fulfilment[$ _wanted_unit_role] = 0; //create a fulfilment structure to log members of squad + var _role_def = fill_squad[$ _wanted_unit_role]; + var _mapped = struct_exists(_role_def, "role") ? _role_def.role : _wanted_unit_role; + _actual_to_key[$ _mapped] = _wanted_unit_role; } var member_length = array_length(members); for (var i = member_length - 1; i >= 0; i--) { @@ -390,10 +481,13 @@ function UnitSquad(squad_type = undefined, company = 0) constructor { array_delete(members, i, 1); continue; } - if (struct_exists(squad_fulfilment, _unit.role())) { - squad_fulfilment[$ _unit.role()]++; + // map actual role to json key so role-overridden slots are counted correctly + var _unit_role = _unit.role(); + var _slot_key = struct_exists(_actual_to_key, _unit_role) ? _actual_to_key[$ _unit_role] : _unit_role; + if (struct_exists(squad_fulfilment, _slot_key)) { + squad_fulfilment[$ _slot_key]++; } else { - squad_fulfilment[$ _unit.role()] = 1; + squad_fulfilment[$ _slot_key] = 1; } } fulfilled = true; @@ -408,8 +502,33 @@ function UnitSquad(squad_type = undefined, company = 0) constructor { var _min_role_allowed = fill_squad[$ _wanted_unit_role][$ "min"]; if (fill_from != undefined) { - while (fill_from.has_role(_wanted_unit_role) && _squad_role_current < _max_role_count) { - var _new_member = fill_from.pop_role_member(_wanted_unit_role); + var _role_def = fill_squad[$ _wanted_unit_role]; + var _fill_role = struct_exists(_role_def, "role") + ? _role_def.role : _wanted_unit_role; + // Also try the JSON key itself as a source role (base role before squad rename) + var _fill_role_base = _wanted_unit_role; + // Build the ordered list of acceptable source roles: the mapped role, the JSON + // key, then any alternative_roles. create_squad considers alternative_roles when + // fetching/matching marines, so refill must too — otherwise valid replacement + // marines (e.g. bikers for a bike_squad) are ignored when scr_company_order + // updates existing squads. + var _fill_roles = [_fill_role, _fill_role_base]; + if (struct_exists(_role_def, "alternative_roles")) { + var _alts = _role_def.alternative_roles; + for (var _ai = 0; _ai < array_length(_alts); _ai++) { + array_push(_fill_roles, _alts[_ai]); + } + } + while (_squad_role_current < _max_role_count) { + var _pick_role = ""; + for (var _fri = 0; _fri < array_length(_fill_roles); _fri++) { + if (fill_from.has_role(_fill_roles[_fri])) { + _pick_role = _fill_roles[_fri]; + break; + } + } + if (_pick_role == "") break; + var _new_member = fill_from.pop_role_member(_pick_role); add_member(_new_member.company, _new_member.marine_number); squad_fulfilment[$ _wanted_unit_role]++; _squad_role_current = squad_fulfilment[$ _wanted_unit_role]; @@ -427,19 +546,23 @@ function UnitSquad(squad_type = undefined, company = 0) constructor { required[$ _wanted_unit_role] = _min_role_allowed - _squad_role_current; } } - var _sarge = obj_ini.role[100][eROLE.SERGEANT]; - if (struct_exists(required, _sarge)) { - if (required[$ _sarge] > 0) { - new_sergeant(); - required[$ _sarge]--; - } - } - //find a new veteran sergeant - var _vet_sarge = obj_ini.role[100][eROLE.VETERANSERGEANT]; - if (struct_exists(required, _vet_sarge)) { - if (required[$ _vet_sarge] > 0) { - new_sergeant(true); - required[$ _vet_sarge]--; + var _default_sarge = obj_ini.role[100][eROLE.SERGEANT]; + var _default_vet_sarge = obj_ini.role[100][eROLE.VETERANSERGEANT]; + var _required_keys = struct_get_names(required); + for (var _ri = 0; _ri < array_length(_required_keys); _ri++) { + var _req_key = _required_keys[_ri]; + if (required[$ _req_key] <= 0) continue; + var _role_def = fill_squad[$ _req_key]; + if (_role_def == undefined) continue; + var _actual_role = struct_exists(_role_def, "role") ? _role_def.role : _req_key; + if (_req_key == _default_sarge || _actual_role == _default_sarge + || string_lower(_req_key) == "sergeant") { + new_sergeant(false, _actual_role); + required[$ _req_key]--; + } else if (_req_key == _default_vet_sarge || _actual_role == _default_vet_sarge + || string_lower(_req_key) == "veteran sergeant") { + new_sergeant(true, _actual_role); + required[$ _req_key]--; } } }; @@ -705,17 +828,104 @@ function UnitSquad(squad_type = undefined, company = 0) constructor { }; } -// creates the origional distribution of squads accross the chapter -// lots of room for customisation of different chapters here +/// @function resolve_company_arrangement +/// @description Resolves the squad template for a specific company number from a loaded +/// arrangement struct. Explicit per-company entries take priority; if none matches, +/// the arrangement's default_squads array is wrapped and returned. Returns undefined +/// if the arrangement contains neither a matching company entry nor a default_squads. +/// @param {Struct} arrangement A parsed squad-arrangement struct (e.g. from lightning_warriors.json). +/// Expected fields: optional {Array} companies, optional {Array} default_squads. +/// @param {Real} company_number The 1-based company index to resolve a template for. +/// @return {Struct|Undefined} A company template struct with fields {Real} company and {Array} squads, +/// or undefined if no template can be resolved. +function resolve_company_arrangement(arrangement, company_number) { + if (struct_exists(arrangement, "companies")) { + var _companies = arrangement.companies; + for (var i = 0; i < array_length(_companies); i++) { + if (_companies[i].company == company_number) { + return _companies[i]; + } + } + } + if (struct_exists(arrangement, "default_squads")) { + return { company: company_number, squads: arrangement.default_squads }; + } + return undefined; +} +/// @function apply_squad_distribution_override +/// @description Merges a distribution_overrides entry into a loaded arrangement struct in-place. +/// Two operations are performed: +/// 1. If the override defines default_squads, a deep clone of that array replaces +/// arrangement.default_squads. Cloning keeps the two references independent so +/// any future in-place mutation of one cannot corrupt the other. +/// 2. If the override defines a companies array, each entry is upserted into +/// arrangement.companies — matching on the company number field, replacing an +/// existing entry if found or appending if not. +/// Squad order within default_squads and company squads arrays matters: squads that +/// only accept their own marine role (e.g. devastator_squad, assault_squad) must be +/// listed before squads that use alternative_roles (e.g. bike_squad, attack_bike_squad) +/// so that specific squads claim their marines before greedy squads can absorb them. +/// @param {Struct} arrangement The live chapter_squad_arrangement struct to mutate. +/// @param {Struct} override One distribution_overrides child struct from the same JSON +/// (e.g. arrangement.distribution_overrides.equal_specialists). +/// Expected optional fields: {Array} default_squads, {Array} companies. +/// @return {Undefined} +function apply_squad_distribution_override(arrangement, override) { + if (struct_exists(override, "default_squads")) { + arrangement.default_squads = variable_clone(override.default_squads) + } + if (struct_exists(override, "companies")) { + if (!struct_exists(arrangement, "companies")) { + arrangement.companies = []; + } + var _ovr_companies = override.companies; + for (var oi = 0; oi < array_length(_ovr_companies); oi++) { + var _ovr = _ovr_companies[oi]; + var _found = false; + for (var ai = 0; ai < array_length(arrangement.companies); ai++) { + if (arrangement.companies[ai].company == _ovr.company) { + arrangement.companies[ai] = _ovr; + _found = true; + break; + } + } + if (!_found) { + array_push(arrangement.companies, _ovr); + } + } + } +} + +/// @function game_start_squads +/// @description Populates obj_ini.squads at game start by iterating every company and calling +/// organise_by_template with the resolved squad template for that company. +/// Templates are resolved from obj_ini.chapter_squad_arrangement via +/// resolve_company_arrangement; companies with no resolvable template are skipped. +/// Must be called after obj_ini.chapter_squad_arrangement has been fully built +/// (including any apply_squad_distribution_override calls) and after all marine +/// individuals have been created by the count-based initialisation pass. +/// @return {Undefined} function get_compay_squad_arrangement(company){ - var _comp_datas = obj_ini.chapter_squad_arrangement.companies; + var _arrangement = obj_ini.chapter_squad_arrangement; + if (!struct_exists(_arrangement, "companies")) { + _arrangement.companies = []; + } + var _comp_datas = _arrangement.companies; for (var i = 0; i < array_length(_comp_datas); i++) { if (_comp_datas[i].company == company){ return _comp_datas[i]; } } + // No explicit entry: this company currently inherits default_squads. Promote it to its own + // explicit entry, deep-cloning default_squads so the editor's in-place edits can't mutate the + // shared array every other defaulted company also points at. Registering it persists the edits + // and lets resolve_company_arrangement pick this company up by its own entry from now on. + var _src = struct_exists(_arrangement, "default_squads") ? _arrangement.default_squads : []; + var _entry = { company: company, squads: variable_clone(_src) }; + array_push(_comp_datas, _entry); + return _entry; } function ProportionalSquadEditor(data) constructor { @@ -1124,11 +1334,11 @@ function SquadArrangementEditor(company) constructor { function game_start_squads() { obj_ini.squads = {}; - if (struct_exists(chapter_squad_arrangement, "companies")) { - var _comp_datas = obj_ini.chapter_squad_arrangement.companies; - for (var i = 0; i < array_length(_comp_datas); i++) { - var _company = collect_company(_comp_datas[i].company); - _company.organise_by_template(_comp_datas[i]); + for (var co = 1; co <= obj_ini.companies; co++) { + var _data = resolve_company_arrangement(obj_ini.chapter_squad_arrangement, co); + if (_data != undefined) { + var _company = collect_company(co); + _company.organise_by_template(_data); } } } diff --git a/scripts/scr_ui_formation_bars/scr_ui_formation_bars.gml b/scripts/scr_ui_formation_bars/scr_ui_formation_bars.gml index 36bcf1ad74..6728c8754b 100644 --- a/scripts/scr_ui_formation_bars/scr_ui_formation_bars.gml +++ b/scripts/scr_ui_formation_bars/scr_ui_formation_bars.gml @@ -31,6 +31,7 @@ function scr_ui_formation_bars() { { unit_id: 11, bat_for: bat_drea_for, size: 2, image_index: 11, unit_type: "Dread", tooltip: "Dreadnoughts", tooltip2: "Dreadnoughts are the most durable and tough marines within your chapter. They are best suited for the front lines." }, { unit_id: 12, bat_for: bat_hire_for, size: 1, image_index: 7, unit_type: "???", tooltip: "Hirelings", tooltip2: "Any and all units that you recieve from other factions are placed within this block." }, { unit_id: 16, bat_for: bat_landspee_for, size: 2, image_index: 14, unit_type: "Land Speeder", tooltip: "Land Speeders", tooltip2: "Land Speeders are incredibly agile attack vehicles that offer a light highly mobile heavy weapon platform." }, + { unit_id: 18, bat_for: bat_bike_for, size: 2, image_index: 14, unit_type: "Biker", tooltip: "Bikers", tooltip2: "Bikers are the swift deathbringers of the Astartes Chapters. Descending upon a foe with speed impossible to follow by mortal means, they decimate enemies in close range by a powerful assortment of melee weapons or heavy ranged weapons on their Attack Bikes." }, ]; var _attack_only_options = [ { unit_id: 13, bat_for: bat_rhin_for, size: 4, image_index: 12, unit_type: "Rhino", tooltip: "Rhinos", tooltip2: "Rhinos offer protection for units behind them but are not well armoured and lacking in firepower." }, @@ -48,7 +49,7 @@ function scr_ui_formation_bars() { temp[ui_formations_data.te] = 0; temp[ui_formations_data.te + 100] = 0; - for (var unit_id = 0; unit_id <= 17; unit_id++) { + for (var unit_id = 0; unit_id <= 18; unit_id++) { for (var _i = 0; _i < array_length(_bar_configs); _i++) { var _cfg = _bar_configs[_i]; @@ -74,6 +75,7 @@ function scr_ui_formation_bars() { if (temp[4800 + bar] > 10) { bat_deva_for[bar] = 1; bat_assa_for[bar] = 4; + bat_bike_for[bar] = 4; bat_tact_for[bar] = 2; bat_vete_for[bar] = 2; bat_hire_for[bar] = 3; diff --git a/scripts/scr_ui_settings/scr_ui_settings.gml b/scripts/scr_ui_settings/scr_ui_settings.gml index a4a06bb21c..462e457b85 100644 --- a/scripts/scr_ui_settings/scr_ui_settings.gml +++ b/scripts/scr_ui_settings/scr_ui_settings.gml @@ -66,6 +66,8 @@ function setup_ui_chapter_settings(){ eROLE.DEVASTATOR, eROLE.ASSAULT, eROLE.SCOUT, + eROLE.BIKER, + eROLE.ATTACK_BIKER, eROLE.SERGEANT, eROLE.VETERANSERGEANT, ]; @@ -529,6 +531,7 @@ function scr_ui_settings() { bat_formation_type[formating] = 1; bat_deva_for[formating] = 1; bat_assa_for[formating] = 4; + bat_bike_for[formating] = 4; bat_tact_for[formating] = 2; bat_vete_for[formating] = 2; bat_hire_for[formating] = 3;