From a81e56dbdb2021ae5741425a512b4d7a503543fc Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 19 Mar 2025 22:32:14 -0400 Subject: [PATCH 01/93] ARFs and Crab Base --- .vscode/settings.json | 8 +-- contrib/cards/Republic/RepublicUnits.json | 34 +++++++++++++ contrib/cards/Separatist/SeparatistUnits.json | 2 +- contrib/cards/official.json | 50 +++++++++++++++++++ 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index dd173d2c..fd1372d1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,10 +11,10 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "editor.codeActionsOnSave": { - "source.fixAll": true, - "source.fixAll.eslint": true, - "source.fixAll.tslint": true, - "source.organizeImports": true + "source.fixAll": "explicit", + "source.fixAll.eslint": "explicit", + "source.fixAll.tslint": "explicit", + "source.organizeImports": "explicit" }, "editor.formatOnSave": true, "prettier.ignorePath": ".gitignore", diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index b6955112..8b71188c 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -448,6 +448,40 @@ } ] }, + { + "name": "ARF Troopers", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59215140638617511/EF41D3B9F89F86EC4E803863C17D1549052C8B55/", + "size": "small", + "type": "Clone Trooper", + "points": 66, + "speed": 2, + "upgrades": { + "Squad Leader": 1, + "Heavy Weapon": 1, + "Personel": 1, + "Training": 1, + "Comms": 1, + "Grenades": 1 + }, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16428815149933614/80D6829DB63FAE3328D00AC36F00E5FD71251E69/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16428815149933714/D3FE0C2DEFBE1E78A71A55516DF87199D66DE2AC/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16428815149649053/B24484CFED06020454EE95B663DB3ECE17871D26/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16428815149646949/50B6AA8C2CC5283537DBA2291F9801E7CD5FE77C/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16428815149643329/8D273B40809BF89188238DE6FC5C207756AA22EF/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16428815149643405/9A3D0A513A31513523E59855980DFCE0A8F0F6E0/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16428815149932871/EE8DF333D45ED841E9C31625FB4A9385C16B2F49/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16428815149933386/736E0CCF2ADE31D6A9F6BE172B20AEB5420B815E/" + } + ] + }, { "name": "Wookiee Warriors", "image": "http://cloud-3.steamusercontent.com/ugc/1619598366708171464/0A12FC8F203E5376156D3C2A207E33E1F5ACB0B6/", diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index d91f0f98..6076f035 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -633,7 +633,7 @@ { "name": "LM-432 Crab Droid", "image": "https://steamusercontent-a.akamaihd.net/ugc/2491139050708063278/ED3246E4E67C5213E1DDDA3064AFAB7213BDD737/", - "size": "medium", + "size": "large", "type": "Ground Vehicle", "points": 70, "speed": 1, diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 4bf64b15..825cce62 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1126,6 +1126,20 @@ } ], "Heavy Weapon": [ + { + "name": "RPS-6 ARF Trooper", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59215140638617630/9AB204944DB662D2D15FC2FA5E50F02138AD1FD6/", + "mini": { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16428815149645590/1B9C028584B5A7DCD488AE1E9C57D33A1B93C92E/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16428815149646251/ADB16A6BF3CADD49D0237A4AB51FB4D7AF0AB521/" + }, + "points": 24, + "restrictions": { + "include": { + "unit": ["ARF Troopers"] + } + } + }, { "name": "Heavy Aqua Droid", "image": "https://steamusercontent-a.akamaihd.net/ugc/1794100632571533/28837AF9D4578A64FDDBF1A1FBCF01B1B500C263/", @@ -2350,6 +2364,26 @@ } ], "Personnel": [ + { + "name": "ARF Trooper Duo", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59215140639032161/5D3AFD658FC9FC30314A98DF8C5F0A7910B4ADF2/", + "minis": [ + { + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/16428815149932608/94AB8C15D97E81FF8040E81BC91F79A7105FF4B4/", + "secondary": "https://steamusercontent-a.akamaihd.net/ugc/16428815149932637/F5D29AC3162A9D47B5440E5A3E3399FC12A594BE/" + }, + { + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/16428815149932871/EE8DF333D45ED841E9C31625FB4A9385C16B2F49/", + "secondary": "https://steamusercontent-a.akamaihd.net/ugc/16428815149933386/736E0CCF2ADE31D6A9F6BE172B20AEB5420B815E/" + } + ], + "points": 26, + "restrictions": { + "include": { + "unit": ["ARF Troopers"] + } + } + }, { "name": "2-1B Medical Droid", "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137676/C282622A211B384895C61D5EE0811E4E627FBDBF/", @@ -3549,6 +3583,22 @@ } } ], + "Squad Leader": [ + { + "name": "Jedi Guardian", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59215140638617630/9AB204944DB662D2D15FC2FA5E50F02138AD1FD6/", + "mini": { + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/16426912929621400/CC4D94EDC24D933D7333F5A35308606846A9B550/", + "secondary": "https://steamusercontent-a.akamaihd.net/ugc/16426912929621469/4B200440B54994D37A5B3667E04769CD92DE4F32/" + }, + "points": 22, + "restrictions": { + "include": { + "faction": ["Republic"] + } + } + } + ], "Training": [ { "name": "Defensive Stance", From 893c5f1f8a3c68cb9a15a4cd739b023e70d4e34b Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 19 Mar 2025 22:50:34 -0400 Subject: [PATCH 02/93] Welcome Update Arf/Crab --- mod/src/includes/ui/Welcome.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index b4162ff8..568b81b2 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -37,7 +37,7 @@ - Welcome to Star Wars: Legion for TTS, v8.0.9-beta. + Welcome to Star Wars: Legion for TTS, v8.0.10-beta. This is a beta release, and is not suitable for tournament play. @@ -47,6 +47,8 @@ (Since last Beta) - Add: Aqua Droids + - Add: ARF Troopers + - Fix: Crab Droid base size - Fix: A Beautiful Friendship typo - Fix: Nanny Programming typo From 92b86a1bf9726526a8762e4cf331645491d42da3 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 12 Apr 2025 19:55:15 -0400 Subject: [PATCH 03/93] New Sleeper/Crab/Stap models --- contrib/cards/Rebel/RebelUnits.json | 24 +++++++++---------- contrib/cards/Separatist/SeparatistUnits.json | 10 ++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index 2af800f4..5280737b 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -1060,28 +1060,28 @@ }, "minis": [ { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862746147/3F3A21962C4AC0DA0445024688BF580A144D3825/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540540670236/6257E7A3CE86803490A8A0FAC7F81C281D700F52/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/2508033889679621098/DAC14B407CC47E5C2B4A3AFCF87A704907C3940A/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/2508033889679614622/B379336947ACE47BF9FBEECA30A93F6ABCD3842B/" }, { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862752262/41BD6AB8B17AB7449226A3D80EF186EB8ABA3952/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862750426/2563904D7561144EE85EBCD3E07D68F48D8DD034/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680567004/0BB417962A0ABE0E6381B767782F6BBA6AE62CE8/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680567174/19D7BF881EDCD2F7FB8718CD79E678CFF56495FF/" }, { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862764799/98CBD8B4DD0C08CFA01A8E6C8B5A70A137804EAB/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862750426/2563904D7561144EE85EBCD3E07D68F48D8DD034/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680568477/ECEA13C55B0C37AED1B8066D2158BE7F9A84FB32/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680568730/452F1CC25775CDE1D107BC6C457E22740182F594/" }, { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862755846/CD522BCA5D6E918E13A3FF593D7212FF4B0E79CA/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862750426/2563904D7561144EE85EBCD3E07D68F48D8DD034/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680572703/37BA3BE34A5FEBE80DB7A4EB6FB3BBFD87CCE22D/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680573104/5EFBE526BA06E0026124917566EC020AACDBB9EB/" }, { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862755846/CD522BCA5D6E918E13A3FF593D7212FF4B0E79CA/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862750426/2563904D7561144EE85EBCD3E07D68F48D8DD034/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680574338/C9C5456700D989BA3389499336AC8C5DA7586755/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680574595/595808E8FBD39F09F2754FE48F97A4A493BD3A37/" }, { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862755846/CD522BCA5D6E918E13A3FF593D7212FF4B0E79CA/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862750426/2563904D7561144EE85EBCD3E07D68F48D8DD034/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680574338/C9C5456700D989BA3389499336AC8C5DA7586755/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680574595/595808E8FBD39F09F2754FE48F97A4A493BD3A37/" } ] } diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index 6076f035..132ee75a 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -648,7 +648,7 @@ }, "minis": [ { - "bundle": "https://steamusercontent-a.akamaihd.net/ugc/2491139050708070860/182C259E1AD62DCEEA34E2F87EE1A4D50908E956/", + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/16432158164944196/4594ADE002844CEEB6FB9A75E17AA802FCAE89FF/", "secondary": "https://steamusercontent-a.akamaihd.net/ugc/2491139050708070994/E763691AC8C24925DA2CA117DE90A54BAC836230/" } ] @@ -669,12 +669,12 @@ }, "minis": [ { - "mesh": "http://cloud-3.steamusercontent.com/ugc/1003681674396285422/B2205A5D8B8CC1718D0C9B507BC3E33BCA815567/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/1752483394081532878/1465E195EC1DB588E665F2951E496E7786239B35/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16431529699543053/6B293027828F96F5A6DF95E8E0D4238F13FF2520/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16431529699543124/C68D34EDD49D80DF4CCD801A50C085BFDE59E13D/" }, { - "mesh": "http://cloud-3.steamusercontent.com/ugc/1003681674396285422/B2205A5D8B8CC1718D0C9B507BC3E33BCA815567/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/1752483394081532941/5B2C00FAB833D43C8381F9431786DF89F54CED12/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16431529699543293/DE945637F2A703763288FF04C08FF27A52F1BB62/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16431529699543380/AB5C8DBAC327E95B4938F575EBFB88A340519A53/" } ] }, From d9d626bd0d8e87721677eed3091e1d9a2d5f3472 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 17 Apr 2025 01:21:20 -0400 Subject: [PATCH 04/93] CIS Unit and CC --- .vscode/settings.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index dd173d2c..fd1372d1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,10 +11,10 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "editor.codeActionsOnSave": { - "source.fixAll": true, - "source.fixAll.eslint": true, - "source.fixAll.tslint": true, - "source.organizeImports": true + "source.fixAll": "explicit", + "source.fixAll.eslint": "explicit", + "source.fixAll.tslint": "explicit", + "source.organizeImports": "explicit" }, "editor.formatOnSave": true, "prettier.ignorePath": ".gitignore", From e4374f3ea0704d2120f8b36045f3bada882a8ffc Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 17 Apr 2025 01:26:16 -0400 Subject: [PATCH 05/93] Generic CIS CC --- contrib/cards/official.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 2e9e39ea..355d018b 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -3939,32 +3939,32 @@ "Separatist": [ { "name": "Mechanized Incursion", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036372/8ED4D92B15E96CF7AB14E89F832262CFF2E55201/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525764/985E35D1F4C14B36C2DDE31CD8426CD3F9499744/", "pip": 1 }, { "name": "AI Coordination", - "image": "http://cloud-3.steamusercontent.com/ugc/1760364156116225551/07EF32E9863F388D901222A5EA9418AB5E88AA5E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131524713/997729685C3711220AD15A620B602F572C0D83E7/", "pip": 1 }, { "name": "Orbital Strike", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034923/43292A8BF63ECE8696FC48EE6273F006BF454A78/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525802/A728666B0EE576499983022E283BD20396880699/", "pip": 2 }, { "name": "Unrelenting Assault", - "image": "http://cloud-3.steamusercontent.com/ugc/1760364156116225627/66471D75B7566E23EC69F1C531AF8F8651AAEEA5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131527688/D4BA941B65A6BB4EB1F08EE5D23C987317D50D8C/", "pip": 2 }, { "name": "Roger, Roger!", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033233/90EE522128D2F8D84425C25F4FC06F2A5A9B09B2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525839/61442FE7510A1E55554FABC1937020A1143104F1/", "pip": 3 }, { "name": "Unstoppable Advance", - "image": "http://cloud-3.steamusercontent.com/ugc/1760364156116225687/B5F68783FEE26F30B0262C0679D0743E5E413AE0/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131527719/02ACE80E8D14CF7DC017156A128F34A09F725CF2/", "pip": 3 } ] From ab079209e75b74729e75bde9893a098615d9695a Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 17 Apr 2025 01:28:38 -0400 Subject: [PATCH 06/93] Update SeparatistUnits.json --- contrib/cards/Separatist/SeparatistUnits.json | 123 +++++++++--------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index a5041ab8..221e7a67 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -3,7 +3,7 @@ { "name": "General Grievous", "title": "Sinister Cyborg", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384862300/B8EEAEEF889F674B7EACE684621043E0EE0297FF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432388/26B30B4276062FA8693A0D3E566671DAD9DF5DD2/", "size": "small", "type": "Trooper", "points": 130, @@ -22,17 +22,17 @@ "commands": [ { "name": "Trained In Your Jedi Arts", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035917/02823FB99DD83199FB8D24F9257F684A251C91D2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131526384/C4406A3487D2E58D1AF4962B9A306EC5882596CC/", "pip": 1 }, { "name": "Supreme Commander", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384887340/8D34B91D3F83FDB5F5D3DE9DF60AE96610B6C80E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525863/124C8C8CAA960D6680FD429BD85DA760AFD9040A/", "pip": 2 }, { "name": "Crush Them!", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033887/5DDF0AA77123DE906D2FA6A681B83BB73A48AAF5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131524786/7D9A98904E7D5F80F9590AAA5EDF107F2B95623A/", "pip": 3 } ] @@ -40,8 +40,7 @@ { "name": "Count Dooku", "title": "Darth Tyranus", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384861895/ACA29DBA6132A94BC000D2A449B3E5D58A160E6B/", - "size": "small", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432037/24F46472FEFFC678779BC1CA2FF6B4460E2E039D/", "type": "Trooper", "points": 165, "speed": 2, @@ -58,22 +57,22 @@ "commands": [ { "name": "Fear, Surprise, Intimidation", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036674/940E3CA38FB4BDD50EE7E6775E0C0A9D42C75C4A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525203/8AE25439B2A4589B48502B5BECE8652BC37CF941/", "pip": 1 }, { "name": "Double The Fall", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035517/E8A15E29EA9390DE382A583AE416BC4DDDDA6A90/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525177/CD39DEA23EB51B132F1263FEF158FBED10C5C32A/", "pip": 2 }, { "name": "You Disappoint Me", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564032838/8524059D85CFD5ED5238782110CAE2F821271706/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131527845/7429252D8F4A59BFD43846F93CBA904ADEC133E3/", "pip": 3 }, { "name": "The Sith Will Rule", - "image": "http://cloud-3.steamusercontent.com/ugc/2022718494201423946/9260CA54A68EF00EBC4EA615D3598D7EA8521A0F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131526321/AD7E17FC8BC3387075D2EDAEB5506F4AAEF2084F/", "pip": 3 } ] @@ -81,7 +80,7 @@ { "name": "T-Series Tactical Droid", "title": "Programmed For Strategy", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384864245/54C08F1B6D9FDEC6254F7978FC834110EF509B27/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131433848/B4160FE013FB7B00BA8A6DE876F507A667602F6A/", "size": "small", "type": "Droid Trooper", "points": 60, @@ -134,10 +133,10 @@ ] }, { - "name": "Super Tactical Droid", - "title": "Kalani", + "name": "Kalani", + "title": "Super Tactical Droid", "displayName": "Kalani", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863960/BEA37F409CB6D7E86832C204163602A1034E9E6F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432463/7A808E698411F5F0692FAF805529083CDB7EFAF8/", "size": "small", "type": "Droid Trooper", "points": 85, @@ -156,26 +155,26 @@ "commands": [ { "name": "They Too Will Suffer", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036005/BCB6B4D75C375323E4CA020BB1AA01497BEE11ED/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131526359/B72758B5C45391BA8FDD1C1E6023685EF3B64EBD/", "pip": 1 }, { "name": "Preservation Protocols", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034846/6DFE5FBF65026639A194D1A915193AD147736713/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525817/8473E63B725077F3D5EED0C66B103F218DA907BF/", "pip": 2 }, { "name": "Do Not Underestimate Our Means", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384885557/BB41754355D4B8C5C9E444C1F3A5D41096109F5E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131524814/1C9294E7627AEFC235A525FED15C4EF7B2341C9E/", "pip": 3 } ] }, { - "name": "Super Tactical Droid", - "title": "Kraken", + "name": "Kracken", + "title": "Super Tactical Droid", "displayName": "Kraken", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384864054/DE21637AFE1E6D05758365664F35F4DF8EE75AB3/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131433103/C2CD6A90911C879E745B1B79DFE9B810F02CA25D/", "size": "small", "type": "Droid Trooper", "points": 80, @@ -195,17 +194,17 @@ "commands": [ { "name": "They Too Will Suffer", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036005/BCB6B4D75C375323E4CA020BB1AA01497BEE11ED/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131526359/B72758B5C45391BA8FDD1C1E6023685EF3B64EBD/", "pip": 1 }, { "name": "Preservation Protocols", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034846/6DFE5FBF65026639A194D1A915193AD147736713/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525817/8473E63B725077F3D5EED0C66B103F218DA907BF/", "pip": 2 }, { "name": "Do Not Underestimate Our Means", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384885557/BB41754355D4B8C5C9E444C1F3A5D41096109F5E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131524814/1C9294E7627AEFC235A525FED15C4EF7B2341C9E/", "pip": 3 } ] @@ -219,7 +218,7 @@ { "name": "Poggle the Lesser", "title": "Public Leader of the Geonosians", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863112/18411AD732A186F1240C9E7D5460C7BA823532E2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131433181/C18229FCF2010EC9491505DEA6AECE1F4F21C147/", "size": "small", "type": "Trooper", "points": 80, @@ -237,17 +236,17 @@ "commands": [ { "name": "Let the Executions Begin!", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384886783/0ED71CFA07CD92764B3D63EFEEDEDBB916D40634/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525219/2DC3EA9357F54CB08F97B0B3449B2923DB7FB9A5/", "pip": 1 }, { "name": "We Serve the Queen", - "image": "http://cloud-3.steamusercontent.com/ugc/2058763770174552859/53927EB5DFA6B6D8252BA7238C22D0FE66D6B7C4/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131527776/24A2E956D84B32FBED8B65F05B05BB32560F0E34/", "pip": 2 }, { "name": "We Make Weapons", - "image": "http://cloud-3.steamusercontent.com/ugc/2058763770174552907/24DA15DC290ED1C5718DC7A1B6AEF5024BA6AB98/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131527739/6639117C739334B7AD7980050721AA7C8028B56B/", "pip": 3 } ] @@ -263,10 +262,10 @@ { "name": "Maul", "title": "Impatient Apprentice", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788789734/ABF8B8F5555C2DF64258B4DD432B479059AFF4D9/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131433136/F385AC355EF21CEED5446F6BB6DEB590B4341000/", "size": "small", "type": "Trooper", - "points": 160, + "points": 130, "speed": 2, "upgrades": { "Force": 2, @@ -280,17 +279,17 @@ "commands": [ { "name": "Duel Of The Fates", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036827/1967B20CCD59FE2EAECDC15FA5750928F75963E0/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525195/991D793228F9D99D199201EEF560C5A4F294C4C1/", "pip": 1 }, { "name": "The Phantom Menace", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384887593/7CF6854E9CAF545BD9AA314581960571E853CA16/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131526286/338C5FAB4F7AB7F57BA3C1D5791B2BB1F6B2DC09/", "pip": 2 }, { "name": "At Last", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034186/C08B05E1C969B6ADBB158DE85C499CD938FBC8B5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131524747/50D631657EE81A881136A50FBDA60E8B22DCD9ED/", "pip": 3 } ] @@ -298,10 +297,10 @@ { "name": "Asajj Ventress", "title": "Separatist Assassin", - "image": "http://cloud-3.steamusercontent.com/ugc/2022718494181434667/C588D389B20D907F8CBE90A9BECFE2FE532E439B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131430684/75EA0FFCF13BC00426CBFA0F3F8E390B0FCDF508/", "size": "small", "type": "Trooper", - "points": 150, + "points": 130, "speed": 2, "upgrades": { "Force": 2, @@ -315,17 +314,17 @@ "commands": [ { "name": "The Jedi Shall Fall", - "image": "http://cloud-3.steamusercontent.com/ugc/2022718494181413453/2F0BCBD3DCACECCA22EAFA0875D419828842CB90/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131526250/371EA639E96CFA36BE3A339BE26403B2923D5C1E/", "pip": 1 }, { "name": "I am Fear", - "image": "http://cloud-3.steamusercontent.com/ugc/2022718494181413531/0E6FEAE5D1DE3D211CFF1FF239CB10508C26C306/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131525215/8F52AEE8AA429791E8333BC3F3F8B89E6157246B/", "pip": 2 }, { "name": "Yes, My Master", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384888087/DD910D40E53B54569925191252816A838AF7DBC7/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131527806/5B8009C57EF4BF1EBAEF31CF6D5AC745D12B5730/", "pip": 3 } ] @@ -333,10 +332,10 @@ { "name": "Sun Fac", "title": "Ruthless Lieutenant", - "image": "http://cloud-3.steamusercontent.com/ugc/2058763770174552697/C50E66D538238AF73B7D64B2517B81EEF3B15C8B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131433813/69B81D6A1264FF5AAA8B683580A55D26BF104EAA/", "size": "small", "type": "Trooper", - "points": 90, + "points": 80, "speed": 2, "upgrades": { "Training": 1, @@ -351,7 +350,7 @@ "commands": [ { "name": "Brutal Enforcer", - "image": "http://cloud-3.steamusercontent.com/ugc/2058763770174553169/36269C24F382521B9400DBCFAD29B1B07C27013A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131524776/66DAC77F67654F866C0B521F0AFB46A4CD6FFC06/", "pip": 1 } ] @@ -360,7 +359,7 @@ "Corps": [ { "name": "B1 Battle Droids", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384861432/2E7E4F170E3589207D7375ECBF90A1744238707D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131430739/5F4B6BDDB4CD6561E87D8203CEEA15B77A2B7149/", "size": "small", "type": "Droid Trooper", "points": 38, @@ -399,7 +398,7 @@ }, { "name": "B2 Super Battle Droids", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384861479/A5E8E4A4E46C4E6CC1181DD26DE6BBE37745B727/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131430767/3830309294245496BAC8E0C74A0F2C7DD1253460/", "size": "small", "type": "Droid Trooper", "points": 64, @@ -433,7 +432,7 @@ { "name": "Geonosian Warriors", "title": "Soldiers of the Hive", - "image": "http://cloud-3.steamusercontent.com/ugc/2058763770174552397/FF46EC2B5C807195067F4674DF8DB9690FBD72EB/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432412/D396E8DD6EEDD4AD239A43CCE45BBC28B720DD77/", "size": "small", "type": "Trooper", "points": 45, @@ -465,7 +464,7 @@ "Special Forces": [ { "name": "BX-Series Droid Commandos", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384861636/B8D96EC9CBB16A0BD899D4717A84E04F6D352716/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131431998/46D37A7B1D195729C7E854687F0F287E5DD8DED2/", "size": "small", "type": "Droid Trooper", "points": 64, @@ -501,7 +500,7 @@ "title": "Strike Team", "displayName": "BX-Series Commandos (Strike Team)", "heavyWeaponTeam": true, - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384861548/C225AF8C4745699185516C1F398B1697F6F3C32E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131430807/F260EA4E79ACB29C3C635836A3EF7E7C632E4FB6/", "size": "small", "type": "Droid Trooper", "points": 20, @@ -522,7 +521,7 @@ }, { "name": "DRK-1 Sith Probe Droids", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788789552/446270D5F6DD23C36A7B268F2026A5B9DCFFC8BF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432071/495850F5BE28D1D1551D13D5B86F473D8EB154EC/", "size": "small", "type": "Droid Trooper", "points": 35, @@ -544,7 +543,7 @@ }, { "name": "IG-100 MagnaGuard", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788789521/5E40422D83C71854A725DB0231D790BAA359574E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432444/15103BB5D4C33479CCB11DE66465798D9F92AFAD/", "size": "small", "type": "Droid Trooper", "points": 72, @@ -574,7 +573,7 @@ "name": "IG-100 MagnaGuard", "title": "Prototype Assassin Droids", "displayName": "Prototype Assassin Droids", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384862442/CCBE289DDC3A9AE15D0C1E3C342387C7877ED0B4/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432434/A38C1603DC1173981D6AE179774F385B400DE073/", "size": "small", "type": "Droid Trooper", "points": 56, @@ -607,10 +606,10 @@ "Support": [ { "name": "Droidekas", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788789475/1521CBD0935E6FB62CE544D89AC89E2F19F21D6B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432109/A0B45534B85F46D38A4F372B04041A5193B31426/", "size": "medium", "type": "Ground Vehicle", - "points": 70, + "points": 60, "speed": 1, "upgrades": { "Comms": 1 @@ -654,17 +653,17 @@ }, { "name": "STAP Riders", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788789443/7BE1E58EECCFE75EA2705CD1345E6C0602711AB7/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131433789/32FE5B587A5DD8D208101FCE74CD2BE690483F37/", "size": "medium", "type": "Repulsor Vehicle", - "points": 75, + "points": 80, "speed": 3, "upgrades": { "Comms": 1 }, "silhouette": { - "height": 1.651, - "offset": 0.595 + "height": 2.246, + "offset": 0.0 }, "minis": [ { @@ -679,10 +678,10 @@ }, { "name": "DSD1 Dwarf Spider Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1616219505081189625/4E13A18FC19BA72238A142EE1D475C2EB16CE20A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432141/809B747B4008E7D3CF6EFB9EE1A4E3330A8D7C50/", "size": "large", "type": "Ground Vehicle", - "points": 55, + "points": 50, "speed": 2, "upgrades": { "Programming": 1, @@ -702,9 +701,9 @@ ], "Heavy": [ { - "name": "AAT Trade Federation Battle Tank", + "name": "AAT Battle TanK", "displayName": "AAT Battle Tank", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384860982/2D38788417BF298DD7A4DF81C33E5697161E5242/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131430642/9FCE0FB0F62A9381DF3CE2F786DBC749CB2A8FFF/", "size": "epic", "type": "Repulsor Vehicle", "points": 165, @@ -726,9 +725,9 @@ ] }, { - "name": "NR-N99 Persuader-Class Tank Droid", + "name": "Persuader-Class Tank Droid", "displayName": "Persuader Tank", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863070/EE8C956E5446D47E55C7593F5E03D3811982250E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131496505/E6D1491724153DE1BE82FA00B4630DC24E9877ED/", "size": "snail", "type": "Ground Vehicle", "points": 130, @@ -749,10 +748,10 @@ ] }, { - "name": "NR-N99 Persuader-Class Tank Droid", + "name": "Persuader-Class Tank Droid", "title": "Prototype Tank Droid", "displayName": "Prototype Tank Droid", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863021/3727DBE5C89CA0A49625A8370BDE605862E23BC5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131496505/E6D1491724153DE1BE82FA00B4630DC24E9877ED/", "size": "snail", "type": "Ground Vehicle", "points": 130, From 28f1f2b0204bf26aadefe62b8e976cb5f8447a3e Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 17 Apr 2025 16:04:00 -0400 Subject: [PATCH 07/93] Rebel Unit and CCs --- contrib/cards/Rebel/RebelUnits.json | 329 +++++++++--------- contrib/cards/Separatist/SeparatistUnits.json | 3 +- contrib/cards/official.json | 20 +- 3 files changed, 176 insertions(+), 176 deletions(-) diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index 5280737b..30b736b4 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -3,7 +3,7 @@ { "name": "Luke Skywalker", "title": "Hero Of The Rebellion", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384862838/DB9F4B7D404060C77F61A5317A71E7203AB1CC02/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133196942/E1A8548A9C0688D979CAD83F32F130B0115D9C50/", "size": "small", "type": "Trooper", "points": 120, @@ -23,32 +23,32 @@ "commands": [ { "name": "Son Of Skywalker", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036127/CF8D8E07205CB523EA3D2EE71AD61D9FB96B17BB/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133712958/CF63D0FDA40318CE886E6DA309CEB291857776E4/", "pip": 1 }, { "name": "You Serve Your Master Well", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035766/83D508C088FC9B893BDA1C497410726302D84452/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133714680/FD14E558D7D2B7EF32BD992FFC1C89141CD1F192/", "pip": 1 }, { "name": "My Ally Is The Force", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035023/A55A52AA8780EEFEB9B8DB24FE4294A844B3678E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711308/CECAE0B5B62858582522F7FBED6463FE5834A64E/", "pip": 2 }, { "name": "Full Of Surprises", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035386/B9B2E0999D5AA4C072B3C45FBD2185A0D7F6A9F5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709999/FA890B2A2EA1528C8BCF2A80AEB3B97A97BB9E15/", "pip": 2 }, { "name": "Return Of The Jedi", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033269/7606E86C0B998BBB4DAADE6F01B300A7931B697C/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711996/90C4CB28C0554DEC3797882252E6C5D3CCAC65A1/", "pip": 3 }, { "name": "I Am A Jedi", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033560/7EFDE383C076E6F171E6B868D1F5C7EE66A466CE/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133710140/F162DE57287EB8C43F845BF34550B6666929E36A/", "pip": 3 } ] @@ -56,7 +56,7 @@ { "name": "Leia Organa", "title": "Fearless And Inventive", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384862782/FAC47493209B1AACD84C4615FD772419D0084A92/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133196907/3603F95E0847EC7E202162FF2BA8E587A3B8E8D8/", "size": "small", "type": "Trooper", "points": 75, @@ -75,22 +75,22 @@ "commands": [ { "name": "Coordinated Bombardment", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036952/79DBBA37340FFEA238E71B07D6969F19AD1B8BD1/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709038/5F4C655FAD4092BA114E76531B141C09D17A413C/", "pip": 1 }, { "name": "No Time For Sorrows", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034953/BDC08D664C3DC9DFE785E312F2F0517921B3C799/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711809/CE6019479334BF3204C4BD05DF6615B7ABA02B44/", "pip": 2 }, { "name": "A Beautiful Friendship", - "image": "http://cloud-3.steamusercontent.com/ugc/2023850162090082691/D81307AA7C4530A264B8CBAA2012FCA9134EA9B4/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133708647/EA50E83AC0C23B932428034601250A9C698E8F87/", "pip": 2 }, { "name": "Somebody Has To Save Our Skins", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564032996/852C2F232B15634E443A2317ED2D2316BA7496C4/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133712915/7EFEAD84C8B64A86D130C765225552D91AEAF814/", "pip": 3 } ] @@ -98,7 +98,7 @@ { "name": "Han Solo", "title": "Unorthodox General", - "image": "http://cloud-3.steamusercontent.com/ugc/1860546411028299008/F99C465CC6C73855A993B00184FD601B4E810F59/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133196276/8433A2CEDE2DA7DE9EFEE663DCD27875BA7CA3F6/", "size": "small", "type": "Trooper", "points": 100, @@ -117,17 +117,17 @@ "commands": [ { "name": "Sorry About The Mess", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036094/C7F82426F9CDB6835C1DC91F0584AADE44F207EE/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133713891/5010BC7FF52D6539147756EF04579FD860630D4E/", "pip": 1 }, { "name": "Reckless Diversion", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384887081/AC30D2023D9B849EF6E6EFF834271F5862F5189F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711955/8E5E53B03E9248B3BB6CCBC1942C831C02ACFF67/", "pip": 2 }, { "name": "Change Of Plans", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034095/F1EFCCA49BBF25049B679261404156EE2B259936/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133708965/8DA6716707434EDAF86655364C9557E3408B9B5E/", "pip": 3 } ] @@ -151,94 +151,19 @@ } ] }, - { - "name": "Jyn Erso", - "title": "Stardust", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788603895/5D4275A9C4F93D4170453A9375C308ABCFE318A1/", - "size": "small", - "type": "Trooper", - "points": 90, - "speed": 2, - "upgrades": { - "Command": 1, - "Training": 1, - "Gear": 1, - "Armament": 1 - }, - "minis": [ - { - "mesh": "http://cloud-3.steamusercontent.com/ugc/951848302305475778/55B558E631655D0430BAF76E71C93899AAECA7A1/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/951848302305475871/06400240648CC39F933BE504C74DA9BEF4D006C4/" - } - ], - "commands": [ - { - "name": "Rebellious", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036244/9C57ADB262F7193BE9B7C07B645915C58498EEDA/", - "pip": 1 - }, - { - "name": "Trust Goes Both Ways", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034481/2822AD43412B26BC38EF8F091201BA501D4FDBD0/", - "pip": 2 - }, - { - "name": "Complete The Mission", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034061/6FF1A4A6372B891E6F06022118ECF635BC40C2F2/", - "pip": 3 - } - ] - }, - { - "name": "Cassian Andor", - "title": "Capable Intelligence Agent", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788603937/418000BFD236B11C08F5EA8AF670673AC63199D2/", - "size": "small", - "type": "Trooper", - "points": 90, - "speed": 2, - "upgrades": { - "Training": 2, - "Gear": 1, - "Armament": 1 - }, - "minis": [ - { - "mesh": "http://cloud-3.steamusercontent.com/ugc/773996263556990389/C38E9CAD7E5BA5496E1A211CA799E9ACBAC51D00/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/770611701143191344/5CAA4F0CE9A3FBE89A81560221ADCDDB5A801CC3/" - } - ], - "commands": [ - { - "name": "Crack Shot", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036864/47CCB05A1DB54F77FADE1652F981DAB2BD39FEE1/", - "pip": 1 - }, - { - "name": "Last Stand", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035098/CD888287DB06A86CEF0E5CA443F2E227C8A0D3E9/", - "pip": 2 - }, - { - "name": "Volunteer Mission", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564032942/4047B661BAEEEFC2CA4DD45010D3029D9FB74C63/", - "pip": 3 - } - ] - }, + { "name": "Lando Calrissian", - "title": "Smooth Operator", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788603976/14129DC0580196A6C188A7D5B037937BE3906C83/", + "title": "Canny General", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133196874/E2254E94528D352BF5AEE9E92D4532EE44E46C7F/", "size": "small", "type": "Trooper", - "points": 95, + "points": 105, "speed": 2, "upgrades": { "Command": 1, "Training": 1, - "Gear": 1, - "Flaw": 1 + "Gear": 1 }, "minis": [ { @@ -247,22 +172,21 @@ ], "commands": [ { - "name": "Idiot's Array", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036507/059AB65ED149A6399024F171211A0D3ECD21F083/", + "name": "Corellian Spike", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709076/0B6C95F039C36F8709EAA7D83441D537E9F10FA9/", "pip": 1 }, { "name": "Ace Up His Sleeve", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035738/83A01C2C466036B1BF2EF3A47F21CB11017522BD/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133708717/1DF313D87604E77DA0B404B8C8388364671B5070/", "pip": 2 }, { - "name": "All In", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034326/44C2D4E2EE50B69C41DDDEA5963FC50152F9AE57/", + "name": "Idiot's Array", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711194/7A82389E04650FF63B89965A425182594B7A7463/", "pip": 3 } - ], - "required": ["I've Altered The Deal"] + ] }, { "content": "contrib/cards/ShadowCollective/PykeSyndicateCapoUnit.json" @@ -270,7 +194,7 @@ { "name": "Wicket", "title": "Hero of Bright Tree", - "image": "http://cloud-3.steamusercontent.com/ugc/2023850162090072588/124E43DA7CDDA8C6B71D1B21264234AE348E6322/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133208061/787C79B49789528F2EF54F1668B0BF28EBD6635C/", "size": "small", "type": "Trooper", "points": 70, @@ -286,17 +210,17 @@ "commands": [ { "name": "Close Call", - "image": "http://cloud-3.steamusercontent.com/ugc/2023850162090072698/DB62A74D030AF926BE96A38707D5976A0E5451FD/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133753263/3C95DC036CEC1999E18A0AB104AEC6EB7FCE3CA6/", "pip": 1 }, { "name": "Sneaky Scouting", - "image": "http://cloud-3.steamusercontent.com/ugc/2023850162090072649/9C830E648CE79DBDE57C0292868EDAC9960B06EF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133752539/377B718FE350EAE5B002F2AC0B72DCB94A98CF08/", "pip": 2 }, { "name": "Courageous Counterattack", - "image": "http://cloud-3.steamusercontent.com/ugc/2023850162090072748/58E123D32E79B1BD0D04F45AB81B7CAF2EC39E92/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133755677/82DB9DB79E1CB8732C43EDD3B117B69EE6DCA6B8/", "pip": 3 } ] @@ -304,7 +228,7 @@ { "name": "Logray", "title": "Superstitious Shaman", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866562278/020C6D8AA24648A687F71982FF2BDA10563C7E2D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133207972/4E2F553BC1FBA06D841591067D3D5E2AED2BF4E5/", "size": "small", "type": "Trooper", "points": 45, @@ -322,7 +246,7 @@ { "name": "C-3P0", "title": "Golden God", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866562667/81CD336814DCD74397F529EA03A934DE9C9C01A0/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133206077/3E485B54786039A916672ED9387F5C2AC4415B97/", "size": "small", "type": "Trooper", "points": 40, @@ -337,6 +261,80 @@ } ], "Operative": [ + { + "name": "Jyn Erso", + "title": "Stardust", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133196300/612F0E84DA6E44E3F325FA031FC6537A314B769F/", + "size": "small", + "type": "Trooper", + "points": 75, + "speed": 2, + "upgrades": { + "Command": 1, + "Training": 1, + "Gear": 1 + }, + "minis": [ + { + "mesh": "http://cloud-3.steamusercontent.com/ugc/951848302305475778/55B558E631655D0430BAF76E71C93899AAECA7A1/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/951848302305475871/06400240648CC39F933BE504C74DA9BEF4D006C4/" + } + ], + "commands": [ + { + "name": "I Rebel", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133710266/ABE39C99F45579E1052C8F63229350F85EBE02D4/", + "pip": 1 + }, + { + "name": "Trust Goes Both Ways", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133714087/00E3558ED7687A4BA73636EA8290E647CE946A5B/", + "pip": 2 + }, + { + "name": "Rebellions Are Built on Hope", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711911/08855BFCF5653BBBEC5E09846AA302C7665BAD94/", + "pip": 3 + } + ] + }, + { + "name": "Cassian Andor", + "title": "Capable Intelligence Agent", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133196164/8178848A6C088BDD3F7D1C3B2A2C52CD55A8ED64/", + "size": "small", + "type": "Trooper", + "points": 90, + "speed": 2, + "upgrades": { + "Training": 2, + "Gear": 1, + "Armament": 1 + }, + "minis": [ + { + "mesh": "http://cloud-3.steamusercontent.com/ugc/773996263556990389/C38E9CAD7E5BA5496E1A211CA799E9ACBAC51D00/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/770611701143191344/5CAA4F0CE9A3FBE89A81560221ADCDDB5A801CC3/" + } + ], + "commands": [ + { + "name": "Crack Shot", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709477/9F40BE3B7E4964FC3D3A6FE5D617D08E584F391B/", + "pip": 1 + }, + { + "name": "Last Stand", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711249/CCC78CABDD6AA13C1CE1421B63F76E18225B8359/", + "pip": 2 + }, + { + "name": "Volunteer Mission", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133714589/039C85DD6F50BC392B1F1D3D542770E877B25A3C/", + "pip": 3 + } + ] + }, { "name": "Chewbacca", "title": "Walking Carpet", @@ -358,17 +356,17 @@ "commands": [ { "name": "Common Cause", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036994/19D8F067B325C88C9A9A3DE2D4CCAD9A7BAB5957/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133708994/B3C343B1AB15D93C121AFBA7DBA5AF00A30519E5/", "pip": 1 }, { "name": "Brains And Brawn", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035622/3BC5E96C0730225BC27EB0CE2BDB471E5CE66F4A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133708752/39258ED34508F7BBCDB7FB38DC6E533D0F76A2AE/", "pip": 2 }, { "name": "Notorious Scoundrels", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033300/4FA05D084E4DECE8DD8EED42E141C413F57201F2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711853/8E3AAF9ACF4DB73F5DA153B39F022E00F50CE865/", "pip": 3 } ] @@ -394,17 +392,17 @@ "commands": [ { "name": "Explosions!", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036773/60EF5E470AE0865CA28E50C31CDEDCD9266E0562/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709550/FD7BD543D5672320D01B8CB84960B07D13BFF009/", "pip": 1 }, { "name": "Symbol Of Rebellion", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034630/5AD8C9A6E0D6ADC0C1BA7E53AE4BDB9653DADA60/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133714045/1B46D54400840D3F7B1C5DC9E65757945FD482C9/", "pip": 2 }, { "name": "Legacy Of Mandalore", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033440/0011AF8A615526AED73E7A61D4E6C78B209F75E9/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711283/D9F6D219F5B6DA873D7D366B31BC6EC667478368/", "pip": 3 } ], @@ -435,32 +433,32 @@ "commands": [ { "name": "Son Of Skywalker", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036127/CF8D8E07205CB523EA3D2EE71AD61D9FB96B17BB/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133712958/CF63D0FDA40318CE886E6DA309CEB291857776E4/", "pip": 1 }, { "name": "You Serve Your Master Well", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035766/83D508C088FC9B893BDA1C497410726302D84452/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133714680/FD14E558D7D2B7EF32BD992FFC1C89141CD1F192/", "pip": 1 }, { "name": "My Ally Is The Force", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035023/A55A52AA8780EEFEB9B8DB24FE4294A844B3678E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711308/CECAE0B5B62858582522F7FBED6463FE5834A64E/", "pip": 2 }, { "name": "Full Of Surprises", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035386/B9B2E0999D5AA4C072B3C45FBD2185A0D7F6A9F5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709999/FA890B2A2EA1528C8BCF2A80AEB3B97A97BB9E15/", "pip": 2 }, { "name": "Return Of The Jedi", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033269/7606E86C0B998BBB4DAADE6F01B300A7931B697C/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711996/90C4CB28C0554DEC3797882252E6C5D3CCAC65A1/", "pip": 3 }, { "name": "I Am A Jedi", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033560/7EFDE383C076E6F171E6B868D1F5C7EE66A466CE/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133710140/F162DE57287EB8C43F845BF34550B6666929E36A/", "pip": 3 } ] @@ -486,17 +484,17 @@ "commands": [ { "name": "Blast Off!", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564037063/A7AE3401CB3A51A58A8682BBCD8D6F6C104804AF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133708728/EE34AF6EF6C63A6BC2C2E8985D9BCBA9809CACA6/", "pip": 1 }, { "name": "Impromptu Immolation", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035209/4C4829AE9D1B01B14F2D4FDE2C6D3935D8055954/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711126/58749B0F102DF1389C70E66298838B28B9B6795E/", "pip": 2 }, { "name": "Smoke Screen", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033033/7FC32081CE8646476E7B0C475F452AA629BA7713/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133712867/BD1886FA3B75683BB1B3D6C95971DFE1BBB5DD30/", "pip": 3 } ] @@ -522,7 +520,7 @@ "commands": [ { "name": "Sacrifice", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033075/A24FA101CA9A3071E18DB79779F8285B8F87B906/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133712817/9454157523A2CCA90773E8795DC9FBD317AB3059/", "pip": 3 } ] @@ -547,17 +545,17 @@ "commands": [ { "name": "A Simple Man", - "image": "http://cloud-3.steamusercontent.com/ugc/1904476875947527247/05CDA2737D98FB18CACE99E65CE51E5AB24ABA0B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133796666/FF13777E67A0A5315AB1DFE941DE403FEC542DA3/", "pip": 1 }, { "name": "Whipcord Launcher", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035802/A1AA027AE84C3CF96C2E671C6BFA78E2C94E2741/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133795508/C3589C8AFAB04E0FD8A92A983532CFCDF6FA1B89/", "pip": 1 }, { "name": "Making His Way in the Galaxy", - "image": "http://cloud-3.steamusercontent.com/ugc/1904476875947527294/F0CD781F4680C3BBBDA58B1CECE1A3FF3B6821FE/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133800518/86AF77B41A2B4EE0C0ED5C2100EEA0E97FAE08A8/", "pip": 2 }, { @@ -567,12 +565,12 @@ }, { "name": "Rule with Respect", - "image": "http://cloud-3.steamusercontent.com/ugc/1904476875947527380/9CF23D52011D3B943E8DE565D19E91EC579F5784/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133795310/09B0E60A61663E865F43404BD844454C358D8B41/", "pip": 3 }, { "name": "Z-6 Jetpack Rocket", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564032805/D4AEE724FC17B87F391BB7B24A3C8D44B40B704B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133796142/01926BBF8B27E512F920012E557A61E05267633A/", "pip": 3 } ] @@ -603,22 +601,22 @@ "commands": [ { "name": "I am no Jedi", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384886277/49B86A9831C48A7BCFB81406E930810046A4DFA0/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133710197/7F9F965E20829475FF9BA4912847B9C623A46ACF/", "pip": 1 }, { "name": "Swift Protector", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2022720184107019696/F82E451FABCD16AB411E88409AFC81201DB9D43A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133713979/17BE69E23BB277BB589B016EAF7779DE55999582/", "pip": 2 }, { "name": "Family Reunion", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2022720184107019640/F5F69B23E0B7E1734BE3B7AE3A8DE6738680EA87/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709585/EB651566F03586E293E563D69EFA56BF515E5251/", "pip": 2 }, { "name": "A New Beginning", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384868187/6AF87B3E28E3057B3096E369E644F62A529B8D7B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133708687/2329C2477E6CB92E3434AC63CC2857D15ED189D0/", "pip": 3 } ] @@ -642,7 +640,7 @@ "commands": [ { "name": "We Do What We Do", - "image": "http://cloud-3.steamusercontent.com/ugc/2475370743175005491/7273C60F103AD3520287BCD56D2DC53B26498A35/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133795415/0235923ED3C127B003714A0C009AE79215524F73/", "pip": 3 } ] @@ -651,7 +649,7 @@ "Corps": [ { "name": "Rebel Troopers", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863457/C2BAB28D85FAABCF7DE0E4A861D955D0FDACDC44/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133198369/1E028B2F335F3E9FC0AABE4C0362F03AFB144C4E/", "size": "small", "type": "Trooper", "points": 40, @@ -683,7 +681,7 @@ }, { "name": "Fleet Troopers", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384862248/8783D7378FCC5551DE3C0591BF1AF701A4AE6DA2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133196242/48ACAF50F0BCD3D831101C13ECAF0BF0FBA9DC03/", "size": "small", "type": "Trooper", "points": 40, @@ -715,7 +713,7 @@ }, { "name": "Rebel Veterans", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863497/768F4478CF6CA7A20ADCB509EF72E32F979AAD1E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133198409/983DC405DCEE4973DB7FA2C3906BCF678F2B011D/", "size": "small", "type": "Trooper", "points": 48, @@ -748,7 +746,7 @@ }, { "name": "Mark II Medium Blaster Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969789216718/13B8F9971804EE108E554C9B348A29611CFE2495/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133197490/1B64AC039E75E3028CFF048F4DCAD95975C34A5D/", "size": "medium", "type": "Emplacement Trooper", "points": 38, @@ -768,7 +766,7 @@ }, { "name": "Ewok Skirmishers", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384862086/27B800F6CAF027A08E432141CFD58BA7125AB70D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133206135/9932914CEED1068F058672A166F0F7A67E5C68D5/", "size": "small", "type": "Trooper", "points": 40, @@ -809,7 +807,7 @@ "Special Forces": [ { "name": "Rebel Commandos", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863292/FBBD466660EE478A3378933C25C312B0C83A29A8/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133198280/BA9DB93D2D168B835C95075E8B1C52C79E044286/", "size": "small", "type": "Trooper", "points": 48, @@ -845,7 +843,7 @@ "displayName": "Rebel Commandos (Strike Team)", "title": "Strike Team", "heavyWeaponTeam": true, - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863292/FBBD466660EE478A3378933C25C312B0C83A29A8/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133197589/A51BFFE52ED2FAE9DACE45BF774C7F5FE12CBC07/", "size": "small", "type": "Trooper", "points": 20, @@ -866,7 +864,8 @@ }, { "name": "Wookiee Warriors", - "image": "http://cloud-3.steamusercontent.com/ugc/1619598366708170837/8CD09FD64849E8D2607D2C03FABD5B2DA092EE4E/", + "title": "Freedom Fighters", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133195004/B68B0EBD3193888339421B0AFE8A9F9803EFC027/", "size": "small", "type": "Wookiee Trooper", "points": 69, @@ -928,7 +927,7 @@ }, { "name": "Mandalorian Resistance", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788604461/D49CF324CDE3C648E683FCCD762B76F94E843065/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133197470/8F69CF0D59AA51A8D0EFB406C7B3594937BB8D3D/", "size": "small", "type": "Trooper", "points": 68, @@ -957,7 +956,7 @@ "name": "Mandalorian Resistance", "title": "Clan Wren", "displayName": "Clan Wren", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788604497/C4CEBD761320F416E03203426388067B95104C90/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133197439/A9AEEBFD286112DACCAB9944794C5521EE60BB8E/", "size": "small", "type": "Trooper", "points": 30, @@ -981,7 +980,7 @@ "name": "Wookiee Warriors", "title": "Kashyyyk Resistance", "displayName": "Wookiee Warriors (Resistance)", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788604546/F387DBCD02315CB56A87FF52D99CE3C1E6E548F2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133195051/93FB6360319FCFB5CABA147A27FB34D2A843FAC2/", "size": "small", "type": "Wookiee Trooper", "points": 72, @@ -1006,7 +1005,7 @@ }, { "name": "Ewok Slingers", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384862175/FF930EBF1633866A1A1C678E3BC80ECFF4862585/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133206165/0E347DAEAF6A94808CAAC5869754E639B33F669F/", "size": "small", "type": "Trooper", "points": 35, @@ -1046,7 +1045,7 @@ { "name": "Rebel Sleeper Cell", "title": "Ready to Strike", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863407/9D7544B1F6C17EA85AB6CD788F3AC4EA0D7666A6/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133198319/3751EC4355C0216944AC0333339EEBB78ADD38D1/", "size": "small", "type": "Trooper", "points": 70, @@ -1089,7 +1088,7 @@ "Support": [ { "name": "AT-RT", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384861274/DAADEA991C8E593DBD605DCEC47239E77B7EDA03/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133195638/6804A989329D472D728452290CD8EFE41A02F9A2/", "size": "large", "type": "Ground Vehicle", "points": 50, @@ -1111,7 +1110,7 @@ }, { "name": "1.4 FD Laser Cannon Team", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384860760/0CD3995BF13C110BDD23F7AFEC3DEDAB82B9DFE2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133195524/B46674A1014A295A986942391D7C9DB1B92D8629/", "size": "huge", "type": "Emplacement Trooper", "points": 65, @@ -1129,10 +1128,10 @@ }, { "name": "Tauntaun Riders", - "image": "http://cloud-3.steamusercontent.com/ugc/1683770969788604655/CC7501F41054D1F68139C8ABCFAEE0CAA855475C/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133194939/65297606E28367C04BF4BB4DE3474EAB723F4416/", "size": "medium", "type": "Creature Trooper", - "points": 90, + "points": 95, "speed": 3, "upgrades": { "Training": 1, @@ -1156,10 +1155,10 @@ "Heavy": [ { "name": "T-47 Airspeeder", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384864184/ABA3C210ACE5381401ED17E7CF0A7A4BBD2469E8/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133194839/3B6BF81A2960A253A671CEDBF09F3C22AD52277C/", "size": "huge", "type": "Repulsor Vehicle", - "points": 130, + "points": 125, "speed": 3, "upgrades": { "Pilot": 1, @@ -1167,8 +1166,8 @@ "Comms": 1 }, "silhouette": { - "height": 1.139, - "offset": 3.494 + "height": 4.633, + "offset": 0 }, "minis": [ { @@ -1178,10 +1177,10 @@ }, { "name": "X-34 Landspeeder", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384864334/A77701CB2D8E525FA48D2B35D06B79F996170BEA/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133195095/8AA9C62E3102445090A15FD71C48234EA2F1D03F/", "size": "huge", "type": "Repulsor Vehicle", - "points": 60, + "points": 55, "speed": 2, "upgrades": { "Pilot": 1, @@ -1190,8 +1189,8 @@ "Comms": 1 }, "silhouette": { - "height": 1.174, - "offset": 0.726 + "height": 1.900, + "offset": 0 }, "minis": [ { @@ -1202,7 +1201,7 @@ }, { "name": "A-A5 Speeder Truck", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384860853/CB2AE07AEBA91192C8D8F029211F7216F122F4B6/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133195560/90F1524F640B97B178EE17A8D5C3D73EDA51E80F/", "size": "long", "type": "Repulsor Vehicle", "points": 65, @@ -1227,7 +1226,7 @@ { "name": "Chewbacca", "title": "Let the Wookiee Win", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384861745/F4DF33E03B759FEDC7E169026FDC282A93A01880/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133206109/0DCD00886E51BDA8114E68212DAB8591AFF28DE6/", "size": "huge", "type": "Ground Vehicle", "points": 180, @@ -1246,17 +1245,17 @@ "commands": [ { "name": "Common Cause", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036994/19D8F067B325C88C9A9A3DE2D4CCAD9A7BAB5957/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133708994/B3C343B1AB15D93C121AFBA7DBA5AF00A30519E5/", "pip": 1 }, { "name": "Brains And Brawn", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035622/3BC5E96C0730225BC27EB0CE2BDB471E5CE66F4A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133708752/39258ED34508F7BBCDB7FB38DC6E533D0F76A2AE/", "pip": 2 }, { "name": "Notorious Scoundrels", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033300/4FA05D084E4DECE8DD8EED42E141C413F57201F2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711853/8E3AAF9ACF4DB73F5DA153B39F022E00F50CE865/", "pip": 3 } ] diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index 221e7a67..77debe4d 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -41,6 +41,7 @@ "name": "Count Dooku", "title": "Darth Tyranus", "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432037/24F46472FEFFC678779BC1CA2FF6B4460E2E039D/", + "size": "small", "type": "Trooper", "points": 165, "speed": 2, @@ -701,7 +702,7 @@ ], "Heavy": [ { - "name": "AAT Battle TanK", + "name": "AAT Battle Tank", "displayName": "AAT Battle Tank", "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131430642/9FCE0FB0F62A9381DF3CE2F786DBC749CB2A8FFF/", "size": "epic", diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 355d018b..6d235086 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -3840,52 +3840,52 @@ "Rebel": [ { "name": "Sabotaged Communications", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564036214/A7A0CC7689A2F66143FB6AE89914414462A09635/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133712792/1E85BFDAD9BED16CED509629CF66A9E4A24A208A/", "pip": 1 }, { "name": "Delaying Tactics", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384885256/30A465EAF72446309109BC2641FFACAB6E3142CD/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709526/A9486B47C4C1BED73083F4069E794A86E0FF708D/", "pip": 1 }, { "name": "Log Traps", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866561877/7DA15C0E3C8B41052DA340049F4A606461C059DC/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133752458/BF28419A06690AC846A210B34221AC931BC890B6/", "pip": 1 }, { "name": "Arboreal Assault", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866562364/A44620A9FA95C5DE36A57435EDD2A0E9C1BEB18E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133750252/A4F22646BC91929E2F3E36D040C0A4D0F27F033D/", "pip": 1 }, { "name": "Turning The Tide", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034438/544487939B8A8BFB04860134ACC616A1EE285039/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133714141/B32483DB97F481A24CA78CB828326E29DCBDFBB4/", "pip": 2 }, { "name": "Courage of the Rebellion", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384885103/81FA9E6F33AC3A5D1CD5A69A557B117A1696F936/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709102/E61A46B9D30667B88527057B1A64B625F3BACAC9/", "pip": 2 }, { "name": "Hiding in Plain Sight", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866562828/642D6593D255FBFBFE683B4CBA7A73303A254DFE/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133750416/5EF67EC8B9A28231AA10411A62FF5DDD0A95EF51/", "pip": 2 }, { "name": "Covering Fire", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564033931/E4C7CE80C1179B43969BE76C60CC2744101E051E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709439/D3EB5EB8A70E3BD95C4FE7EDAB952C2A9830159F/", "pip": 3 }, { "name": "Hold at Any Cost", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384886209/00BD7A78EB75B16E3CF3F367B3249DC7D34E5A36/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133710068/45014708453E80B4389733FE14A889B5C99496EB/", "pip": 3 }, { "name": "For the Tribe", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866561984/9CD2F9B809D231CE871F130AEA717D1CD4B9A81D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133750350/A56DBAF5154B106C429885A598899D03A3A37525/", "pip": 3 } ], From 860ad77f110f309edbc4c19ed9fbfa2d461bac85 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 19 Apr 2025 01:34:27 -0400 Subject: [PATCH 08/93] CIS Upgrade Cards --- contrib/cards/official.json | 154 ++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 6d235086..14916fe0 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -62,9 +62,9 @@ } }, { - "name": "Deflector Shields", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139844/502DCA18D664C848F5992832178ECCB8968C9358/", - "points": 10, + "name": "BX Deflector Shields", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927385/CD3E586656B39D35246C84BAFD6ADB12B665E5E5/", + "points": 18, "restrictions": { "include": { "unit": ["BX-Series Droid Commandos"] @@ -190,8 +190,8 @@ } }, { - "name": "Vibroswords", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146464/0B98A26254DDCEB3CECED855EA07ABF988C377A3/", + "name": "BX Vibroswords", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927392/0B4491CA615FAA34F7EFEDCE08BAD5A06571BE90/", "points": 6, "restrictions": { "include": { @@ -1036,7 +1036,7 @@ }, { "name": "Nose-Mounted Flamethrower", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857446/9906781E6E1CE737B3CCD387A1561C0BD3D98ED0/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139932109/2E0AB29034B53D4523B5370D1EF77159460BB222/", "points": 15, "restrictions": { "include": { @@ -1046,7 +1046,7 @@ }, { "name": "Nose-Mounted Laser Cannon", - "image": "http://cloud-3.steamusercontent.com/ugc/1616219505081188770/4969CF4FCB92FABFF423E8E382E47FB2B1DD6454/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139932245/3F68293534A56C2DAF62AC227A3F271F1C201610/", "points": 15, "restrictions": { "include": { @@ -1056,7 +1056,7 @@ }, { "name": "Nose-Mounted Ion Blaster", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857520/217F4C81506C55D44FA0509E24A2AC335B3D4C76/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139932171/BC04BFC46285EB872E4B7A65DF2E6A2C8DC5EBA9/", "points": 30, "restrictions": { "include": { @@ -1183,8 +1183,8 @@ } }, { - "name": "B2-ACM Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138530/E4F2F46CD0EFCCBEEFAC5F8CB2BA4E1D43B5BB88/", + "name": "B2-ACM Battle Droid", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139926955/7598527939653076FB60A126D883BC9FCEFDD38C/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/775105953849585917/4D4047069796D1E0567BE9ABD836984FDF12E80E/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/775105953849585789/91A639AB9F57BE393683DA53212E395886307C52/" @@ -1197,8 +1197,8 @@ } }, { - "name": "B2-HA Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138574/542DD11379A68CFAAB14FFC165A582FB0CDC4377/", + "name": "B2-HA Battle Droid", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927358/329BE3BD01363997AED00F270306F36F9DD1C8C9/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/775105953849585631/7FF051E2079AB28A4FB77CCC739BCEB42F4CCFAA/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/775105953849585789/91A639AB9F57BE393683DA53212E395886307C52/" @@ -1231,12 +1231,12 @@ }, { "name": "BX-Series Droid Sniper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139153/0CC9E44B3A97DEFEE965349938D03A01B829B231/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927410/552314F29830D4A8A63F5B8238699336F17ECD92/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457847963/0EA6BE014DA494689FF32F9570C6A73CD88F1894/", "secondary": "http://cloud-3.steamusercontent.com/ugc/773995528924546789/8774CBBDC3C4F18B11A444CC96EA5FE7F00DB48F/" }, - "points": 30, + "points": 40, "restrictions": { "include": { "unit": ["BX-Series Droid Commandos"] @@ -1460,7 +1460,7 @@ }, { "name": "Dioxis Mine Saboteur", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384856069/816C4641B4EABC592275FC9115EC7AE82E9E5C3C/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927460/BE9022FA696C172E68110895A9DDCDDD635E2AB3/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457847567/079157D001A1520085C6A30620750817C6C01BFF/", "secondary": "http://cloud-3.steamusercontent.com/ugc/773995528924546789/8774CBBDC3C4F18B11A444CC96EA5FE7F00DB48F/" @@ -1473,8 +1473,8 @@ } }, { - "name": "E-5C B1 Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568140436/6A3EBB89A4172017C75366D8A7E1613D22953C17/", + "name": "E-5C B1 Battle Droid", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927471/01EFFA28B26ABFA90DE16CEEF694EBB083EB1F20/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780871782781/4DCE4780DE1CCDFB2996C28200A26C3B0696DD5A/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780871782881/ADC1DBC3987C84AF486F9CD0E19A0455F4C33291/" @@ -1487,8 +1487,8 @@ } }, { - "name": "E-5S B1 Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568140479/A9EE65EB6F30F9FCAA43DBA0C025E5B76508D6BC/", + "name": "E-5S B1 Battle Droid", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139931066/BDF09AC95FA4C63AC25459B1636B8F0E6F0D7AFC/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/766102422959434489/60F05B7FC8544DFC640CB33DA55F19EB5F2CB55A/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/766102422959428835/10D3F8862BDECE8C0D7A874CC000F25CD6232168/" @@ -1501,8 +1501,8 @@ } }, { - "name": "E-60R B1 Trooper", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384856220/D48D7E28DF2C25BC2AA2467D9710B7499C9D68A3/", + "name": "E-60R B1 Battle Droid", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139931091/2EF91BFA17C138987CC997C6A359AC948DB2D3BA/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780871782500/34D317CFE0BE32E4FFFB84567822CDF7AC4D4C98/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780871782620/9A9EA2B54691F42EA6D34762D33D523A836201B2/" @@ -1533,7 +1533,7 @@ }, { "name": "Electro-Whip MagnaGuard", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568226197/D447CE12E500790F2192A55991E31520C0DEBCFA/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139931140/87A4E668E6141DCF96DD8090761177033DDDD43C/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1621849308236386662/7ABB72B5A8A301611033E4A424FD61271024FB4C/", "secondary": "http://cloud-3.steamusercontent.com/ugc/1621849538641208262/A0E48F7D9C94617C81BAD4D649717BAC7A8B9B64/" @@ -1624,7 +1624,7 @@ }, { "name": "IG-100 MagnaGuard", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568226161/53E8C5C2A3547D37E918BCE12B5200DC9DB1DBB7/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139931971/525A56FA820F4B27F1C1E46AAA5ED5FA8AA86EB5/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1621849308236386555/F47418A458CF12C80756A80B842817EA1CCC62CF/", "secondary": "http://cloud-3.steamusercontent.com/ugc/1621849538641208262/A0E48F7D9C94617C81BAD4D649717BAC7A8B9B64/" @@ -1707,12 +1707,12 @@ }, { "name": "RPS-6 MagnaGuard", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568226088/BF380B0BD77661C3735E03B48120CCC4104B339F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139933591/76EA9AB16AFB8FD6B3DA57A31F40EEF7150F38E1/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1621849308236386787/E93D6E65A61C9262B5934C5A419736B45B50D75B/", "secondary": "http://cloud-3.steamusercontent.com/ugc/1621849538641208262/A0E48F7D9C94617C81BAD4D649717BAC7A8B9B64/" }, - "points": 34, + "points": 40, "restrictions": { "include": { "unit": ["IG-100 MagnaGuard"] @@ -1748,8 +1748,8 @@ } }, { - "name": "Radiation Cannon B1 Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144682/FA1C881FCC17F3A4857FFE03FD564CC3BFCE6B1E/", + "name": "Radiation Cannon B1 Battle Droid", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139933524/FFF766FA4F641B413DF8E7BC37DB6088B7F31358/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/767228764785008444/482C29486FD00312B2C7848D26A82793BCDF7D7F/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/767228764784997566/39300970854603B1F1539C46D70CC21280E278FF/" @@ -2173,10 +2173,10 @@ }, { "name": "Force Pike Warrior", - "image": "http://cloud-3.steamusercontent.com/ugc/2058763770174552121/CA4B8F51C796DBCC8CF02C1BAED477EEFCA9FE0A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139984742/4CBAEA0926D770481377C8C3556D0F4F55D403F4/", "flip": { "name": "Force Pike Warrior", - "image": "http://cloud-3.steamusercontent.com/ugc/2264810342738275190/2DA293784588C5E18E40CC315355315FEB070444/" + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139931290/4D91F65FD4C56E472083227DF40A98D978735E4B/" }, "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177195807/C1E3DCF8DC0772F56B22E42E89DECC9F30110E8D/" @@ -2188,41 +2188,9 @@ } } }, - { - "name": "Geonosian Warrior Squad", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384856681/5402A09506D0B2D787CC87053FC64B80D93E6109/", - "minis": [ - { - "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177201554/7AEA5B0A98A298731F6BE51591401BBF095A5797/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" - }, - { - "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177201933/E0DBF0D812FFF4F7F3A77CF1E4EAB84F78B8C49E/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" - }, - { - "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177202012/76CB0371503304D5AAF562E5E2F04961CD206090/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" - }, - { - "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177201933/E0DBF0D812FFF4F7F3A77CF1E4EAB84F78B8C49E/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" - }, - { - "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177202012/76CB0371503304D5AAF562E5E2F04961CD206090/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" - } - ], - "points": 51, - "restrictions": { - "include": { - "unit": ["Geonosian Warriors"] - } - } - }, { "name": "Sonic Cannon Warrior", - "image": "http://cloud-3.steamusercontent.com/ugc/2058763770174552203/03C83AACA87DB9432EADA6FFC5AF16BE6F524AA6/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139933665/6D7AF85CB42A453D1AAD2B1B4DD03F00BD9C66CF/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177195589/5E52A1E47746FF179ED6327F681D51D92C47D1D5/" }, @@ -2445,7 +2413,7 @@ }, { "name": "B1 Battle Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138406/B83A83613C2F77D3BD237309689D70C47BD34350/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139926619/12E85E2D56E590D6D718B15BDE100799D6F9D077/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780869167318/DBC7863706A08B96020D3BFBE7A0C2C5689B7B08/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780869166919/FE5AD3A71430C729735E3FF7023D9E546E12FEF1/" @@ -2459,7 +2427,7 @@ }, { "name": "B1 Security Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138465/E66B9C058BA3D7B2B767CFA6E0A4D5581E744285/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139926745/A596D5C24CC8AFBA36C85A7307BFF2985BFCA2F5/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/766102173467517125/1BEF7A7795DF3F6DB91B84C1C82D8FBB24BF574B/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/766102173467517405/DC3A7F38CF3839B7C5F2FCCE85745041094ADA81/" @@ -2473,7 +2441,7 @@ }, { "name": "B1 Battle Droid Squad", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384854943/DE80B471DBA735782A6AD5A5DE6AA2A7942E0CA3/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139926468/DB7AB15E9634E1EE003C27E65E33A67D9F2876CC/", "minis": [ { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780869167061/48F42C02EDD970E6DE8E59B8601EF3E7F11C1742/", @@ -2513,7 +2481,7 @@ }, { "name": "B2 Super Battle Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138503/8FCB5B6A5739323F1E2F890625E041B67E2F93ED/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139926862/55D56B7AA8FC3FDC335499AC5DA66CD5A8DE3620/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/775105953849586324/B5D59E535AFA2BF99FCA3C584E28FA500ADB89BD/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/775105953849585789/91A639AB9F57BE393683DA53212E395886307C52/" @@ -2527,7 +2495,7 @@ }, { "name": "B2 Super Battle Droid Squad", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384855015/79EDE83B3F4DB323E63288C312DFCBD828E8A8F5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139926806/AE2E14125DB171D14F95D09EB4D7530A1A22E2BA/", "minis": [ { "mesh": "http://cloud-3.steamusercontent.com/ugc/775105953849586430/E5941CF7A3631E6D4A28832AE489A4340C96B0B9/", @@ -2608,7 +2576,7 @@ }, { "name": "EV-Series Medical Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141000/351590750AFE04E144C510815B8C7D55B9A23D1F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139931203/4E82B1CC654C69E194324C5EF25525BD01BBA09A/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/1683750364035000354/6248E4CAF3E978154106E7095E6984DCE6677CCA/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/1683750364035001447/E6B6EC2DDAE8CEA46A1384161E789D76F8F296BA/" @@ -2679,7 +2647,7 @@ }, { "name": "OOM-Series Battle Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143867/A8A2CA8F507CEE4C16CAA27C0865FB334824A4AB/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139933383/26696A10589DBF5D8D9FC3E389F9C9F74CE2AE27/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/767228764785018348/F0B0C50757F4043A6C9B8746A73F8EC4342CA7DA/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/767228764785012711/4DF65424F7D5400F0DC8A4DE7A028B373143CE6E/" @@ -2769,7 +2737,7 @@ }, { "name": "PK-Series Worker Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144469/375C3AB7DF614E63735F3A1FBF98F4FCD9E1507E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139933460/B95170257A3FA060A1A39CB5927F72FA40D8F821/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2174736446809185654/E58BE2AB7BC859E1A896FF9B9909B6976E69CF4B/" }, @@ -3142,7 +3110,7 @@ }, { "name": "T-Series Tactical Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146138/2F28A64E405A9426CB3248FB8D9D4F9B7FFA2E4B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139934011/3AA1AB05A592268E4C08E2CEAFB6B9B143105470/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2174736446809185719/FF120BC2FA5DC8258021E2849C2B81591CCA36B9/" }, @@ -3156,11 +3124,11 @@ }, { "name": "Viper Recon Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146561/848DFAE83E6BF72D5D657ACB7B07CAFFABDCEA68/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139934033/79C8D8FEC5E44ACAE43E0679E50A3B517ABD33A2/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2174736446809185784/C7B137C19E24986DBE1E9A2712676AB320E4B2A9/" }, - "points": 6, + "points": 9, "restrictions": { "include": { "type": ["Droid Trooper"] @@ -3281,9 +3249,41 @@ } } }, + { + "name": "Geonosian Warrior Squad", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139931345/26339BC464EC1009F100CC1BACFDC3F726B3CD27/", + "minis": [ + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177201554/7AEA5B0A98A298731F6BE51591401BBF095A5797/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177201933/E0DBF0D812FFF4F7F3A77CF1E4EAB84F78B8C49E/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177202012/76CB0371503304D5AAF562E5E2F04961CD206090/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177201933/E0DBF0D812FFF4F7F3A77CF1E4EAB84F78B8C49E/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177202012/76CB0371503304D5AAF562E5E2F04961CD206090/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" + } + ], + "points": 51, + "restrictions": { + "include": { + "unit": ["Geonosian Warriors"] + } + } + }, { "name": "Geonosian Warrior", - "image": "http://cloud-3.steamusercontent.com/ugc/2058763770174552042/8F31F3873ED370C376A85285CF67F7F31C51C382/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139931935/2F3CADBA40F493D24A6AB5146FFF64A612C2398B/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177201933/E0DBF0D812FFF4F7F3A77CF1E4EAB84F78B8C49E/", "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" @@ -3428,7 +3428,7 @@ }, { "name": "Lok Durd", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143492/DDD77075BD08BC796F7B7C4DFE927F95ABF68280/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139932011/B3D3CF2244185894532A6D27D795AAA947DBD2C0/", "points": 9, "restrictions": { "include": { @@ -3438,7 +3438,7 @@ }, { "name": "OOM-Series Droid Pilot", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143908/515B5D66730319DA2FE69696B0EB70ECCC297A81/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140015474/EBD6D1C9D3C7DBCB66C1C5976D7F6B2A7303655D/", "points": 7, "restrictions": { "include": { @@ -3501,7 +3501,7 @@ }, { "name": "T-Series Tactical Droid Pilot", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146102/336E82649F7A62C9767E86924A1038AB8D81AF83/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139933742/5DA785FEB16866F613EF45A9998324B648F28026/", "points": 5, "restrictions": { "include": { From 97830f9eb6918a82da134a2eb9a6dbc6462de134 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 16:21:15 -0400 Subject: [PATCH 09/93] Update official.json --- contrib/cards/official.json | 38 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 14916fe0..aa8f9e33 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -38,11 +38,11 @@ } }, { - "name": "A280-CFE Sniper Config", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137831/63A163395CE6017A0EB530C2B68DA6539467AFA9/", + "name": "A280, Rifle Config", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081159/19C93D73F4F86E9D7531E2CD784F5492772ED28F/", "flip": { - "name": "A280-CFE Pistol Config", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137975/9505EB828E9F8B8F746E484527E05FC515D34891/" + "name": "A280, Pistol Config", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081077/A5B040BCD34352C6E5375640DE539E526067DBFE/" }, "points": 0, "restrictions": { @@ -131,7 +131,7 @@ }, { "name": "Jyn's SE-14 Blaster", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331066557077/E3E2DE71CD4C1BEB7C071189AEFEFC5EEC75A6F3/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140086376/D2D709BDACCC6415CBC5786450D1BFDFBDF34ED9/", "points": 10, "restrictions": { "include": { @@ -181,7 +181,7 @@ }, { "name": "The Darksaber", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146048/4D9FDAC4679C5636DCBD4244667ADF7BDCC2E8EA/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140090412/816EE4F5B469EC8334D0E84042E5C008FC67B04B/", "points": 15, "restrictions": { "include": { @@ -926,7 +926,7 @@ }, { "name": "AT-RT Flamethrower", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384854877/AC134FFAE75590E8367AEDC6C2C7AE4483A76D22/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081769/07D4C1667BDEE090F39484775AEE7210086485B3/", "points": 15, "restrictions": { "include": { @@ -936,7 +936,7 @@ }, { "name": "AT-RT Laser Cannon", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138277/BF6E7C05171B0D9397AA521903AAB5B47B97CB7B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081788/A032FAAFF3DB96A0423D0691209C65FFCC272D32/", "points": 10, "restrictions": { "include": { @@ -946,7 +946,7 @@ }, { "name": "AT-RT Rotary Blaster", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138320/7C66EABCF4FF73DC6630F387C726068EA548FD9A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081805/40CE85B06069C7ABA4D93C271976B49CD2ED57C1/", "points": 20, "restrictions": { "include": { @@ -966,7 +966,7 @@ }, { "name": "AX-108 \"Ground Buzzer\"", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138379/A525E05DAFAAB7129F8D2A9626E33C169B28C462/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081842/2473F73E42DEF62BE49EA8789193A8AEDEECB097/", "points": 10, "restrictions": { "include": { @@ -996,8 +996,8 @@ }, { "name": "Heavy Laser Retrofit", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141789/1466FB973611960CE6C870F27FE8D1735671048B/", - "points": 16, + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140085434/9779C6885402FF554F2CB4DEEDADEE725DE3C1A7/", + "points": 12, "restrictions": { "include": { "unit": ["A-A5 Speeder Truck"] @@ -1006,7 +1006,7 @@ }, { "name": "M-45 Ion Blaster", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857032/171B4114B7C311F10B6A0359B584825A41FFF443/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140086470/7B7DF79267F695621DFF1C1D68859F71CBE521C5/", "points": 28, "restrictions": { "include": { @@ -1016,7 +1016,7 @@ }, { "name": "Mark II Medium Blaster", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143619/E3949CD69ED911AF724373F0EE17CD656AD19556/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140086552/D1D6D09E2363DE1AD638A6516E6272CACB2B7A23/", "points": 21, "restrictions": { "include": { @@ -1026,7 +1026,7 @@ }, { "name": "MO/DK Power Harpoon", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143652/AB3D07BAC21F77DB7AFA6572CC962B972A19E6FE/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140086620/85D8AFAE73115CB7E7F35C5167E442B97CD1B01E/", "points": 0, "restrictions": { "include": { @@ -1213,7 +1213,7 @@ { "name": "Battle Shield Wookiee (Offensive)", "displayName": "Shield Wookiee (Offensive)", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138999/81A76F415A7B73A3AEF6B5EEF5DF6F0ABC9C9CF5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140084858/2608BF39F19557F71C8FF8D04314A0387DB7586C/", "flip": { "name": "Battle Shield Wookiee (Defensive)", "displayName": "Shield Wookiee (Defensive)", @@ -3432,7 +3432,7 @@ "points": 9, "restrictions": { "include": { - "unit": ["AAT Trade Federation Battle Tank"] + "unit": ["AAT Battle Tank"] } } }, @@ -3442,7 +3442,7 @@ "points": 7, "restrictions": { "include": { - "unit": ["AAT Trade Federation Battle Tank"] + "unit": ["AAT Battle Tank"] } } }, @@ -3505,7 +3505,7 @@ "points": 5, "restrictions": { "include": { - "unit": ["AAT Trade Federation Battle Tank"] + "unit": ["AAT Battle Tank"] } } }, From d9fdaceebd1fbc19d11070283f84edcb07c37518 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 18:18:12 -0400 Subject: [PATCH 10/93] Update official.json Generic Upgrades --- contrib/cards/official.json | 246 ++++++++++++++++++------------------ 1 file changed, 123 insertions(+), 123 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index aa8f9e33..086f280d 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -312,39 +312,39 @@ }, { "name": "Improvised Orders", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143118/8E9AFD74A3EEAD6F270FA6E15C62741A59BA4671/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9869682046426627943/152512DE2EE5592159A7D5A4E9BB7111A0C729B5/", "points": 5 }, { "name": "Inspiring Presence", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143173/36FADA7AC1AD3A80CEE85B159804D134CFD766FF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13424493991629241458/67E4A2FA7A3C475FC2936C5029591460B190FC1F/", "points": 5 }, { "name": "Strict Orders", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568145675/43305265BDF594D84C9605D28F454F373A29BE05/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13144615001636608131/4F2C4FF797BFB30D3FA835BE743C20EF7A1D333A/", "points": 5 }, { "name": "Vigilance", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146515/D0D3C15D0DC26175A41EB79729AA4680E3E24954/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10166497509018197631/139646D733278E6B4FDD81AC4AC6D4A9CEE967C0/", "points": 12 }, { "name": "Lead by Example", - "image": "http://cloud-3.steamusercontent.com/ugc/1856061112418868276/768CFB5A47D1E938A608DD99AE45B6A6051C9E08/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13953261892853714692/D7C43A7643D1580E6B7859A468734D1784D046EA/", "points": 8 }, { "name": "Underworld Connections", - "image": "http://cloud-3.steamusercontent.com/ugc/1856061112418869490/825263D96998C42E66FC1414CD4942C3BE676C33/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13075491375596331661/7404D8A06A3E978E3729E9D32EE0EBA855FA18FF/", "points": 5 } ], "Comms": [ { "name": "Command Control Array", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139481/5040B2F80867500C98B1F599B64FE07F7AF51EC0/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13152173042783439846/9068A2E707EA6AD6B201273F43B7A275E41ED1C0/", "points": 2, "restrictions": { "include": { @@ -354,7 +354,7 @@ }, { "name": "Comms Jammer", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139564/6382F97170B7A79B493E485C5858452E91ADEEC7/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12840451931503255129/4E1CDC738ACDB452A4AE50E7358EDAA2C95E191F/", "points": 5 }, { @@ -369,12 +369,12 @@ }, { "name": "HQ Uplink", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568142585/BFF381C7358C83BF8ECE84008577EB5060B55135/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12685640959616769169/065354E9812F61C36C60B9EFD08829CD70C51487/", "points": 10 }, { "name": "Hacked Comms Unit", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568226344/20A0A5F26921D6132907912F6EF6CA039568AA8E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16875746628328702203/66EFD3995E6C5DDB8B687B86323884DFA50E767D/", "points": 5 }, { @@ -389,7 +389,7 @@ }, { "name": "Linked Targeting Array", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143463/337C8B311582824E462A167350CBC98346EA8C8C/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14619952568753696955/4661936FB9F23F26D08151D8D8F82CC3140F9B5A/", "points": 5, "restrictions": { "include": { @@ -404,7 +404,7 @@ }, { "name": "Onboard Comms Channel", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857602/E6D6F79EEDAFEA15192C32DEF911F908CD3EF8A8/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13942732418073164981/DF52655D5DBDA266A9A0F3609B151FEC449ACE22/", "points": 10, "restrictions": { "include": { @@ -414,7 +414,7 @@ }, { "name": "Emergency Transponder", - "image": "http://cloud-3.steamusercontent.com/ugc/1856061112418869398/CCAF4D60ADF57B292F573608566B7863134D9E0A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9908400255536413440/38A6CDC80C3D7D0A2F9A9903EC1E0651380D8526/", "points": 4 } ], @@ -607,7 +607,7 @@ "Force": [ { "name": "Anger", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138122/92F8FD5EE7BBB3DDA644C0C12DEC40619DACB2D2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15480929961350237792/459C25A6310A790CCD57B24B19BCEF4AC8A0F4AB/", "points": 3, "restrictions": { "include": { @@ -622,12 +622,12 @@ }, { "name": "Burst Of Speed", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568226275/92131009B53AF7A0AFE4094D2F4D9AD1C8C94B6D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14427447140841744055/38C9A267DCB2BBD3F385EB1FBDA2A4D12687CC66/", "points": 10 }, { "name": "Fear", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141053/B44AFA0D2364EB909A34EF2542775A236DC40ACA/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9673306764281530635/A29BF04A277C5491400B32B97D8FEA512722A7B3/", "points": 3, "restrictions": { "include": { @@ -637,12 +637,12 @@ }, { "name": "Force Barrier", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141252/A95771E904324EFB0DC8AA87120282C986D27B1B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15143817809089088858/42064C6532794B58C1E96FF46E84ACE559FEC283/", "points": 10 }, { "name": "Force Choke", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141299/B617CD9EC6D6C01C720989C66E7153E60E47126D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14947659096167109315/5A48D7B33D304D35557DCC61D91BC6685583C78A/", "points": 10, "restrictions": { "include": { @@ -652,7 +652,7 @@ }, { "name": "Force Guidance", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141346/20FACD17AA921D67E6DE9CA8562A204A1ABCA92C/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17502599399888225537/5BCB94ABA69828E206589A9494A69496CD9BE1D9/", "points": 5 }, { @@ -663,17 +663,17 @@ }, { "name": "Force Push", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141385/A275EF302DAE3DFA231E8EA1EA7B9AC84212666A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15906597476264837051/ED34165894E2D37CF5194D7FDD51ADA7B3ED0596/", "points": 10 }, { "name": "Force Reflexes", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141421/28E98E095ED1469D198952D50B62D798E3D0CB44/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16096715596179885171/664DA23B337498B297143163C2B92A87EF60F515/", "points": 5 }, { "name": "Hope", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568142424/DD85A70E637C72F5F789DE89BA790FBC20A341A4/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10760287538524062730/28C02EF23C693E082481D9AC02407542CDD79CC6/", "points": 3, "restrictions": { "include": { @@ -683,7 +683,7 @@ }, { "name": "Jedi Mind Trick", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143339/DEFF1AF83C787C26075825C8E4B4E39CB9FC8DE7/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16576180526883700218/91A8C17FBF0B3D84BC8ED5CB727BABB1AB69790A/", "points": 5, "restrictions": { "include": { @@ -693,7 +693,7 @@ }, { "name": "Saber Throw", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568145235/8F1CAA7B51F644DBFB34E6D114579CF7B64CA6EB/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14367574201845804889/D5DB44897C38C9633AC5168A038FFEA4F7119A0E/", "points": 5 } ], @@ -705,7 +705,7 @@ }, { "name": "Ascension Cables", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138190/DBFD3BC31AD275E480FFAAB10F2C97A25EC53F7C/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17457507251375076663/CDA2350510DE5988DF0E4A5BF8929A2E75E3A498/", "points": 4 }, { @@ -720,7 +720,7 @@ }, { "name": "Electrobinoculars", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568140740/445C229E49CEC494DD591071BCEFBCAD2E0D5E30/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10199441544306107703/133B792AACF4901AEA9F35EC0F8C2D7B17DA7866/", "points": 8, "restrictions": { "override": { @@ -730,17 +730,17 @@ }, { "name": "Emergency Stims", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568140811/F8A07014225764F951B635DF595113FADDAB51BB/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13446832343829634893/9CF7AF6D397F06FB0D1A7205ED6139829D6F6E92/", "points": 8 }, { "name": "Environmental Gear", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568140911/1A4EFB505CE02253DCEAEC1D4BF67834344D91A7/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16652798721637422056/91F7C8F27389BFDA9D9136B527122F23530B8501/", "points": 2 }, { "name": "Grappling Hooks", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141742/DA7EFC4BC5B4AC25BDB4AE6C3A084A9B10F66A8D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13745066816000738957/3B66D6274F0A9783C51378EF3C318D3DF82A2514/", "points": 2 }, { @@ -765,7 +765,7 @@ }, { "name": "Portable Scanner", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144545/37084A685608E563517BF1AEE00C9ECC55C058AF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9364238650847600159/DC2666F9D8630D77DF25B6ACD52083D350CD926D/", "points": 6, "restrictions": { "override": { @@ -775,12 +775,12 @@ }, { "name": "Recon Intel", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144963/63594FB3AE94D1B4D9DBF1162AA53FDAB595E9FC/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13884509065011813667/94DC6344FD7E9D9860530CA21D7F80375C9D19C7/", "points": 2 }, { "name": "Targeting Scopes", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568145981/32BA3AE81620BAF925DD2C0AEF04C207A350691B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17003971957433179861/711F8AAE5EAC0E70AF2B911805EFE33CA2E2A97C/", "points": 4 }, { @@ -805,13 +805,13 @@ }, { "name": "Prepared Supplies", - "image": "http://cloud-3.steamusercontent.com/ugc/1856061112418869351/DE9290CC7840AD39A4EFFE0B0CD6D5E99A651C52/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15123875138493055282/186AF8D3483E44991F4BF5405DE6A84C4BF9664B/", "points": 5 }, { "name": "Din's Flame Projector", "image": "http://cloud-3.steamusercontent.com/ugc/1787388827710906580/1EC967CC4F871D07D3F419B4CF574394D289DBB6/", - "points": 8, + "points": 5, "restrictions": { "include": { "unit": ["Din Djarin"] @@ -842,44 +842,44 @@ "Generator": [ { "name": "Barrage Generator", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138795/B580C5ABBBE219D5BDD3A8AE097B4EC2D148B16F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12495820504141574252/66E55B65B1BC1B339B432F04AD0D130DFBC8D1BE/", "points": 3 }, { "name": "Overcharged Generator", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143974/6EA2B41E48F30FB9343FFAA98DB54AF04F1CD742/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10994758540793540020/A06F99541DB44F59DCDE885471D4F9E77A37DA08/", "points": 3 } ], "Grenades": [ { "name": "Concussion Grenades", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139643/9A4EAA2DB046B365D70E1EBFBF51E7FD9FDACE9F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14360371654209496022/CE2625348FFF9EF0BB5DC55F43ADBBD742F3073B/", "points": 3 }, { "name": "EMP \"Droid Poppers\"", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568140850/EA16F88F6B1308D37422702397315EEE731CEC2A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16979329673403509645/1A03673B650864D15B2500B83248522FFBABB2FB/", "points": 3 }, { "name": "Fragmentation Grenades", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568141461/8491940897B67853CD64E4E4948ED023D6602180/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17481227970231967061/040B13006EEBC5098CD852141C001CCE30B9A7F8/", "points": 5 }, { "name": "Impact Grenades", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384856827/A3EDF145437F8B6C6EDA9908B27A9E587A21CD1A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13262048397259590169/3F4068C180970A83C3676C515AADEDC561B243CA/", "points": 3 }, { "name": "Smoke Grenades", - "image": "http://cloud-3.steamusercontent.com/ugc/1860546411028298677/D4379214870C631017E66075B5457409AB19A77D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15223973175401373403/AB9C869FE463738878D0DFE6EF14529452DD08DD/", "points": 3 }, { "name": "Sonic Imploders", - "image": "http://cloud-3.steamusercontent.com/ugc/1856061112418869141/C4E525D13AE8D5DCDF9101E91C21038B866ACDD7/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9365292732961081475/6E1280991DDA4A40FD4B58948CCCEB2FBDD23C1A/", "points": 3 } ], @@ -1217,7 +1217,7 @@ "flip": { "name": "Battle Shield Wookiee (Defensive)", "displayName": "Shield Wookiee (Defensive)", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138967/E5E495CD6EEFD77393CD99C35CF651BBEDC1210A/" + "image": "https://steamusercontent-a.akamaihd.net/ugc/12854197019477585426/9DCA829B612203A963C6A0C1B9B4CF1896D7CD1C/" }, "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1621849308236386933/DCD62751F06157E85CE575C8BB1CCF86379BB7DE/" @@ -1259,7 +1259,7 @@ }, { "name": "Beskad Duelist", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139032/FD73A6FDED667993F98B7FBD7A1B3A408065B07F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140084887/6AE6E148D79D03A2A4C1B9B8D22DC0B9816222E0/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2174736446809252570/7F009B812ED06B7E45CFCE48142293A4B9C77991/" }, @@ -1286,7 +1286,7 @@ }, { "name": "Bowcaster Wookiee", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139088/9E0C04EE7D22BAE7023E8C8DB51A30B8877E0EEA/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140084912/2AFB89610DC5DFDB593DC830F48E76682A5B78FA/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541174665/9E1AA98159F76E271185345E9D347D7453C05D4B/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541174034/90C4A124BB82CCE0B5E38E13860ED49BB9A4973B/" @@ -1300,7 +1300,7 @@ }, { "name": "CM-0/93 Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139436/52C486B75EF3C105B674884E3930B773340F2936/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140084937/CFB33EA9EA5EECBC2AC894C172C560BEB13B6EAA/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/786359682702687947/DCC60167A78C152DDAE932E699D7C70C5629008B/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/788608699499192135/526538CE10AC6856578996BE6B8AB7263D32D5C7/" @@ -1638,7 +1638,7 @@ }, { "name": "Long Gun Wookiee", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568226123/FC3395575F2DA5995F599877AEF70D14F53400D5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140086434/5583DF53D29E565AFACC0B9535B7B5798B4183F6/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1619598366708042667/2DABB5788E51006EDB3FCB03400ACD4CE07755F3/" }, @@ -1651,7 +1651,7 @@ }, { "name": "MPL-57 Ion Trooper", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857353/B83529DEB37062D8687A005BC87975BDF37F32FC/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140087169/073B1CD7DB6904AA0874D42AB3B3B3EE8C08885C/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540540670338/28625FDF1F56EF08F0DB236C4ECFF67A54083B2B/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862750426/2563904D7561144EE85EBCD3E07D68F48D8DD034/" @@ -1763,7 +1763,7 @@ }, { "name": "SX-21 Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568145748/C825ACC9B08D2EBBACCDF2E20D81A7BD992FB076/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140090364/742815B4FE24A223EC893A9697A6CA8C284A7377/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/766102173464725382/9DADA91B7A5BE8DEEC1BDE838B3140A350D661A4/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/766102173464726852/2ABBCC6938695708489FB87C27C6B7481A1D294A/" @@ -1777,7 +1777,7 @@ }, { "name": "Scatter Gun Trooper", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857995/81E5662FC80211DE6602EB838A936670737EEEE2/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140089843/7386BFA25F833C0D475BDF1DC2C4089A9E533457/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540540671094/4A4BCE6286ACB413B661F827BB00B920617F3E91/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540540670760/47DF3E87B3F71E8EA42A89ECFE96E4FB3990D7ED/" @@ -1894,7 +1894,7 @@ }, { "name": "Tristan Wren", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146078/510B97CE7AEF65B5AC75FE3D94C769AB2CD070CD/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140090454/E3ADEFEA3EA70EEA160F391208AA8EE822DF1278/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2174736446809185988/83F7A4664576B192905C6393A6D9A5DAA776E01A/" }, @@ -1907,7 +1907,7 @@ }, { "name": "Ursa Wren", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146355/375DF871CB9930A2C15C7A310420AC1541AB8830/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140090523/750235F212F45721D81F9776FD957A26D40B3AC5/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2174736446809186071/A6DB57CF6BA2EEDAB6EB8A719DB8D0702708C759/" }, @@ -1921,7 +1921,7 @@ }, { "name": "Z-6 Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146746/5B85C86D1B259A5452AE5E7E1B60B9210A73EB95/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140091094/420B06E5C9EB605F3CF37DB609BE1961F8861B66/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862734756/1F48FA301357905B28DA35A03658DFA39154DBFE/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862736228/22D1A1ACC79493FE82165CC95A5E23170A4FBC5B/" @@ -2103,7 +2103,7 @@ }, { "name": "Axe Ewok", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866621745/E6F7651F238AEA622ACDF61F7F04868A05FF4878/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9885217524913261667/1618CC576AAF98E0DA209802203DD03717CED738/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2008089545867128895/152E8A0BE0A0FB98FAD48338552C6282DD8954B7/", "secondary": "http://cloud-3.steamusercontent.com/ugc/2008089545867129049/686925E47D218727E1C79A25E0F44FC80FAEE43F/" @@ -2348,17 +2348,17 @@ "Ordnance": [ { "name": "\"Bunker Buster\" Shells", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139117/F5DA5B7B6E57320977FD363E55781B7B620296D6/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9396219603588818774/E619EEF276816268108D1338C25AD01D6B6B7D5D/", "points": 12 }, { "name": "Armor-Piercing Shells", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568138159/C02E6C43ED68319D2CC2F9ADF605A3D21B3748AA/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9408502186349462134/5D735A7FAB7ECC984A0D591167B24FF02CE4133E/", "points": 10 }, { "name": "High-Energy Shells", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568142305/71D387B62FD63BF9EF7698D644082586A771B2D4/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17302862727198256496/5D94931ED170BE02B13FBA03D1F5894CF5FEF237/", "points": 8 } ], @@ -2385,7 +2385,7 @@ }, { "name": "2-1B Medical Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137676/C282622A211B384895C61D5EE0811E4E627FBDBF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081057/7B8C4C39468574A77EFF23E38A393B7E65D48867/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/949599512594268997/6248E4CAF3E978154106E7095E6984DCE6677CCA/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/949599512594282950/7A9A3E5518394273A507A64CCF7425A2E7EFC29B/" @@ -2764,7 +2764,7 @@ }, { "name": "Astromech", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384854817/828A0E540142CA963290F81E9D440D44AE7A840E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081721/9FBE2EBFF8A54C66DAC860A08E68AF4D108A09A2/", "mini": { "mesh": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680571476/57CF310CEE5CCDFE2F0769BE7BAF97A6A4104035/", "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/2508033889680571745/E93CBD4D8EBD90588639BCA658F36E208A58E465/" @@ -2778,7 +2778,7 @@ }, { "name": "R5 Astromech Droid", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144649/80478EA8C2BE8E3A92E3088FCCD4C3029AC573B0/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081288/0419F2D2F5D687DC9B927FE7C39AF89D52FD4ABD/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/949599512594428341/4AC5A309D701979A4B3E94725BC1E8745C598BCE/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/949599512594267684/F84F03E4A52F50EF5255A464FF85083CF4FB3D26/" @@ -2792,7 +2792,7 @@ }, { "name": "Rebel Comms Technician", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144715/C334BCF632593A2F8754691D5A62C1265BA573AF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140087441/1E88F5C5944887DA1471BA18283D3373A1CFD68A/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/949599512594474909/F8F049D7F88171ADBA83AF3EAF819495053C2873/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/949599512594268734/CF3C536F7B068020A99C5E3EE11CA9C54438E250/" @@ -2806,7 +2806,7 @@ }, { "name": "Rebel Officer", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144749/756F13AF16D25AED9D6AC864E92D17F0C28FF4DD/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140088186/0366C06A9E09B953E9B8557F671300731E83A209/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862726770/62FECAB3FED284E098751CC3BBC70C4CFF828A58/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862727097/DE589901F66E4079D82BA2C819672F622A5D7240/" @@ -2821,7 +2821,7 @@ }, { "name": "Rebel Trooper", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144840/A19523153BF3B7D13E7D8B892EF56F3DA0670482/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140088390/D15BD2F8088BB8162C050F2D1DB26CCEBA5E9CBB/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862761532/2FB3D83FC2BD077F4ADE619382382B9A59CD0ABE/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862750426/2563904D7561144EE85EBCD3E07D68F48D8DD034/" @@ -2835,7 +2835,7 @@ }, { "name": "Rebel Trooper Squad", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857823/10EA129449B49AF5F7B40A9825BF8E029C3E92E4/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140088345/8469135908290D4083FD268CA0E02EEB5B69A81F/", "minis": [ { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862752262/41BD6AB8B17AB7449226A3D80EF186EB8ABA3952/", @@ -2867,7 +2867,7 @@ }, { "name": "Rebel Trooper Captain", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144780/9EC770A152D6E780070467F0A5240DCC1594C26A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140088271/76AF8576BFC2E7EED7B065DB76A83688C13BD43A/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/766102173464670173/ECC18FB91315A55181F38A6899122E005460C688/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/766102173464683373/FE0A29CF1A46E7F97FEAE0C9FD665AB192A8D9A8/" @@ -2882,7 +2882,7 @@ }, { "name": "Rebel Trooper Specialist", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144806/4E3FBF8193DF96A2AF0306C84B225898DBEB1455/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140088307/7C272AB0828A4D728C3D1C0E487F686817D5B542/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/766102173464719285/839B79B3594F31585EB38763EDF43AD32E46F37F/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/766102173464692456/FD09533718942CE5B08526B2CD1A00D5D38CB734/" @@ -2896,7 +2896,7 @@ }, { "name": "Rebel Veteran", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144901/B379F1B7C63F853ECAA61149EDEEA7D2AD83D813/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140089489/88E830A8F6CC5CC3F6589C7F5D6680AF2006DC5F/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/788608699499192589/466305BFF474DBAB11F58FA13B3F4ACC9BB7127C/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/788608699499192135/526538CE10AC6856578996BE6B8AB7263D32D5C7/" @@ -2910,7 +2910,7 @@ }, { "name": "Rebel Veteran Squad", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144901/B379F1B7C63F853ECAA61149EDEEA7D2AD83D813/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140088439/DAD81738594065C60010622C47AC9FC71021F2E9/", "minis": [ { "mesh": "http://cloud-3.steamusercontent.com/ugc/788608699499192232/D15615AF2A0F4F4887E9BD3DFFA141C8ACCCEC78/", @@ -3237,7 +3237,7 @@ }, { "name": "Ewok Trapper", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866621848/AABE07DBD74C5C3520EB3E5DA77744D764C09515/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140103150/0A2C3B844131196AFD07DAEF9FE79F2CA1563234/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/2008089545867128844/558E1277513B0AF48911A68B5FAAB981BC0A56A1/", "secondary": "http://cloud-3.steamusercontent.com/ugc/2008089545867129049/686925E47D218727E1C79A25E0F44FC80FAEE43F/" @@ -3395,7 +3395,7 @@ }, { "name": "Hotshot Pilot", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568142465/1100E3B8812032C0DA0E0889C61A17497CD5CF78/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140085473/EFDD0076A93ED598B0D58CD718A765B2A56AE228/", "points": 8, "restrictions": { "include": { @@ -3448,7 +3448,7 @@ }, { "name": "Outer Rim Speeder Jockey", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143941/3BC3D08463BE51C7EE594BD45C8467F440AF3737/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140087292/9D5546C2AC5D1B59952078E53AF7E83003DFB912/", "points": 10, "restrictions": { "include": { @@ -3490,7 +3490,7 @@ }, { "name": "Shriv Suurgav", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384858186/51EFAD35701C4C731621610E3BF9C2E2FAB17D58/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140089939/0B2428B8D00A6B3DED4916E7AA55D7940CB1DFA9/", "points": 10, "restrictions": { "include": { @@ -3521,7 +3521,7 @@ }, { "name": "Wedge Antilles", - "image": "http://cloud-3.steamusercontent.com/ugc/1860546411028298597/12022AA00835B271627DB885D5B1756773A23B75/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140091023/B1341EA467EFD4D50C2D7C677E1422B2FC84A4AD/", "points": 5, "restrictions": { "include": { @@ -3554,8 +3554,8 @@ "Programming": [ { "name": "Attack Protocols", - "image": "http://cloud-3.steamusercontent.com/ugc/1616219505081189556/B615AA77261F49A55D5424BF61863365D8147D13/", - "points": 3, + "image": "https://steamusercontent-a.akamaihd.net/ugc/10231463032862954270/5569EEFD92CA844450DCFDF7BC652B4DDC055DD5/", + "points": 8, "restrictions": { "include": { "type": ["Vehicle"] @@ -3564,8 +3564,8 @@ }, { "name": "Defense Protocols", - "image": "http://cloud-3.steamusercontent.com/ugc/1616219505081189482/1C8F98036D5D02047E16BD1043E44A388A6CF58A/", - "points": 3, + "image": "https://steamusercontent-a.akamaihd.net/ugc/16109346406166037874/376653EF5683F1DBF5DCDFF7742530E1C357FBAA/", + "points": 8, "restrictions": { "include": { "type": ["Vehicle"] @@ -3574,8 +3574,8 @@ }, { "name": "Engagement Protocols", - "image": "http://cloud-3.steamusercontent.com/ugc/1616219505081189429/CE2795767E26EC108F8E8AE10E7DE2FE933385AD/", - "points": 5, + "image": "https://steamusercontent-a.akamaihd.net/ugc/12618446894076894989/F2992658DAEDCD5F593270F3CF14EABFCD95019F/", + "points": 3, "restrictions": { "include": { "type": ["Vehicle"] @@ -3617,10 +3617,10 @@ "Training": [ { "name": "Defensive Stance", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568139810/3DFF9DE65260A8180F887D0581523E9B69391F77/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10394379497446642242/A484E8DE817DC612497E99A36F6D33FBA8E423C9/", "flip": { "name": "Offensive Stance", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143796/D0FC9D6D3C0F2AA900FB7366B356188272BE1786/" + "image": "https://steamusercontent-a.akamaihd.net/ugc/16537339389726247952/720B5E7A361CEEC36F674F1CF36298A5997DE2F5/" }, "points": 5, "restrictions": { @@ -3631,42 +3631,42 @@ }, { "name": "Duck And Cover", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568140354/46D43C9B55C4EDB047E4B6B49F2DD0C3CB31C302/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15529355718365557412/FFC51C64FA7773BCC25038D8D5D818D2AE5241BC/", "points": 2 }, { "name": "Endurance", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568140878/B43C130BAB58110E55D17F2B4E571FFB99631E7F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9415595468930295182/7ACC3ED6894ECD1FDED41AC058BF318868605636/", "points": 6 }, { - "name": "Hunter", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568142636/489DEDF6E445694D94ED3E8CA238B0B0DE4B1DE6/", + "name": "On the Hunt", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11536628804561707799/CE3417E31989D043648719A766916DF1A91B9228/", "points": 6 }, { "name": "Into The Fray", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568226053/CB89272FB33D325DA4C7EA56545B22112CB630F5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14109187506581343144/00287D0E98E6349774A91B470804179CCBFE529E/", "points": 4 }, { "name": "Offensive Push", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143751/9080C15E59832CCE8A15D97665832E6573554A2E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16907375567208427027/88097E66EB0822A2A2956F6BDCC92A37E9E3E464/", "points": 6 }, { "name": "Overwatch", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144022/17F4FAC24AE1F25A530D7E392355B9126C6787AA/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15085498882065075341/D3305FB0E1D762750532A11CDCB3195A3587F987/", "points": 4 }, { "name": "Protector", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568226011/140C2DB7EA56E3DB5C11919A3C0923E64B827667/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16502499181280123877/013B5483D56B59EC250FE6A45B2DED594E1BF45F/", "points": 5 }, { "name": "Seize The Initiative", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568145308/93CDCF8E86134737E0D89FED6BB10317BA7545CF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15625749152448275367/CF9CACC1FA903168B23F5EACE6E091603EF4D0BA/", "points": 5, "restrictions": { "include": { @@ -3676,22 +3676,22 @@ }, { "name": "Situational Awareness", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568145416/5965B627A7C6C0584CED87D7D08CF0C86F9C5418/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17536138674603170323/AE86A4AB47B30D6BBC2DAFCC1E760AF8CCCB0F8E/", "points": 4 }, { "name": "Tenacity", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568146012/69F96BBBF484ABB6A9CAD2637064D4C199BC6BE8/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13667627159132611482/8C1C77825EEAB8D7099989FAF8A583725D8D9127/", "points": 6 }, { "name": "Up Close and Personal", - "image": "http://cloud-3.steamusercontent.com/ugc/1856061112418869444/1E439AB8897062691976E3BE15D17C0C4A0380AA/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9232863277993940777/071132D1F5F4BF88B379A1BF94EB78ED4D9559BA/", "points": 8 }, { "name": "Herbal Medicine", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866562515/AA30CC1B876E89EF97F2C0BDA05F5E17411D0B13/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140105386/419AA1B3264BA3ACD8243358E80630E91B11DBC9/", "points": 10, "restrictions": { "include": { @@ -3701,7 +3701,7 @@ }, { "name": "Onward to Victory", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866562747/ABEA46EB90A5161E77598003BCBD0EB5A48C2250/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140105386/419AA1B3264BA3ACD8243358E80630E91B11DBC9/", "points": 10, "restrictions": { "include": { @@ -3711,7 +3711,7 @@ }, { "name": "Secret Ingredients", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866562047/CB4CF0706A201A80F6B3F5B11AFA1859F5D705A5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140104858/F33E3DEECEB2996128560B29C8E41C38B84EB371/", "points": 5, "restrictions": { "include": { @@ -3721,17 +3721,17 @@ }, { "name": "Call to Arms", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866562443/1ED7D2D33E17712DB46173EE2F91A149A0412D4C/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140099258/36B8495C43AD6C6CA175204A01096A05D647F70C/", "points": 5 }, { "name": "Forest Dwellers", - "image": "http://cloud-3.steamusercontent.com/ugc/2008089545866562596/986DAEFC7C6C8E6BBBAB88010DEBAD2DECE17946/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140100329/4F2A45ECABD3B325A456366332FF93683D6AA296/", "points": 3 }, { "name": "Insatiable Curiosity", - "image": "http://cloud-3.steamusercontent.com/ugc/2005838139147443657/789DD16EEFF928B7DDA85D8C6D658E6D52926627/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140100414/E3E6C5563CD533DC0560C50590B8A989E382E7BC/", "points": 8, "restrictions": { "include": { @@ -3975,7 +3975,7 @@ "objective": [ { "name": "Marked Targets", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052458/8DF8DF5875440F68E9655A3D95F04BBE10174416/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11843919298206935699/A3B1D5201F36075D53E0ACE69E0C1ED6AFB4DD22/", "type": "vertical", "additionalObjects": [ "Victory Token", @@ -3990,17 +3990,17 @@ }, { "name": "Sweep and Clear", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501053422/1EC807CFAFBA0902E839759D5004A7F607512F3D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12307394332451477333/DF65C02CC017BD25A960FC2A737AE28829325CD2/", "type": "vertical" }, { "name": "Bring Them to Heel", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051406/16444E4BBFB1A4CC8B895AA3087DDAF697271957/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16790576095549234351/278EA3877E97AD155ADA4602D42DF023B7CEB78B/", "type": "vertical" }, { "name": "Surface Scan", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501053304/A7BFCCDFEF3DEA3FABA49147633D1116431E36D5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16343945853969639984/24ABD9DF34B74E343BD4F79D9461385D76C0CD88/", "type": "vertical", "additionalObjects": [ "Objective Token", @@ -4011,7 +4011,7 @@ }, { "name": "Destroy Enemy Base", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051929/41B0019F8EA238DA34EF3BEA52A4100C903A1320/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15702722297992072119/D6071563AAC98F86DDD790E6DE034350EEF17F5F/", "type": "vertical", "additionalObjects": [ "Objective Token", @@ -4022,7 +4022,7 @@ }, { "name": "Recon Mission", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052682/2B61256E5F0F7C504CA94950802B062C98235346/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/18385343105541227063/7F990BB73C8675734EB02873157F392EA265DC20/", "type": "vertical", "additionalObjects": [ "Objective Token", @@ -4033,7 +4033,7 @@ "deployment": [ { "name": "Close the Pocket", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051767/3260475F48F6C58305E14413669DE2D10455871B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17314598212129638590/62EB33594611850FEBF0567D57ABA8CFB8077091/", "type": "vertical", "zone": [ ["r", "r", "r", "rs", " ", " ", " ", " ", "rss", "r", "r", "r"], @@ -4052,7 +4052,7 @@ }, { "name": "Bunker Assault", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051586/C240D83E26587E5F8988CADF48CDABCC1B479F5E/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16995954044322736468/0D0086DE04A57350DF77F62FDEC94F5148B7C4D5/", "type": "vertical", "zone": [ [" ", " ", "r", "r", "r", "r", "r", "r", "r", "r", " ", " "], @@ -4072,7 +4072,7 @@ }, { "name": "Breakthrough", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051322/D6F545BB57A0DD65152698FFFB087623633E1898/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9648599321290140597/26C606119E07DCACAAF07B1F5429CB9A03EC59F3/", "type": "vertical", "zone": [ ["r", "r", "r", "r", "r", "r", "r", "r", "r", " ", " ", " "], @@ -4092,7 +4092,7 @@ }, { "name": "Intercept Signals", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052352/6E77C47C3CBCC5095E3E7E014913A71E55595214/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13661793580985225615/BB706B264CE5C71456271DE54E765EC27EA6F786/", "type": "vertical", "zone": [ [" ", " ", "r", "r", "r", "r", "r", "r", "r", "r", " ", " "], @@ -4112,7 +4112,7 @@ }, { "name": "Recover the Research", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052764/CBD5436D90279F40330A62F85582862E6FA4F0F0/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16282482143036636007/FD7F429F82E2F61B9EC390C9FBAB6782B26D8507/", "type": "vertical", "zone": [ ["r", "r", "r", "r", "r", "r", "r", "r", "r", " ", " ", " "], @@ -4134,7 +4134,7 @@ }, { "name": "Shifting Priorities", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501053065/3A8AE5DD93D1FEEEE72DB9E9936F918CFFDCF7CC/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16497333789704302385/06FFACF38887F17BF4E0351DA7ED0D22CE26AEC7/", "type": "vertical", "zone": [ ["r", "r", "r", "r", "r", "r", "r", "r", "r", " ", " ", " "], @@ -4157,7 +4157,7 @@ "condition": [ { "name": "Advanced Intel", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051063/9A271D248EECC921D4B5324569C9650C945EDD07/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13272907091260444770/243391B61F192F53AA7CF0360D3F846C7A7A0A97/", "type": "vertical", "additionalObjects": [ "Condition Token" @@ -4165,7 +4165,7 @@ }, { "name": "Fortified Position", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052045/6735E7091357D4EB2DFC02F0DA7F255BD5AC9AA7/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16927846453234643948/97D5A7F0E4C9D3DAB14C3754AC3A484C15CE3567/", "type": "vertical", "additionalObjects": [ "Barricade", @@ -4175,7 +4175,7 @@ }, { "name": "Cunning Deployment", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051845/AEB90A07F87FC31E653E786079D0C463C3E3CE64/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14925552919945327142/06659E9120D6246C6048165ECDE9F5F5AEA7A5EB/", "type": "vertical", "additionalObjects": [ "Dodge Token", @@ -4185,7 +4185,7 @@ }, { "name": "Garrison", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052128/95ECCC86C62DB20C0BE8239241F5E67FC8DB5E26/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11060842461995269445/3EC4678860C9AD82F7912D3FC3A56A519BCA3CEE/", "type": "vertical", "additionalObjects": [ "Condition Token" @@ -4193,7 +4193,7 @@ }, { "name": "Ordnance", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052576/4BD3DE74B27DC9DE36F1E9EBDDEF0E4A58AF297B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12637307440348267234/446B8F97B7C1EF2452E5B4DD6AF36FC1BAAD7FA2/", "type": "vertical", "additionalObjects": [ "Objective Token" @@ -4201,7 +4201,7 @@ }, { "name": "Strafing Run", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501053181/96FE3E7561BCEFF9C770B8334F0D2061AD896592/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16276132125547808705/8A61415CDD0F2E20417F6D8371858B206FA0A775/", "type": "vertical", "additionalObjects": [ "Objective Token" @@ -4884,37 +4884,37 @@ }, "Breakthrough Map": { "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" } }, "Bunker Assault Map": { "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/" } }, "Close the Pocket Map": { "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/" } }, "Intercept Signals": { "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/" } }, "Recover the Research Map": { "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/" } }, "Shifting Priorities Map": { "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" } } From 865cd67ed03f2423643708ad5db805294dac0798 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 20:03:20 -0400 Subject: [PATCH 11/93] Update Welcome.xml --- mod/src/includes/ui/Welcome.xml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 8f36ff9b..e57fc982 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -46,13 +46,8 @@ CHANGELOG (Since last Beta) - - Fix: Crab Base Size - - Fix: Update Crab Art - - Add: Arfs and their Upgrades - - Fix: Update Sleepers Art - - Add: Ahsoka - - Fix: Staps Art - - Fix: Add Commando Token + - Add: New Card Format + - Fix: json Discrepancies From b6eb7b1e4009bcab290fba1bba9b59105346f59e Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 20:06:10 -0400 Subject: [PATCH 12/93] Update official.json --- contrib/cards/official.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 086f280d..7e60f693 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -2202,7 +2202,7 @@ } }, { - "name": "Hunter (The Bad Batch)", + "name": "Hunter", "image": "http://cloud-3.steamusercontent.com/ugc/2475370743175005230/53DDE7626A5573411C2116565252DBB566D42E57/", "leader": true, "points": 0, From 1988a9f676b38b12fd885f41fd27a9b138d1fa58 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 20:48:46 -0400 Subject: [PATCH 13/93] Update SeparatistUnits.json --- contrib/cards/Separatist/SeparatistUnits.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index 77debe4d..0f6a51cb 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -172,7 +172,7 @@ ] }, { - "name": "Kracken", + "name": "Kraken", "title": "Super Tactical Droid", "displayName": "Kraken", "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131433103/C2CD6A90911C879E745B1B79DFE9B810F02CA25D/", From 23023a5473faa3de51bd5c2dc3fcbfc5e4d6316b Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 20:59:59 -0400 Subject: [PATCH 14/93] Update Welcome.xml --- mod/src/includes/ui/Welcome.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index e57fc982..2bc4ef85 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -37,7 +37,7 @@ - Welcome to Star Wars: Legion for TTS, v8.0.9-beta. + Welcome to Star Wars: Legion for TTS, v8.0.10-beta. This is a beta release, and is not suitable for tournament play. From 3fb56c8edc8972555a91fabf9ec4d294496f4187 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 21:38:34 -0400 Subject: [PATCH 15/93] Merge remote-tracking branch 'upstream/main' --- contrib/cards/official.json | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index e457b4fa..3322e17d 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1126,6 +1126,20 @@ } ], "Heavy Weapon": [ + { + "name": "RPS-6 ARF Trooper", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59215140638617630/9AB204944DB662D2D15FC2FA5E50F02138AD1FD6/", + "mini": { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16428815149645590/1B9C028584B5A7DCD488AE1E9C57D33A1B93C92E/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16428815149646251/ADB16A6BF3CADD49D0237A4AB51FB4D7AF0AB521/" + }, + "points": 24, + "restrictions": { + "include": { + "unit": ["ARF Troopers"] + } + } + }, { "name": "RPS-6 ARF Trooper", "image": "https://steamusercontent-a.akamaihd.net/ugc/59215140638617630/9AB204944DB662D2D15FC2FA5E50F02138AD1FD6/", @@ -5037,37 +5051,61 @@ }, "Breakthrough Map": { "card": { +<<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", +>>>>>>> Stashed changes "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" } }, "Bunker Assault Map": { "card": { +<<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", +>>>>>>> Stashed changes "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/" } }, "Close the Pocket Map": { "card": { +<<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", +>>>>>>> Stashed changes "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/" } }, "Intercept Signals": { "card": { +<<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", +>>>>>>> Stashed changes "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/" } }, "Recover the Research Map": { "card": { +<<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", +>>>>>>> Stashed changes "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/" } }, "Shifting Priorities Map": { "card": { +<<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", +>>>>>>> Stashed changes "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" } } From 2ee64a8568f973db9566fb0468b02f750a03f635 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 21:39:32 -0400 Subject: [PATCH 16/93] Merge remote-tracking branch 'upstream/main' --- contrib/cards/official.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 3322e17d..4e1dd012 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5051,8 +5051,12 @@ }, "Breakthrough Map": { "card": { +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes @@ -5061,8 +5065,12 @@ }, "Bunker Assault Map": { "card": { +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes @@ -5071,8 +5079,12 @@ }, "Close the Pocket Map": { "card": { +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes @@ -5081,8 +5093,12 @@ }, "Intercept Signals": { "card": { +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes @@ -5091,8 +5107,12 @@ }, "Recover the Research Map": { "card": { +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes @@ -5101,8 +5121,12 @@ }, "Shifting Priorities Map": { "card": { +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes From bf9115658e168fd7d8bd77282e8f0f919f3cdd7a Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 21:39:43 -0400 Subject: [PATCH 17/93] Merge remote-tracking branch 'upstream/main' --- contrib/cards/official.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 4e1dd012..96efd40c 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5052,11 +5052,15 @@ "Breakthrough Map": { "card": { <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes @@ -5066,11 +5070,15 @@ "Bunker Assault Map": { "card": { <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes @@ -5080,11 +5088,15 @@ "Close the Pocket Map": { "card": { <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes @@ -5094,11 +5106,15 @@ "Intercept Signals": { "card": { <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes @@ -5108,11 +5124,15 @@ "Recover the Research Map": { "card": { <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes @@ -5122,11 +5142,15 @@ "Shifting Priorities Map": { "card": { <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes From 0ca8f010abddb19fcdcc8b47a3d302022cab93fc Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 21:39:52 -0400 Subject: [PATCH 18/93] Merge remote-tracking branch 'upstream/main' --- contrib/cards/official.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 96efd40c..c57b527d 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5053,6 +5053,7 @@ "card": { <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", ======= @@ -5061,6 +5062,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes @@ -5071,6 +5075,7 @@ "card": { <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", ======= @@ -5079,6 +5084,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes @@ -5089,6 +5097,7 @@ "card": { <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", ======= @@ -5097,6 +5106,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes @@ -5107,6 +5119,7 @@ "card": { <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", ======= @@ -5115,6 +5128,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes @@ -5125,6 +5141,7 @@ "card": { <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", ======= @@ -5133,6 +5150,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes @@ -5143,6 +5163,7 @@ "card": { <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", ======= @@ -5151,6 +5172,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes From 582747b902259d404972099144c79a5221362d69 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 22:06:30 -0400 Subject: [PATCH 19/93] Update official.json --- contrib/cards/official.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index c57b527d..ccc94495 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5054,6 +5054,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", ======= @@ -5065,6 +5066,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes @@ -5076,6 +5080,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", ======= @@ -5087,6 +5092,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes @@ -5098,6 +5106,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", ======= @@ -5109,6 +5118,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes @@ -5120,6 +5132,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", ======= @@ -5131,6 +5144,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes @@ -5142,6 +5158,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", ======= @@ -5153,6 +5170,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes @@ -5164,6 +5184,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", ======= @@ -5175,6 +5196,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes From e0e72d8d770eb698112a9cb96be64f58bbc84886 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 8 May 2025 22:07:02 -0400 Subject: [PATCH 20/93] Update official.json --- contrib/cards/official.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index ccc94495..8b9ce58a 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5055,6 +5055,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", ======= @@ -5069,6 +5070,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes @@ -5081,6 +5085,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", ======= @@ -5095,6 +5100,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes @@ -5107,6 +5115,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", ======= @@ -5121,6 +5130,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes @@ -5133,6 +5145,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", ======= @@ -5147,6 +5160,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes @@ -5159,6 +5175,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", ======= @@ -5173,6 +5190,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes @@ -5185,6 +5205,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", ======= @@ -5199,6 +5220,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes From 0eb01f00d1e9750cb1204f343f12d510d7a48676 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 24 Oct 2025 21:27:42 -0400 Subject: [PATCH 21/93] Wheel Greg cards CCs and Turbocharge --- contrib/cards/Separatist/SeparatistUnits.json | 41 +++++++++++++++++-- contrib/cards/official.json | 10 +++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index e1679b5d..31c7f6c2 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -38,12 +38,17 @@ { "name": "General Grievous", "title": "Wheel Bike Warlord", - "image": "https://steamusercontent-a.akamaihd.net/ugc/14797403978689987926/064DA0169CEEAE3EF0AFA49AE394936BBCDFDCDB/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10719584612465428796/38DFBFBDFC11B138DD5FC2DE9F0B6BA67E4450C6/", "size": "large", "type": "Ground Vehicle", - "points": 0, + "points": 150, "speed": 2, - "upgrades": {}, + "upgrades": { + "Command": 1, + "Training": 1, + "Gear": 1, + "Grenades": 1 + }, "silhouette": { "height": 2.57, "offset": 0 @@ -69,6 +74,21 @@ "name": "Crush Them!", "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131524786/7D9A98904E7D5F80F9590AAA5EDF107F2B95623A/", "pip": 3 + }, + { + "name": "I'll Do it Myself!", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15891905199014084776/E70951C31E286BCF03E7D6BB08ED6DA6C96D9701/", + "pip": 1 + }, + { + "name": "Pincer Attack", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13331811042949504722/5032E1A16107F4D2C8DA6FE2C3AA707D5EB55EDD/", + "pip": 2 + }, + { + "name": "Full Throttle", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17267278540148426820/E310561126BE74D0A9CB502C551C8DF2D76E7B1A/", + "pip": 3 } ] }, @@ -106,6 +126,21 @@ "name": "Crush Them!", "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131524786/7D9A98904E7D5F80F9590AAA5EDF107F2B95623A/", "pip": 3 + }, + { + "name": "I'll Do it Myself!", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15891905199014084776/E70951C31E286BCF03E7D6BB08ED6DA6C96D9701/", + "pip": 1 + }, + { + "name": "Pincer Attack", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13331811042949504722/5032E1A16107F4D2C8DA6FE2C3AA707D5EB55EDD/", + "pip": 2 + }, + { + "name": "Full Throttle", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17267278540148426820/E310561126BE74D0A9CB502C551C8DF2D76E7B1A/", + "pip": 3 } ] }, diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 2dccbe02..5a19b559 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1188,6 +1188,16 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/15123875138493055282/186AF8D3483E44991F4BF5405DE6A84C4BF9664B/", "points": 5 }, + { + "name": "Turbo-Charge", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16761979492775816091/37108458441EBB0A6D7090B98BA9B0C3DC2AD80B/", + "points": 5, + "restrictions": { + "include": { + "unit": ["General Grievous", "TSMEU-6 Wheel Bikes"] + } + } + }, { "name": "Din's Flame Projector", "image": "https://steamusercontent-a.akamaihd.net/ugc/13750229648005754282/BBA7B66BB64E77DD6485518A99475DE0DC684BE8/", From 42fde3a73dd22c71e1199207a25b61847937d03c Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 22 Nov 2025 01:46:17 -0500 Subject: [PATCH 22/93] Unit Upgrades --- contrib/cards/Republic/RepublicUnits.json | 2 +- contrib/cards/official.json | 69 +++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index 0a7d1f25..20796bbe 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -889,7 +889,7 @@ { "name": "Infantry Support Platform", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385135344629/540E23C816C00B4B7E086B626329F13633403C9E/", - "size": "huge", + "size": "laat", "type": "Repulsor Vehicle", "points": 65, "speed": 3, diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 5a19b559..6d16b823 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1526,6 +1526,59 @@ } ], "Heavy Weapon": [ + { + "name": "Agent Kallus", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11243613263901997875/0DD9BAD82731CF9B82AA13471D5EC164B8583545/", + "mini": { + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/2446089105227059084/0EB4EE32683338BC754DA5EC1F613ABA71048E90/" + }, + "leader": true, + "points": 28, + "restrictions": { + "include": { + "faction": ["Empire"], + "rank": ["Corps", "Special Forces"] + } + } + }, + { + "name": "Cassian Andor", + "displayName": "Cassian Andor (Sniper Config)", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9591158490068968132/FEEFE73E35F1BFA0C632F973EA56454D377007FC/", + "flip": { + "name": "Cassian Andor", + "displayName": "Cassian Andor (Black Ops)", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11525894432535452100/E903693ACFB3CFA2C08225AC65BBF46C06F293F6/" + }, + "mini": { + "mesh": "http://cloud-3.steamusercontent.com/ugc/773996263556990389/C38E9CAD7E5BA5496E1A211CA799E9ACBAC51D00/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/770611701143191344/5CAA4F0CE9A3FBE89A81560221ADCDDB5A801CC3/" + }, + "leader": true, + "points": 25, + "restrictions": { + "include": { + "faction": ["Rebel"], + "rank": ["Corps"] + } + } + }, + { + "name": "Kraken", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15597539132515958482/63F091887EF5669192CD96A978FDAA0A1B9BD50D/", + "mini": { + "bundle": "http://cloud-3.steamusercontent.com/ugc/1619598366706113202/449F19DA94D42DAD0F0B4D0B77A8E9AD45C320E5/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/1619598366706441991/04B29BB48A98AF8D79FC1414548496BAD49CCB28/" + }, + "leader": true, + "points": 30, + "restrictions": { + "include": { + "faction": ["Separatist"], + "rank": ["Corps"] + } + } + }, { "name": "RPS-6 ARF Trooper", "image": "https://steamusercontent-a.akamaihd.net/ugc/12717002306739299639/3A8542FCC457C05E120FB9E59AD8BA4D1551DFE6/", @@ -2613,6 +2666,22 @@ } ], "Squad Leader": [ + { + "name": "Clone Captain Rex", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17732060543639041010/F75242323BF212E3ABCCC41199991C8FBFAEBF68/", + "mini": { + "mesh": "http://cloud-3.steamusercontent.com/ugc/776232727456806720/869654FC3B0590BFFF2E1845C3CFA9169838EBF9/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/776232727456806858/52327997163A5113DF9E11B85F9DA8B6B970012C/" + }, + "points": 38, + "leader": true, + "restrictions": { + "include": { + "faction": ["Republic"], + "rank": ["Corps"] + } + } + }, { "name": "Jedi Guardian", "image": "https://steamusercontent-a.akamaihd.net/ugc/11522429422550427771/2AE47A46390BAC96C6EAE879130F4F01D738E37D/", From 716a8d92d9cb0d91c09317ab1122a8f05a8b26d4 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 22 Nov 2025 23:22:25 -0500 Subject: [PATCH 23/93] 212th CCs --- contrib/cards/official.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 6d16b823..27592c28 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -4537,6 +4537,11 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/14615871148955326200/5C4A46932D48C89AC97B650847429D97701993D7/", "pip": 1 }, + { + "name": "Sneak Attack", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17800273845434794252/84425184A9FA3237F85342B84FD168824FBF90C8/", + "pip": 1 + }, { "name": "Air Support", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385134030778/90E5CEF9833FED1C5F926FB8878BDBCD5B5099F6/", @@ -4562,6 +4567,11 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/17056187462617014571/3898DDC0BF25F87D73C3F40CDA7AA52DDB6CD765/", "pip": 2 }, + { + "name": "Recon Division", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17814277548406010305/67FBF2832F88C0A2B3923023FA53418900EC7C92/", + "pip": 2 + }, { "name": "Attack Of the Clones", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385134033797/3AA33B36A7B9EAAC8444A6CAC176F2130EE30E6C/", @@ -4577,6 +4587,11 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/12871643868680759199/5CC33092968E7DE16416344B01D7BC76211DB23E/", "pip": 3 }, + { + "name": "Flexible Strategy", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15462092410085872515/FF4DD4B2D64809CF0A86A3288A8D500DD7E30240/", + "pip": 3 + }, { "name": "Leaders of the 501st", "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384886698/41DC9E2547F47EEA11B6DEC9AE7AC677CF1FBAAD/", From 684e3f2f6a4845cb9f0f2608952b6c4894356cc1 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sun, 23 Nov 2025 00:22:42 -0500 Subject: [PATCH 24/93] New Objectives --- contrib/cards/official.json | 81 +++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 27592c28..e464dcba 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -4687,6 +4687,17 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/18385343105541227063/7F990BB73C8675734EB02873157F392EA265DC20/", "type": "vertical", "additionalObjects": ["Objective Token", "Objective Token"] + }, + { + "name": "Supply Run", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9519163714205143198/44DD261B633320C5FE3A406F76B56FD73EDC2E76/", + "type": "vertical", + "additionalObjects": [ + "Objective Token", + "Objective Token", + "Objective Token", + "Objective Token" + ] } ], "deployment": [ @@ -4729,10 +4740,10 @@ ["b", "b", "b", "bss", " ", " ", " ", " ", "bs", "b", "b", "b"] ], "additionalObjects": [ - "Close the Pocket Map", "POI Token", "POI Token", - "POI Token" + "POI Token", + "Close the Pocket Map" ] }, { @@ -4774,11 +4785,11 @@ [" ", " ", "b", "b", "b", "b", "b", "b", "b", "b", " ", " "] ], "additionalObjects": [ - "Bunker Assault Map", "POI Token", "POI Token", "POI Token", - "POI Token" + "POI Token", + "Bunker Assault Map" ] }, { @@ -4820,11 +4831,11 @@ [" ", " ", " ", "b", "b", "b", "b", "b", "b", "b", "b", "b"] ], "additionalObjects": [ - "Breakthrough Map", "POI Token", "POI Token", "POI Token", - "POI Token" + "POI Token", + "Breakthrough Map" ] }, { @@ -4866,11 +4877,11 @@ [" ", " ", "b", "b", "b", "b", "b", "b", "b", "b", " ", " "] ], "additionalObjects": [ - "Intercept Signals Map", "POI Token", "POI Token", "POI Token", - "POI Token" + "POI Token", + "Intercept Signals Map" ] }, { @@ -4912,13 +4923,13 @@ [" ", " ", " ", "b", "b", "b", "b", "b", "b", "b", "b", "b"] ], "additionalObjects": [ - "Recover the Research Map", "POI Token", "POI Token", "POI Token", "POI Token", "POI Token", - "POI Token" + "POI Token", + "Recover the Research Map" ] }, { @@ -4960,12 +4971,32 @@ [" ", " ", " ", "b", "b", "b", "b", "b", "b", "b", "b", "b"] ], "additionalObjects": [ - "Shifting Priorities Map", "POI Token", "POI Token", "POI Token", "POI Token", - "POI Token" + "POI Token", + "Shifting Priorities Map" + ] + }, + { + "name": "Outflank", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13495770785016686863/572E6718AF1D92854622E20A2C7BBF8E8EAF4793/", + "type": "vertical", + "zone": [ + [" ", " ", " ", "r", "r", "r", "r", "r", "r", " ", " ", "r"], + [" ", " ", " ", "rh", "rh", "rh", "rh", "rh", "rh", " ", " ", "r"], + ["b", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "r"], + ["b", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "r"], + ["b", " ", " ", "bh", "bh", "bh", "bh", "bh", "bh", " ", " ", " "], + ["b", " ", " ", "b", "b", "b", "b", "b", "b", " ", " ", " "] + ], + "additionalObjects": [ + "POI Token", + "POI Token", + "POI Token", + "POI Token", + "Outflank Map" ] } ], @@ -5005,6 +5036,26 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/16276132125547808705/8A61415CDD0F2E20417F6D8371858B206FA0A775/", "type": "vertical", "additionalObjects": ["Objective Token"] + }, + { + "name": "Black Ops", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17788491822893443475/FCEB325A1C045662381E47E751482700521D2219/", + "type": "vertical" + }, + { + "name": "Command Override", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12943860816298787957/AC216DBF0F8880EC568FCAA34B5AC347187723A8/", + "type": "vertical" + }, + { + "name": "Coordinated Strike", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15369793059824781345/5FA5B970C2370F879FFAF0650E8F7FD361F06F91/", + "type": "vertical" + }, + { + "name": "No Time to Lose", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11496058962921126665/041D2E72354726A4E39C6A2CCA2DA6C0E66946B2/", + "type": "vertical" } ] }, @@ -5716,6 +5767,12 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" } + }, + "Outflank": { + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/15283876349976838373/93B3D53B8AB0DCF7FE15F606E81FD962A738B455/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" + } } } } From 3ff310746f7e073403baebaf5acc057a22918ca0 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sun, 23 Nov 2025 00:37:06 -0500 Subject: [PATCH 25/93] Mortar Errata --- contrib/cards/Empire/EmpireUnits.json | 2 +- contrib/cards/official.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index d48e6687..2300f2e2 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -675,7 +675,7 @@ }, { "name": "DF-90 Mortar Trooper", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385139662551/E8496B01C9619FBA2E5EEEBEF285ABEEF95ECF00/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16783716143455086871/5D15ECEA1884F21248D89506357639418A5FBF3B/", "size": "medium", "type": "Emplacement Trooper", "points": 40, diff --git a/contrib/cards/official.json b/contrib/cards/official.json index e464dcba..5f2a42f6 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5768,7 +5768,7 @@ "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" } }, - "Outflank": { + "Outflank Map": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/15283876349976838373/93B3D53B8AB0DCF7FE15F606E81FD962A738B455/", "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" From 419d38c160d252edd8e5c375b30ea8b07aef4322 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 26 Nov 2025 23:49:38 -0500 Subject: [PATCH 26/93] Marksmen Upgrades --- contrib/cards/official.json | 50 +++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 5f2a42f6..0f7c56e9 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -2125,7 +2125,7 @@ "points": 20, "restrictions": { "include": { - "unit": ["Clone Trooper Infantry"] + "unit": ["Clone Trooper Infantry", "Clone Trooper Marksmen"] } } }, @@ -2167,7 +2167,7 @@ "points": 21, "restrictions": { "include": { - "unit": ["Clone Trooper Infantry"] + "unit": ["Clone Trooper Infantry", "Clone Trooper Marksmen"] } } }, @@ -2817,6 +2817,52 @@ } ], "Personnel": [ + { + "name": "Clone Marksman", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16972794900197879773/6B1534C765EB2036E2D189C0F8F49AA5F3BDF920/", + "mini": { + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/15225529675747905291/99FAF0DF1F4C90B3BE029846EF98D28ED4AC4DE7/", + "secondary": "https://steamusercontent-a.akamaihd.net/ugc/14173133310269661753/77DC4409C0694CEE7BD5968DBC36B6AD9F582A90/" + }, + "points": 14, + "restrictions": { + "include": { + "unit": ["Clone Trooper Marksmen"] + } + } + }, + { + "name": "Clone Trooper Marksmen Squad", + "image": "https://steamusercontent-a.akamaihd.net/ugc/18125099481279293755/B6F721DB564585BE62A893E382B128E12CD6592C/", + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15225529675747905291/99FAF0DF1F4C90B3BE029846EF98D28ED4AC4DE7/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14173133310269661753/77DC4409C0694CEE7BD5968DBC36B6AD9F582A90/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15225529675747905291/99FAF0DF1F4C90B3BE029846EF98D28ED4AC4DE7/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14173133310269661753/77DC4409C0694CEE7BD5968DBC36B6AD9F582A90/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15225529675747905291/99FAF0DF1F4C90B3BE029846EF98D28ED4AC4DE7/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14173133310269661753/77DC4409C0694CEE7BD5968DBC36B6AD9F582A90/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15225529675747905291/99FAF0DF1F4C90B3BE029846EF98D28ED4AC4DE7/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14173133310269661753/77DC4409C0694CEE7BD5968DBC36B6AD9F582A90/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15225529675747905291/99FAF0DF1F4C90B3BE029846EF98D28ED4AC4DE7/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14173133310269661753/77DC4409C0694CEE7BD5968DBC36B6AD9F582A90/" + } + ], + "points": 53, + "restrictions": { + "include": { + "unit": ["Clone Trooper Marksmen"] + } + } + }, { "name": "Fleet Trooper Squad", "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384856618/9DED8502636167A62F6D2AEEE5AE8B004D61F3CF/", From 6cd2fdfbb6509c315a6ac51b4f4c89b57ed8635c Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 27 Nov 2025 00:01:07 -0500 Subject: [PATCH 27/93] Marksmen Upgrades Fix --- contrib/cards/official.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 0f7c56e9..ed641d0a 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -2120,7 +2120,8 @@ "name": "Mortar Clone Trooper", "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857192/CF4DF5C1BB0C410B79E2F7EE0B658D2C9F4B8DE3/", "mini": { - "bundle": "http://cloud-3.steamusercontent.com/ugc/2174736446809185328/4A6DA42CDB70EF979D5B8B675CEFA5459FA60D4E/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/17111559006544179444/7D68555838D7B0D758FED0E3791ED418B90A6DD7/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9727272902330348247/0B0C390FEF4295D2877C3FD619D77C165F71537B/" }, "points": 20, "restrictions": { @@ -2161,8 +2162,8 @@ "name": "RPS-6 Clone Trooper", "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857930/2CEB66226D92B8B803D3654A7F59977A0D0B6BA2/", "mini": { - "mesh": "http://cloud-3.steamusercontent.com/ugc/767228764785603568/BB31C6873BD81E6B5B6F6D41B907DA95421063F0/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/767228764785565294/2AA6ABC62F52DCD30C56D5DED210C35E7B4FD526/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/10674439379183912978/1891910DC13EACBBDD75B96F220C5826CFF127FA/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/13318126355031753710/BA185DB103CF16D1BFAC9209F348A43498AAFE04/" }, "points": 21, "restrictions": { @@ -2821,8 +2822,8 @@ "name": "Clone Marksman", "image": "https://steamusercontent-a.akamaihd.net/ugc/16972794900197879773/6B1534C765EB2036E2D189C0F8F49AA5F3BDF920/", "mini": { - "bundle": "https://steamusercontent-a.akamaihd.net/ugc/15225529675747905291/99FAF0DF1F4C90B3BE029846EF98D28ED4AC4DE7/", - "secondary": "https://steamusercontent-a.akamaihd.net/ugc/14173133310269661753/77DC4409C0694CEE7BD5968DBC36B6AD9F582A90/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15225529675747905291/99FAF0DF1F4C90B3BE029846EF98D28ED4AC4DE7/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14173133310269661753/77DC4409C0694CEE7BD5968DBC36B6AD9F582A90/" }, "points": 14, "restrictions": { From 716a75b93ab31ad535db2a3e84d644980b2b232a Mon Sep 17 00:00:00 2001 From: halestom15 Date: Mon, 1 Dec 2025 22:58:29 -0500 Subject: [PATCH 28/93] ISP Update --- contrib/cards/Republic/RepublicUnits.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index 20796bbe..4f7ecb79 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -904,7 +904,7 @@ }, "minis": [ { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1619598523372574861/CCF68AF1C35F52A714AB0344FCB170F5BAA1672A/" + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/14282592344641491251/15FE05F31AF626C6839758EB55F1CE175915ABB7/" } ] } From 9d8af2fb35aedc0e46635240d9258ddd2fc9e916 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Mon, 1 Dec 2025 23:32:58 -0500 Subject: [PATCH 29/93] Skirmish Cards added --- contrib/cards/official.json | 184 ++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index ed641d0a..961e3f5e 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5369,105 +5369,107 @@ "skirmish": { "objective": [ { - "name": "Breach", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279492/85477A894623DDA23BC182995DAF396671A0C007/", - "type": "vertical" - }, - { - "name": "Control", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279456/10F29ECE1579A06D7DC564A6480F2BE27CC6EF04/", - "type": "vertical", - "additionalObjects": ["Objective Token"] - }, - { - "name": "Elimination", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279416/6160501F308751CB9266FF991FF62E1409058C4B/", - "type": "vertical" - }, - { - "name": "Pivotal Positions", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279382/6E006F31C9D5200AAA94392D94C2AE78EE4D73C7/", + "name": "Surface Scan", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16343945853969639984/24ABD9DF34B74E343BD4F79D9461385D76C0CD88/", "type": "vertical", "additionalObjects": [ + "Objective Token", "Objective Token", "Objective Token", "Objective Token" ] + }, + { + "name": "Recon Mission", + "image": "https://steamusercontent-a.akamaihd.net/ugc/18385343105541227063/7F990BB73C8675734EB02873157F392EA265DC20/", + "type": "vertical", + "additionalObjects": ["Objective Token", "Objective Token"] + }, + { + "name": "Bring Them to Heel", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16790576095549234351/278EA3877E97AD155ADA4602D42DF023B7CEB78B/", + "type": "vertical" } ], "deployment": [ { - "name": "Battle Lines", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279345/8A96884057E13BF488E89177C2B9CCB0ADDDFFE0/", + "name": "Close the Pocket", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17314598212129638590/62EB33594611850FEBF0567D57ABA8CFB8077091/", "type": "vertical", "zone": [ - [" ", " ", " ", "r", "r", "r", "r", "r", "r", " ", " ", " "], - [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], - [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", "r", "rs", " ", " ", "rss", "r", " ", " ", " "], + [" ", " ", " ", "rh", "rc", " ", " ", "rcc", "rh", " ", " ", " "], [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], - [" ", " ", " ", "b", "b", "b", "b", "b", "b", " ", " ", " "] + [" ", " ", " ", "bh", "bcc", " ", " ", "bc", "bh", " ", " ", " "], + [" ", " ", " ", "b", "bss", " ", " ", "bs", "b", " ", " ", " "] + ], + "additionalObjects": [ + "POI Token", + "POI Token", + "POI Token", + "Close the Pocket Map (Recon)" ] }, { - "name": "Faceoff", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279307/F41700690107CC7398BD5B95CDED30B041538517/", - "type": "vertical", - "zone": [] - }, - { - "name": "Flanking Positions", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279273/0D6B1CF6762E532AA4C0D039C2CC96F0E25A419F/", + "name": "Bunker Assault", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16995954044322736468/0D0086DE04A57350DF77F62FDEC94F5148B7C4D5/", "type": "vertical", "zone": [ - [" ", " ", " ", "r", "r", "r", " ", " ", " ", " ", " ", " "], - [" ", " ", " ", "r", " ", " ", " ", " ", " ", " ", " ", " "], - [" ", " ", " ", "r", " ", " ", " ", " ", " ", " ", " ", " "], - [" ", " ", " ", " ", " ", " ", " ", " ", "b", " ", " ", " "], - [" ", " ", " ", " ", " ", " ", " ", " ", "b", " ", " ", " "], - [" ", " ", " ", " ", " ", " ", "b", "b", "b", " ", " ", " "] + [" ", " ", " ", " ", "r", "r", "r", "r", " ", " ", " ", " "], + [" ", " ", " ", " ", "rh", "rh", "rh", "rh", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", "bh", "bh", "bh", "bh", " ", " ", " ", " "], + [" ", " ", " ", " ", "b", "b", "b", "b", " ", " ", " ", " "] + ], + "additionalObjects": [ + "POI Token", + "POI Token", + "POI Token", + "POI Token", + "Bunker Assault Map (Recon)" ] }, { - "name": "Meeting Engagement", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279232/4F4C1C54E5DCE9FD1E9C46BB75379AEF60E2DAFC/", + "name": "Intercept Signals", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13661793580985225615/BB706B264CE5C71456271DE54E765EC27EA6F786/", "type": "vertical", "zone": [ - [" ", " ", " ", "r", "r", " ", " ", " ", " ", " ", " ", " "], - [" ", " ", " ", "r", "r", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", "r", "r", "r", "r", " ", " ", " ", " "], + [" ", " ", " ", " ", "rh", "rh", "rh", "rh", " ", " ", " ", " "], [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], - [" ", " ", " ", " ", " ", " ", " ", "b", "b", " ", " ", " "], - [" ", " ", " ", " ", " ", " ", " ", "b", "b", " ", " ", " "] + [" ", " ", " ", " ", "bh", "bh", "bh", "bh", " ", " ", " ", " "], + [" ", " ", " ", " ", "b", "b", "b", "b", " ", " ", " ", " "] + ], + "additionalObjects": [ + "POI Token", + "POI Token", + "POI Token", + "POI Token", + "Intercept Signals Map (Recon)" ] } ], "condition": [ { - "name": "War Weary", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279193/052C3637BE4F31FAA691235E186CC73B1CE928F7/", - "type": "vertical" - }, - { - "name": "Improvised Defenses", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279155/3F72099B06947F23231D8B2228D25C63E01FED92/", + "name": "Advanced Intel", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13272907091260444770/243391B61F192F53AA7CF0360D3F846C7A7A0A97/", "type": "vertical", - "additionalObjects": [ - "Barricade", - "Barricade", - "Barricade", - "Barricade" - ] + "additionalObjects": ["Condition Token"] }, { - "name": "Dawn", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279122/1952ADAEF00EAAF039C3D0C160AABF3BD4F7B0E1/", - "type": "vertical" + "name": "Fortified Position", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16927846453234643948/97D5A7F0E4C9D3DAB14C3754AC3A484C15CE3567/", + "type": "vertical", + "additionalObjects": ["Barricade", "Barricade", "Barricade"] }, { - "name": "Clear Conditions", - "image": "http://cloud-3.steamusercontent.com/ugc/4112341331071279082/F2116A3FBAB46DAD85EE43AB1CBF9497C567B2A8/", - "type": "vertical" + "name": "Cunning Deployment", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14925552919945327142/06659E9120D6246C6048165ECDE9F5F5AEA7A5EB/", + "type": "vertical", + "additionalObjects": ["Dodge Token", "Dodge Token", "Dodge Token"] } ] }, @@ -5780,46 +5782,44 @@ "scripted": "poi-token" }, "Breakthrough Map": { - "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" - } + "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" }, "Bunker Assault Map": { - "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/" - } + "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/" }, "Close the Pocket Map": { - "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/" - } + "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/" }, - "Intercept Signals": { - "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/" - } + "Intercept Signals Map": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/" }, "Recover the Research Map": { - "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/" - } + "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/" }, "Shifting Priorities Map": { - "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" - } + "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" }, "Outflank Map": { - "card": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/15283876349976838373/93B3D53B8AB0DCF7FE15F606E81FD962A738B455/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" - } + "image": "https://steamusercontent-a.akamaihd.net/ugc/15283876349976838373/93B3D53B8AB0DCF7FE15F606E81FD962A738B455/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" + }, + "Bunker Assault Map (Recon)": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/15532220751502518416/FA34BE3822D18F2C6D04F8987569B0D4F6A1F9D3/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/" + }, + "Close the Pocket Map (Recon)": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/17088281433024932061/E9983676D51E1EE76B7152BD13CF83C794F02C81/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/" + }, + "Intercept Signals Map (Recon)": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/13979927353592007130/EA574041CFDFF4712CEC9A8E18F5A8DDE7A18216/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/" } } } From d9e6ac2968da0df54dddb6b2aca23e7bc133d725 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Tue, 2 Dec 2025 00:07:01 -0500 Subject: [PATCH 30/93] ISP Sihl --- contrib/cards/Republic/RepublicUnits.json | 2 +- contrib/cards/official.json | 60 +++++++++++++++-------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index 4f7ecb79..dc86299c 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -899,7 +899,7 @@ "Comms": 1 }, "silhouette": { - "height": 1.55, + "height": 2.33, "offset": 0.0 }, "minis": [ diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 961e3f5e..4359fa77 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5782,44 +5782,64 @@ "scripted": "poi-token" }, "Breakthrough Map": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } }, "Bunker Assault Map": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } }, "Close the Pocket Map": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } }, "Intercept Signals Map": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } }, "Recover the Research Map": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } }, "Shifting Priorities Map": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } }, "Outflank Map": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/15283876349976838373/93B3D53B8AB0DCF7FE15F606E81FD962A738B455/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/15283876349976838373/93B3D53B8AB0DCF7FE15F606E81FD962A738B455/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } }, "Bunker Assault Map (Recon)": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/15532220751502518416/FA34BE3822D18F2C6D04F8987569B0D4F6A1F9D3/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/15532220751502518416/FA34BE3822D18F2C6D04F8987569B0D4F6A1F9D3/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } }, "Close the Pocket Map (Recon)": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/17088281433024932061/E9983676D51E1EE76B7152BD13CF83C794F02C81/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/17088281433024932061/E9983676D51E1EE76B7152BD13CF83C794F02C81/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } }, "Intercept Signals Map (Recon)": { - "image": "https://steamusercontent-a.akamaihd.net/ugc/13979927353592007130/EA574041CFDFF4712CEC9A8E18F5A8DDE7A18216/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/" + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/13979927353592007130/EA574041CFDFF4712CEC9A8E18F5A8DDE7A18216/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + } } } } From 08d69aa5dad14103e6afd20c093928e4477189b2 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Tue, 2 Dec 2025 00:12:57 -0500 Subject: [PATCH 31/93] Update Welcome --- mod/src/includes/ui/Welcome.xml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 6af0b07a..136f5803 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -37,7 +37,7 @@ - Welcome to Star Wars: Legion for TTS, v8.0.13-beta. + Welcome to Star Wars: Legion for TTS, v8.0.14-beta. This is a beta release, and is not suitable for tournament play. @@ -46,9 +46,12 @@ CHANGELOG (Since last Beta) - - Add: Jedi Knight General and Jedi Knight - - Add: Super Tactical Command Droids - - Add: Revealed General Grievous, Wheelbike Warlord Card + - Add: General Grievous, Wheelbike Warlord + - Add: Clone Trooper Marksmen + - Add: Recon Deployments + - Add: Outflank, Supply Run, Faction Specific Advantages + - Add: Cassian, Kallus, Kraken, Rex unit upgrades + - Fix: ISP base size From a148968c1be7913f32102984eb2ad1a2ee54753b Mon Sep 17 00:00:00 2001 From: halestom15 Date: Tue, 2 Dec 2025 00:30:26 -0500 Subject: [PATCH 32/93] ISP Sihl 2 --- contrib/cards/Republic/RepublicUnits.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index dc86299c..65c8721e 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -899,7 +899,7 @@ "Comms": 1 }, "silhouette": { - "height": 2.33, + "height": 2.25, "offset": 0.0 }, "minis": [ From a12ba72f4b9a32a3e07983ef311770ea5dc6027a Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 19 Dec 2025 00:55:30 -0500 Subject: [PATCH 33/93] Errata and AAT card fix --- contrib/cards/Separatist/SeparatistUnits.json | 2 +- contrib/cards/official.json | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index 31c7f6c2..4df4ac23 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -812,7 +812,7 @@ { "name": "AAT Battle Tank", "displayName": "AAT Battle Tank", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131430642/9FCE0FB0F62A9381DF3CE2F786DBC749CB2A8FFF/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15356261010346172115/9FCE0FB0F62A9381DF3CE2F786DBC749CB2A8FFF/", "size": "epic", "type": "Repulsor Vehicle", "points": 165, diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 4359fa77..6484bf3f 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -2669,7 +2669,7 @@ "Squad Leader": [ { "name": "Clone Captain Rex", - "image": "https://steamusercontent-a.akamaihd.net/ugc/17732060543639041010/F75242323BF212E3ABCCC41199991C8FBFAEBF68/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17837568269244781354/A462DDBE46166711CD3E69DD3F6F6947D25AEF3F/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/776232727456806720/869654FC3B0590BFFF2E1845C3CFA9169838EBF9/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/776232727456806858/52327997163A5113DF9E11B85F9DA8B6B970012C/" @@ -4435,7 +4435,7 @@ "Generic": [ { "name": "Ambush", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564037146/9E4C2F5453A9016854F388BB078666B71AC794B6/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11854788151162080590/F8156890B5410050033E6A67015DB0E0DCE7FA95/", "pip": 1 }, { @@ -4445,7 +4445,7 @@ }, { "name": "Push", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034819/0E3D77E869D24AD638EB438F92577F44368487D3/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17909236182554732778/41F641B354B5819E8B6D231B7D2593E29DF87BE7/", "pip": 2 }, { @@ -4455,7 +4455,7 @@ }, { "name": "Assault", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564034224/40364C1DDCF973A233C4A8F7AD43734067014C06/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17765277411252241548/FA0AA2F53A229D6F470578728A59AE31FF626C3A/", "pip": 3 }, { @@ -4465,7 +4465,7 @@ }, { "name": "Standing Orders", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564032760/662157DB1D462926F585301B5D6003D6B8172F14/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15678505752415930382/55DE6AD796E3FE3FDDE5B2D11CAC8087389D5C71/", "pip": 4 } ], @@ -5096,7 +5096,7 @@ }, { "name": "Coordinated Strike", - "image": "https://steamusercontent-a.akamaihd.net/ugc/15369793059824781345/5FA5B970C2370F879FFAF0650E8F7FD361F06F91/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16289701377728989133/61345ED59F6A20461A2BD1526AC38D12E2E4BBB3/", "type": "vertical" }, { From 5214b2dc9db8392a10c43324c85d647208fcb9a3 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 19 Dec 2025 01:30:29 -0500 Subject: [PATCH 34/93] Table Test --- mod/src/StarWarsLegion/TABLE.3a3ed9.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/src/StarWarsLegion/TABLE.3a3ed9.json b/mod/src/StarWarsLegion/TABLE.3a3ed9.json index bb12aecc..ec02e14b 100644 --- a/mod/src/StarWarsLegion/TABLE.3a3ed9.json +++ b/mod/src/StarWarsLegion/TABLE.3a3ed9.json @@ -35,7 +35,7 @@ "HideWhenFaceDown": false, "Hands": false, "CustomAssetbundle": { - "AssetbundleURL": "http://cloud-3.steamusercontent.com/ugc/2474241114697635226/5F40782A5897A732EE6A75C68352D2FF70CB79C9/", + "AssetbundleURL": "http://cloud-3.steamusercontent.com/ugc/2259180117394918498/97BE48DA78615BE3ED08933C1E08328D6E5BBE7C/", "AssetbundleSecondaryURL": "", "MaterialIndex": 0, "TypeIndex": 0, From 1174286b72237a1ab9accd793da588c25ec98c47 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 19 Dec 2025 01:34:15 -0500 Subject: [PATCH 35/93] Update TABLE.3a3ed9.json --- mod/src/StarWarsLegion/TABLE.3a3ed9.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/src/StarWarsLegion/TABLE.3a3ed9.json b/mod/src/StarWarsLegion/TABLE.3a3ed9.json index ec02e14b..349342ff 100644 --- a/mod/src/StarWarsLegion/TABLE.3a3ed9.json +++ b/mod/src/StarWarsLegion/TABLE.3a3ed9.json @@ -35,7 +35,7 @@ "HideWhenFaceDown": false, "Hands": false, "CustomAssetbundle": { - "AssetbundleURL": "http://cloud-3.steamusercontent.com/ugc/2259180117394918498/97BE48DA78615BE3ED08933C1E08328D6E5BBE7C/", + "AssetbundleURL": "http://cloud-3.steamusercontent.com/ugc/1744555589998518180/70BD19FBE4D4D8B319F6A4A2ACCE401C25D94656/", "AssetbundleSecondaryURL": "", "MaterialIndex": 0, "TypeIndex": 0, From d4f864d26efa78b4d05af7e1e14bca7ed51598ba Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 24 Jan 2026 19:16:03 -0500 Subject: [PATCH 36/93] Ahsoka one pip fix and wheel mode token fix --- contrib/cards/Republic/RepublicUnits.json | 2 +- .../Wheel_Mode_Tokens/Wheel_Mode_Token.950f36.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index 65c8721e..54cb437d 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -21,7 +21,7 @@ "commands": [ { "name": "You Are Beaten", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59217531459831475/BAB718B98F827E294BA50680227A363E3256DC7A/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14515441792303199216/BAB718B98F827E294BA50680227A363E3256DC7A/", "pip": 1 }, { diff --git a/mod/src/StarWarsLegion/Wheel_Mode_Tokens/Wheel_Mode_Token.950f36.json b/mod/src/StarWarsLegion/Wheel_Mode_Tokens/Wheel_Mode_Token.950f36.json index 188d8d35..723428e6 100644 --- a/mod/src/StarWarsLegion/Wheel_Mode_Tokens/Wheel_Mode_Token.950f36.json +++ b/mod/src/StarWarsLegion/Wheel_Mode_Tokens/Wheel_Mode_Token.950f36.json @@ -29,7 +29,7 @@ "MeasureMovement": false, "DragSelectable": true, "Autoraise": true, - "Sticky": true, + "Sticky": false, "Tooltip": true, "GridProjection": false, "HideWhenFaceDown": false, From 4d15596cc6be386ceb939605ec0d6bf3cf1c594b Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 24 Jan 2026 19:22:25 -0500 Subject: [PATCH 37/93] Welcome Message --- mod/src/includes/ui/Welcome.xml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 136f5803..6b1a615f 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -37,21 +37,19 @@ - Welcome to Star Wars: Legion for TTS, v8.0.14-beta. + Welcome to Star Wars: Legion for TTS, v8.0.15-beta. - This is a beta release, and is not suitable for tournament play. + This is a beta release, and is . To access the stable version of the mod visit https://go.swlegion.dev/mod. CHANGELOG (Since last Beta) - - Add: General Grievous, Wheelbike Warlord - - Add: Clone Trooper Marksmen - - Add: Recon Deployments - - Add: Outflank, Supply Run, Faction Specific Advantages - - Add: Cassian, Kallus, Kraken, Rex unit upgrades - - Fix: ISP base size + - Revert Table + - Fix: AAT unit card + - Fix: GAR Ahsoka one pip + - Add: New art for generic command cards From 8934f8bd2e1f4ef74c3a921b563473f49541cf51 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Mon, 26 Jan 2026 23:40:26 -0500 Subject: [PATCH 38/93] Add: Probes --- contrib/cards/Empire/EmpireUnits.json | 15 +++++++++++++++ mod/src/includes/ui/Welcome.xml | 1 + 2 files changed, 16 insertions(+) diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index 2300f2e2..bbd84bf2 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -845,6 +845,21 @@ } ], "required": ["Gideon Hask", "Del Meeko"] + }, + { + "name": "Imperial Probe Droid", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17742743731740446795/0CCBB41ECD7F6A3AB0477DD280E75F07DDF26BE1/", + "size": "small", + "type": "Droid Trooper", + "points": 35, + "speed": 1, + "upgrades": {}, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/14972533488140369423/D390682F502B9999AE930DA6A78817DDE5D97027/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15762532285823663057/2254B8B0FBA723C3F840CDB6DB639404B0DA9DB1/" + } + ] } ], "Support": [ diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 6b1a615f..8b512b70 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -50,6 +50,7 @@ - Fix: AAT unit card - Fix: GAR Ahsoka one pip - Add: New art for generic command cards + - Add: Imperial Probe Droid From 8d3674f89fe614ca2e8f62687f18dbd62472ac01 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Mon, 26 Jan 2026 23:56:04 -0500 Subject: [PATCH 39/93] Welcome Fix --- mod/src/includes/ui/Welcome.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 8b512b70..b6a44f85 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -39,7 +39,7 @@ Welcome to Star Wars: Legion for TTS, v8.0.15-beta. - This is a beta release, and is . + This is a beta release . To access the stable version of the mod visit https://go.swlegion.dev/mod. From 4abdf84f033003861c4d37018d42a0cb1f48232f Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 6 Feb 2026 22:50:21 -0500 Subject: [PATCH 40/93] Update Deck.ttslua --- mod/src/includes/Deck.ttslua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mod/src/includes/Deck.ttslua b/mod/src/includes/Deck.ttslua index 883ad871..3d3267e2 100644 --- a/mod/src/includes/Deck.ttslua +++ b/mod/src/includes/Deck.ttslua @@ -7,9 +7,9 @@ function Deck:create() command = "http://cloud-3.steamusercontent.com/ugc/923676470468121470/B601C237FC093688B758499B99CC87FDFE2FDD0F/", contignency = "http://cloud-3.steamusercontent.com/ugc/1753560235958347056/F845CB170FEF761DC7025AA3EEAE731F018C0E73/", horizontal = { - objective = "http://cloud-3.steamusercontent.com/ugc/786360308354563159/BDB495B60FFED8707BD013502C2F03D3F047B63E/", - condition = "http://cloud-3.steamusercontent.com/ugc/786360308354563381/FF7DF081311A20CC396CAC9391D85EB1FBCA44E1/", - deployment = "http://cloud-3.steamusercontent.com/ugc/786360308354562872/AF68672CC3B5A0A27A37FA0C02FD87F2210FAB86/", + objective = "https://steamusercontent-a.akamaihd.net/ugc/10061127123107977322/847E7AAD563EF0AFD5926BE05266DC82E5FD04FC/", + condition = "https://steamusercontent-a.akamaihd.net/ugc/13818252657402930588/FB0312AED90098252D2E53426F99F2072279EE64/", + deployment = "https://steamusercontent-a.akamaihd.net/ugc/9428670987266773291/C115F2EAE00FD59B7A9D7CEB9906CB91B872DFBE/", }, vertical = { objective = "http://cloud-3.steamusercontent.com/ugc/4112341331071278905/7391A9D8F237C6D5361EE068AB9083F51FAEE83E/", From 7df1986cd843e4f7384874885a1e6868b0b6632f Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 6 Feb 2026 23:21:51 -0500 Subject: [PATCH 41/93] WLO Tank --- contrib/cards/Empire/EmpireUnits.json | 3 +++ contrib/cards/Rebel/RebelUnits.json | 3 +++ contrib/cards/Republic/RepublicUnits.json | 3 +++ contrib/cards/Separatist/SeparatistUnits.json | 3 +++ .../ShadowCollective/WLO5SpeederTankUnit.json | 23 +++++++++++++++++++ 5 files changed, 35 insertions(+) create mode 100644 contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index d48e6687..a22da1ea 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -1078,6 +1078,9 @@ "pip": 3 } ] + }, + { + "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" } ] } diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index 2d89483e..94a102a4 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -1323,6 +1323,9 @@ "pip": 3 } ] + }, + { + "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" } ] } diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index 0a7d1f25..a7cc8462 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -907,6 +907,9 @@ "bundle": "http://cloud-3.steamusercontent.com/ugc/1619598523372574861/CCF68AF1C35F52A714AB0344FCB170F5BAA1672A/" } ] + }, + { + "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" } ] } diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index e1679b5d..22807f60 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -874,6 +874,9 @@ "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16428277771241663/4DEF50D36EB484BB1BBD9FD6092708DEF89A28EA/" } ] + }, + { + "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" } ] } diff --git a/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json b/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json new file mode 100644 index 00000000..b02580d9 --- /dev/null +++ b/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json @@ -0,0 +1,23 @@ +{ + "name": "WLO-5 Speeder Tank", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12892745162360850606/8F7C7F70F1604DCA9BB39BE0F0EE859149D3A3F8/", + "size": "snail", + "type": "Repulsor Vehicle", + "points": 100, + "speed": 1, + "upgrades": { + "Crew": 1, + "Pilot": 1, + "Comms": 1 + }, + "silhouette": { + "height": 2.44, + "offset": 0 + }, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/1950639240971178878/094F5D9E47B4CECEAE1A529E46E70C0ED2DE737B/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/1930372848016475033/1C5A975045A289BF43FBDC29DC3F2BD0ED7E1E0C/" + } + ] +} From eeef269474647b3cedb6c8ef4e3a34c3b947d3e3 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 6 Feb 2026 23:33:43 -0500 Subject: [PATCH 42/93] Crew and Pilot WLO --- contrib/cards/official.json | 54 +++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 2dccbe02..adfae350 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -674,6 +674,16 @@ } } }, + { + "name": "Black Sun Crew", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13580814712079812423/759CBAED43A1DFA12E7876B92C53EA8CD536F4DE/", + "points": 5, + "restrictions": { + "include": { + "unit": ["WLO-5 Speeder Tank"] + } + } + }, { "name": "Door Gunners", "image": "https://steamusercontent-a.akamaihd.net/ugc/10380945357802391916/5D21C9620D0382828965E9FA8E1534BF067A4E7A/", @@ -699,6 +709,16 @@ } } }, + { + "name": "Pyke Syndicate Crew", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13352335398418306228/C01292CB459585462D5A41DD3CE0D743C74C115A/", + "points": 5, + "restrictions": { + "include": { + "unit": ["WLO-5 Speeder Tank"] + } + } + }, { "name": "Refurbished \"Gonk\" Droid", "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144996/C187B41FE3AE53493F4A9AA23B0F74B795F9B2C3/", @@ -738,6 +758,16 @@ "unit": ["A-A5 Speeder Truck", "X-34 Landspeeder"] } } + }, + { + "name": "Weequay Crew", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11962013218940355603/44A620E6F9A81692F86F2F331672632D95E0425C/", + "points": 0, + "restrictions": { + "include": { + "unit": ["WLO-5 Speeder Tank"] + } + } } ], "Doctrine": [ @@ -3752,6 +3782,16 @@ } } }, + { + "name": "Gang Boss", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14505275334722387359/9A721686833EB9E13254ABE90BDBB19E4192853F/", + "points": 10, + "restrictions": { + "include": { + "unit": ["WLO-5 Speeder Truck", "AA5 Speeder Truck"] + } + } + }, { "name": "General Weiss", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148691201/0F949459DDCA1EAFC03EC7145C7402A636C9C4BE/", @@ -3858,6 +3898,16 @@ } } }, + { + "name": "Pirate Captain", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14002409862158051174/46C30FAB65D4F9F6A8C725359F1D65795575973B/", + "points": 5, + "restrictions": { + "include": { + "unit": ["WLO-5 Speeder Tank"] + } + } + }, { "name": "Reckless Driver", "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568144930/910352F2AA646B8E97628B4DF56C364805ABD906/", @@ -3928,7 +3978,7 @@ "points": 5, "restrictions": { "include": { - "unit": ["A-A5 Speeder Truck"] + "unit": ["A-A5 Speeder Truck", "WLO-5 Speeder Tank"] } } }, @@ -3938,7 +3988,7 @@ "points": 10, "restrictions": { "include": { - "unit": ["A-A5 Speeder Truck"] + "unit": ["A-A5 Speeder Truck", "WLO-5 Speeder Tank"] } } } From 631dff53637b6b0ad7a4591150f96f3936c4a828 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sun, 8 Feb 2026 12:57:28 -0500 Subject: [PATCH 43/93] Update Welcome.xml --- mod/src/includes/ui/Welcome.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 6af0b07a..b50c05f5 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -37,7 +37,7 @@ - Welcome to Star Wars: Legion for TTS, v8.0.13-beta. + Welcome to Star Wars: Legion for TTS, v8.0.14-beta. This is a beta release, and is not suitable for tournament play. @@ -46,9 +46,8 @@ CHANGELOG (Since last Beta) - - Add: Jedi Knight General and Jedi Knight - - Add: Super Tactical Command Droids - - Add: Revealed General Grievous, Wheelbike Warlord Card + - Add: WLO-5 Speeder Truck + - Add: New Objective Card Backing From 9b7e7a4cdc1d16d4d2ff5f07a7aa1e5ab9457f5c Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 6 Mar 2026 00:47:47 -0500 Subject: [PATCH 44/93] Update official.json --- contrib/cards/official.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 8b9ce58a..f972e4a5 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5056,6 +5056,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", ======= @@ -5073,6 +5074,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes @@ -5086,6 +5090,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", ======= @@ -5103,6 +5108,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes @@ -5116,6 +5124,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", ======= @@ -5133,6 +5142,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes @@ -5146,6 +5158,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", ======= @@ -5163,6 +5176,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes @@ -5176,6 +5192,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", ======= @@ -5193,6 +5210,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes @@ -5206,6 +5226,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", ======= @@ -5223,6 +5244,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes From b00c4b838270a12d51d4ddf492320b31472030bb Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 6 Mar 2026 00:49:11 -0500 Subject: [PATCH 45/93] Update official.json --- contrib/cards/official.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index f972e4a5..241daeac 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -5057,6 +5057,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", ======= @@ -5077,6 +5078,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/", >>>>>>> Stashed changes @@ -5091,6 +5095,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", ======= @@ -5111,6 +5116,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051483/82F1D1EAA55C5794FD0301E5FADB5EEA3DAEFE49/", >>>>>>> Stashed changes @@ -5125,6 +5133,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", ======= @@ -5145,6 +5154,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051694/8B05A05A5B24115F84B0A13F6FD392BB5C00E527/", >>>>>>> Stashed changes @@ -5159,6 +5171,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", ======= @@ -5179,6 +5192,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052240/E4DFCE250EAB2459C05A7C73ED2C47B78DB5D467/", >>>>>>> Stashed changes @@ -5193,6 +5209,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", ======= @@ -5213,6 +5230,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052886/30E5BFA96EBC5AFA4197AE40882EC3B576420F51/", >>>>>>> Stashed changes @@ -5227,6 +5247,7 @@ <<<<<<< Updated upstream <<<<<<< Updated upstream <<<<<<< Updated upstream +<<<<<<< Updated upstream <<<<<<< Updated upstream "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", ======= @@ -5247,6 +5268,9 @@ ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes +======= + "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", +>>>>>>> Stashed changes ======= "image": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501052974/0D4D7ECABE727D484B8ECDF44DBA664BBF7E609D/", >>>>>>> Stashed changes From 5c8163943402bd52d870f2fdad2120c63dc4b6b3 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 7 Mar 2026 01:59:00 -0500 Subject: [PATCH 46/93] Deck Back and Tauns --- contrib/cards/Rebel/RebelUnits.json | 81 +++++++++++++++++++ .../ShadowCollective/WLO5SpeederTankUnit.json | 4 +- contrib/cards/official.json | 4 +- mod/src/includes/Deck.ttslua | 6 +- mod/src/includes/ui/Welcome.xml | 2 +- 5 files changed, 89 insertions(+), 8 deletions(-) diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index 94a102a4..838eda41 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -53,6 +53,59 @@ } ] }, + { + "name": "Luke Skywalker", + "title": "Commander Skywalker", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14055439616909991431/49C4E67617260BEB9BA30A7186B2F0A6E7E1F1B0/", + "size": "medium", + "type": "Creature Trooper", + "points": 110, + "speed": 2, + "upgrades": { + "Command": 1, + "Force": 1, + "Training": 1, + "Comms": 1 + }, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/17466138295659250864/64486F9383C23FBFA17327847189F8F798E0BFB8/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14381694965898057201/52C1277D712E3B1E4F54C16E4F74CB76639BECB1/" + } + ], + "commands": [ + { + "name": "Son Of Skywalker", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133712958/CF63D0FDA40318CE886E6DA309CEB291857776E4/", + "pip": 1 + }, + { + "name": "On Patrol", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10927164661502635503/8DB9E1079B4D67CAA8E7C19A6B1BB2EAE4AAB2F9/", + "pip": 2 + }, + { + "name": "My Ally Is The Force", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711308/CECAE0B5B62858582522F7FBED6463FE5834A64E/", + "pip": 2 + }, + { + "name": "Full Of Surprises", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133709999/FA890B2A2EA1528C8BCF2A80AEB3B97A97BB9E15/", + "pip": 2 + }, + { + "name": "Return Of The Jedi", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133711996/90C4CB28C0554DEC3797882252E6C5D3CCAC65A1/", + "pip": 3 + }, + { + "name": "Not Out of it Yet", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17986576672810143634/656B6E1AB636857E500DC13A8C94F7FB26C78F84/", + "pip": 3 + } + ] + }, { "name": "Leia Organa", "title": "Fearless And Inventive", @@ -294,6 +347,34 @@ { "content": "contrib/cards/ShadowCollective/HondoUnit.json" }, + { + "name": "Han Solo", + "title": "Reluctant Hero", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15311985354658964881/F8E81D8263D5350BE0AB94FF7A83706B7B5734A8/", + "size": "medium", + "type": "Creature Trooper", + "points": 90, + "speed": 2, + "upgrades": { + "Command": 1, + "Training": 1, + "Gear": 1, + "Comms": 1 + }, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/13940285596618766501/847E2E5B577E5F3AAE1D473FE9A10BCE468A3094/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/11319334885600705090/2ACE36DD1F4FAE8ED21CC59A54D3DB6D780C885D/" + } + ], + "commands": [ + { + "name": "See You in Hell", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10773409737362779349/F1880C8286BA798323F594FA7DF7ABDB85A0A3DD/", + "pip": 1 + } + ] + }, { "name": "Rebel Agent", "title": "Defender of Democracy", diff --git a/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json b/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json index b02580d9..c4619225 100644 --- a/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json +++ b/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json @@ -16,8 +16,8 @@ }, "minis": [ { - "mesh": "https://steamusercontent-a.akamaihd.net/ugc/1950639240971178878/094F5D9E47B4CECEAE1A529E46E70C0ED2DE737B/", - "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/1930372848016475033/1C5A975045A289BF43FBDC29DC3F2BD0ED7E1E0C/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/18162736975687770648/643CAEA98913694A90A7BC6B9ED78668C9F46386/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14670109106110705253/18525D8840EEFDB2DC159B4988BD6A734BC2418E/" } ] } diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 71e61b04..c7053fdc 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1730,8 +1730,8 @@ "name": "MPL-57 Barrage Trooper", "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857286/60AC7BEDA003F95429F6312717EBAB9488C51852/", "mini": { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540540670338/28625FDF1F56EF08F0DB236C4ECFF67A54083B2B/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862750426/2563904D7561144EE85EBCD3E07D68F48D8DD034/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/785234540540671382/63267B140C2A619525B99FD943DF2421F75B9130/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/785234540540671503/D8BCEE815B07EA7074085CF1F43AF8041DEC8981/" }, "points": 22, "restrictions": { diff --git a/mod/src/includes/Deck.ttslua b/mod/src/includes/Deck.ttslua index 3d3267e2..79953fc9 100644 --- a/mod/src/includes/Deck.ttslua +++ b/mod/src/includes/Deck.ttslua @@ -12,9 +12,9 @@ function Deck:create() deployment = "https://steamusercontent-a.akamaihd.net/ugc/9428670987266773291/C115F2EAE00FD59B7A9D7CEB9906CB91B872DFBE/", }, vertical = { - objective = "http://cloud-3.steamusercontent.com/ugc/4112341331071278905/7391A9D8F237C6D5361EE068AB9083F51FAEE83E/", - condition = "http://cloud-3.steamusercontent.com/ugc/4112341331071279002/608E86CFE4BAF5BA63E5AAE86ECBA1526E6EB26C/", - deployment = "http://cloud-3.steamusercontent.com/ugc/4112341331071278944/D0E9A51135A2AEEF1CB100554DA46B0704ECCB28/", + objective = "https://steamusercontent-a.akamaihd.net/ugc/10061127123107977322/847E7AAD563EF0AFD5926BE05266DC82E5FD04FC/", + condition = "https://steamusercontent-a.akamaihd.net/ugc/13818252657402930588/FB0312AED90098252D2E53426F99F2072279EE64/", + deployment = "https://steamusercontent-a.akamaihd.net/ugc/9428670987266773291/C115F2EAE00FD59B7A9D7CEB9906CB91B872DFBE/" }, }, _unknown = { diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index c63d6196..b0726ab2 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -47,7 +47,7 @@ (Since last Beta) - Add: WLO-5 Speeder Truck - - Add: New Objective Card Backing + - Add: New Objective Card Backing test From 0e8ae49686242a0c18cab8e7335639a5d18aee0d Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 7 Mar 2026 02:05:20 -0500 Subject: [PATCH 47/93] DtG update --- contrib/cards/Empire/EmpireUnits.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index 6d43a69f..1d8a985c 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -112,7 +112,7 @@ }, { "name": "Deploy The Garrison", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865564035541/3076FCDDC51B733A5970AD91FE0698F9A1888084/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12892698832465049230/B1371A5A16B4A487680730048ABA1D8AD5711C43/", "pip": 2 }, { From fe26c1f30ad875afb4bcc1a5a23b2cdee440bff1 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 14 Mar 2026 00:32:54 -0400 Subject: [PATCH 48/93] Wheel Bike and Objective cards --- contrib/cards/Separatist/SeparatistUnits.json | 34 ++- .../ShadowCollective/WLO5SpeederTankUnit.json | 3 +- contrib/cards/official.json | 212 +++++++++++++++++- 3 files changed, 236 insertions(+), 13 deletions(-) diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index cf577a46..5ae15e4a 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -710,6 +710,34 @@ "secondary": "http://cloud-3.steamusercontent.com/ugc/1621849538641208262/A0E48F7D9C94617C81BAD4D649717BAC7A8B9B64/" } ] + }, + { + "name": "TSMEU-6 Wheel Bikes", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15288499232659659250/25E204B8F8CE71424524AEC8158D526F0FCA5347/", + "size": "large", + "type": "Ground Vehicle", + "points": 90, + "speed": 2, + "upgrades": { + "Pilot": 1, + "Training": 1, + "Comms": 1, + "Gear": 1 + }, + "silhouette": { + "height": 2.38, + "offset": 0.0 + }, + "minis": [ + { + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/10144700341503599438/88D1E2DC8FFF46535836C0B531A2565289E67BB6/", + "secondary": "https://steamusercontent-a.akamaihd.net/ugc/17563221817980340918/4801D5FAE716911B413B5F0C790F000B63E0104C/" + }, + { + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/15728085712538195211/C8E5D2D006905324C7541C35765C4FF43D3D06B6/", + "secondary": "https://steamusercontent-a.akamaihd.net/ugc/17607413312287217729/FD59E769888166ADDE570190F4B5866F31E59409/" + } + ] } ], "Support": [ @@ -730,11 +758,13 @@ "minis": [ { "bundle": "http://cloud-3.steamusercontent.com/ugc/1686023445067168845/FBADA8EDE2ED248CA7F50C1DB3BFF46215E0360B/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1686023445067146631/305531F34B6598A008F5925D828457DF1A8195F6/" + "secondary": "http://cloud-3.steamusercontent.com/ugc/1686023445067146631/305531F34B6598A008F5925D828457DF1A8195F6/", + "collider": "https://steamusercontent-a.akamaihd.net/ugc/1009315641457670521/DAD6653F422046BD6BC4360FE18C96927D5F4C44/" }, { "bundle": "http://cloud-3.steamusercontent.com/ugc/1686023445067146269/A9189AC12193D3BD24624F7A5137C4E69FADDBBF/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1686023445067146631/305531F34B6598A008F5925D828457DF1A8195F6/" + "secondary": "http://cloud-3.steamusercontent.com/ugc/1686023445067146631/305531F34B6598A008F5925D828457DF1A8195F6/", + "collider": "https://steamusercontent-a.akamaihd.net/ugc/1009315641457670521/DAD6653F422046BD6BC4360FE18C96927D5F4C44/" } ] }, diff --git a/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json b/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json index c4619225..8a4a77f9 100644 --- a/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json +++ b/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json @@ -17,7 +17,8 @@ "minis": [ { "mesh": "https://steamusercontent-a.akamaihd.net/ugc/18162736975687770648/643CAEA98913694A90A7BC6B9ED78668C9F46386/", - "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14670109106110705253/18525D8840EEFDB2DC159B4988BD6A734BC2418E/" + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14670109106110705253/18525D8840EEFDB2DC159B4988BD6A734BC2418E/", + "collider": "https://steamusercontent-a.akamaihd.net/ugc/10402477424751392481/9585E316040CEA861ABF6A551F35C2473BEC0C52/" } ] } diff --git a/contrib/cards/official.json b/contrib/cards/official.json index c7053fdc..52a62df1 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -3993,6 +3993,16 @@ } } }, + { + "name": "IG-100 MagnaGuard Pilot", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13932498646258125528/C8DBD74A2D1CC4D0A5722DD77352314CB70ED9C3/", + "points": 10, + "restrictions": { + "include": { + "unit": ["TSMEU-6 Wheel Bikes"] + } + } + }, { "name": "OOM-Series Droid Pilot", "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140015474/EBD6D1C9D3C7DBCB66C1C5976D7F6B2A7303655D/", @@ -4795,6 +4805,36 @@ "Objective Token", "Objective Token" ] + }, + { + "name": "Align the Relay", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14491977904273934664/881FE4169E463AB25699171975636C18E2957251/", + "type": "vertical", + "additionalObjects": [ + "Objective Token", + "Objective Token", + "Objective Token", + "Objective Token", + "Objective Token" + ] + }, + { + "name": "Failed Negotiation", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15810516017228580607/C88C46C8188C2175C572E511FCBF7DA0C3A3C38D/", + "type": "vertical", + "additionalObjects": ["Objective Token", "Objective Token"] + }, + { + "name": "Retrieve the Data", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12099398395755541092/779E148B5ECFEE495D3C1652FB31A10482C694B7/", + "type": "vertical", + "additionalObjects": [ + "Objective Token", + "Objective Token", + "Objective Token", + "Objective Token", + "Objective Token" + ] } ], "deployment": [ @@ -5095,6 +5135,120 @@ "POI Token", "Outflank Map" ] + }, + { + "name": "Cauldron", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15627037991188349167/2271D8A45AA921E7FCB81C124DC41F2C46D247ED/", + "type": "vertical", + "zone": [ + ["r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r"], + [ + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh" + ], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [ + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh" + ], + ["b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b"] + ], + "additionalObjects": [ + "POI Token", + "POI Token", + "POI Token", + "POI Token", + "POI Token", + "POI Token", + "Cauldron Map" + ] + }, + { + "name": "Contact, Contact!", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12700346236472029468/7D62CD68C323798CA715971917CF33F42E3B118B/", + "type": "vertical", + "zone": [ + [" ", " ", " ", " ", " ", " ", "r", "r", "r", "r", "r", "r"], + [" ", " ", " ", " ", " ", " ", "rh", "rh", "rh", "r", "r", "rh"], + [" ", "b", "b", " ", " ", " ", " ", " ", " ", "r", "r", " "], + [" ", "b", "b", " ", " ", " ", " ", " ", " ", "r", "r", " "], + ["bh", "b", "b", "bh", "bh", "bh", " ", " ", " ", " ", " ", " "], + ["b", "b", "b", "b", "b", "b", " ", " ", " ", " ", " ", " "] + ], + "additionalObjects": [ + "POI Token", + "POI Token", + "POI Token", + "POI Token", + "POI Token", + "Contact, Contact! Map" + ] + }, + { + "name": "Payload", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16906983444643756097/831C0FCBF977904A6E0D99980AA93894BCC64917/", + "type": "vertical", + "zone": [ + ["r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r", "r"], + [ + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh", + "rh" + ], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [ + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh", + "bh" + ], + ["b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b"] + ], + "additionalObjects": [ + "POI Token", + "POI Token", + "POI Token", + "Payload Map" + ] } ], "condition": [ @@ -5153,6 +5307,26 @@ "name": "No Time to Lose", "image": "https://steamusercontent-a.akamaihd.net/ugc/11496058962921126665/041D2E72354726A4E39C6A2CCA2DA6C0E66946B2/", "type": "vertical" + }, + { + "name": "Armored Assault", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10425618194745828599/81AA48C3F07BB313B7A3CA7E76F88F0CC2167E9F/", + "type": "vertical" + }, + { + "name": "Extreme Discipline", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9721205986412257000/CC9C2007345D883D9D1287E44D74CAE089114D29/", + "type": "vertical" + }, + { + "name": "Rapid Deployment", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14859786331088523291/F2ECDE4D7BB7F3260A049D627C9801BB32093B0B/", + "type": "vertical" + }, + { + "name": "Scrambled Orders", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12845531429756028876/8BAA1BF01969CACC268BDC0847D316A2E96DFD48/", + "type": "vertical" } ] }, @@ -5834,61 +6008,79 @@ "Breakthrough Map": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/16613435332891317382/334404D722726ECE56866DBBF444836E8AC2F3A3/" } }, "Bunker Assault Map": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/12304553798171400164/69AB4EB2BA8A39D91996852580DCA1AB783913C4/" } }, "Close the Pocket Map": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/14769093936620373412/BC975CBADAEBD9AA231D83443BE9EDB59FF4308A/" } }, "Intercept Signals Map": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/12020228019721678387/E635199A7837BC89D679A0D848FB4EA7D7677239/" } }, "Recover the Research Map": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/15862931608322641380/4C8D35B94855B94A233EBF9D01FB0D1A0EEE3B32/" } }, "Shifting Priorities Map": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/12429316638634710380/005746FFEA77696118B237B650C7628F9089B946/" } }, "Outflank Map": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/15283876349976838373/93B3D53B8AB0DCF7FE15F606E81FD962A738B455/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/15283876349976838373/93B3D53B8AB0DCF7FE15F606E81FD962A738B455/" + } + }, + "Cauldron Map": { + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/16251599508539301812/3439E5658197D7006038C5BFDB48CE8F7DFC6DAD/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/16251599508539301812/3439E5658197D7006038C5BFDB48CE8F7DFC6DAD/" + } + }, + "Contact, Contact! Map": { + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/15184660976021577609/475B87420012E83B5AE2865DC1E4620E93ADDECE/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/15184660976021577609/475B87420012E83B5AE2865DC1E4620E93ADDECE/" + } + }, + "Payload Map": { + "card": { + "image": "https://steamusercontent-a.akamaihd.net/ugc/12399717920746649417/C9AC372588552230C9F0C00A5ADB0D468672E726/", + "back": "https://steamusercontent-a.akamaihd.net/ugc/12399717920746649417/C9AC372588552230C9F0C00A5ADB0D468672E726/" } }, "Bunker Assault Map (Recon)": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/15532220751502518416/FA34BE3822D18F2C6D04F8987569B0D4F6A1F9D3/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/15532220751502518416/FA34BE3822D18F2C6D04F8987569B0D4F6A1F9D3/" } }, "Close the Pocket Map (Recon)": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/17088281433024932061/E9983676D51E1EE76B7152BD13CF83C794F02C81/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/17088281433024932061/E9983676D51E1EE76B7152BD13CF83C794F02C81/" } }, "Intercept Signals Map (Recon)": { "card": { "image": "https://steamusercontent-a.akamaihd.net/ugc/13979927353592007130/EA574041CFDFF4712CEC9A8E18F5A8DDE7A18216/", - "back": "https://steamusercontent-a.akamaihd.net/ugc/2482129948501051192/5A19D255E47E80FF3CCE0EBFA095CB8677C4188D/" + "back": "https://steamusercontent-a.akamaihd.net/ugc/13979927353592007130/EA574041CFDFF4712CEC9A8E18F5A8DDE7A18216/" } } } From ba148130575eb54ee9f829a2c7b429b982b0c83c Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 14 Mar 2026 00:53:38 -0400 Subject: [PATCH 49/93] Welcome and error pass --- contrib/cards/Separatist/SeparatistUnits.json | 14 ++++++-------- contrib/cards/official.json | 2 +- mod/src/includes/ui/Welcome.xml | 6 +++++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index 5ae15e4a..cc0979fd 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -730,12 +730,12 @@ }, "minis": [ { - "bundle": "https://steamusercontent-a.akamaihd.net/ugc/10144700341503599438/88D1E2DC8FFF46535836C0B531A2565289E67BB6/", - "secondary": "https://steamusercontent-a.akamaihd.net/ugc/17563221817980340918/4801D5FAE716911B413B5F0C790F000B63E0104C/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/10144700341503599438/88D1E2DC8FFF46535836C0B531A2565289E67BB6/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/17563221817980340918/4801D5FAE716911B413B5F0C790F000B63E0104C/" }, { - "bundle": "https://steamusercontent-a.akamaihd.net/ugc/15728085712538195211/C8E5D2D006905324C7541C35765C4FF43D3D06B6/", - "secondary": "https://steamusercontent-a.akamaihd.net/ugc/17607413312287217729/FD59E769888166ADDE570190F4B5866F31E59409/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15728085712538195211/C8E5D2D006905324C7541C35765C4FF43D3D06B6/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/17607413312287217729/FD59E769888166ADDE570190F4B5866F31E59409/" } ] } @@ -758,13 +758,11 @@ "minis": [ { "bundle": "http://cloud-3.steamusercontent.com/ugc/1686023445067168845/FBADA8EDE2ED248CA7F50C1DB3BFF46215E0360B/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1686023445067146631/305531F34B6598A008F5925D828457DF1A8195F6/", - "collider": "https://steamusercontent-a.akamaihd.net/ugc/1009315641457670521/DAD6653F422046BD6BC4360FE18C96927D5F4C44/" + "secondary": "http://cloud-3.steamusercontent.com/ugc/1686023445067146631/305531F34B6598A008F5925D828457DF1A8195F6/" }, { "bundle": "http://cloud-3.steamusercontent.com/ugc/1686023445067146269/A9189AC12193D3BD24624F7A5137C4E69FADDBBF/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1686023445067146631/305531F34B6598A008F5925D828457DF1A8195F6/", - "collider": "https://steamusercontent-a.akamaihd.net/ugc/1009315641457670521/DAD6653F422046BD6BC4360FE18C96927D5F4C44/" + "secondary": "http://cloud-3.steamusercontent.com/ugc/1686023445067146631/305531F34B6598A008F5925D828457DF1A8195F6/" } ] }, diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 52a62df1..fb615712 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -4819,7 +4819,7 @@ ] }, { - "name": "Failed Negotiation", + "name": "Failed Negotiations", "image": "https://steamusercontent-a.akamaihd.net/ugc/15810516017228580607/C88C46C8188C2175C572E511FCBF7DA0C3A3C38D/", "type": "vertical", "additionalObjects": ["Objective Token", "Objective Token"] diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index b0726ab2..6279e1fc 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -47,7 +47,11 @@ (Since last Beta) - Add: WLO-5 Speeder Truck - - Add: New Objective Card Backing test + - Add: New Objective Card Backing + - Add: New Objectives + - Add: TSMEU-6 Wheel Bikes + - Fix: Deploy the Garrison + - Fix: Fleet Trooper MPL model From a0fe8875b8dbd96bdb31f87ee91a237a2cf98fa9 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 14 Mar 2026 22:41:32 -0400 Subject: [PATCH 50/93] Sihl update --- contrib/cards/Separatist/SeparatistUnits.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index cc0979fd..eaf9a068 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -725,7 +725,7 @@ "Gear": 1 }, "silhouette": { - "height": 2.38, + "height": 2.91, "offset": 0.0 }, "minis": [ From 9fd8df1cdaf0937f476429d8e2c366dbea0f4c65 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sat, 14 Mar 2026 22:42:15 -0400 Subject: [PATCH 51/93] Update Welcome.xml --- mod/src/includes/ui/Welcome.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 6279e1fc..aab3645e 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -47,6 +47,7 @@ (Since last Beta) - Add: WLO-5 Speeder Truck + - Add: Taun Commanders - Add: New Objective Card Backing - Add: New Objectives - Add: TSMEU-6 Wheel Bikes From c4d0d993a5979dc8d53de38f00e768b1edd21b9d Mon Sep 17 00:00:00 2001 From: halestom15 Date: Mon, 23 Mar 2026 23:24:54 -0400 Subject: [PATCH 52/93] add WLO to SC --- contrib/cards/ShadowCollective/ShadowCollectiveUnits.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json b/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json index f0103910..6d56a546 100644 --- a/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json +++ b/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json @@ -172,6 +172,9 @@ "bundle": "http://cloud-3.steamusercontent.com/ugc/1802024425338475527/29D3660FA84E5B9F804B840C18E68F5631EE6462/" } ] + }, + { + "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" } ] } From 435632dee9b1f0d3dd4b4e9a14d6327cfe00935c Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 2 Apr 2026 00:42:54 -0400 Subject: [PATCH 53/93] IHC --- contrib/cards/Empire/EmpireUnits.json | 99 +++++++++++++++++++++++++++ contrib/cards/official.json | 25 +++++++ 2 files changed, 124 insertions(+) diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index 1d8a985c..b2acc44b 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -122,6 +122,105 @@ } ] }, + { + "name": "Grand Admiral Thrawn", + "title": "Imperial High Command", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9436299354256705736/3DCE14B424BBB4F76939DA580884B08F5597866A/", + "size": "small", + "type": "Trooper", + "points": 110, + "speed": 2, + "upgrades": { + "Command": 3, + "Training": 1, + "Gear": 1 + }, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16882569187315189060/9CC8F02A05022AC6AE93534E6C95F72197C296C3/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/17874999880093691776/296F0B6569A22FA39F40E4A61D207900C1CCE512/" + } + ], + "commands": [ + { + "name": "Architects of Thier Own Destruction", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12184173414394445819/F6446EC3BC802836DC5A6E7C19A5BD77F30B7278/", + "pip": 1 + }, + { + "name": "We Must Wait and Watch", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13718292354738588716/0DB30394AA3AA3D13D9FA23F2B7DC17CF34116F0/", + "pip": 2 + }, + { + "name": "I Want You to Know Failure", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15617948462890110491/4C39E34E102D2071D0EEAA0AC7F4F8953E108265/", + "pip": 3 + } + ] + }, + { + "name": "Grand Moff Tarkin", + "title": "Imperial High Command", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12830085949468452139/30B1D3C7B93F18C834C172A25D605C01DAFD9A31/", + "size": "small", + "type": "Trooper", + "points": 95, + "speed": 2, + "upgrades": { + "Command": 3 + }, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/11491712071340896017/8364014C41D2C85817AA0641F36F066504E46226/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/10067944761455784205/EA5A44F1A042F71C0D397173A893E5316CA42463/" + } + ], + "commands": [ + { + "name": "Moment of Triumph", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13309803368671609766/C5893E5966D2FE573DD589E9B5E94EBD01E20707/", + "pip": 1 + }, + { + "name": "Fear Will Keep Them in Line", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10284086872796146114/8FC131D739DFBCA729DC1B5F31DD440B65EEB4C6/", + "pip": 3 + } + ] + }, + { + "name": "General Tagge", + "title": "Imperial High Command", + "image": "https://steamusercontent-a.akamaihd.net/ugc/18037706666284034311/7683A6889CF63CFFAC109DF58DDD103AA7ADFAF8/", + "size": "small", + "type": "Trooper", + "points": 85, + "speed": 2, + "upgrades": { + "Command": 3, + "Training": 1, + "Gear": 1 + }, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/11491712071340896017/8364014C41D2C85817AA0641F36F066504E46226/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/10067944761455784205/EA5A44F1A042F71C0D397173A893E5316CA42463/" + } + ], + "commands": [ + { + "name": "I Have Graphs AND the Command", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16492485163580443866/035B19E6BD2FC184EDE9E2782054E52EABFE973C/", + "pip": 1 + }, + { + "name": "Canny Adversary", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15603846312607201943/6B2F2EC17A80B2F190532E33057F16931B8D4F19/", + "pip": 3 + } + ] + }, { "name": "Iden Versio", "title": "Inferno Squad Leader", diff --git a/contrib/cards/official.json b/contrib/cards/official.json index fb615712..fbdfca9f 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -4263,6 +4263,26 @@ } ], "Training": [ + { + "name": "Security Detail", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12222061891853555901/CD168A0212E4D0E6D145E0802870606F91B35F68/", + "points": 5, + "restrictions": { + "include": { + "unit": ["Imperial Death Troopers"] + } + } + }, + { + "name": "Logistical Prowess", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11474714211228603788/9066E07B5E314B6AC9B8D46367F9A980AD3257A9/", + "points": 0, + "restrictions": { + "include": { + "unit": ["General Tagge"] + } + } + }, { "name": "Jedi Training, Force Adept", "image": "https://steamusercontent-a.akamaihd.net/ugc/12390960585643083301/04DD725AE236156E74D8235599206979F3670D57/", @@ -4545,6 +4565,11 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/13986899663843994273/C86535A24E53CB437CF5E6DFB02A6FCF21E1EC63/", "pip": 2 }, + { + "name": "The Empire Does Not Tolerate Failure", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13148519556397561204/A85B3068F61D78D4E11CF222AA32EF0BEA1B997B/", + "pip": 2 + }, { "name": "Overwhelming Barrage", "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384886936/A2D77342B1EE6DD9E4413C3F2344E37694BD7CFB/", From 78407b5d72c4d2fad1084ac69de559b0cffec69e Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 2 Apr 2026 01:16:52 -0400 Subject: [PATCH 54/93] Weequay Pirates --- contrib/cards/Empire/EmpireUnits.json | 3 + contrib/cards/Rebel/RebelUnits.json | 3 + contrib/cards/Republic/RepublicUnits.json | 3 + contrib/cards/Separatist/SeparatistUnits.json | 3 + .../ShadowCollective/WeequayPiratesUnit.json | 33 +++++++++ contrib/cards/official.json | 67 +++++++++++++++++++ 6 files changed, 112 insertions(+) create mode 100644 contrib/cards/ShadowCollective/WeequayPiratesUnit.json diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index b2acc44b..aced0c78 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -794,6 +794,9 @@ }, { "content": "contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json" + }, + { + "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" } ], "Special Forces": [ diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index 838eda41..2f577242 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -909,6 +909,9 @@ { "content": "contrib/cards/ShadowCollective/PykeSyndicateFootSoldiersUnit.json" }, + { + "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" + }, { "name": "Ewok Skirmishers", "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133206135/9932914CEED1068F058672A166F0F7A67E5C68D5/", diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index 01561010..0c642121 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -523,6 +523,9 @@ }, { "content": "contrib/cards/ShadowCollective/PykeSyndicateFootSoldiersUnit.json" + }, + { + "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" } ], "Special Forces": [ diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index eaf9a068..637c4e7d 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -538,6 +538,9 @@ { "content": "contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json" }, + { + "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" + }, { "name": "Geonosian Warriors", "title": "Soldiers of the Hive", diff --git a/contrib/cards/ShadowCollective/WeequayPiratesUnit.json b/contrib/cards/ShadowCollective/WeequayPiratesUnit.json new file mode 100644 index 00000000..33b7b28a --- /dev/null +++ b/contrib/cards/ShadowCollective/WeequayPiratesUnit.json @@ -0,0 +1,33 @@ +{ + "name": "Weequay Pirates", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11819796991863488676/7F77BE629CF0B253A3DCEECAD2FA148D8627995C/", + "size": "small", + "type": "Trooper", + "points": 40, + "speed": 2, + "upgrades": { + "Heavy Weapon": 1, + "Personnel": 2, + "Training": 1, + "Gear": 1, + "Grenades": 1 + }, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16427319945525081840/0BC18D14AA5B5EDE59BB753572CA11BA8DFB093C/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15128098244470197051/122A93D394F779A72D4DFD24D7BEFE3BEF7DBBCB/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/13445091018596770169/04299452D8946357EA16CD9DDE32B787C6DBBEDA/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/12521217169728315650/79F32E9C7B535418920B0C335F8B5FC3F9AE0726/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16223639926626886253/6DAD82EB229219B62298B7B4C22F6586ED23E9BB/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9341836019986456990/3940A22021782AC05911D4DCD13463623411B60F/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/10420975901939976395/93572C150848711C2D7919BB5E0A66DD51E33DEC/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15879012411449849405/6339E5B40A0F222280349C23BCB3E79CC5136B43/" + } + ] +} diff --git a/contrib/cards/official.json b/contrib/cards/official.json index fbdfca9f..2423c138 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1556,6 +1556,34 @@ } ], "Heavy Weapon": [ + { + "name": "Long Rifle Pirate", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12069676993247554413/429E3382BCE4E5FB5A38F8E4E43DAE97DD7C3587/", + "mini": { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/13407742837548351880/9642E26DE11D4BCC37AF18D7DAFB67E3635A3C65/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15244879389526567538/5FBC4A69F53811F7BB2B1AD495CFEC1F0EB643E0/" + }, + "points": 20, + "restrictions": { + "include": { + "unit": ["Weequay Pirates"] + } + } + }, + { + "name": "Electrostaff Pirate", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10005657645978308453/F2B28DC87052D87780B32EEA43883DF069CDD23A/", + "mini": { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16132237456890768608/44919D8C156835F39D911E99F6FE864C08C3E425/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9435972990529503531/027081EFAAAAA333CB323E2267AECA0A668B0C8A/" + }, + "points": 15, + "restrictions": { + "include": { + "unit": ["Weequay Pirates"] + } + } + }, { "name": "Agent Kallus", "image": "https://steamusercontent-a.akamaihd.net/ugc/11243613263901997875/0DD9BAD82731CF9B82AA13471D5EC164B8583545/", @@ -2848,6 +2876,45 @@ } ], "Personnel": [ + { + "name": "Weequay Pirate Squad", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17099305818157449093/AB25320EE6C23F1CFA10608400AA0881A83EFCB4/", + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/13445091018596770169/04299452D8946357EA16CD9DDE32B787C6DBBEDA/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/12521217169728315650/79F32E9C7B535418920B0C335F8B5FC3F9AE0726/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16223639926626886253/6DAD82EB229219B62298B7B4C22F6586ED23E9BB/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9341836019986456990/3940A22021782AC05911D4DCD13463623411B60F/" + }, + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/10420975901939976395/93572C150848711C2D7919BB5E0A66DD51E33DEC/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15879012411449849405/6339E5B40A0F222280349C23BCB3E79CC5136B43/" + } + ], + "points": 30, + "restrictions": { + "include": { + "unit": ["Weequay Pirates"] + } + } + }, + { + "name": "Pirate Captain", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9436415482602380066/C3B7D0EBC2A34FC536B903E13C4958D0F9C699AA/", + "mini": { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15406802960249254940/269FFE5E1C0ACBA9B2607E7B3DEDF4B54A9A8904/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9648645391611954855/C5E2F7BAA5F0573B72DEA0FF33C57550394E46DA/" + }, + "points": 18, + "leader": true, + "restrictions": { + "include": { + "unit": ["Weequay Pirates"] + } + } + }, { "name": "Clone Marksman", "image": "https://steamusercontent-a.akamaihd.net/ugc/16972794900197879773/6B1534C765EB2036E2D189C0F8F49AA5F3BDF920/", From ea51e63e343967e6b9bfc8265102838ab231aa2c Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 2 Apr 2026 01:29:05 -0400 Subject: [PATCH 55/93] Welcome Update --- contrib/cards/Empire/EmpireUnits.json | 6 +++--- contrib/cards/official.json | 2 +- mod/src/includes/ui/Welcome.xml | 12 ++++-------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index aced0c78..bc5ac85c 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -143,7 +143,7 @@ ], "commands": [ { - "name": "Architects of Thier Own Destruction", + "name": "Architects of Their Own Destruction", "image": "https://steamusercontent-a.akamaihd.net/ugc/12184173414394445819/F6446EC3BC802836DC5A6E7C19A5BD77F30B7278/", "pip": 1 }, @@ -172,8 +172,8 @@ }, "minis": [ { - "mesh": "https://steamusercontent-a.akamaihd.net/ugc/11491712071340896017/8364014C41D2C85817AA0641F36F066504E46226/", - "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/10067944761455784205/EA5A44F1A042F71C0D397173A893E5316CA42463/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16733402417968018171/CDE38AF3BCF6C2601F389A7AEB03B7399E440D9B/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16733402417968018171/CDE38AF3BCF6C2601F389A7AEB03B7399E440D9B/" } ], "commands": [ diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 2423c138..a2935f8b 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -2901,7 +2901,7 @@ } }, { - "name": "Pirate Captain", + "name": "Weequay Captain", "image": "https://steamusercontent-a.akamaihd.net/ugc/9436415482602380066/C3B7D0EBC2A34FC536B903E13C4958D0F9C699AA/", "mini": { "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15406802960249254940/269FFE5E1C0ACBA9B2607E7B3DEDF4B54A9A8904/", diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index aab3645e..89df1e22 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -37,7 +37,7 @@ - Welcome to Star Wars: Legion for TTS, v8.0.15-beta. + Welcome to Star Wars: Legion for TTS, v8.0.16-beta. This is a beta release . @@ -46,13 +46,9 @@ CHANGELOG (Since last Beta) - - Add: WLO-5 Speeder Truck - - Add: Taun Commanders - - Add: New Objective Card Backing - - Add: New Objectives - - Add: TSMEU-6 Wheel Bikes - - Fix: Deploy the Garrison - - Fix: Fleet Trooper MPL model + - Add: Imperial High Command + - Add: Weequay Pirates + - Fix: WLO Tank Spawns in SC From 752749ca6709abc6f39af0ee12c095112a62ae69 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 2 Apr 2026 01:31:30 -0400 Subject: [PATCH 56/93] Update EmpireUnits.json --- contrib/cards/Empire/EmpireUnits.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index bc5ac85c..076e237f 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -173,7 +173,7 @@ "minis": [ { "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16733402417968018171/CDE38AF3BCF6C2601F389A7AEB03B7399E440D9B/", - "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16733402417968018171/CDE38AF3BCF6C2601F389A7AEB03B7399E440D9B/" + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/10086133497934154614/77993863423B7453DB85E31D4CA9B7F13DCD4626/" } ], "commands": [ From 09b3354b6422938a5ef9502508da8526aeca2f38 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 3 Apr 2026 14:01:41 -0400 Subject: [PATCH 57/93] Ben's Table --- mod/src/StarWarsLegion/TABLE.3a3ed9.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/src/StarWarsLegion/TABLE.3a3ed9.json b/mod/src/StarWarsLegion/TABLE.3a3ed9.json index 349342ff..49494d71 100644 --- a/mod/src/StarWarsLegion/TABLE.3a3ed9.json +++ b/mod/src/StarWarsLegion/TABLE.3a3ed9.json @@ -35,7 +35,7 @@ "HideWhenFaceDown": false, "Hands": false, "CustomAssetbundle": { - "AssetbundleURL": "http://cloud-3.steamusercontent.com/ugc/1744555589998518180/70BD19FBE4D4D8B319F6A4A2ACCE401C25D94656/", + "AssetbundleURL": "https://steamusercontent-a.akamaihd.net/ugc/9562901817362450638/0D1D2A8AE25E903CD94C8BE040B2E4DD7F1E4B9E/", "AssetbundleSecondaryURL": "", "MaterialIndex": 0, "TypeIndex": 0, From c2761bc646aad73403e7e5cb1df1f36df2de4b95 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 3 Apr 2026 15:44:26 -0400 Subject: [PATCH 58/93] update movement template shaders --- mod/src/StarWarsLegion.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mod/src/StarWarsLegion.lua b/mod/src/StarWarsLegion.lua index 2ddaa696..6bfbdbc0 100644 --- a/mod/src/StarWarsLegion.lua +++ b/mod/src/StarWarsLegion.lua @@ -152,20 +152,20 @@ function onload(saveData) templateInfo.moveTemplate[1].colorTint = {1,1,1} templateInfo.moveTemplate[1].shortBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778007615968/4EE8D2332DCB6F4837551255CE5B1F240937BBE5/" templateInfo.moveTemplate[1].longBundle = "http://cloud-3.steamusercontent.com/ugc/1848161512056297932/A762E187E43177F01CD89D1348F51B1E3C1C744D/" - templateInfo.moveTemplate[1].sharedBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009533333/AD7C02CEE53FB4B2EA823B3DFE570674F8998019/" + templateInfo.moveTemplate[1].sharedBundle = "https://steamusercontent-a.akamaihd.net/ugc/14063680635734530221/FCD291B71472CA8636D00D0B82B1BA02671FF414/" templateInfo.moveTemplate[2] = {} templateInfo.moveTemplate[2].colorTint = {0.5,0.5,0.5} templateInfo.moveTemplate[2].shortBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009510155/77119B471FE3C51B06443F8ABE1C1B522431DF9B/" templateInfo.moveTemplate[2].longBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009510102/15B368A8D753AB35E1D60F7A377AE273EBEC0145/" - templateInfo.moveTemplate[2].sharedBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009533401/0FBA682D7D5AA7DA1A7EFC18B33C71EB1C0898E4/" + templateInfo.moveTemplate[2].sharedBundle = "https://steamusercontent-a.akamaihd.net/ugc/10737538167305927398/637135B07177CA895B71593EB85563FEFD0567D7/" templateInfo.moveTemplate[3] = {} templateInfo.moveTemplate[3].colorTint = {1,0.2,0.2} templateInfo.moveTemplate[3].shortBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009510243/26BA9DD7881AF63E1A19EB6BF9802630D792F836/" templateInfo.moveTemplate[3].longBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009510200/6723334F35444B547AA1382AE80F12E3FE9B72B1/" - templateInfo.moveTemplate[3].sharedBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009533447/E5D1D5169F0DB6025808BEE8C9647B3E7057BDB9/" + templateInfo.moveTemplate[3].sharedBundle = "https://steamusercontent-a.akamaihd.net/ugc/14511655092146905869/6D223BCC7D94DBDD342B6F887F7C777CE47D5585/" --This is actually diameter... just saying templateInfo.baseRadius = { From 428f702680eb47c5ab441d9662eb3cd47e9c55a5 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 3 Apr 2026 15:50:07 -0400 Subject: [PATCH 59/93] Also update the table so that range projections don't affect the skybox --- mod/src/StarWarsLegion/TABLE.3a3ed9.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/src/StarWarsLegion/TABLE.3a3ed9.json b/mod/src/StarWarsLegion/TABLE.3a3ed9.json index 349342ff..40aef82e 100644 --- a/mod/src/StarWarsLegion/TABLE.3a3ed9.json +++ b/mod/src/StarWarsLegion/TABLE.3a3ed9.json @@ -35,7 +35,7 @@ "HideWhenFaceDown": false, "Hands": false, "CustomAssetbundle": { - "AssetbundleURL": "http://cloud-3.steamusercontent.com/ugc/1744555589998518180/70BD19FBE4D4D8B319F6A4A2ACCE401C25D94656/", + "AssetbundleURL": "https://steamusercontent-a.akamaihd.net/ugc/10056915662299769833/848514DAF6FBC6488078EA9E0A4EB8C4582FCADF/", "AssetbundleSecondaryURL": "", "MaterialIndex": 0, "TypeIndex": 0, From 782cdcdd397424c7ad49cdaf24067c52c583ee3c Mon Sep 17 00:00:00 2001 From: halestom15 Date: Fri, 3 Apr 2026 16:38:40 -0400 Subject: [PATCH 60/93] Ben Changes --- mod/src/StarWarsLegion.lua | 8 ++++---- mod/src/StarWarsLegion/TABLE.3a3ed9.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mod/src/StarWarsLegion.lua b/mod/src/StarWarsLegion.lua index 2ddaa696..df762f4c 100644 --- a/mod/src/StarWarsLegion.lua +++ b/mod/src/StarWarsLegion.lua @@ -152,20 +152,20 @@ function onload(saveData) templateInfo.moveTemplate[1].colorTint = {1,1,1} templateInfo.moveTemplate[1].shortBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778007615968/4EE8D2332DCB6F4837551255CE5B1F240937BBE5/" templateInfo.moveTemplate[1].longBundle = "http://cloud-3.steamusercontent.com/ugc/1848161512056297932/A762E187E43177F01CD89D1348F51B1E3C1C744D/" - templateInfo.moveTemplate[1].sharedBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009533333/AD7C02CEE53FB4B2EA823B3DFE570674F8998019/" + templateInfo.moveTemplate[1].sharedBundle = "https://steamusercontent-a.akamaihd.net/ugc/14063680635734530221/FCD291B71472CA8636D00D0B82B1BA02671FF414/" templateInfo.moveTemplate[2] = {} templateInfo.moveTemplate[2].colorTint = {0.5,0.5,0.5} templateInfo.moveTemplate[2].shortBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009510155/77119B471FE3C51B06443F8ABE1C1B522431DF9B/" templateInfo.moveTemplate[2].longBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009510102/15B368A8D753AB35E1D60F7A377AE273EBEC0145/" - templateInfo.moveTemplate[2].sharedBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009533401/0FBA682D7D5AA7DA1A7EFC18B33C71EB1C0898E4/" - + templateInfo.moveTemplate[2].sharedBundle = "https://steamusercontent-a.akamaihd.net/ugc/10737538167305927398/637135B07177CA895B71593EB85563FEFD0567D7/" + templateInfo.moveTemplate[3] = {} templateInfo.moveTemplate[3].colorTint = {1,0.2,0.2} templateInfo.moveTemplate[3].shortBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009510243/26BA9DD7881AF63E1A19EB6BF9802630D792F836/" templateInfo.moveTemplate[3].longBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009510200/6723334F35444B547AA1382AE80F12E3FE9B72B1/" - templateInfo.moveTemplate[3].sharedBundle = "http://cloud-3.steamusercontent.com/ugc/1761462778009533447/E5D1D5169F0DB6025808BEE8C9647B3E7057BDB9/" + templateInfo.moveTemplate[3].sharedBundle = "https://steamusercontent-a.akamaihd.net/ugc/14511655092146905869/6D223BCC7D94DBDD342B6F887F7C777CE47D5585/" --This is actually diameter... just saying templateInfo.baseRadius = { diff --git a/mod/src/StarWarsLegion/TABLE.3a3ed9.json b/mod/src/StarWarsLegion/TABLE.3a3ed9.json index 49494d71..40aef82e 100644 --- a/mod/src/StarWarsLegion/TABLE.3a3ed9.json +++ b/mod/src/StarWarsLegion/TABLE.3a3ed9.json @@ -35,7 +35,7 @@ "HideWhenFaceDown": false, "Hands": false, "CustomAssetbundle": { - "AssetbundleURL": "https://steamusercontent-a.akamaihd.net/ugc/9562901817362450638/0D1D2A8AE25E903CD94C8BE040B2E4DD7F1E4B9E/", + "AssetbundleURL": "https://steamusercontent-a.akamaihd.net/ugc/10056915662299769833/848514DAF6FBC6488078EA9E0A4EB8C4582FCADF/", "AssetbundleSecondaryURL": "", "MaterialIndex": 0, "TypeIndex": 0, From 5fb307a479aeb76286bbe9d6aad92cc77a5fa1ad Mon Sep 17 00:00:00 2001 From: halestom15 Date: Sun, 5 Apr 2026 15:35:56 -0400 Subject: [PATCH 61/93] Update ShadowCollectiveUnits.json --- contrib/cards/ShadowCollective/ShadowCollectiveUnits.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json b/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json index 6d56a546..7ae50cfd 100644 --- a/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json +++ b/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json @@ -110,6 +110,9 @@ }, { "content": "contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json" + }, + { + "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" } ], "Special Forces": [ From 46f99b17271824200805ea85f9879399e1ff86cc Mon Sep 17 00:00:00 2001 From: halestom15 Date: Mon, 6 Apr 2026 00:03:38 -0400 Subject: [PATCH 62/93] Update official.json --- contrib/cards/official.json | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 028b5978..a2935f8b 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -4329,22 +4329,6 @@ } } ], - "Squad Leader": [ - { - "name": "Jedi Guardian", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59215140638617630/9AB204944DB662D2D15FC2FA5E50F02138AD1FD6/", - "mini": { - "bundle": "https://steamusercontent-a.akamaihd.net/ugc/16426912929621400/CC4D94EDC24D933D7333F5A35308606846A9B550/", - "secondary": "https://steamusercontent-a.akamaihd.net/ugc/16426912929621469/4B200440B54994D37A5B3667E04769CD92DE4F32/" - }, - "points": 22, - "restrictions": { - "include": { - "faction": ["Republic"] - } - } - } - ], "Training": [ { "name": "Security Detail", From a3268ae33e715fec0b14ce935da4f873d41fef30 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 22 Apr 2026 20:02:01 -0400 Subject: [PATCH 63/93] Empire Changes + Ewok Correction --- contrib/cards/Empire/EmpireUnits.json | 48 ++++++++++++------------- contrib/cards/Rebel/RebelUnits.json | 19 ++++------ contrib/cards/official.json | 51 ++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 40 deletions(-) diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index 076e237f..4ab0a774 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -833,30 +833,6 @@ } ] }, - { - "name": "Scout Troopers", - "title": "Strike Team", - "displayName": "Scout Troopers (Strike Team)", - "heavyWeaponTeam": true, - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384863628/C90F310AB2FFFFB744B68B2A0D3B0C657FA8ABA4/", - "size": "small", - "type": "Trooper", - "points": 20, - "speed": 2, - "upgrades": { - "Heavy Weapon": 1, - "Training": 1, - "Comms": 1, - "Gear": 1, - "Grenades": 1 - }, - "minis": [ - { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541911155/36E8C01A67E5E7A4188FC619261E0BAB0DF438C1/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/950714557093539603/6584D58DCD3135C901F302599C304E2D7044AD4B/" - } - ] - }, { "name": "Imperial Death Troopers", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385139667689/20539A9EAB988308E299A3DE18818D7B9FBA63C9/", @@ -1027,6 +1003,30 @@ } ] }, + { + "name": "Scout Troopers", + "title": "Strike Team", + "displayName": "Scout Troopers (Strike Team)", + "heavyWeaponTeam": true, + "image": "https://steamusercontent-a.akamaihd.net/ugc/11473212592953545707/1E52D2488DA72C302214A5A1B55D64D36BFFE757/", + "size": "small", + "type": "Trooper", + "points": 22, + "speed": 2, + "upgrades": { + "Heavy Weapon": 1, + "Training": 1, + "Comms": 1, + "Gear": 1, + "Grenades": 1 + }, + "minis": [ + { + "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541911155/36E8C01A67E5E7A4188FC619261E0BAB0DF438C1/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/950714557093539603/6584D58DCD3135C901F302599C304E2D7044AD4B/" + } + ] + }, { "name": "Range Troopers", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385139672651/467BD8F23216F7AE5274DC84837483AE40571B02/", diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index f53c92b4..2bdf343f 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -280,17 +280,14 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133208061/787C79B49789528F2EF54F1668B0BF28EBD6635C/", "size": "small", "type": "Trooper", - "points": 75, + "points": 64, "speed": 2, "upgrades": { - "Command": 1, - "Training": 1, - "Gear": 1 + "Training": 3 }, "minis": [ { - "mesh": "http://cloud-3.steamusercontent.com/ugc/951848302305475778/55B558E631655D0430BAF76E71C93899AAECA7A1/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/951848302305475871/06400240648CC39F933BE504C74DA9BEF4D006C4/" + "bundle": "http://cloud-3.steamusercontent.com/ugc/2023850162090106066/B6E661CAB8049A1B1CE975B53BB57B337E2C5DEE/" } ], "commands": [ @@ -317,17 +314,15 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133207972/4E2F553BC1FBA06D841591067D3D5E2AED2BF4E5/", "size": "small", "type": "Trooper", - "points": 90, + "points": 45, "speed": 2, "upgrades": { - "Training": 2, - "Gear": 1, - "Armament": 1 + "Command": 1, + "Training": 3 }, "minis": [ { - "mesh": "http://cloud-3.steamusercontent.com/ugc/773996263556990389/C38E9CAD7E5BA5496E1A211CA799E9ACBAC51D00/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/770611701143191344/5CAA4F0CE9A3FBE89A81560221ADCDDB5A801CC3/" + "bundle": "http://cloud-3.steamusercontent.com/ugc/2008089545867129309/3D6C219F243BDE984C42BB3444B01DB09B522CBB/" } ] }, diff --git a/contrib/cards/official.json b/contrib/cards/official.json index a2935f8b..70521fda 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1556,6 +1556,49 @@ } ], "Heavy Weapon": [ + { + "name": "Imperial Probe Droid", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12096037766204385743/C1C46E710472C45972A1CA308269B91EE9E691B0/", + "mini": { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/14972533488140369423/D390682F502B9999AE930DA6A78817DDE5D97027/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15762532285823663057/2254B8B0FBA723C3F840CDB6DB639404B0DA9DB1/" + }, + "points": 10, + "restrictions": { + "include": { + "faction": ["Empire"], + "rank": ["Corps", "Special Forces", "Support"] + } + } + }, + { + "name": "Seventh Sister", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12786414084835381664/A059896EB0382A23E79AC5B4A93BB1B80CB47DA8/", + "mini": { + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/10804385137011773/5377A3D119B96061C7EFAF2F8469BD5E889F3F13/" + }, + "leader": true, + "points": 30, + "restrictions": { + "include": { + "unit": ["Imperial Special Forces"] + } + } + }, + { + "name": "Fifth Brother", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14099995001149120961/E9E1ACB414FFC071E6B08B9F268040166D5D5C35/", + "mini": { + "bundle": "steamusercontent-a.akamaihd.net/ugc/2456221571853672166/B86F663B609267644D837B4F18C59614C6785229/" + }, + "leader": true, + "points": 32, + "restrictions": { + "include": { + "unit": ["Stormtrooper Riot Squad"] + } + } + }, { "name": "Long Rifle Pirate", "image": "https://steamusercontent-a.akamaihd.net/ugc/12069676993247554413/429E3382BCE4E5FB5A38F8E4E43DAE97DD7C3587/", @@ -1895,12 +1938,12 @@ }, { "name": "DLT-19X Sniper", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140103101/607C9BEDEB2C3780F1AE58D72DEE649F941A7CAD/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15525633462179263891/86D8A2EF8EBA4D9BF3DD245487375FB807A7E8B3/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/770610077943667377/92306C3BFB675310563691C3C582BF5B9FC9ACD4/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/770610077943667167/66F3188269E49FE5DAD649E771D799F2593263FF/" }, - "points": 28, + "points": 20, "restrictions": { "include": { "unit": ["Scout Troopers"] @@ -2289,12 +2332,12 @@ { "name": "Sonic Charge Saboteur", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140103507/922568C5FAACC25FCE80574BE5459CB511022824/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15530089580724049994/20B4C2D6907EBDEC74CEF88C5B10B71ED016F94B/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/950714557093539716/6A0D63306F505A074395AFA61EB990280DB8995D/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/950714557093539899/FF7A535E91BF79D4C3E0D55E874C1ACEB6FBF434/" }, - "points": 23, + "points": 17, "restrictions": { "include": { "unit": ["Scout Troopers"] From 2723b0e9f8c030a1bad6724335358395e32965fd Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 22 Apr 2026 20:14:53 -0400 Subject: [PATCH 64/93] Rebel Changes --- contrib/cards/Rebel/RebelUnits.json | 50 +++++++++++++------------- contrib/cards/official.json | 56 +++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 31 deletions(-) diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index 2bdf343f..ee07950c 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -958,7 +958,7 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133198280/BA9DB93D2D168B835C95075E8B1C52C79E044286/", "size": "small", "type": "Trooper", - "points": 48, + "points": 42, "speed": 2, "upgrades": { "Heavy Weapon": 1, @@ -986,30 +986,6 @@ } ] }, - { - "name": "Rebel Commandos", - "displayName": "Rebel Commandos (Strike Team)", - "title": "Strike Team", - "heavyWeaponTeam": true, - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133197589/A51BFFE52ED2FAE9DACE45BF774C7F5FE12CBC07/", - "size": "small", - "type": "Trooper", - "points": 20, - "speed": 2, - "upgrades": { - "Heavy Weapon": 1, - "Training": 1, - "Comms": 1, - "Gear": 1, - "Grenades": 1 - }, - "minis": [ - { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541173156/D637683F992DD0C4488252BE266EEEA1F26006B0/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541172655/B10522A40482843AF69A3F321BD091B0ECF52C6B/" - } - ] - }, { "name": "Wookiee Warriors", "title": "Freedom Fighters", @@ -1274,6 +1250,30 @@ } ] }, + { + "name": "Rebel Commandos", + "displayName": "Rebel Commandos (Strike Team)", + "title": "Strike Team", + "heavyWeaponTeam": true, + "image": "https://steamusercontent-a.akamaihd.net/ugc/15391478724315537430/D37281FE62D2FB944EF610EB01C66232CA9BB8EA/", + "size": "small", + "type": "Trooper", + "points": 20, + "speed": 2, + "upgrades": { + "Heavy Weapon": 1, + "Training": 1, + "Comms": 1, + "Gear": 1, + "Grenades": 1 + }, + "minis": [ + { + "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541173156/D637683F992DD0C4488252BE266EEEA1F26006B0/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541172655/B10522A40482843AF69A3F321BD091B0ECF52C6B/" + } + ] + }, { "name": "Tauntaun Riders", "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081133194939/65297606E28367C04BF4BB4DE3474EAB723F4416/", diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 70521fda..9d9e7fb7 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1556,6 +1556,51 @@ } ], "Heavy Weapon": [ + { + "name": "Luke Skywalker", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10466047961901782395/2898B46094ACFC7D44564F3E1B650BC01D8FA0C9/", + "mini": { + "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540537100371/0B15B2810ABEBE5429E4A24852D96BAC2DC28BF7/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540537079134/11A17263AD81DC9D4271B7C6BD809B69141DCB61/" + }, + "leader": true, + "points": 25, + "restrictions": { + "include": { + "unit": ["Rebel Veterans", "Rebel Troopers"] + } + } + }, + { + "name": "Jyn Erso", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16079811724149638991/B0AED05E50E5EC32550F8970C8F7C6AA412ADB18/", + "mini": { + "mesh": "http://cloud-3.steamusercontent.com/ugc/951848302305475778/55B558E631655D0430BAF76E71C93899AAECA7A1/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/951848302305475871/06400240648CC39F933BE504C74DA9BEF4D006C4/" + }, + "leader": true, + "points": 22, + "restrictions": { + "include": { + "unit": ["Rebel Commandos", "Rebel Troopers", "Guerilla Troopers"] + } + } + }, + { + "name": "Chewbacca", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15050970341342733786/8231EFDD964C904D2B3F2B170525EA2B5AECE9AC/", + "mini": { + "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540540669973/90DD9EA44756CCF9B2473302DC644F3AEEED07B4/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540540670117/5AA04499FC8CC467864F9F335601E6335C03326D/" + }, + "leader": true, + "points": 40, + "restrictions": { + "include": { + "type": ["Wookiee Trooper"] + } + } + }, { "name": "Imperial Probe Droid", "image": "https://steamusercontent-a.akamaihd.net/ugc/12096037766204385743/C1C46E710472C45972A1CA308269B91EE9E691B0/", @@ -1896,12 +1941,12 @@ }, { "name": "DH-447 Sniper", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140084953/59EB5B5BADDFEACF7BA997C247F16A54080A6E31/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16916821861583515066/6783DCC4833EAE22DCA9EDB9C8F585AE29BA13A3/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862776611/B8222AA4A3B680E969DC106C8A281D11845994EB/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862776974/21649563BC0CB84E12C0B249C720919F528596D3/" }, - "points": 38, + "points": 20, "restrictions": { "include": { "unit": ["Rebel Commandos"] @@ -2233,12 +2278,12 @@ }, { "name": "Proton Charge Saboteur", - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384857685/BBFA286A722F4FD50AB0DADDBF343A6CBC9EB79F/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11476891187215628852/91F18B14DEB61395ADFE0900F2CB5C658414C7F6/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541173465/8193FD1F0862270CD23BA9243679840BE6324DC6/", "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541173570/3C100E92F0DEDEF117D3A0392D3271951BC90DD0/" }, - "points": 20, + "points": 14, "restrictions": { "include": { "unit": ["Rebel Commandos"] @@ -2342,8 +2387,7 @@ "include": { "unit": ["Scout Troopers"] } - }, - "additionalObjects": ["Proton Charge Token"] + } }, { "name": "T-21 Stormtrooper", From efc7ce4bf940c1f1485b3f3ef3e3136cf58e9225 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 22 Apr 2026 20:43:41 -0400 Subject: [PATCH 65/93] GAR Changes --- contrib/cards/Republic/RepublicUnits.json | 144 ++++++++++++++++++---- contrib/cards/official.json | 64 +++++++++- 2 files changed, 181 insertions(+), 27 deletions(-) diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index e268f142..9a5d76e6 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -1,5 +1,55 @@ { "Commander": [ + { + "name": "Ki Adi Mundi", + "title": "Jedi Council", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11929402197203734988/9CF2A7578A3EB20BA278864950D163A26901C4F5/", + "size": "small", + "type": "Trooper", + "points": 0, + "speed": 2, + "upgrades": {}, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/12461470789103203864/DACD5524454E91F35C586DDEABAD420ED4E1FF46/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/17297173129004280551/384F67D13FCA3EBA539BD8D3818F5A0E5A8F2A0B/" + } + ], + "commands": [ + { + "name": "Force of Will", + "image": "https://steamusercontent-a.akamaihd.net/ugc/18170241117176866803/0C12349B15161AD068F2BFD1AB13681EB31255FD/", + "pip": 3 + } + ] + }, + { + "name": "Mace Windu", + "title": "Jedi Council", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12731488793753419264/4F069455F4652D7D87B92F26C1EB7C0888C7D48E/", + "size": "small", + "type": "Trooper", + "points": 165, + "speed": 2, + "upgrades": { + "Command": 1, + "Force": 3, + "Training": 2 + }, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/12925628276853873664/6E5E101942F74FC183F7154481D3EB5CFEAAAD65/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/18287494378041429592/02BB80E1E8A06309E2424A0AD83A7A68E9A42FD6/" + } + ], + "commands": [ + { + "name": "This Party's Over", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11977462417526561880/68CB1C0EABB464F67AF434BE84666B6FF126E6CD/", + "pip": 1 + } + ] + }, { "name": "Ahsoka Tano", "title": "Padawan Commander", @@ -323,6 +373,52 @@ { "content": "contrib/cards/ShadowCollective/HondoUnit.json" }, + { + "name": "Shaak Ti", + "title": "Jedi Council", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17555033359148455110/D2F1B99C8AB43C8B73071DAED15D378AC38F65C5/", + "size": "small", + "type": "Trooper", + "points": 0, + "speed": 2, + "upgrades": {}, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/18026057114183857483/0D959FB4A356818AA1FA6D8034166916BB6EA84B/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9607653251760381402/E7F70FBA0C514FFA35DB2213F1F958835192D473/" + } + ], + "commands": [ + { + "name": "Unity Wins Wars", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17101391156956704931/3299D62F86B634A6850085F40EA1DD60DC3BDEDE/", + "pip": 3 + } + ] + }, + { + "name": "Plo Koon", + "title": "Jedi Council", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11338358295243396609/72E584709E6C16445AFBDB6820E58449092839EF/", + "size": "small", + "type": "Trooper", + "points": 0, + "speed": 2, + "upgrades": {}, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/10428072234539757120/3154697B7EB8A5076E1CB4C312068560C7B3DE19/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16082411658181568841/AC55908D9B799DAEE652A7505DD8E06857464614/" + } + ], + "commands": [ + { + "name": "I Don't Believe in Chance", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15864173543972429126/27FCE434351A715B9DDDC4B033C3DE79B0260000/", + "pip": 2 + } + ] + }, { "name": "Jedi Knight", "title": "Keeper of the Peace", @@ -563,30 +659,6 @@ } ] }, - { - "name": "Arc Troopers", - "title": "Strike Team", - "displayName": "Arc Troopers (Strike Team)", - "heavyWeaponTeam": true, - "image": "https://steamusercontent-a.akamaihd.net/ugc/2495640747384861099/0BF691267928341734741CDF04AAE59A41E5E20B/", - "size": "small", - "type": "Clone Trooper", - "points": 24, - "speed": 2, - "upgrades": { - "Heavy Weapon": 1, - "Training": 1, - "Comms": 1, - "Gear": 2, - "Grenades": 1 - }, - "minis": [ - { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457802621/01F605D855048F823A0ECA6C46D2A62B56C04124/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/773995200348530664/5E248091F8CC37B1023257338D4947E4ADFE48AB/" - } - ] - }, { "name": "ARF Troopers", "image": "https://steamusercontent-a.akamaihd.net/ugc/59215140638617511/EF41D3B9F89F86EC4E803863C17D1549052C8B55/", @@ -714,6 +786,30 @@ } ], "Support": [ + { + "name": "Arc Troopers", + "title": "Strike Team", + "displayName": "Arc Troopers (Strike Team)", + "heavyWeaponTeam": true, + "image": "https://steamusercontent-a.akamaihd.net/ugc/11640734367175082150/31600D0C168D48D5E353E7E0A6D60C4935531D62/", + "size": "small", + "type": "Clone Trooper", + "points": 23, + "speed": 2, + "upgrades": { + "Heavy Weapon": 1, + "Training": 1, + "Comms": 1, + "Gear": 2, + "Grenades": 1 + }, + "minis": [ + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457802621/01F605D855048F823A0ECA6C46D2A62B56C04124/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/773995200348530664/5E248091F8CC37B1023257338D4947E4ADFE48AB/" + } + ] + }, { "name": "BARC Speeder", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385135357592/ED3AA636CBB456AA395C1694D27B84A296DD406B/", diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 9d9e7fb7..76eca2ce 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1926,13 +1926,27 @@ } }, { - "name": "DC-15X ARC Trooper", - "image": "https://steamusercontent-a.akamaihd.net/ugc/9724672671096889082/B22903B590FA5B6D2AAEC2988AA0F9826C9D1EB2/", + "name": "DC-15X ARC Trooper Sniper", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9340306715371279306/77343FF20A60FEBDE4596D9E73806219B98BFB74/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457800878/34EC84D7ECD99E073417866A9BEC4288E00F5359/", "secondary": "http://cloud-3.steamusercontent.com/ugc/773995200348530664/5E248091F8CC37B1023257338D4947E4ADFE48AB/" }, - "points": 37, + "points": 26, + "restrictions": { + "include": { + "unit": ["ARC Troopers"] + } + } + }, + { + "name": "DC-15X ARC Trooper Gunner", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15700525996033034674/C04F340906CF0532B3825D5C67357C094FA10B29/", + "mini": { + "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457800878/34EC84D7ECD99E073417866A9BEC4288E00F5359/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/773995200348530664/5E248091F8CC37B1023257338D4947E4ADFE48AB/" + }, + "points": 26, "restrictions": { "include": { "unit": ["ARC Troopers"] @@ -2812,6 +2826,35 @@ } ], "Squad Leader": [ + { + "name": "Obi-Wan Kenobi", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16739187061396312294/0BFA142DE447B3EAFC7447BBDBE40BB10C51A844/", + "mini": { + "mesh": "http://cloud-3.steamusercontent.com/ugc/785236717873394134/94407058FBA842AD0FDCC0BBB8AB613EA9A739C2/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/785236717873394267/272C1C9A8C15C36DC5C10DD62D5EFCEF9BFF4496/" + }, + "points": 25, + "leader": true, + "restrictions": { + "include": { + "unit": ["ARF Troopers"] + } + } + }, + { + "name": "Clone Commander Cody", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16353428824806788196/B2AEE50E55B08A53411690988BCE579A7EBFE4A8/", + "mini": { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2174736446813135529/6A6C874DBCA77E800A77C20FD36C2F223661CD10/" + }, + "points": 28, + "leader": true, + "restrictions": { + "include": { + "unit": ["ARF Troopers", "Clone Trooper Marksmen"] + } + } + }, { "name": "Clone Captain Rex", "image": "https://steamusercontent-a.akamaihd.net/ugc/17837568269244781354/A462DDBE46166711CD3E69DD3F6F6947D25AEF3F/", @@ -2963,6 +3006,21 @@ } ], "Personnel": [ + { + "name": "Shaak Ti", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12779783962834644042/1D8FF74F712A51FB40C0B25B0EE6DC5183FD8D60/", + "mini": { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/18026057114183857483/0D959FB4A356818AA1FA6D8034166916BB6EA84B/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9607653251760381402/E7F70FBA0C514FFA35DB2213F1F958835192D473/" + }, + "points": 22, + "restrictions": { + "include": { + "faction": ["Republic"] + } + }, + "leader": true + }, { "name": "Weequay Pirate Squad", "image": "https://steamusercontent-a.akamaihd.net/ugc/17099305818157449093/AB25320EE6C23F1CFA10608400AA0881A83EFCB4/", From c60dcfac070d9fcd30ea71e5fbed5af1a84d9c98 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 22 Apr 2026 21:01:58 -0400 Subject: [PATCH 66/93] CIS Updates --- contrib/cards/Separatist/SeparatistUnits.json | 89 +++++++++++-------- contrib/cards/official.json | 38 +++++++- 2 files changed, 86 insertions(+), 41 deletions(-) diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index 59e173cd..49740196 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -325,6 +325,29 @@ } ], "Operative": [ + { + "name": "Savage Opress", + "title": "Dooku's Apprentice", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13168414506047365764/A8CEC8BB31ACD5D20F2472F1FE2A1C0BE1179F34/", + "size": "small", + "type": "Trooper", + "points": 0, + "speed": 2, + "upgrades": {}, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/11082776058831308047/3A974FCF53960CCBBC537A4134AD12242D4DDF89/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/17875535680143582051/1C009264C71FD35359145238DFA2CBD5D1872CD4/" + } + ], + "commands": [ + { + "name": "We Shall Start With Revenge", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10750064270635517385/492A552BB4056A4CBF9CD01FB5195521104C9F9E/", + "pip": 1 + } + ] + }, { "name": "Super Tactical Command Droid", "title": "Auxiliary Command Droid", @@ -607,30 +630,6 @@ } ] }, - { - "name": "BX-Series Droid Commandos", - "title": "Strike Team", - "displayName": "BX-Series Commandos (Strike Team)", - "heavyWeaponTeam": true, - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131430807/F260EA4E79ACB29C3C635836A3EF7E7C632E4FB6/", - "size": "small", - "type": "Droid Trooper", - "points": 20, - "speed": 2, - "upgrades": { - "Heavy Weapon": 1, - "Training": 1, - "Comms": 1, - "Armament": 1, - "Grenades": 1 - }, - "minis": [ - { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457847081/F7D4E8D3EC85BAEEB5C1B5B913B7F484CC28FC10/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/773995528924546789/8774CBBDC3C4F18B11A444CC96EA5FE7F00DB48F/" - } - ] - }, { "name": "DRK-1 Sith Probe Droids", "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432071/495850F5BE28D1D1551D13D5B86F473D8EB154EC/", @@ -744,20 +743,40 @@ } ], "Support": [ + { + "name": "BX-Series Droid Commandos", + "title": "Strike Team", + "displayName": "BX-Series Commandos (Strike Team)", + "heavyWeaponTeam": true, + "image": "https://steamusercontent-a.akamaihd.net/ugc/9315317574108774179/CDF58FFA2BD16DDB0B5DEFDBC073AF2AE2D91F65/", + "size": "small", + "type": "Droid Trooper", + "points": 20, + "speed": 2, + "upgrades": { + "Heavy Weapon": 1, + "Training": 1, + "Comms": 1, + "Armament": 1, + "Grenades": 1 + }, + "minis": [ + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457847081/F7D4E8D3EC85BAEEB5C1B5B913B7F484CC28FC10/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/773995528924546789/8774CBBDC3C4F18B11A444CC96EA5FE7F00DB48F/" + } + ] + }, { "name": "Droidekas", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131432109/A0B45534B85F46D38A4F372B04041A5193B31426/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17494420314363813844/3FE04FCFAF3815AA2DE9E9C52101B5038AB6F892/", "size": "medium", - "type": "Ground Vehicle", - "points": 60, + "type": "Emplacement Trooper", + "points": 67, "speed": 1, "upgrades": { "Comms": 1 }, - "silhouette": { - "height": 1.85, - "offset": 0.0 - }, "minis": [ { "bundle": "http://cloud-3.steamusercontent.com/ugc/1686023445067168845/FBADA8EDE2ED248CA7F50C1DB3BFF46215E0360B/", @@ -914,19 +933,15 @@ }, { "name": "Aqua Droids", - "image": "https://steamusercontent-a.akamaihd.net/ugc/1794100632571587/D3308B09BB6167322BFFB83C1A7E420C431D2396/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15835541998063761576/AFCAE9EC6C8FD26B11D7E2D3391F8242C7D4CFF0/", "size": "medium", - "type": "Ground Vehicle", + "type": "Emplacement Trooper", "points": 65, "speed": 1, "upgrades": { "Heavy Weapon": 1, "Comms": 1 }, - "silhouette": { - "height": 1.85, - "offset": 0.0 - }, "minis": [ { "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16428277771257521/99A8ACEC9864DE29C7CE97006E86248BB2A89554/", diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 76eca2ce..9f2f106b 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1556,6 +1556,36 @@ } ], "Heavy Weapon": [ + { + "name": "Assajj Ventress", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15926262740421549818/7F23F097083233A0C9348C3215BF09DB072C58E6/", + "mini": { + "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780869167436/AAA2C61A17BB43F2673AF16D30BD0478B9AAAD2A/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780869167571/EF86CBDA5F361282AD077D61FA81ABF0FEE061A0/" + }, + "leader": true, + "points": 26, + "restrictions": { + "include": { + "unit": ["B1 Battle Droids", "BX-Series Droid Commandos"] + } + } + }, + { + "name": "General Grievous", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13889602850013621809/37188B84F5B6D6E49F5A9C62942D33562B65CF9E/", + "mini": { + "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780869167436/AAA2C61A17BB43F2673AF16D30BD0478B9AAAD2A/", + "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780869167571/EF86CBDA5F361282AD077D61FA81ABF0FEE061A0/" + }, + "leader": true, + "points": 36, + "restrictions": { + "include": { + "unit": ["B1 Battle Droids", "IG-100 MagnaGuard"] + } + } + }, { "name": "Luke Skywalker", "image": "https://steamusercontent-a.akamaihd.net/ugc/10466047961901782395/2898B46094ACFC7D44564F3E1B650BC01D8FA0C9/", @@ -1830,12 +1860,12 @@ }, { "name": "BX-Series Droid Sniper", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927410/552314F29830D4A8A63F5B8238699336F17ECD92/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11012640175296329862/0E3952E994DB7BDA80F0614E3539909EAEC0049D/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457847963/0EA6BE014DA494689FF32F9570C6A73CD88F1894/", "secondary": "http://cloud-3.steamusercontent.com/ugc/773995528924546789/8774CBBDC3C4F18B11A444CC96EA5FE7F00DB48F/" }, - "points": 40, + "points": 20, "restrictions": { "include": { "unit": ["BX-Series Droid Commandos"] @@ -2073,12 +2103,12 @@ }, { "name": "Dioxis Mine Saboteur", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927460/BE9022FA696C172E68110895A9DDCDDD635E2AB3/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16211846897932945944/4A7D6789318AB8E685AF487F02231CC291248FAB/", "mini": { "bundle": "http://cloud-3.steamusercontent.com/ugc/1009315641457847567/079157D001A1520085C6A30620750817C6C01BFF/", "secondary": "http://cloud-3.steamusercontent.com/ugc/773995528924546789/8774CBBDC3C4F18B11A444CC96EA5FE7F00DB48F/" }, - "points": 28, + "points": 20, "restrictions": { "include": { "unit": ["BX-Series Droid Commandos"] From 20762095a20bdb2a242dbf8a250990139b8cb545 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 22 Apr 2026 21:09:09 -0400 Subject: [PATCH 67/93] SC updates --- .../ShadowCollectiveUnits.json | 23 +++++++++++++ contrib/cards/official.json | 32 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json b/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json index 7ae50cfd..79dda81c 100644 --- a/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json +++ b/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json @@ -97,6 +97,29 @@ } ] }, + { + "name": "Savage Opress", + "title": "Maul's Enforcer", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10520678764152114554/0350C3463ADCAF50D1936AC3893348C12F0A5A62/", + "size": "small", + "type": "Trooper", + "points": 0, + "speed": 2, + "upgrades": {}, + "minis": [ + { + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/11082776058831308047/3A974FCF53960CCBBC537A4134AD12242D4DDF89/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/17875535680143582051/1C009264C71FD35359145238DFA2CBD5D1872CD4/" + } + ], + "commands": [ + { + "name": "We Shall Start With Revenge", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10750064270635517385/492A552BB4056A4CBF9CD01FB5195521104C9F9E/", + "pip": 1 + } + ] + }, { "content": "contrib/cards/ShadowCollective/CadBaneUnit.json" }, diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 9f2f106b..0e527921 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -3036,6 +3036,38 @@ } ], "Personnel": [ + { + "name": "Pyke Syndicate Foot Soldier Squad", + "image": "https://steamusercontent-a.akamaihd.net/ugc/14983704573819999353/9B2E64176C2D5134C74D77CBC58E19E05B68481C/", + "minis": [ + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/1745728387706845646/BE0DEED782F96BD44C389DEDEA41EBEA0C7A6D04/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143543/CBAC94E0D1731CB8D1034D24F58987E854A8596A/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/1745728387706845697/15B6DCCA8D8D76E1CB78219C58790E40DDD7CE8A/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143543/CBAC94E0D1731CB8D1034D24F58987E854A8596A/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/1745728387706845389/0D7DFE0C5BF5CDD527A619B75A25A2177D417BD0/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143543/CBAC94E0D1731CB8D1034D24F58987E854A8596A/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/1745728387706845389/0D7DFE0C5BF5CDD527A619B75A25A2177D417BD0/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143543/CBAC94E0D1731CB8D1034D24F58987E854A8596A/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/1745728387706845389/0D7DFE0C5BF5CDD527A619B75A25A2177D417BD0/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143543/CBAC94E0D1731CB8D1034D24F58987E854A8596A/" + } + ], + "points": 38, + "restrictions": { + "include": { + "unit": ["Pyke Syndicate Foot Soldiers"] + } + } + }, { "name": "Shaak Ti", "image": "https://steamusercontent-a.akamaihd.net/ugc/12779783962834644042/1D8FF74F712A51FB40C0B25B0EE6DC5183FD8D60/", From 0417f4b92156039eaebb68e05e7f27e36302adb2 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 22 Apr 2026 21:12:44 -0400 Subject: [PATCH 68/93] Welcome Screen --- contrib/cards/Rebel/RebelUnits.json | 3 ++- mod/src/includes/ui/Welcome.xml | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index ee07950c..52952d09 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -530,7 +530,8 @@ }, "minis": [ { - "bundle": "http://cloud-3.steamusercontent.com/ugc/2174736446809186599/4B154E18EF2C0ADE6C347FC4B15AD856D00546DF/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/14052244614810856390/56285EFFD6CBC4799ADAA95830EB2F10E4120F69/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15565584712668003223/F742EEF3DC4CAE87C9E9D5974891C2A445C48649/" } ], "commands": [ diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 89df1e22..3c9abccd 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -37,7 +37,7 @@ - Welcome to Star Wars: Legion for TTS, v8.0.16-beta. + Welcome to Star Wars: Legion for TTS, v8.0.17-beta. This is a beta release . @@ -46,9 +46,11 @@ CHANGELOG (Since last Beta) - - Add: Imperial High Command - - Add: Weequay Pirates - - Fix: WLO Tank Spawns in SC + - Add: Jedi Council + - Add: New April Upgrades + - Add: Strike Team Changes + - Add: Savage Opress + - Fix: Logray, Sabine, Wicket Models From 08e4ad8a810220ffe51a2998dccdfdc983a4a93a Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 22 Apr 2026 21:29:16 -0400 Subject: [PATCH 69/93] inq fix --- contrib/cards/Republic/RepublicUnits.json | 2 +- contrib/cards/official.json | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index 9a5d76e6..99f61bd2 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -1,7 +1,7 @@ { "Commander": [ { - "name": "Ki Adi Mundi", + "name": "Ki-Adi-Mundi", "title": "Jedi Council", "image": "https://steamusercontent-a.akamaihd.net/ugc/11929402197203734988/9CF2A7578A3EB20BA278864950D163A26901C4F5/", "size": "small", diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 0e527921..f3e30bb9 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1650,7 +1650,7 @@ "name": "Seventh Sister", "image": "https://steamusercontent-a.akamaihd.net/ugc/12786414084835381664/A059896EB0382A23E79AC5B4A93BB1B80CB47DA8/", "mini": { - "bundle": "https://steamusercontent-a.akamaihd.net/ugc/10804385137011773/5377A3D119B96061C7EFAF2F8469BD5E889F3F13/" + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/2446090001278329724/E8E229E0E3FB6230D9F5A1823C71032A0FC9F2D0/" }, "leader": true, "points": 30, @@ -1664,7 +1664,7 @@ "name": "Fifth Brother", "image": "https://steamusercontent-a.akamaihd.net/ugc/14099995001149120961/E9E1ACB414FFC071E6B08B9F268040166D5D5C35/", "mini": { - "bundle": "steamusercontent-a.akamaihd.net/ugc/2456221571853672166/B86F663B609267644D837B4F18C59614C6785229/" + "bundle": "https://steamusercontent-a.akamaihd.net/ugc/2456221571853672166/B86F663B609267644D837B4F18C59614C6785229/" }, "leader": true, "points": 32, @@ -4948,6 +4948,11 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/17800273845434794252/84425184A9FA3237F85342B84FD168824FBF90C8/", "pip": 1 }, + { + "name": "A Disturbance in the Force", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15573825798028976140/1303767C2A7775954093FE0105F0E9B2985AA64B/", + "pip": 2 + }, { "name": "Air Support", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385134030778/90E5CEF9833FED1C5F926FB8878BDBCD5B5099F6/", From e4480afa0e0e7730ee35680224acbbae8207e45c Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 22 Apr 2026 21:34:34 -0400 Subject: [PATCH 70/93] Update official.json --- contrib/cards/official.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index f3e30bb9..1c6fe01b 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1557,7 +1557,7 @@ ], "Heavy Weapon": [ { - "name": "Assajj Ventress", + "name": "Asajj Ventress", "image": "https://steamusercontent-a.akamaihd.net/ugc/15926262740421549818/7F23F097083233A0C9348C3215BF09DB072C58E6/", "mini": { "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780869167436/AAA2C61A17BB43F2673AF16D30BD0478B9AAAD2A/", From 6deaf7cb6127c72a336e421497358908a2aec3d1 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 22 Apr 2026 21:37:20 -0400 Subject: [PATCH 71/93] Update official.json --- contrib/cards/official.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 1c6fe01b..70101b98 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1560,8 +1560,7 @@ "name": "Asajj Ventress", "image": "https://steamusercontent-a.akamaihd.net/ugc/15926262740421549818/7F23F097083233A0C9348C3215BF09DB072C58E6/", "mini": { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780869167436/AAA2C61A17BB43F2673AF16D30BD0478B9AAAD2A/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780869167571/EF86CBDA5F361282AD077D61FA81ABF0FEE061A0/" + "bundle": "http://cloud-3.steamusercontent.com/ugc/2022718494181455957/4BB3E42AA412A7ED2ED6F26BFDFA25414F127B71/" }, "leader": true, "points": 26, From 42ed899012852412ad392459c16dcfe888a11251 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 23 Apr 2026 20:52:41 -0400 Subject: [PATCH 72/93] Improv and Arm Alphabetical From b4430646480362790d34cf82e6dd429e7fe2bde0 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 23 Apr 2026 20:53:25 -0400 Subject: [PATCH 73/93] improv and arm alphabetical --- contrib/cards/official.json | 294 ++++++++++++++++++------------------ 1 file changed, 147 insertions(+), 147 deletions(-) diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 70101b98..3b809e7e 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -10,204 +10,198 @@ "upgrades": { "Armament": [ { - "name": "Heavy Arm Cannon", - "image": "https://steamusercontent-a.akamaihd.net/ugc/9491942057538415219/C158C02B7135405F2EC1E5B21BC2FB09876E21F8/", - "points": 20, - "restrictions": { - "include": { - "unit": ["Super Tactical Command Droid"] - } - } - }, - { - "name": "Electrostaff", - "image": "https://steamusercontent-a.akamaihd.net/ugc/18437389315593431740/A0F53051C57B1167C6C2A631755BD8D4F0AFCB9B/", - "points": 10, + "name": "A-180 Pistol Config", + "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137779/795B34FF0E53DA5355EA7952A034C6A4CFD003D0/", + "flip": { + "name": "A-180 Rifle Config", + "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137807/208481F3DC8BE18ED10D2A17F12F35869213235C/" + }, + "points": 0, "restrictions": { "include": { - "unit": ["Super Tactical Command Droid"] + "unit": ["Jyn Erso"] } } }, { - "name": "Twin Lightsabers", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10644823733316041135/289BDD492BB3E295276192F60944E251986DAAAF/", + "name": "A-300 Long Range Config", + "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137867/156C35C00A49AC23AE84E5ACCF35ADD6D7CBD820/", + "flip": { + "name": "A-300 Short Range Config", + "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137938/8B2FA2C71505CA93AB086154658132C221BEBE1D/" + }, "points": 0, "restrictions": { "include": { - "unit": ["Jedi Knight Genral", "Jedi Knight"] + "unit": ["Rebel Pathfinders"] } } }, { - "name": "Lightsaber", - "image": "https://steamusercontent-a.akamaihd.net/ugc/9358476422022162198/D59B90B25355B223E8D91DF23E233DFCB591B3F7/", + "name": "A280, Pistol Config", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081077/A5B040BCD34352C6E5375640DE539E526067DBFE/", + "flip": { + "name": "A280, Rifle Config", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081159/19C93D73F4F86E9D7531E2CD784F5492772ED28F/" + }, "points": 0, "restrictions": { "include": { - "unit": ["Jedi Knight Genral", "Jedi Knight"] + "unit": ["Cassian Andor"] } } }, { - "name": "Double-Bladed Lightsaber", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10386689818644119201/B5A0C4C02D76641FFCF96A105E27DFE48C9DEDA9/", - "points": 0, + "name": "Beskar Spear", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148639488/EDE8AA68D0589BFB956D68E2A08B31715C5F9D61/", + "points": 15, "restrictions": { "include": { - "unit": ["Jedi Knight Genral", "Jedi Knight"] + "unit": ["Din Djarin"] } } }, { - "name": "Heavy Blaster Pistol (Rebel)", - "image": "https://steamusercontent-a.akamaihd.net/ugc/16982319690732285379/8FA680F434D4EF30BB0DCDB8835BABFB8C90B16F/", - "points": 5, + "name": "BX Deflector Shields", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927385/CD3E586656B39D35246C84BAFD6ADB12B665E5E5/", + "points": 18, "restrictions": { "include": { - "unit": ["Rebel Officer", "Rebel Agent"] + "unit": ["BX-Series Droid Commandos"] } } }, { - "name": "Repeating Blaster", - "image": "https://steamusercontent-a.akamaihd.net/ugc/13484211657005252288/0AFB1BBC7DA74BA6713EDCA6869C28E2522C7128/", - "points": 25, + "name": "BX Vibroswords", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927392/0B4491CA615FAA34F7EFEDCE08BAD5A06571BE90/", + "points": 6, "restrictions": { "include": { - "unit": ["Rebel Officer", "Rebel Agent"] + "unit": ["BX-Series Droid Commandos"] } } }, { - "name": "Vibro Axe", - "image": "https://steamusercontent-a.akamaihd.net/ugc/12824619755788191989/83D29D8C052D145897BAEF171074D066C4CB143A/", - "points": 8, + "name": "CR-24 Flame Rifle", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140106922/DC1ED64816ECD4A7AB128F05D70EF97408305AAF/", + "points": 10, "restrictions": { "include": { - "unit": ["Rebel Officer", "Rebel Agent"] + "unit": ["Dewback Rider"] } } }, { - "name": "Heavy Blaster Pistol (Empire)", - "image": "https://steamusercontent-a.akamaihd.net/ugc/16148048905111619924/4146F9FBD1CE36B93A82B0403DB3E956510A7271/", - "points": 5, + "name": "DC-17m ICWS Config", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385135385953/007D0426E485963714BCBE0B74B2B8AE5B7F1869/", + "flip": { + "name": "DC-17m ICWS Config", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385135386003/EB0729A53546B70CB9054BAF5306E3F5368FC9DF/" + }, + "points": 0, "restrictions": { "include": { - "unit": ["Imperial Officer", "Imperial Agent"] + "unit": ["Clone Commandos", "Clone Commandos Delta Squad"] } } }, { - "name": "Stun Baton", - "image": "https://steamusercontent-a.akamaihd.net/ugc/11788115204668483271/6494041D37500BD80CE8CB640CC058D4CDB7C5DD/", - "points": 8, + "name": "Double-Bladed Lightsaber", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10386689818644119201/B5A0C4C02D76641FFCF96A105E27DFE48C9DEDA9/", + "points": 0, "restrictions": { "include": { - "unit": ["Imperial Officer", "Imperial Agent"] + "unit": ["Jedi Knight Genral", "Jedi Knight"] } } }, { - "name": "Z-6 Riot Baton", - "image": "https://steamusercontent-a.akamaihd.net/ugc/11921155978538693026/3426025D9984B223902084BA7DA742C27DEC0D63/", - "points": 8, + "name": "Din's Amban Rifle", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148639183/C2D75F184A5F66EA154C4DF44AADA7A9443A9468/", + "points": 10, "restrictions": { "include": { - "unit": ["Imperial Officer", "Imperial Agent"] + "unit": ["Din Djarin"] } } }, { - "name": "A-180 Rifle Config", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137807/208481F3DC8BE18ED10D2A17F12F35869213235C/", - "flip": { - "name": "A-180 Pistol Config", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137779/795B34FF0E53DA5355EA7952A034C6A4CFD003D0/" - }, - "points": 0, + "name": "Electro Gauntlets", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148639059/89380FD599DD6819950BB21C5F940A9074C34A51/", + "points": 10, "restrictions": { "include": { - "unit": ["Jyn Erso"] + "unit": ["Cad Bane"] } } }, { - "name": "A-300 Long Range Config", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137867/156C35C00A49AC23AE84E5ACCF35ADD6D7CBD820/", - "flip": { - "name": "A-300 Short Range Config", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568137938/8B2FA2C71505CA93AB086154658132C221BEBE1D/" - }, - "points": 0, + "name": "Electrostaff", + "image": "https://steamusercontent-a.akamaihd.net/ugc/18437389315593431740/A0F53051C57B1167C6C2A631755BD8D4F0AFCB9B/", + "points": 10, "restrictions": { "include": { - "unit": ["Rebel Pathfinders"] + "unit": ["Super Tactical Command Droid"] } } }, { - "name": "A280, Rifle Config", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081159/19C93D73F4F86E9D7531E2CD784F5492772ED28F/", + "name": "E-11D Focused Fire Config", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140103278/AC9A32D518E9E70AF6ABBA41F7E10DF58DADFF20/", "flip": { - "name": "A280, Pistol Config", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140081077/A5B040BCD34352C6E5375640DE539E526067DBFE/" + "name": "E-11D Grenade Launcher Config", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140103366/FB39C5636706702276566978A5EDD34907C1D8DB/" }, "points": 0, "restrictions": { "include": { - "unit": ["Cassian Andor"] + "unit": ["Imperial Death Troopers"] } } }, { - "name": "CR-24 Flame Rifle", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140106922/DC1ED64816ECD4A7AB128F05D70EF97408305AAF/", - "points": 10, + "name": "Heavy Arm Cannon", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9491942057538415219/C158C02B7135405F2EC1E5B21BC2FB09876E21F8/", + "points": 20, "restrictions": { "include": { - "unit": ["Dewback Rider"] + "unit": ["Super Tactical Command Droid"] } } }, { - "name": "BX Deflector Shields", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927385/CD3E586656B39D35246C84BAFD6ADB12B665E5E5/", - "points": 18, + "name": "Heavy Blaster Pistol (Empire)", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16148048905111619924/4146F9FBD1CE36B93A82B0403DB3E956510A7271/", + "points": 5, "restrictions": { "include": { - "unit": ["BX-Series Droid Commandos"] + "unit": ["Imperial Officer", "Imperial Agent"] } } }, { - "name": "E-11D Focused Fire Config", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140103278/AC9A32D518E9E70AF6ABBA41F7E10DF58DADFF20/", - "flip": { - "name": "E-11D Grenade Launcher Config", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140103366/FB39C5636706702276566978A5EDD34907C1D8DB/" - }, - "points": 0, + "name": "Heavy Blaster Pistol (Rebel)", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16982319690732285379/8FA680F434D4EF30BB0DCDB8835BABFB8C90B16F/", + "points": 5, "restrictions": { "include": { - "unit": ["Imperial Death Troopers"] + "unit": ["Rebel Officer", "Rebel Agent"] } } }, { - "name": "Electro Gauntlets", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148639059/89380FD599DD6819950BB21C5F940A9074C34A51/", - "points": 10, + "name": "Iden's DLT-20A Rifle", + "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568142679/5583B4B2E96BA55F3A4D2DE46EBF31B130AAA757/", + "points": 15, "restrictions": { "include": { - "unit": ["Cad Bane"] + "unit": ["Iden Versio"] } } }, { - "name": "Iden's DLT-20A Rifle", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568142679/5583B4B2E96BA55F3A4D2DE46EBF31B130AAA757/", + "name": "Iden's TL-50 Repeater", + "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568142734/E13169C3DF9153C7196A29DB82578DE1FD659F6D/", "points": 15, "restrictions": { "include": { @@ -249,6 +243,16 @@ } } }, + { + "name": "Lightsaber", + "image": "https://steamusercontent-a.akamaihd.net/ugc/9358476422022162198/D59B90B25355B223E8D91DF23E233DFCB591B3F7/", + "points": 0, + "restrictions": { + "include": { + "unit": ["Jedi Knight Genral", "Jedi Knight"] + } + } + }, { "name": "Looted E-5 Blaster", "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568143554/AAE0C21ACF5E130BCCD15B21BDC1027613DC26BA/", @@ -260,18 +264,18 @@ } }, { - "name": "RT-97C Blaster Rifle", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140107008/1524B081B7155F22B18F85E7789813478AE6EA3C/", - "points": 5, + "name": "Repeating Blaster", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13484211657005252288/0AFB1BBC7DA74BA6713EDCA6869C28E2522C7128/", + "points": 25, "restrictions": { "include": { - "unit": ["Dewback Rider"] + "unit": ["Rebel Officer", "Rebel Agent"] } } }, { - "name": "T-21 Blaster Rifle", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140107073/9B787B4695966A18D0659B28A979EBEFFA1C29BD/", + "name": "RT-97C Blaster Rifle", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140107008/1524B081B7155F22B18F85E7789813478AE6EA3C/", "points": 5, "restrictions": { "include": { @@ -280,126 +284,122 @@ } }, { - "name": "Iden's TL-50 Repeater", - "image": "http://cloud-3.steamusercontent.com/ugc/1680393865568142734/E13169C3DF9153C7196A29DB82578DE1FD659F6D/", + "name": "Saxon's Galar-90 Rifle", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638360/9640DCEEFC8C952C5828F120F9527886B6AD1AEB/", "points": 15, "restrictions": { "include": { - "unit": ["Iden Versio"] + "unit": ["Gar Saxon"] } } }, { - "name": "The Darksaber", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140090412/816EE4F5B469EC8334D0E84042E5C008FC67B04B/", - "points": 15, + "name": "Saxon's Jetpack Rockets", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638324/2B2554057A17324B946ADF61B44CFB94081A770D/", + "points": 10, "restrictions": { "include": { - "unit": ["Sabine Wren"] + "unit": ["Gar Saxon"] } } }, { - "name": "BX Vibroswords", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081139927392/0B4491CA615FAA34F7EFEDCE08BAD5A06571BE90/", - "points": 6, + "name": "Saxon's ZX Flame Projector", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638292/3120A41DA39C3D76C2B2584CB20390E4D8FE90D4/", + "points": 5, "restrictions": { "include": { - "unit": ["BX-Series Droid Commandos"] + "unit": ["Gar Saxon"] } } }, { - "name": "Saxon's Jetpack Rockets", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638324/2B2554057A17324B946ADF61B44CFB94081A770D/", - "points": 10, + "name": "Stun Baton", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11788115204668483271/6494041D37500BD80CE8CB640CC058D4CDB7C5DD/", + "points": 8, "restrictions": { "include": { - "unit": ["Gar Saxon"] + "unit": ["Imperial Officer", "Imperial Agent"] } } }, { - "name": "Saxon's Galar-90 Rifle", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638360/9640DCEEFC8C952C5828F120F9527886B6AD1AEB/", - "points": 15, + "name": "Super Commando Jetpack Rockets", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638126/810C46F8A1B873A7CFFBDC1DDDEE400DFC3AF388/", + "points": 8, "restrictions": { "include": { - "unit": ["Gar Saxon"] + "unit": ["Mandalorian Super Commandos"] } } }, { - "name": "Saxon's ZX Flame Projector", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638292/3120A41DA39C3D76C2B2584CB20390E4D8FE90D4/", + "name": "T-21 Blaster Rifle", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385140107073/9B787B4695966A18D0659B28A979EBEFFA1C29BD/", "points": 5, "restrictions": { "include": { - "unit": ["Gar Saxon"] + "unit": ["Dewback Rider"] } } }, { - "name": "The Darksaber (Maul)", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148637945/3B49E86E68EBCB65154B12ECD4336463F7400B95/", - "points": 10, + "name": "The Darksaber", + "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140090412/816EE4F5B469EC8334D0E84042E5C008FC67B04B/", + "points": 15, "restrictions": { "include": { - "unit": ["Maul"] + "unit": ["Sabine Wren"] } } }, { - "name": "Super Commando Jetpack Rockets", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638126/810C46F8A1B873A7CFFBDC1DDDEE400DFC3AF388/", - "points": 8, + "name": "The Darksaber (Gideon)", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16625983135926386325/491ADB4AF793015C774CBB05E327DFBB53A59E7B/", + "points": 15, "restrictions": { "include": { - "unit": ["Mandalorian Super Commandos"] + "unit": ["Moff Gideon"] } } }, { - "name": "Din's Amban Rifle", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148639183/C2D75F184A5F66EA154C4DF44AADA7A9443A9468/", + "name": "The Darksaber (Maul)", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148637945/3B49E86E68EBCB65154B12ECD4336463F7400B95/", "points": 10, "restrictions": { "include": { - "unit": ["Din Djarin"] + "unit": ["Maul"] } } }, { - "name": "Beskar Spear", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148639488/EDE8AA68D0589BFB956D68E2A08B31715C5F9D61/", - "points": 15, + "name": "Twin Lightsabers", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10644823733316041135/289BDD492BB3E295276192F60944E251986DAAAF/", + "points": 0, "restrictions": { "include": { - "unit": ["Din Djarin"] + "unit": ["Jedi Knight Genral", "Jedi Knight"] } } }, { - "name": "The Darksaber (Gideon)", - "image": "https://steamusercontent-a.akamaihd.net/ugc/16625983135926386325/491ADB4AF793015C774CBB05E327DFBB53A59E7B/", - "points": 15, + "name": "Vibro Axe", + "image": "https://steamusercontent-a.akamaihd.net/ugc/12824619755788191989/83D29D8C052D145897BAEF171074D066C4CB143A/", + "points": 8, "restrictions": { "include": { - "unit": ["Moff Gideon"] + "unit": ["Rebel Officer", "Rebel Agent"] } } }, { - "name": "DC-17m ICWS Config", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385135385953/007D0426E485963714BCBE0B74B2B8AE5B7F1869/", - "flip": { - "name": "DC-17m ICWS Config", - "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385135386003/EB0729A53546B70CB9054BAF5306E3F5368FC9DF/" - }, - "points": 0, + "name": "Z-6 Riot Baton", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11921155978538693026/3426025D9984B223902084BA7DA742C27DEC0D63/", + "points": 8, "restrictions": { "include": { - "unit": ["Clone Commandos", "Clone Commandos Delta Squad"] + "unit": ["Imperial Officer", "Imperial Agent"] } } } @@ -422,7 +422,7 @@ }, { "name": "Improvised Orders", - "image": "https://steamusercontent-a.akamaihd.net/ugc/9869682046426627943/152512DE2EE5592159A7D5A4E9BB7111A0C729B5/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/16226117530699489950/0082F67676066542C5BA5DA1D970DD18CFAFCCA1/", "points": 5 }, { From d349a8c868ae664df314991a71186e91d89b76c1 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 23 Apr 2026 21:03:12 -0400 Subject: [PATCH 74/93] Rebel Commando Change --- contrib/cards/Rebel/RebelUnits.json | 20 ++++++++++---------- contrib/cards/official.json | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index 52952d09..85c3ba24 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -970,20 +970,20 @@ }, "minis": [ { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541173156/D637683F992DD0C4488252BE266EEEA1F26006B0/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541172655/B10522A40482843AF69A3F321BD091B0ECF52C6B/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/17504878929475056902/7D83CFA0BD23993580F90863A33AD5743E8FC778/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9547146995441004401/223F75C5260F31457426962BDE853215FD2E4696/" }, { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541172497/B3E1593143504101920CC6419BE7C2EAEF52993F/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541188141/6E23E29BB42963B94A0B7DFE01C4C49D776D9657/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/11486357574412562426/7547BC81C08E0B15022770FF6FBF33FA66D1DB43/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9789374328584101623/2B3AABFCECA29D3EBA6B87F1D33C1913A3FB3FAE//" }, { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541173253/155BAA01BA51A8EB524DD434A3C512D8C0B3A04B/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541172655/B10522A40482843AF69A3F321BD091B0ECF52C6B/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/17377233370710228946/0A4A965CB67E1E6B4B2A58A0E949D588D41AAD6B/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14988427562404509763/03DF88DCE5809143B7DEA0C3ADD9F756DA86B23E/" }, { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541173354/3483C6FB73C66481BC6CCC398BAB8E796F7EC076/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541188141/6E23E29BB42963B94A0B7DFE01C4C49D776D9657/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16097408999552065934/9F18078D897B967B552FCE7B9D7F198FC50B58E4/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9566804180720822422/B4A75666546AF78CC290D4030754377548983638/" } ] }, @@ -1270,8 +1270,8 @@ }, "minis": [ { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541173156/D637683F992DD0C4488252BE266EEEA1F26006B0/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541172655/B10522A40482843AF69A3F321BD091B0ECF52C6B/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16453620303646761666/4D8DBD4999B75F314799A70F47115CAA5BE658E2/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/17012966922360507699/78F075440D9D93CD87119353FA24E886EEBBAB3A/" } ] }, diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 3b809e7e..604904d7 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -1986,8 +1986,8 @@ "name": "DH-447 Sniper", "image": "https://steamusercontent-a.akamaihd.net/ugc/16916821861583515066/6783DCC4833EAE22DCA9EDB9C8F585AE29BA13A3/", "mini": { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234780862776611/B8222AA4A3B680E969DC106C8A281D11845994EB/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234780862776974/21649563BC0CB84E12C0B249C720919F528596D3/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16764529609771011077/213762EEF0A83B94E394EC2F3F0C200D5A335C58/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/11792523840308175523/31EE56B2C0FEE8CE25150EADE79C5AA483182889/" }, "points": 20, "restrictions": { @@ -2323,8 +2323,8 @@ "name": "Proton Charge Saboteur", "image": "https://steamusercontent-a.akamaihd.net/ugc/11476891187215628852/91F18B14DEB61395ADFE0900F2CB5C658414C7F6/", "mini": { - "mesh": "http://cloud-3.steamusercontent.com/ugc/785234540541173465/8193FD1F0862270CD23BA9243679840BE6324DC6/", - "diffuse": "http://cloud-3.steamusercontent.com/ugc/785234540541173570/3C100E92F0DEDEF117D3A0392D3271951BC90DD0/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/10169115375508355169/2616D215477E25CD2E74B2F11C39CB911461A989/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/14732946777135222961/B81F98352131D2B5ED40D18272103E8E127C9964/" }, "points": 14, "restrictions": { From 12b115eed7af133544fafd8092ba6ac7ad9b54d7 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 23 Apr 2026 21:26:53 -0400 Subject: [PATCH 75/93] BSE new models --- .../ShadowCollective/BlackSunEnforcersUnit.json | 15 ++++++++------- .../cards/ShadowCollective/BlackSunVigoUnit.json | 3 ++- contrib/cards/official.json | 15 ++++++++------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json b/contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json index 4a232376..c5c7e7d9 100644 --- a/contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json +++ b/contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json @@ -13,19 +13,20 @@ }, "minis": [ { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1745728387706845475/01738B0D52E2D9C2870D6E33A2D05ECD12201C85/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/15119947962689322389/8EC382E90223BC184162C54AFF3EDAE1436DA2AA/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/18136160122757222499/97D7733312EE4677B4694F72C21A61FD8365690E/" }, { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1856061112420143724/48B5A35E10B4977E255D889A8FCAEA8C980A11A9/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143328/3D6F3C2E8DFB78B9B748FFC8F339B9C568D7408E/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/11857728936195634014/66FB83A411E5A561A9A5927C0FDEB396DAF6AE20/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/16452722805588228328/EDA05A0B40EFFA898015BE31588B19E6BB72B4A2/" }, { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1856061112420143724/48B5A35E10B4977E255D889A8FCAEA8C980A11A9/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143328/3D6F3C2E8DFB78B9B748FFC8F339B9C568D7408E/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/13538216675182514309/80D3D2926D6723131723E027151D80E807BA1FF4/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15549057142882936018/8E3A2116FFD18C5122767F9038D43FF549248278/" }, { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1856061112420143724/48B5A35E10B4977E255D889A8FCAEA8C980A11A9/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143328/3D6F3C2E8DFB78B9B748FFC8F339B9C568D7408E/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/16839420302039891942/3031489A090A438C10C4B37690E4E5B507DD4913/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/13883212304201737617/9B792D3CE9B23D2354F80B2B159F4FD13728A98F/" } ] } diff --git a/contrib/cards/ShadowCollective/BlackSunVigoUnit.json b/contrib/cards/ShadowCollective/BlackSunVigoUnit.json index 9ed7b4a8..b817a460 100644 --- a/contrib/cards/ShadowCollective/BlackSunVigoUnit.json +++ b/contrib/cards/ShadowCollective/BlackSunVigoUnit.json @@ -11,7 +11,8 @@ }, "minis": [ { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1856061112420143763/E93BA6716A05CA95AE2EA3B7C0DFD8AF145D5C22/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/13653552510521459623/B060AC4F7AE0132A573CF92984494FDF2C4640F2/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9553158029415913253/8761E25E4B155BE4124E8B1A5B922C6941A153A3/" } ] } diff --git a/contrib/cards/official.json b/contrib/cards/official.json index 604904d7..aa0c69d6 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -2631,8 +2631,8 @@ "name": "Mag-Det Enforcer", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638823/D41C1DD1A9B63A173D667167C2CA1E79D0BA2F1E/", "mini": { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1856061112420143649/BBBA0D26FB46EAD96421C4B77FE779B5F0F848AA/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143328/3D6F3C2E8DFB78B9B748FFC8F339B9C568D7408E/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/12110813277322191269/AD4182EA4619D0E4B9DA7F6F12F70BEAFD8D6D02/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/11212068194581458113/33DF944B89A6B7DBC0DE2141A800CCF5950553E6/" }, "points": 16, "restrictions": { @@ -2645,8 +2645,8 @@ "name": "Scatter Gun Enforcer", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148638229/542B7031B727D74585CE7FA38EEA086252152001/", "mini": { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1856061112420143685/C75388115AFCF42ACC79E2C16FEF36755C975AA0/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143328/3D6F3C2E8DFB78B9B748FFC8F339B9C568D7408E/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/13391993677710573883/836E63E52F681D338B1DBCF19D7EB431E0491BCD/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15016334003745601497/65BF4564688C26E8ECC5EE615C195320B0BFA978/" }, "points": 28, "restrictions": { @@ -3989,8 +3989,8 @@ "name": "Black Sun Enforcer", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148639440/31A3527F3D9D8579BDBD433D5F30F1EF973A47C8/", "mini": { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1856061112420143724/48B5A35E10B4977E255D889A8FCAEA8C980A11A9/", - "secondary": "http://cloud-3.steamusercontent.com/ugc/1856061112420143328/3D6F3C2E8DFB78B9B748FFC8F339B9C568D7408E/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/13538216675182514309/80D3D2926D6723131723E027151D80E807BA1FF4/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/15549057142882936018/8E3A2116FFD18C5122767F9038D43FF549248278/" }, "points": 11, "restrictions": { @@ -4003,7 +4003,8 @@ "name": "Black Sun Vigo", "image": "https://steamusercontent-a.akamaihd.net/ugc/10804385148639389/E73718337CFAD8FD65CD341B6CF649BA181707AD/", "mini": { - "bundle": "http://cloud-3.steamusercontent.com/ugc/1856061112420143763/E93BA6716A05CA95AE2EA3B7C0DFD8AF145D5C22/" + "mesh": "https://steamusercontent-a.akamaihd.net/ugc/13653552510521459623/B060AC4F7AE0132A573CF92984494FDF2C4640F2/", + "diffuse": "https://steamusercontent-a.akamaihd.net/ugc/9553158029415913253/8761E25E4B155BE4124E8B1A5B922C6941A153A3/" }, "leader": true, "points": 20, From 447c16feb31a8a060ee5d86a01e44a9c072d7b0b Mon Sep 17 00:00:00 2001 From: Martin Pourrat Date: Fri, 15 May 2026 12:30:53 +0200 Subject: [PATCH 76/93] Add macOS and Linux support for symlink helpers Replace the win32-only guard in createSymlink/destroySymlink with a defaultTTSHomeDir() dispatch on os.platform(). Resolves the TODO comment in both functions. - macOS: ~/Library/Tabletop Simulator - Linux: ~/.local/share/Tabletop Simulator - Windows: unchanged (steam.homeDir.win32) Verified on macOS: npm start creates the TTSDevLink symlink under ~/Library/Tabletop Simulator/Saves/ pointing to ./dist correctly. --- src/index.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/index.ts b/src/index.ts index 45d99efd..ba63a1e4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -103,25 +103,31 @@ export async function compileSaveFile( } } +function defaultTTSHomeDir(): string { + const platform = os.platform(); + if (platform === 'win32') { + return steam.homeDir.win32(process.env); + } + if (platform === 'darwin') { + return path.join(os.homedir(), 'Library', 'Tabletop Simulator'); + } + if (platform === 'linux') { + return path.join(os.homedir(), '.local', 'share', 'Tabletop Simulator'); + } + throw new Error(`Unsupported platform: ${platform}`); +} + export async function destroySymlink(homeDir?: string): Promise { - // TODO: Add non-win32 support. if (!homeDir) { - if (os.platform() !== 'win32') { - throw new Error(`Unsupported platform: ${os.platform()}`); - } - homeDir = steam.homeDir.win32(process.env); + homeDir = defaultTTSHomeDir(); } const from = path.join(homeDir, 'Saves', 'TTSDevLink'); return fs.remove(from); } export async function createSymlink(homeDir?: string): Promise { - // TODO: Add non-win32 support. if (!homeDir) { - if (os.platform() !== 'win32') { - throw new Error(`Unsupported platform: ${os.platform()}`); - } - homeDir = steam.homeDir.win32(process.env); + homeDir = defaultTTSHomeDir(); } await destroySymlink(homeDir); const from = path.join(homeDir, 'Saves', 'TTSDevLink'); From ec973e89e0e7a1ed60c050592e1e7cfc3c2be155 Mon Sep 17 00:00:00 2001 From: Martin Pourrat Date: Sat, 16 May 2026 15:28:08 +0200 Subject: [PATCH 77/93] Add Mac fallback overlays for Unity 6 Projector magenta bug TTS on macOS renders the Cohesion / Range / Maximum Move / Deployment Boundary AssetBundle Projectors as solid magenta because the legacy shaders shipped with these bundles are no longer supported by the Berserk engine after the Unity 6 upgrade. This commit adds a per-seat fallback that re-renders the overlays using Global.setVectorLines plus Global.setDecals (vector outlines with PNG fills) instead of the native Projector bundles. Mac players see the vector fallback, Windows players keep the original Projectors via a UI toggle on their seat panel. Default is "Windows Original" so the majority of users see no change. PNG assets live in mod/data/mac-fallback-assets/ and are loaded over HTTPS from raw.githubusercontent.com at runtime. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../mac-fallback-assets/cohesion_halo.png | Bin 0 -> 22585 bytes .../mac-fallback-assets/deployment_blue.png | Bin 0 -> 856 bytes .../mac-fallback-assets/deployment_red.png | Bin 0 -> 856 bytes .../mac-fallback-assets/max_move_cyan.png | Bin 0 -> 4124 bytes .../mac-fallback-assets/range_bombCart.png | Bin 0 -> 12426 bytes .../range_fig_leader_epic.png | Bin 0 -> 19173 bytes .../range_fig_leader_huge.png | Bin 0 -> 18811 bytes .../range_fig_leader_laat.png | Bin 0 -> 19058 bytes .../range_fig_leader_large.png | Bin 0 -> 18753 bytes .../range_fig_leader_long_stadium.png | Bin 0 -> 19025 bytes .../range_fig_leader_medium.png | Bin 0 -> 18599 bytes .../range_fig_leader_small.png | Bin 0 -> 18475 bytes .../range_fig_leader_snail_stadium.png | Bin 0 -> 19127 bytes mod/data/mac-fallback-assets/range_poi.png | Bin 0 -> 10364 bytes .../mac-fallback-assets/range_smokeToken.png | Bin 0 -> 10364 bytes mod/data/mac-fallback-assets/range_token.png | Bin 0 -> 10364 bytes .../range_tokenRangeTwo.png | Bin 0 -> 12344 bytes mod/src/StarWarsLegion.lua | 1 + mod/src/StarWarsLegion/Order_Token.a57c41.lua | 97 +- .../SETUP_CONTROLLER.1cb552.lua | 32 +- mod/src/includes/Cohesion.ttslua | 28 +- mod/src/includes/Overlays.ttslua | 929 ++++++++++++++++++ mod/src/includes/RangeRulers.ttslua | 30 + 23 files changed, 1074 insertions(+), 43 deletions(-) create mode 100644 mod/data/mac-fallback-assets/cohesion_halo.png create mode 100644 mod/data/mac-fallback-assets/deployment_blue.png create mode 100644 mod/data/mac-fallback-assets/deployment_red.png create mode 100644 mod/data/mac-fallback-assets/max_move_cyan.png create mode 100644 mod/data/mac-fallback-assets/range_bombCart.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_epic.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_huge.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_laat.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_large.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_long_stadium.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_medium.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_small.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png create mode 100644 mod/data/mac-fallback-assets/range_poi.png create mode 100644 mod/data/mac-fallback-assets/range_smokeToken.png create mode 100644 mod/data/mac-fallback-assets/range_token.png create mode 100644 mod/data/mac-fallback-assets/range_tokenRangeTwo.png create mode 100644 mod/src/includes/Overlays.ttslua diff --git a/mod/data/mac-fallback-assets/cohesion_halo.png b/mod/data/mac-fallback-assets/cohesion_halo.png new file mode 100644 index 0000000000000000000000000000000000000000..4b3bb83534b7edcf3527e9b076cfeb26a9b77e56 GIT binary patch literal 22585 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/deployment_red.png b/mod/data/mac-fallback-assets/deployment_red.png new file mode 100644 index 0000000000000000000000000000000000000000..a8e05c5e28d935439f57cf4d2f709049cdbfa337 GIT binary patch literal 856 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJD{c~2L|kcv5PuNX2iDDWKE z&}jGPHlw7BugI5IyNuHtmi)D5_;H*;;mjx+4S^9C0tvh1F7Idd-q+F|3d}JKp00i_ I>zopr0J6>oo&W#< literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/max_move_cyan.png b/mod/data/mac-fallback-assets/max_move_cyan.png new file mode 100644 index 0000000000000000000000000000000000000000..dc017745ade7c63aa858f7608a3ced343b7a245f GIT binary patch literal 4124 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

Bd<4L18 zPhy1_KviTWqb66G4Wh}(ygXz&9|O)f3uwdFCa!4?KlGMbAr;%khN$<(Y752wfi=7~ zWG4G*^5@_P?P@?2+hFLeI(3EmX(Km6l@0{RXC?DJIj|^xzb^^HF3sM z9~PS`6uUoN8GIH{0(P50y^o{>`Mwj7&yB=yv(@-6K(qgn8eWbySkKes&)x$Z9RE3* za02qV^R>uF*@S3(l2A;Yjo6Tj5aKKL4 zu=%7aeAiN__!24|{fT8`0nNkY9a?!V z2v_uGfr~Z0(yKMiEmatc!CA%v8h^#3B;(YCdOu0pdwj|uQwv`&QSUVCoau))5ypqA zv!Qu!vUoYgeZX8+>#Pj9vozsWiEZWlGfHL1JVgkn?@1cm$X4=nb3n3m3_fi@+k@bR zI+`%|Q{6+MP!xim!pt~U7Y=Q%(Yk-1WiTd4I^Gn3Bdu+VLTH}4D-^r-vkRISV5>kH z_so){ZNVQx)jgnCB}StZF&z|CWw3a>bf&5kaM)@1jK#ho#=CL?JWzdsqc%RefUiho z&(!|!c?lYt*PzcFW#g{)7KJFotcVp!ipMD&aeI^AG+})$1IEA4q{DHC!~98D?POGG ziYr^^Nql-nabO~oy#5I`Mb_#;?vq;tp5A1rqFQs-Xh0gm=h5H=FXm`Kh8GlDr#Yb$ zI)?Tu0v8aVDbMqfY_-$m`gwx=l6{5EabF9YCmj`u(w9C9 zQZ#cX>%Uh@9X5^i#>>R5QkyYzu{v%{TK_?U5GF2utp#f)5!r0$iXzi=g}_5*K@Fi zOA0w^<2+1nMESu5Oob^6aVXb9`9zevpj;W{T$H1^h)b1$D|H0EVLCs634DvniQJ$~8psD0W)Fkby;G_juxYBqbDUNaWx{ zaP)=HJLF^!rWA0qJxI816Nu#l~@=dv@RHBjpU2((sL j_or@ySp6?@>S;C418-OBbs(nX|BS)Y)z4*}Q$iB}(dK-T literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_huge.png b/mod/data/mac-fallback-assets/range_fig_leader_huge.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad39ebcba76a691f394fefe08f950cb75aa9228 GIT binary patch literal 18811 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_laat.png b/mod/data/mac-fallback-assets/range_fig_leader_laat.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2ac1d221b1d13e9c357efadb6afdb7fda6c4dd GIT binary patch literal 19058 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_large.png b/mod/data/mac-fallback-assets/range_fig_leader_large.png new file mode 100644 index 0000000000000000000000000000000000000000..c1baafb4cda99568d4f54cd6317be0e17ec5320f GIT binary patch literal 18753 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png b/mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png new file mode 100644 index 0000000000000000000000000000000000000000..81c68a68c4138627192149b6f1f66b0ae59d152a GIT binary patch literal 19127 zcmeIa`#;p%7eBsdMvTx5N=Rl9x~N=o%VnmJE~48xhoT0dj*du)$c#%aDU`}3jC4+y zb0pG*R0f0OGMqXtr7#MWn2Ez+%7=9GM#~>4c@DUR!ql;E^p9Bv!nMj_TcOSAKeyp4&`3Ir}Kv z`B^1GI;YFUU+46%-E6wcY>8GvoX&SoXP5-kGH}|WmVngV#01VpAA5sZJPLOa6Idl{ zc`U>l<89n_WJh}+(0T)2jiaxxR$ zP@O)`SdKU@dhE$ zd^BTK9Ace<7Ro*ptFzvS9?hpvO{z(`EmTA`6q3@Dy5tH?6w&hyVuFmJD8@(;kN7GW z!pTQdZQxUP_>>$=x%GmI81_Pv@r6FQ$0kISRy65NmEIaI2SM@CTA zhI(sg(_hUJDE$!Hj=?+~NW zep4cPOvdO6g%nn#B98TmOWTn%EXPoCKm{v>q)U1toO(4C-EtHY{F0(kDuamjP$e-F zjx-ctEQ3r{G5Xe63M(6WKfrG{lKN;hEgppvWLn;{OFchEo z5m7E|eWL0d5@hM1;AKLR)}{#p=TrjXD06AXxx;d-Iq3cZl+Qs~2xX`*xeDb04LpjB zD*H@Rx^F1P^=XY(aJ0iDG24ykh@pKGu^vD>SOLolzWEKxNl-3_vNM+!oC#%HDDQ*v zWhg&^a%a>Vnjk?S$Li^6is>8?6WAWh5#7!ehj?5XEWZHffQGk=9efKZXvRc_m2W#v z+AhyT!;PBg)V*<1OL;#5v90Ez(@D%j8z%_pkvg7!nol_ki{tD{Ksz!6B-f1lNvO#C z3p&v~ofz9wj$=K|#k2N~h#gF7C`i3J2mf>FILWDe0|7aQ%tv2Uv2@r1)apY-5!REW zWEif`r9(SCGmp9X`T4~tt7QdxUe`)nrYFX+h(%kdi*2y<_$5Y&e0et_vqg!0u*>OZ zbP)GL7fr?h8o3mCQP7boE0Fp&aV?{U zVpWrtS+(}UT*!3LQik znb0k;hH^!9mYnGgJBR2$@!1dKX|@I;9ZDPuZ$_+CF6~QFbourX2fGAZ@Kqg@m&Dww zED>DkOSD(S_pK&KWy5_Palb+}NnM7sSQZ_pN)B%j#r4SrnGq2IeNx3P8Hzu;2nc~1 zGHuy7bUB&%LS^o_z9?p=B7XP*LT$GgN>Vy2;nDe|My%pVvV@Xuv5H8Jf4d zIf0n1p|Z{@;+}66XIv57)NCj1@gvgBWzoT9k}X?AyU%BuwhfgBz(B~ld#s8AV;*Aq9w zxXmy#Sy|+$E0K2+spbvKCDoBx!-^+UxJE;wZQC3I620|WmQU;=Vzo`tv0-ESbtj`Mq4BAN? zy9Cq><7jYcH_4J_-5H#FEt%z^h^$^)CSpqtxrxkD1agekQw#h{{Ra<)NvtoWsGGT$ zqvk#t7y=5bjm70rZtTROJHLbK1B6E|f1RejN{rphJCnIQ-=;@-~7%PXK1sy5~Sunli# zJ=vR1vI+EXNAxId0%LU=n(!H-ly4Ie=nF=>Id=RijY>(8w=xb@l%Fh1uo$i@#WqEi zd%UQ@p|9iWXq&a9H!cq|h^K7>b#<%SF7LPd8%g{s9L4|2z0HWVx95$=RKPC*?ZPAqDv}I>+Q>AAk*lQLE(NKO4-Y0>e%P@3=A$np1$Wf zcAD20@WhMz9U4~$4TDmCCdb+rB}b1A`!B_ujNwOSLeF2dgd=4x(|IUWtTpS_nwTEg zbN^Z~<=NbvN+TN+ubb6JazY~Nw34thMQ$#l(DQ;s+rl$q4@E^PiGYet(DN-~Lw#|? z_6Y|guiEH^1iVy4bSk2+5wzKPXjYz?<U+o%F zl&TCcbrg}0DpoDe%$H}1a=HxhwMdlnt(p(_QpQlcVI*VV^kH@S$!BuNr)->(<;9Gr z<;h2znh^`k2nc#zCW%v#5J5@I?(tWrTgoDJ#MP4!zSWV%#}AU}pn0Hr15nD2wbgP_v%IP(@+~l3!Vzp11*ZtD}j7$T%@T z((hQqc5$S=pX<@Yr=JP@YfJZz+nI2p9nb#**B21loaVQKmjk^%I9H7}cqfokIn(O4bovjY2!TEq5mqy?`_ zBN%54V8#h~7@_;`F$a-Z+_0R#i3~%Qz_K5aP#nY*M-lXE4K{e8u}t3)S#(+*og3<+ zjZnqb8Bi&-LILAMbR=L1Lt%4~WJ#{m1i?ccAFM`TX^%)?3UaCvyKibBrU*G(GK=o%;cHI1n;n$sMAnv zeNiB{GW7hw=8i*uAHHJ(8$7RPkJZ_ECT(Y&aaI?dQJ+bJM9w(*_wAxknRe3g03xU` z^mC|~NwL9#69ob5+DU>78Q+vcd5all)BU&H70_WvD=hHa4&ObkHQQT#^{z#wotNJp(Ft1ft9kK<{eURYl|9W<(Qir1`u zc-|LV5?=P#2ZQSUYDLHw1o3`X^y-5MTOTIrhm-R(p2i6;zBA%?UXIc|eW0n8W{W5gBe5|UamBvx3p-?HE zL9EXs;$H90D(y;%k#|3FN8D~?1hdet#5%jDZz}ckRB9*%3p$TqDcSU^f{+>Kh3)lx z>C>X%@_zhw6GOn$U zr#%84Vjtqxrxw__e%-spe9EC(T@tIaX9;V~yOwC&&Me|z-n*It)xwZbzX{qODy~YE z`aryL%EsMR)UZ3_X=qzTfFS3A{^cZQrNpn?YCOkD3k6o36y%uuD^b?5MN9HV!r z>xG@n&ogaL=ES#YTa7P#P*t!%n*>8B0<^^S*bW=)ts#gfraf2%=LX-^76 z-5FquJ>#djd_~sv@&-Zj1HWV{g>(lUO^_c(_OR-4kw z_lUdrSLHSbFGIV_<-V5g4vue%xu4%)i{b^vzSxo5`ZE3vWC4TdF zVvo^7lvHCUe{8m`Na;--ts=W*(*MFd`nu8WPd%<)-13KL%%b~tecc1glvSwkSlXu+ zP4h>j^O~jG#V2Qe+i0%#19BYNKQH3+p+C?5!e{rm?3ulng{LY^6+J8Zp|QY8d@}p} zo)12+Go2Qq?(&m=`?ch}NtA67MNcZQB0U`=?*-!vYEF;vn9(_NyRu5c<_5mL9m&Mm zos`(P1Q)jH@p^*Ugq%;R{wy zl}8u(jnC{T*V)!UOOJC>Mt@aw;>s?c$6lyj{p7MW68tCy3R6$^7afLH_w}3VY1S2+ zCw-J<=$50WpSr(M+0rPwyZ&~}R-kr@zYI*0q%)eQ8`7~SHHGF{!~g<)UBX6FTW-9#_*pZ89Vn=C9aF+`_|Vw6zU zHSV_$xy)Z`*grZ-tQF@S0)1d1WWU_~^)1r|bI}n8s!A>yPWp;_lnBe4o%okcVcd@5sl2-V02Wpeis|K_&8dX$8^;be^!zF zN}Y^oyp1k~;0xANV!8TXq*L~cmwwbHL))h=L(am{y<|~^t$xqkqjAy^1F`%TL0RTn zrDev3i1nil7B&}J`uRYHz)-85cR>+HtVabP>Y)7_Y5O8*?;r?Oy9s}gM+M?<-NRPu ziL+e=s>vHrh6Z}&J4MU9=~#({NpR0MOsGzuK1@XPU_QGR7S>QqyQwNZ*k6haLc~?t z%$c<7zpkGgd(PCZsnYJ}-FBoc0?nmMV4VV+D}3v*3G8b3T9o7_3jKLPf%&^2deCw7 zFM4(!NQnsOuFfa6Hd$|dh$bt}8n+|mEoPgqG9r01Ef=IB(Rxw5d}%~)sPR+~^#oi^ zUTrZ=kS@Zp2@K`H&$jl<4s|RPl5%;ixZ32U;Y~xagz`i}XA(2PB)h1#Fy{V>5C!guYq6&O6@tZB_q3e7szlLSPIYqJ z+J`(9Ms<>oSq)=myT_D9-C$wvp+f?l;M#;#W}j`CY**bHeXWL}l)RpSm^Pl+IX_Eq zM*g6Z+PotMoBN`kkyz(#u>)}dSZMfs(Fpv&hg?Nv$$6Wz8ivx&_l#e&6!QGT=B}-1 z$C-$XHg4JRd%uKW^$Srmd-R8?`;vNpMBpHz??9kN(1f;$HtMq&qv#X8Tvkg#GIl629F+8O9JL zhb-yQ7hkhrhm4kNVrkcK;Y^A~W*j!;D<#-Ry}Pz7a6)7+bNH<>cq1De*?%~qiT7o^ zMIG~ZutfpNs&!9#nj>mQDShiXi#ik|JpRZqkX+Y_L(6$Sl<-D#T*3D7ge0bq*_P%e z8TMWG_k_-J3qkAyhy5KRwz`sQ%Po)Ehdx;x&OH&kxJakq#z!HgUqpD6P2G4*ZJeW7 ztdXAcAo2*s;L+HGNUq+jbm)MZkfT<-)O{O$%h-B1`*PlUV?)b8qn;S==&N)1tS{e0 zt4dd2TGL4oIwx(tqUS!F#^fT#IsffAt-Ka)#a=8x}9 zud^U02kx=~&t+$28FnUB+~8-Lm2&l{nOYb9cok^m+>hT^;okp!6_(#taZJSqL+$cR zDxq^FtBp&;?tNybcww82Zp65Fmc1@fYpmPLekqk(tVKqJG3Puh(MF4V;2W*~^{ISH z0M)FsL=BvxnJ@Xh#Z1ps+=qUHH}OtpJ7NMo6gmtd+{YVE{J07(XA;*(;}+LkiK-~< z&6Q_p8+=xBUq)C|@$hoM`HP7Um`5RMfKTx)FzvKrX$!|9uq+qJSL2;)TMN~PIS-20 z1zxsYM|TNWR|Z>`ZIlq>>N$>XC}ZT@EvLNIs_D+fr~N$2=;N^6G=qlI$AVQ?H}e+{ zH=CCj1!ij9r@J^ml73m{F@Y~v!{@q)Ef4;g8P^!GQ>_vmC^xurP$?tMV0fra`g8To zOYvkq@?3&*J$y*&#!Zgcp!Ntos@QbJcQrwng!yZY#mF1|jMEOi6aVsX-2MeAa0|Vc zWPJ58^2c2XZ856Lk@b0SKM&jFGTC=ftrUH(2&=ihZX<_HTn)}Ow9gQ3G}?=s3|+mtR@2b_PFtKvPQB4*5ksqU56hcNlVRxY zk9S%rHD~qRKhvv4Q%{(-sX~stkt;5BKvN;{rK0d$P@oOG&$fi))P6$^p?9|Pa^wb8 zu)6F%975X;CB9CmhqG0ZDj$vuD@3gWIal)^keUOJz)9HW5s%O1Jy^_86&}d)wJFVx zG7J#P8>a;Z|J*?@J)HEIOM^(+LQ5lw-59AUJKQj!L7rjX$$wRJrcd1QaQwiff%U=U zljTWs@{1bsB9xA%Z|;h2lz8r4zmh!GB~ zmc6#O%Y70q%Bqx1(RPkTvWHQq#xiM^)S_X0^-8+n=I)-cxhc&BEn-nM7jZsoWY4Bdy7&d3U`(aye z6P;z&J?GXi%5>q=u1F2qU#5AW2g0|ALhlEaV#8Hc-rzkGQ~J!9pY9SXB_{CNv6maW ziS-9YTW}OQTulz2*M!JNlusx70(ayYJ6jb84;~7mx3y|$E@l@zZMy2T8+aElcn-vD zeE6_Dk7*$K^a|2-^r(G@H)7Bk%o&wz17tzEf>Epi&88aArxxfU2)qj z-Xvve|LIX3`i8N9Dumpf!j(Us7F9LgC7?~Cuqm|grqVLbNY^WUadLg+y){!8ONy!) z`^s!4*+~IEvpO8rc19e1%)B=@PrVRu@V?vk5+8w)Qd?@0?y51b0*yN=wb#R78LO=QWev%0 z2Z!tw6Uz+ZPC)N*zS$>|41O2vFRrzdT}~Gh$cgTSnq!AiEJQS;!w&vT-m>SzQ?Iy4 zrSx2p8)xw$ZhyC_RgzWV`mqqJ_y-1F z+r-iG-8gcAS?e^sU>GYq{`79v_>#FMgk9stM-+SdMSlDFiK9`nUyqH&T)n^T2!yIb zleI<918^+WWG2n^NyIMz?Yf>eI?y4`nmUD!XtAYcIo7j?@IWMrcsr)hm$F(y@GRU4G5F3h_ z+I+w4pq)Kms>ZbK9U{staa)qGf~c2FMYxX%WvZhANUQ$!YT4DQ86r7`kX6EF?3X9(cUzY`%D zm83jPWA}DOJ!C8jv~pL+B+V3Z>cAyoaz>itXZPs*1EO)Vf^BJ0^ATuF*Zj&#xMTo8!D^;{j_3Q$@x_ANp1cNgN9=J+5$EeLbfAIC>Iw!PVgG^71t^tx`qqj$*li zeJ1_$8VmYt-`wdq_}9miogdAz zlOxs1=`ic*r8fx)Dok0yR0Sd^^xdxNqt-zmr0gL^vSM&Ep=>n{(EZUU%WNxS2~g* zcpnmOp~CvP=M9Sr>i6`Xw;%m%#^g~BARZ>HER4PmPrSP&(vW2=7D^Rs9qiJlKg!FR z+~4UT5QWujR?J=NHHzjUquPLuQ-T9xrWBGgvKYT^=uNuy*`;1rzV7c|J{rO~$ZI(| zL}5MVY6V8{(2h$Yxju!4_-ktH!$<;0Juc4_IEMd4h-s^Z#aG_2$ z#C_?x_tS4vJAUI?psUGDhJA0WBK^eZ3C=-21*UaY6}vxG`>2W+Ezj9}IW^EMb4uls zPmA+O_$yDw;e`DF`XZfq$?m2@?{=ewIubBe3atKF5@QW>CTXV@Z*cj$edqZ3axTjx zxX0s8jJ7!5dV1jZ5oQN#KV*UsCZevo(j%#ZcCz$i@F4xKZm`DaCt^DYEW97IvIr3F zCv-W@HtS1p>&L(Jwx#fF>HGNXj%d;3RQFmOVr96`wr7jv5MuGan>~h-n26~7Is9hc z;*t=HG3&`=V*~?b3|6-z_kY{r9)F#BcFmA|nsw!$Paz)usMsuJN*l?;IuTk9I1hyA zA^+^o|64ErHTD1hlm8Fx*s+=Mw({xi4zg~6MeG5>!K&3~T&M!xjX z{pa|d&$%s;sZV=mSzG>nxW2V>3;eFM;971eni~c@+3~@#(++Fx$I&kkhmn*=S$;eF z3SCwyQ<|JLuLIWnbHAhqa`ftVl$tUsNg3$UYKc^2y$4hE`ynl`>6IwiiHZpK4J0uY z(r5AVTDWBVr*+FmBRG!#k5gQ*prK5oC2~GX7iRd3sQCN1hTI`H@ymDr3he21-}YNL zroX%{CryfVgC_;tt!eQ`J!#OGU;wzTSCIx?A-_!iKB4{lM7J!%Oi1BSGRMH#0N1hh zQ2sQ0W&XqKiVvReY|uj3Ls5;8ajPyslZAnX{B$p_Y41BTv?mq&z9b!b=(Bm**ORRO zSJvfsMt%!4(zR0M9hB^w1lbR?AxJ`(3p*acjg-D0QPS9 zy3hO4v?&?1J&8$O(3_x0Hy{0lPUh2+Xlr^~qUDiYs?wW}zs*Em`xLNv^R~}iDuf~= zGK%YVna{Yl{v+~f$nGS$?!bY|%&E(?GHJ&#?zT(hr4Cy<6~yYZm18J0uh$yA9l$<* z{d^$wzC-8vSUg z{q>TV2TP-LBn08q+J4$IYO=?AM<2`Ro6Y?Fve=(iA+TLnKHe)^uJ(EOYvst-Zkca- z#Or#gX_W&KofXOX9P>22;c9b^Z*teQs?wF+=+aZmV!{6R{JBE+4}lGV^#_;sgulM>J5v_#>Cl89J^JDf!5)GXyp8ur*~Y97K*w+_m;R#)E@@D8;)fM;r`2P%pT}v&k`7x!h4<{ z@&x~FY0kZ4F6Vr-F{1IJm5a`1VOQsLFGgrI(!^EUwr*tF^n){pNmUk-6!MqO_P^;3 zs8TGr?h=QpNiKsC%yK}V1{+mk4RwDP#jx6F30{B~h2S!C+aSQhplwn^I~D~&(er|x z4!8?U?S6Ke9u|E0=u8^yaqt2HYdmSvGoFUbuvhG(;>5MK4zh@x`9RY)X!@#Y?21W?QbSf3M5NZm{_sTG|5e5A_>ft8W+bWSS1*W+m!t5`QwQd{OCC_=TE=v5Q-=91R48DuI2y%t1bMaPvq^8uA+7Y)jdp%d>hw!zX z{n~_uREf7f?@G12qiS~qskXnUkr6DOt;f`(Vr1J=!~gej^{U_9-%j`JktYG4LyqFk zlChE)U;kiT6DPHIe*|j=nz+NxKIpo3d&?f>ys1OmM_15w_%vnSpXg5Vs=}}DWHetc zujBV-N~-szUY+NE^TwJBPV3%foT$i*_fwL6lQTDW9c~O!V;^QYeWDG*z*LO}NlXqr zA`PEq)kWU5pCT7fh3sp4GgF;wvpNlgghSUnMgL-P~ZN9HxoRD z9ye62og+}!dDriJ@(2-+9RB0YG(3lC3atxV*XsV)b9ho2laF{P+b0#?|9&{jeJaa% znGx)}YAi3mXO&bWD)y=G#jBmL>wfB|G?w|SN3W)=BQ|3ImV-;bSTLYGri@hfz8nT0 z1H8hIFamB`RzCI|I2{v7D7xWv4EzU41BcS%y9TnSaKCz=pXZyzELdz~8>K6r`C2)o zIwezZ;V1!kr72-np(dUS1cFRDD8Zq@OyB9E#5y!8IEkqJT0@}icp%)lOnT_q1= z0lV0i;Y8_7;_emOSJc^|Lz@BRXz*$W>rivc7-?(Cjo4By^H_dG%+e9DAStQ2ZY)LL zbJpAJOS`e;^hXN1lk}oX9E}bQ9(Krpy&+iLxRSJ#`q1%4TP|-bcD3jD%aSXxLM{`R z+RJfOi#wuYJ0u7VSk-x|%PI8zlr#?aLiIBsS58khq0~uJN9-v7^DUPVhgD&7I$hw{ zk;Lp8A)3TqA07~Sm|hID-SNCb4W2oIVWT~hDh zuzM+|ud^s`gFu<{XcL+McE)QEWT><&%PwyA@+;6jsjToK%bcaOZdyDDA_ zGxR-3G1nqb9rW#_Ev=LU+X_j+DLB-fbet?f&ZpmC`2h>md8JwiF=T#GZhAL4c~jE2 zEVWGEPfngn>KwTwg`omeTRCM5OXT?nZd3)EXPvK_*%;=db{U0KY`W?9%fieXw*oec zBS$Hq^ixd=E53+p8(qp(%gSWPX@Q}8I~;@82oGHHm3k3f{_!H-@D`2@(~JWC%*(Aa z;6xSI-S$wZp1$)7sJ(9wi!;{6s#lXw9t+DwgCR<;Et<YfHGY+D$Z2@$d@c4q+S+=@*Q4;p#)S&r{zdw(M7Ti!$J!aWBG;4DH>U zVD1{V_gbO@A7((0RJ()TR3zYA?3PL$zB=TL5LBQNxFpFzY{|DGN36kN9rff6mE>bF4V~H$V>;U3oa(d#+L2g<+x~$O_cYbzL%{E7755e2oQ%bWrGd zqlUe*Ins%vweCImCm&uDgSR@onLheln6Ks1oO18^JW-8#>9W8qt#un{@KYTD|99~j z^cTOk9X#$c$BeDlHW&V1esf~$_WQ;u2MY8$pWQ3+paLA%IK#=fEjdt|{zY_hLh5n< zYP@A_tIz-Kt0x+rjd|fY0Q3Z`C0qQ#yYL;W&Sb}hz1F8_~Cr75uw}>dE^JQaN9f$*NPt=lo&t-K(w*TDl zn($Qxi>f@BNjX|9H~ z{Gp}#qOqmIS|Rj5@g$x(pHiTba-6ZOb0N!AinE}ssfwb_ng_r6e)%m#-;}(Jt|$T` z;eV>kb^YxI0skXzsuU&&oOu-<<7-n+G88%&6zLcI`cXKRg87(P$G0&a1M+B9cd zu{`=E$I?-(%HHC$-$3-FKYZ=KBY0);qD0xA=86|Jlv7{3hvo;)yS3Iu>}(}lecIsc zj`X**;o704Tkma38(XpYb3!t+&)s0AN1s^mPYCgySL}4~^oQB*yD_u>`rT^3|8C^B zys`rb$O}Wbe)d&}Gv>#R%+SiDbb-e4ba$3!)RhQM4mzcb75Uc?mBKC5zkZ!QqgK?o zg@Rp3b)x@ZFlS^yr7mWBsNGXzMgOv7jij;qR}-4$X|=KxqHsF*UaUDix`Tfy89St^ za_dD4`v>m4|B1F*XIX9By^-J}@QA-sRNcK*><}7;y0}4l%>&~Nn?|SG-c)X5{Ogwz zbbbG&WdHR`na;v5#gdhw*$^>J2q6j<3Z7$THW;VMXnt3n>#~31#R4JagilSU3pIu( zGymKNvw7l%9Gk?O9Hk}eB{8Ah8Vq9Pr4lwzIj;N>VMYEl#x29F!zZg2@G!GWV+7U* z85%(zTmNl<@Jh*ifn?arQiy0o?I|V z?$3JW#DHp)x2e4CvM|mNa_7EYpLSEGB?s&b>k+Ns^c0c)9t3{)}wM`#MtYoAw$mY3sC7lkR}*f4sRsS z5I~AAE2OZN{ke3OIXKK*6NY^*opn=}#Whn!@-@W(W1S0)*n0fEblD8^228%z7nkcK zP(3G*17ZlRY?ci$f*eIeY$UTTIMQ(PSPpE|*GosfZ4|@I5dLx)O{$22-w_k`eZl}z zTSGv*jEQ~P)o%Ejsx_4oDZ=7!rvDK8rRXK|gf+yO%FPsqw~u6;6WrY<3cd2``2`JUUQji+_s#YV|Pyv?E&ORD4umccmAy{BfD9GyMU>?F}28GZ5W1A ze~~FWs4v1Sju!c>HA_TOM4ZYf#o$VnT9cjtOxuK`vI*!P99fNDoO@*89Z$+PQp zr4W_obcq6HA2LCs2oJDv=!?euBYIyCy8^yXG9Ixt`rFRjD4jjPRz>$uuJTcuXRk61 zOdSBlm~G3>!veyvQ1}6O2>p1=LY*us$&&tQXkriJ+Ya>wyCTkpQro~C)M0O{ev;Om zSFM;?C1%|{H&zWQ`W;|ByZ+{Vy#2fwD?184jPOC9uDCj-f5Uv1FUBq#nC-YAMgF|c zCzjeR3T!$iblY!Gc-nmgwdF;6Q##5WcbZE6*nX%$H$@vgK1PZc<eU4#6QckIpID?&X z_<-6@bg)1FugnK4HSQ;43o^8Bz3`dO8t(andiSQmoh}23&D^d(Om}{1DV)!`)pPw+ z*`}5+w|DA2Jj?tV8uF!uKc97|=jy343;m2RVejF4ghd_)apW_RD(@#>eZ!zlXP5Yt z&f^>zb#l+R*iyM|_rJtX3Gci3!T(GAAWh{tz$Z`JnZNmVbnkl}kMLKP<#_ajhWjgR z{qdK9s)1kSe!r(q=sPrOzN2CHOSL0Quvd()FAUZEkXD^tf?JVYW6=rm$vn}X0o`Up zwSa%4@NCV4IMR(e_gH-vPv5`h&nRUoicE?INcEn!Q$tm=@8wpO9U?GRO_6VXyd5$7 zAPoz6+bi=!*7L79bxZyY1$4`C6Yow*F}v%*#g$x&l)dY*Z&zbw_gd;jQs#kY>^V*K z+HuTtAQGYSO%nhTzgt_3IRj12!e|tQw>-rSZxmNCboV4~S^dcUJx-7uiGSq2t>c>T znc${mc@rBOP-8A27g)CQ?0_>fBChf=-IJ1HIpr?KO$@x|*>$8oI8=Ez8C&&aO93f` z&a(F$zed_Vb;TFEG$f*>g)0Ietepqw@7t}dn393hgYvAh_&OcBUGWzq?>p`(S1Mh_ zFB4MMZ*yOUf7PGUs?w2kTo^Q|kTm+;TP<7vm58=QC<0ruowo}hQI(QM5K%6#<}3e$ z0$t1a2Ky}fun^IY6tW)x`pUNx1NN9gxawa6KD@o(&Fiu&4;XeiUrd;v(jJQC=^bAU zaX264xg$E_k1txWjRsrHD@0{yzFW{oyubXE@#DUFGi+6aq{_e$P?jqYMB}0#sz}V` zu@4}qM;OZU|3SkZg&6=QYy@V&F>$0tcK?01vG_Z@7Lc@lA@hy%16M_Q3{=C$Ti%;J zkg1B!^+8+vLbftM(Zc3B%f_oNSnl%*QaQ(HpI?#}xLnWu0&TFEUsD z+&>E*yOUND-XZOIz}}n)1w9jd)fbDik{pooXn;ofSb0v%N!01z$)wp}B`j4qw_8ww z0-)W1-8GnvZEz|46uP^AvsxY9uQ)`jQS*-d9rFa}!dfDgh+04aESTTUYmWz@G*hbq z`~+zTP~3RbogCdt6?-qW!Q@J2d*g5@07kndC0wX+rh9V63hf zv*u!r0Qv{=maYTpQ*C#1tf^Yu&(s#u0PemP_*wpBG)IiqRh2v?Vxa(&UodEclA5RR zbV?z6IG&~jF!^O+*erBFa6q@5Co*CLV-3aFG#3LP{+v2W2MFPNeNiJo-u1*o!6Ovc z_sYmrxEa0In`zu6s*M;CyZKL583HHGl*kH~p?Sz-`6DwtAUqfYGZab5u?+imH~*R{ z!+u((h*X0L)&kwkjK~@%M&DDNJ1vh*17{;+S{tpbOWJ_W8H#Ri6xDi$NeHF0y>LYS z0;*nEEc@qp=FzY@Uout=Yy^Kq|3MZf$m+YfvcLDLAk9H6cMy0I&{zX_eX;CM(71-C z#uGajKO?X{MLDCl^3jjU5-Gi+U_{~zJWili7>GV@62WU)TS&>mAz%XnUBlmp-5O7; zp&1A%xzaiYnZ6*FBCRon-h!VI(7R2Sy^(U#X;F?Kv1>$Z$e3aW$fDq@-Y9@P8+=tT zlW+-I#L{@$+U~!34q`)KvTcI_Z5?8?9PPE- zMB^6)QjUz@w`$|WYaA_I${<-vq!|}B(-!vGAnN{jO~nyCfsR@~X}Wvbt$CEz3EW(5 zFFBbS?XToHPuK)QAsZIlsgCGg?Xt2PwLhsyJoUum;3NQY-FbM1ms}PMY4)?Q z_HmE|wWUPTCiEo=W~ILJDd^v1snj{kJ`qhStkEasIU{{>VA*&ti)6CRLkrPYkA5-w zO#>L7+eOA@mz?nERbPJVuD#df8YEYN#}o*f0)17UE7B3qmcT2XWEtpgi|A`c-xgg{ zg2xHROHCQ2$30juEH<}p2Gx|;dGRQ&L+>BkOn*YptBvR$<+ zph@a*0I8n`4~?k7LXNhYJ_=Txa-S@zA5sGytasuwhfwX9U^_m_F7if`ElWUa&061==&q z3>3pMu|}gArZ}Wqmu*yotWqKu1l+sfzs?5J0vvVgAncPfXbLH7M##ISh_b5TJ7uZl z2hyd)8m>y2Iv&pdaDZAr?}F_(uiulKRmWFiJ?OE?s>{i~4)29^pfxjgBh;felMM(o zSzxF_M=j7%rX#IPo+)Lh=HY<7iboR$Gs$$|lYV<1IC>M|hz}di@8@*`6ILCOlXVdN zmqPaDc$$<63sgl-2jCGp(%Okke`U-C$k2|k(2C?}T@^|5GKqXOV%<{2p<|$E^u^iR zfs%`p(n}E+hH(+80ws4Lka^8yP!ebo;L*+(x>*5d7}6maXAT<@`rVfC+3CO;2CdZR zS;#h$vU8a=Fz%8#$ngMj8yHwH43Eq@0>jjk=ML&Y$3QT~0kas7j(~3146I<0p^{WD z$RPJk6B(BUh6JJ8N*E@9j0EP@ZecZSBQEWuIwa1}fmg)vO?-9z+9seaE=C(-uC^Zsbr=*oPQq^ zGDG$nsWd6teUrF$D+H6+*N|RP6p#UGk3nt$W5mi;oL9c-y@P8FMVeb6P6xeWEN_c@ zo~Y?aJEAA?1~cJS3xm2ihV($5+2ty(1r6Jl+0Uuq+D+(-y(vNx0glg<`a(~`;h0EA znl#ISF##0}^wBv4XR^dNRg_3jhd1kpp4i(+NRoqNfZUxYYE7P553?1@BXFq5=X*{C zLl88mZ#|?T0AG_~$Zp7)(xfR51j2sDpdf?}xG$-W0Jhu~TUmsT>(BH@Ns~HQ+5ssn zkeXH7N|ONt8STg$POJc>gMg_{_v=`1%!XtU^&p_Z;3S1W>IX*8G8}7u(P63@V3D)n zuG4`}?{jaQ6jKp_N7?R`bTDLeM}S@2Sl)<7%I+ETHPFaJ z+6egT&l|y+Iyk5Rq6*xZ(3KOU)IeHJ6AqBO^>7;8_wOF`OHWD@Se zQ)u)Uj0{EuXEmSV&LsmLP-Tvz2W5E7BR;i&VE{Cq+{sL(H!r0_g_}Dbh4Ax1l*Xd06fqG~WudkupY)KvtOi4G2s;mzq=|!;`KzPc?IVQu5JEm{Ohl==A{=CHK~5U7XkYT_Z~2D^vN(=jECtwEq_%W# zM4iNeQH9iH=>#82fhB+xmn7iQ*%e8fSPj*WbwvnZIGP-%4FbZYn75X z`@yeRt4`#c+DL)jXChZWDmp+sElv4=%W9>w7R1{lGk|f`m@cI1T_Di`|H3-1I4_C* zo{*j_k}FVjz@<{Z;%))Pwk@_AO%bEe?=WA6)sFmfxdM=?NI`?lT{eR2Gl8<4iBZ?4 eW&+2r93u&CcdEQOiyZ(Fgtj=jIu>ta#Qq=Yj?8cX literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_poi.png b/mod/data/mac-fallback-assets/range_poi.png new file mode 100644 index 0000000000000000000000000000000000000000..5a282adb3a6a5f2a524b878032b466cb29a15285 GIT binary patch literal 10364 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_smokeToken.png b/mod/data/mac-fallback-assets/range_smokeToken.png new file mode 100644 index 0000000000000000000000000000000000000000..5a282adb3a6a5f2a524b878032b466cb29a15285 GIT binary patch literal 10364 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_token.png b/mod/data/mac-fallback-assets/range_token.png new file mode 100644 index 0000000000000000000000000000000000000000..5a282adb3a6a5f2a524b878032b466cb29a15285 GIT binary patch literal 10364 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_tokenRangeTwo.png b/mod/data/mac-fallback-assets/range_tokenRangeTwo.png new file mode 100644 index 0000000000000000000000000000000000000000..4a8a2f488e72d64ace0344269757f910d16f74ed GIT binary patch literal 12344 zcmd6Nc~}%z({FXp%CO7;BZ8oeAObEhfFcNi7EnMV8Y)cMt^ z+M)=aY$zHe0szA)fdMlBxJYtA$j5(D29HbwaPFBBFkx0g{-3`-%pP|0J6^Su`PdCZ zLM#Ja9x24-KUZx{D{G$-v~13#bmyidT^fJT^gPt5w*Z|Wh1t>~WSnv2A;9MvGPU0`4f<7=oOz+$E2WiMyCD3F3EmBpreqfc9_n8m56}Ls14Z* zf77W1{REq*>%Fhu?+O}g9#E#FuWqv+Xnc?kMOw5Gs82*d`=uuB$-r+eaZSL)tYGm@kmth_o%zxH0st^kn+!ZUWjI z0ESE65(j7UA@dl3c|fzkBiI5|I0xV_%{ob z(nwIGk`Nf86DZ9lf;tcBAfyK%osIOwW`S%_B&atcJwYdsjh_gT10)EiBW;QF9;Bxr z?S(YJhk@iCx`Q`gb$i;TLN=h)OX{D&l8ivf| z!VyQH3%DszGuaq24+CtHbP?LITR|Hm0`05~zK|be4EYlY$j6@O?@;<{DKJ4Ok&7R! za1V@(y9niI94?218k`axhD>vC2vZzlF%D7H^;i2Lcx2j5$b8<6)nyuj)+vn(^-C?G z7&}fvRZ@mHqAd?}&M5QQ3@*$S0fk)+mlD*R8lWLO6DYwsRH-Et<>HdoE^0~rAqE&5Ckixfk3oLO z5W=Vq0sQDDE=zI;rOm*>z{I13ktN8tpoD6_r(UB17_QS;pl?kfw@EA2Q7&SqMRYVn`6}!h? z0Jyd^IBg~BE~Hoi(5jmXwWy;Zl5PTGT}sQo6hZOT1_IQ(JdIYhi6E)&3c!i0BMIGp zL+D_aeByeNpwtnx)=mPT_f;h7c9DQS1RC<*F!4~GzYYDQ^;3Ont~*2rA%F8Nt3WiR z`LWF$XkRwuD4N)x9xn(eb{+Ur)=SVB3cLZ{bVR`TB7)LKrNEBdfxlA(m1q=)-dH|v z#sQN4$?6%vi$%#tU3n1cSweuv6{{(;FLTwgRyF8Oujg=PY%zqX#<&WPU7b6pN?~`M ziVsR|-0;~B;82Dhh6!w(K0*QNgICRgwx~F;v=C%tGvokndA_$Q77{W$fiu9@o%+Pm zB^-J!sts)9IXkmRdX^Pl3jvFL>`ZyE+#{TT&E@%1%|tLmb%F#*Ug91DDWo^ac_8D& zjr@-z2=5NXqcgm5uFXu4?7eCVbY{hO0mnc#EMqu8US8Ce=I`GM=p9zO0cOQ@{`LCJ zABTuNN6(SKFkECNH0F*v)9+Of0S^)>(dG1Y5KZ03>hY7-}!Yd<0ux?E` z0A0sKsz)8hp1na++>W* z0An=h^8Q3U)-T!KG=vA^JjdX*>fGji-T^vpEFu6p^PYE3B^Y}nT)>H|NmY|Y(A}T{ zpuec->^cnE6f3lHMNZWM*4u6?Xh)Zfa(pRF zU;ZACgL9(Bo=5rnyuilrY80}tjz#qi?9>e8QMNv{2sq}(`dN$U;YlZO0qg5G4v~Uk zha3l~DmQ$E+Qm)?9<^pZ9u?)t&H{HHwaBLg#Uf$@7KrG>Nzo{Fq<+gjDeyXrNPyEj zNB$8C(%<}X%J~y7pFkgvKSKu){ihPis9kPTU% zv!S(cM6~SeVkBm_1{;WINHRyUKk5&@lESu5334nb_g(Q(xs2Z&*;HC#wPi9 zROq+$1!JXbPt`Q3BvuYY;z0PAL!{ERv>sc1Z%vMKgyF4sPy@QW{4waQ@rTNqcKAM$ z#nU79`4jTgh0Ac%oAt;4>hDTE+6=eOz4cz0h)(TlMcx;-le3Pn((LlmqQ60Edv@Us z1sG^^2B4*kjlIu8)ZUqBm(E9vr{l`!HqC(ia%X$&>q7l07MAr|2{Yv^0!CO;{%Ml} z@)>7a{2mtj;Ca9N#QDfdd)A+R-~bWY95I@dljjQrY52oa;t}{9v3JoX?{%3U7i6Vb ziDX!i)8i1LXxBnl6gRIw^JQO;Oyn!EddoUJTHd|-uausYfjw_Hj3CE)b;{{(3)haS zKdb?SlIF=hB_a-lRq~lh2fI1dBeYe=WuTpS4i3(Q7+DvmXp7o+Q!LO~yJ1Hn&gHd9 z`;xAg-=MEv5Ch(Izxz^gSCX9&1HXlp#zeUTl!r7& z-FUgz73lgHy!dcPlJqrp;KGa|o^C&><??;Uul z56bMRf>>K#aqM?9>M@D; zu{ov7XmccCQ}LKl1YYaRb8d?H&F2Ufk2dh>!XB`fyj?Un5*SCPyXV+Gi*{(m&mSVP zq}TP1X*I0FKOD!r>Xg;{U6-pB|MgG^%CtGX-(Sc&{3G!D0;lPH-}AKMPW-NE>+L>7 z*sWkO`$?lu0d@GJX;*=dB>`(6g09_K%P+Hji%{}xE!nZ;l^7WEif?J_#& zf17$bh686GlDffo#R)VR94{V z+*KUS;vkM{mri_2`+^vh=)Bi?C3=}99sD_nAk{NY8q%NrX~2xT;w#zWrWdWfq^LZL zpeS{y<>uS&Kn;p?hY>%x!XaS~#jhNpDSBg*c#jX%tWC>hmX84n_SC?MpKd@G0wCA8 zO$6`K!14pA9UGT1oY^&e5Zb@zQyb0r+LZJZjZKuLqI`X8SKehnq(U3aYRu#a>st4Q zy?)JO6b5I1+XEW<&W`h?PfZZC7H!pu*Vez_gbiQGT7-HZVcuL}AsqJR;Fuk6MW*NgqK_OP@ntipzQo{^h{S=X!6u=2K*EAU}LP&lXP3)6jMw0=_>{*uxc@q~vHvwMaE#L(ZY(A_Yeq(_*HP^xX|bN7)U_uQVM5lh)jXiLu`q zg3LF~0*+i*L6cITR`Fe7*_tM}HQgCb^>7MZHWTvw7X^X&J8)Y(Ef@&9KA1b~Sl`T# zo53$VsWTH=N#?T_8`+{@9I<|}mXwL&P1JbxlwZ~Gs4*?WW!cMl^eaT;5SEj}_LFdh zq-M1^$nuN0D55M5PciLq14$lzcc!rtp=jkSL2iiz_rrGlBcXk7??| zeOJ_LO55y*!K{YuoDKEME%C90Zwp_=Js^mWw=fj#wYPz_b(NeEX7Hp(;O-(MsimPqXrIT<&b(6FYWqA>o zVd!vh8p4v6`C4w@7+Mvf)rZuczXC{TR5g*#jcfkyxN?^ea_{`AUxKs7pCLm2k{)pa zNx-(^wAAxUF4SW9(hFYTbz40Fzr+sY2tFv|S04SQr@%AB3S|6v*RyXfyB$KEu}9}4 zAi`khtD$i1*((0J54iE72o{1cEAa||%Y`EaKfD?2`D_0O2iuksusCim$MVAnci9ne zVzPtZBqEzGcaI;GmmM(l{%}O`t>(*v!+>&XS*0aDoTQVWdm#qeURLwJc&dKf zZx%Q?)^3xcK9@jjTn=YkzfG}g)SU(0K+ss=lc*xiYw)AQl$}reMYZEXM4&N?_tN1T z7yb~8;*WYmG6TWSb`F=&43GCd+uAL=`EMN*4HClr3(x!J*REm3*aK6FU@1(GOAo2- ztiEx{do|*&{$6n#@yAZ}K;l3Ne6i+@|BC)5{IWnFQb@_5B2oySpAuO~K%@u|R+N?h1>$&mfP4v0UDW$=el5v+Z- zz!zVx&JbtOOaG6J#lLV*z+C zo!IAHmAs2 z-Nu90axZH6f8kEO^J?Lfo-f|Ty%LiK+E08ydAjG@Zh{U);MR9?@E=wKv*+Q7-7fd1 z=KYsmK3JKCmHCIA<6pQ8YGQY?Gw4KTxV!r!D*S>m4$J;wogemAyW>silkV{M{_LkE z5840!U;SYld!q5j&g*{HQx@<$luaWV`gaJwec05$4;{As&$0f!t^fZC-S3J=3=6xJ z$LZMrVjF)e0jK_9_fvhHe4lhj{BIO^qPg^IyyawL|2G@+Z)MGDc=?8e$+<%{qxw9I z2ItVfSI3~ZE8*RGv?*-NTyyt*|NVl0vo?Rb%L3mL#b-FXkN;EdznKKP$DQsA@nzSI zv%9dDJDA6?d6g9@tWvZwrLC9$bx<{&g@17VpJxj7)_mpSm;XGJzlwSR?}PWf$1KmM zF{%GDz#&*8w!d62fR6GQ_~yS`S#?(Yd!R?U5ogd(s8=U?84Co4h_3x+u;lb$2Q$SMAEy>ZC=fU5-6rZG~w+~&)r_fdV7h`ox>q}umsifP14g+PO%ygzOG5oJ6_ZUo8x zi-T(VZIfQ-$0D!NE5saRX7O%CY5#IMO6#vTfh5h#!hFGp zwS=u+?SpH1(X@pFry892Mj!5I#1Q5oLl8s7%jLL3pRnB*Rc42;;($k`>HowF4nRLh zFd-nWI4*TIkABIjhVI{PhP^4sH+h+PZ0Va`0$$e>X~zB_ zKImVl{f61%`8VLGY?#7>{Hq5<0VAP=4@pau1pS~mD3$1Z+<)8M(AAT$eN6_L*0*V? zh*qC+ptfGG4}kF-SM}Ss`JG(`P;=veo&g@k8*bIT7)~~m(5`1!s7;D$Sc$W?V!_?Y zmO!OR-=3;I!;BHWd4_m^)-B#v?$QekU7LXkX$er`qdERq7u?u(jYGH8ZlpH$?#O;~ zgq#RKN8SD#au#j3m%-ZKS~y<~fCvt_yCHDh=p_!z{|9jWBqRV%R4x#3vCGABb3(Dl z%jiS|bPQp~*}SU|0m+&KKav{T;x5DBL9%Uv9zCLVIaNq9D_9nZ?PNTO&;Gi`{fNG0 z!_!QAarw7A`T;&h7${)rZ!IL*6xFYp&3AW6`_TP#M^@D(*C-yfwq=BD!YVd&P!^H0 z5%E$rVom%F%Ef@j5EAn2M?t`Et4NcENDOzJ_@m*l%AUA|7LjBS4yPKONRxf@Sx)@@ z!luKh;DaR?WP1q%F?_6#1dh@@UaYQZXJ^iwG*}E3&nk(XO-qn78N;1PaSwopy$v#0 z2AR*c6I;V(^z}wpI_a|klEx?`8a(!2HRm8{G4RL7z9x9#I~)Jga|&}}^WTjXuD8SNxK0q}^e3Nc{Dr5rOmk7> zxfwas@ASwxj%u3fBI6A3d<<6t-^~S|)`0{ai|;~9?K?QU1tz}lOY!YVl3iv|USYwP zV6u%K`6*EI&Jpw|Zi=QTsI{;M6Mk^mO~JA7_!_Q^JVU4=^}(Ua9rNT`@fek(uD$0r zK9LRQuaucYnNTB+xx=?H1gym8usOzEsgs;0_AS2rHW;ywa)Dph@|iuOK{NO-wvId-d$BW8&$ zJVTC}f?xYMOdUw&;4*8iG-uL>LB4=cK9k_*3(lWi=+(FqCPF?|H<&;B>Ba9od(g2} z{k z(R!6x(_0>`hVX}2#}HsaP4@F;Tyt*p4PLa}*Di1%j!o1+N5*KNWl~}mYAk)t^6uWv zhVN=bGOq$_T7&QDN_~*YBOzPwJrpFq?b(Ghi!HLC#-RKyl)qvIl%I%LCg~t%w6^z* z<1vO)v3O-Skw2wb5IDV~rvg36&(CHP5q9ky#co5|3ynv5RH8LYwbl#^=_x%!0QFQ; zC~2Oe6=)nSL5wamI?z$Etw>zGv#{s9BOFwaFs4vaK2a-(4(TYZ!VZhZDMs)XOI{Po zftFy25=ztSjQIv1u*50m;uLd@XYJwa(CY+wz9F53L&|5p+CVV^r97nP&`fxt;essl z{>D^FOv$eFxJO{xD3hxKWufouZo&9_BM@p_vngEJ2b3}T@mijP?s~Dh>UAhsX>$xP z`7-)KjPJ(_+9UAszmqQ|6q}Ec=UA=FC$$rkJ5jt*T^ezOb^B~EW?$`xgJjypUkWBv zCZ(C!+?V@7f=z*$Ef)uZ_I>g&vGmfqqrw7H~P4s z+`zAq>`RYf-mDgR`|@UHx*#J=UfU16+pn8#(HC*>zO0*m=P?0B@7Za;grZ3ALav`( zh{V9ws`(OJ13yGWVbdJw{VOV8m>JWA!`jbdi4L0E)?Ka6eFv>6&iU(!;Y zE;dCNv!`Td8Ywfj9)V(mJnh6ER~5>iv_jKlodN^@W+WMGS{Ih zJ+>uXU9(bgv|?wp*{BHg*SFqBQRQj%lLCg*xFWyL!R4d{n<=*ZLF2> z&nDn5yT$r{$!4K*QLHZuoz04%(v_oV~u`>PJ8mz*vBA{55d`{S79JdDuxwJs6> zeYrmM;76O$xO`*%F%ceJnSu0MdC!~USn7tO8RotV1)Z-Ec6;JZNfoB>Q#?^nGbGNZ z6jSln>X7I2+?Sn!1T&6J#g|uwS7H6gs@O1`X+&e%Rrr7TM`_FDg28b;kau?ZJz5LpyuvS z?C^QsorggLGqktt;HGz_u?URfno%uDjjHW4KxdMH(SOB~?#2Nkh-j2xX~SO?zm`lT z7{}hyhV6;_v6jLki%qJ3EP!ZGAy-@OY0q73%>=%;e5jdF3N!FjSw$UO#^u3PkF#h9 zzHwuYU@69Jc55Ez9kQ50FeJ9QY8j_iIM$W57t1H>9l zK_{Fg@{kg6qC7uHnu9NNwXNL`9v@=MCLv*?He`%D2yZRY z18Q00JewI<;o|>yh0B->drY(CF5EJWpc0Jm=(@_!n;al%snu~j56dF#IBW^ctiMZW z))Zq2&6dvH)1+|pH#L^fka3O=!!h>OZT*Okpn97&WR5$gY3uZXnbsKMIuomS{QIkT zG~5hSlWpF(lN3Q&7)1h1l1I(hNYXQ`DzW59|D_oWC zx$`S56l&CAp^&)Ec8?>%m7zw!m^DuNY9>@~xQb;|lPk8Z)ds=xn>id3bT|=vENi+s z6jPadE~r;Y;cR^omN?z@4#aAaN4+IZOO1|>e2P_@ST3O9l^tq{0s0P--fcr1K)cWh z)6gG^rhHVMc%v=8oc1(H0JU}NaeA#LU3Feq0hSTO0hxWARqb)01>qP~UC4c^^*Ig9 zB*cEOIY^h$gz?`brvVsVS1d2WLd12d3uxJkebWBK9IznK4h;%{!fU(~1sB$0Xd}-k za}hy8>K#E}%~;a_@YsS`LbzevRrk3&Xoor3;md!;JQcd`{PG4azT~$J$iF!fYaY)!5K5!RfwF^GJZ+DP6;b;g5g`Z4{ok#fLGh5^DT(8IPn`{)m33c%m+22yQ}FaVNKs``&kgA78;nMn|n7X;RYXywyi|Vz}nAq=>IctNy^b>)-^!{ zlpwH!IP5kQyOM6jI|b`qBaQ(C%>R2}Kk9j6sbx$*M!BO+h5SbRqh6M}agHevU=V?@%D(D84lz z%=ZY!0%jYmv*=7ursEKa@Nr-(MA`XR;v`SSGHA~NYbCzUGD~k`K^`(C;;pCe$f36* z&DI%BaNy9lk;d{QAh2D8bQscQNKZq$80lLMTsn}OOnW1}1=|N;I~K*lMr=36_CZKb z#P+#3yH_}geF`D$5Ox7uL$+^*kXlXpQfPy?`s#G7q~4E~zkfHW-d`kU@QwxdP$3mV z`a%_odISuG6?$Fp0u`zqGnFv+uMubskZDt>ka0mKTV%?e>IjGQ-a!RwSH`^#_x0Yw zDRlL+QO95&wg=#qCmVbnLWOT(De`Ef4G?A8O7vFHTizyGj9ZCj3O!$sLh`enmP3;O u>q1Sg+17GcO=EHSJXO9z466&@rA)~BH2w&+?Erccm@+Xq;9EcChW`T!pR+#z literal 0 HcmV?d00001 diff --git a/mod/src/StarWarsLegion.lua b/mod/src/StarWarsLegion.lua index 2ddaa696..989babcf 100644 --- a/mod/src/StarWarsLegion.lua +++ b/mod/src/StarWarsLegion.lua @@ -4,6 +4,7 @@ require('!/common/SHA256') require('!/data/ListBuilder') require('!/generated/cards') require('!/UI') +require('!/Overlays') require('!/RangeRulers') require('!/Cohesion') diff --git a/mod/src/StarWarsLegion/Order_Token.a57c41.lua b/mod/src/StarWarsLegion/Order_Token.a57c41.lua index fbf2ff74..17eee6fd 100644 --- a/mod/src/StarWarsLegion/Order_Token.a57c41.lua +++ b/mod/src/StarWarsLegion/Order_Token.a57c41.lua @@ -366,15 +366,20 @@ function resetButtons() end end -function toggleCohesionRuler() +function toggleCohesionRuler(_, playerColor) + -- Mac fallback: capture playerColor (TTS passes it as 2nd arg on click) + -- and route through gCohesionTrigger so the per-seat router picks the + -- right renderer for the clicking player. if not rulerOn then - selectedUnitObj.call("spawnCohesionRuler", selectedUnitObj) + Global.call("gCohesionTrigger", { + figGUID = selectedUnitObj.getGUID(), + playerColor = playerColor, + }) rulerOn = true else selectedUnitObj.call("clearCohesionRuler") rulerOn = false end - end ------------------------------------------------- NEXTUNIT ------------------------------------------------------------ @@ -551,26 +556,37 @@ function moveUnit(isDeploy) local maxMoveBundles = getMovementLinks() local baseSizeMoveBundles = maxMoveBundles[unitData.baseSize] - local maxMoveTemplateBundleToSpawn = baseSizeMoveBundles[unitData.selectedSpeed] + local maxMoveTemplateBundleToSpawn = baseSizeMoveBundles and baseSizeMoveBundles[unitData.selectedSpeed] - if isDeploy == false then - --max movement ring projector + -- Mac fallback: permissive condition (covers nil + false) so the overlay + -- also fires when changeSpeed2/3 calls moveUnit() with no isDeploy arg. + if isDeploy ~= true then if maxMoveTemplateBundleToSpawn ~= nil then - maxMoveTemplate = spawnObject({ - type = "Custom_AssetBundle", - position = {basePos.x, basePos.y + 20, basePos.z}, - rotation = {0, basePos.y, 0}, - scale = {0,0,0} -- 0 scale will hide TTS default box and won't impact projector - }) - - maxMoveTemplate.setCustomObject({ - type = 0, - assetbundle = maxMoveTemplateBundleToSpawn - }) - - maxMoveTemplate.setLock(true) - maxMoveTemplate.use_gravity = false - maxMoveTemplate.setName("Maximum Move") + local _macMode = Global.call("gGetMode", {color = macActivePlayerForMove}) + if _macMode == "windows" then + -- WINDOWS ORIGINAL: spawn Custom_AssetBundle Projector + maxMoveTemplate = spawnObject({ + type = "Custom_AssetBundle", + position = {basePos.x, basePos.y + 20, basePos.z}, + rotation = {0, basePos.y, 0}, + scale = {0,0,0} + }) + maxMoveTemplate.setCustomObject({ + type = 0, + assetbundle = maxMoveTemplateBundleToSpawn + }) + maxMoveTemplate.setLock(true) + maxMoveTemplate.use_gravity = false + maxMoveTemplate.setName("Maximum Move") + else + -- MAC FALLBACK: route through the Global Overlays manager + Global.call("gSpawnMaxMove", { + figGUID = selectedUnitObj.getGUID(), + baseSize = unitData.baseSize, + speed = unitData.selectedSpeed, + }) + maxMoveTemplate = nil + end end end ------------------------------------------- SPAWN BUTTON ------------------------------------------- @@ -722,7 +738,8 @@ function moveStart() end) end -function moveBackwards() +function moveBackwards(_, playerColor) + macActivePlayerForMove = playerColor -- Mac fallback per-seat capture self.editButton({ index = 11, click_function = "moveForward", @@ -733,7 +750,8 @@ function moveBackwards() moveUnit() end -function moveForward() +function moveForward(_, playerColor) + macActivePlayerForMove = playerColor -- Mac fallback per-seat capture self.editButton({ index = 11, click_function = "moveBackwards", @@ -744,12 +762,14 @@ function moveForward() moveUnit() end -function moveLeft() +function moveLeft(_, playerColor) + macActivePlayerForMove = playerColor -- Mac fallback per-seat capture moveDirection = "left" moveUnit() end -function moveRight() +function moveRight(_, playerColor) + macActivePlayerForMove = playerColor -- Mac fallback per-seat capture moveDirection = "right" moveUnit() end @@ -780,9 +800,14 @@ function clearMovementTemplates() if templateB ~= nil then destroyObject(templateB) end + -- Mac fallback dual-path: destroy bundle if Windows-mode spawn produced + -- one, AND clear the Global Overlays manager entry (Mac fallback). Either + -- may be set depending on the active player's mode at spawn time. if maxMoveTemplate ~= nil then - destroyObject(maxMoveTemplate) + pcall(destroyObject, maxMoveTemplate) end + Global.call("gClearAllMaxMove", {}) + maxMoveTemplate = nil end function clearCohesionRulers() @@ -803,21 +828,27 @@ end ------------------------------------------------- CHANGESPEED------------------------------------------------------------ -function changeSpeed1() +-- Mac fallback per-seat capture: each speed button stashes the clicking +-- player's color so the dual-path branch in moveUnit() can pick the right +-- renderer for the Maximum Move overlay. +function changeSpeed1(_, playerColor) + macActivePlayerForMove = playerColor unitData.selectedSpeed = 1 setTemplateVariables() clearTemplates() moveUnit() end -function changeSpeed2() +function changeSpeed2(_, playerColor) + macActivePlayerForMove = playerColor unitData.selectedSpeed = 2 setTemplateVariables() clearTemplates() moveUnit() end -function changeSpeed3() +function changeSpeed3(_, playerColor) + macActivePlayerForMove = playerColor unitData.selectedSpeed = 3 setTemplateVariables() clearTemplates() @@ -919,11 +950,15 @@ function attack() attackMode() end -function targetingMode() +function targetingMode(_, playerColor) + -- Mac fallback: capture playerColor and route through gRangeTrigger. if not enemyHighlighted then exitAttackMode() highlightEnemies() - spawnRangeRuler(selectedUnitObj) + Global.call("gRangeTrigger", { + figGUID = selectedUnitObj.getGUID(), + playerColor = playerColor, + }) enemyHighlighted = true resetRangeButtons() else diff --git a/mod/src/StarWarsLegion/SETUP_CONTROLLER.1cb552.lua b/mod/src/StarWarsLegion/SETUP_CONTROLLER.1cb552.lua index 8c407c4c..399273af 100644 --- a/mod/src/StarWarsLegion/SETUP_CONTROLLER.1cb552.lua +++ b/mod/src/StarWarsLegion/SETUP_CONTROLLER.1cb552.lua @@ -251,17 +251,24 @@ function spawnBoundaryCell(cell, x, z) pos = AddVectors(pos, offset) - local projector = spawnObject({ - type = "Custom_AssetBundle", - position = pos, - scale = {0, 0, 0}, - rotation = {0, deployRotations[cell], 0} - }) - projector.setName("Deployment Boundary") - projector.setLock(true) - projector.setCustomObject({ - assetbundle = asset, - }) + -- Mac fallback dual-path: branch on the table-wide deploymentMode toggle. + local _macDeployMode = Global.call("gGetDeploymentMode") + local projector = nil + if _macDeployMode == "windows" then + projector = spawnObject({ + type = "Custom_AssetBundle", + position = pos, + scale = {0, 0, 0}, + rotation = {0, deployRotations[cell], 0} + }) + projector.setName("Deployment Boundary") + projector.setLock(true) + projector.setCustomObject({ + assetbundle = asset, + }) + else + Global.call("gSpawnDeployment", { cell = cell, pos = pos }) + end end function spawnDeploymentBoundary(matrix) @@ -314,6 +321,9 @@ function spawnDeploymentBoundary(matrix) end function clearDeploymentBoundary() + -- Mac fallback: clear Global Overlays manager entries first (no-op if + -- deployment ran in Windows mode and no Mac entries exist). + Global.call("gClearAllDeployment", {}) local battlefieldObjs = battlefieldZone.getObjects() for _, obj in pairs(battlefieldObjs) do if obj.getName() == "Deployment Boundary" then diff --git a/mod/src/includes/Cohesion.ttslua b/mod/src/includes/Cohesion.ttslua index 1a28778a..38fadbe3 100644 --- a/mod/src/includes/Cohesion.ttslua +++ b/mod/src/includes/Cohesion.ttslua @@ -58,4 +58,30 @@ function clearCohesionRuler() cohesionRuler.destruct() cohesionRuler = nil end -end \ No newline at end of file +end +-- ============================================ +-- Mac fallback per-seat router (appended). +-- Alias the original spawn/clear so Windows-mode players still get their +-- bundle Projector, while routing default calls through Global Overlays. +-- See !/Overlays for the manager. The `*Original` aliases are called by +-- gCohesionTrigger when the active player has chosen "windows" mode. +-- ============================================ +spawnCohesionRulerOriginal = spawnCohesionRuler +clearCohesionRulerOriginal = clearCohesionRuler + +function spawnCohesionRuler(cohesionSourceObject) + if not cohesionSourceObject then return end + Global.call("gCohesionTrigger", { + figGUID = cohesionSourceObject.getGUID(), + playerColor = nil, + }) +end + +function clearCohesionRuler() + if self and self.getGUID and self.getGUID() ~= "-1" then + Global.call("gClearCohesion", { figGUID = self.getGUID() }) + end + if cohesionRuler ~= nil then + pcall(clearCohesionRulerOriginal) + end +end diff --git a/mod/src/includes/Overlays.ttslua b/mod/src/includes/Overlays.ttslua new file mode 100644 index 00000000..d4849270 --- /dev/null +++ b/mod/src/includes/Overlays.ttslua @@ -0,0 +1,929 @@ +-- MacFallback.ttslua — Mac fallback for the TTS Unity 6 Projector magenta bug. +-- Replaces every Custom_AssetBundle Projector overlay (Cohesion / Range / +-- Maximum Move / Deployment Boundary) with a vector-lines + decal hybrid +-- rendered via Global.setVectorLines / Global.setDecals. Per-seat toggle so +-- Windows players keep the original Projectors. + +-- Manager Global + event handlers + vector-lines rendering. +-- Called by Unit_Leader / Order_Token via Global.call from their patched +-- Cohesion functions. +-- ============================================ + +activeOverlays = activeOverlays or {} +hiddenWhilePickedUp = hiddenWhilePickedUp or {} + +-- Public hosting for the PNG assets used by the Mac fallback overlays. +-- TTS loads decals from HTTP(S) URLs (Steam Workshop UGC, GitHub raw, or any +-- static host). After merge, this points to the repo's mac-fallback-assets/ +-- folder; before merge, replace with the PR branch URL for testing. +local ASSETS_BASE = "https://raw.githubusercontent.com/ironsquadronfr-hub/tts/mac-projector-fallback/mod/data/mac-fallback-assets/" +local COHESION_HALO_URL = ASSETS_BASE .. "cohesion_halo.png" +local MAX_MOVE_CYAN_URL = ASSETS_BASE .. "max_move_cyan.png" +local DEPLOYMENT_RED_URL = ASSETS_BASE .. "deployment_red.png" +local DEPLOYMENT_BLUE_URL = ASSETS_BASE .. "deployment_blue.png" +local RANGE_DECAL_URLS = { + smokeToken = ASSETS_BASE .. "range_smokeToken.png", + token = ASSETS_BASE .. "range_token.png", + tokenRangeTwo = ASSETS_BASE .. "range_tokenRangeTwo.png", + poi = ASSETS_BASE .. "range_poi.png", + bombCart = ASSETS_BASE .. "range_bombCart.png", + -- Fig leaders: per-base-size PNGs (bands pre-offset by the base radius so + -- the decal bands line up with the vector-line rings). + fig_leader = { + small = ASSETS_BASE .. "range_fig_leader_small.png", + medium = ASSETS_BASE .. "range_fig_leader_medium.png", + large = ASSETS_BASE .. "range_fig_leader_large.png", + huge = ASSETS_BASE .. "range_fig_leader_huge.png", + laat = ASSETS_BASE .. "range_fig_leader_laat.png", + epic = ASSETS_BASE .. "range_fig_leader_epic.png", + long = ASSETS_BASE .. "range_fig_leader_long.png", + snail = ASSETS_BASE .. "range_fig_leader_snail.png", + }, +} +local RANGE_MAX_RADIUS = { + fig_leader = 30, -- R5 max + smokeToken = 1, + token = 1, + tokenRangeTwo = 2, + poi = 3, + bombCart = 2, +} + +-- Range band colors (RGBA) extracted from BB_RangeProjector material defaults +-- + demi-portée blanche pour SWL Range 0.5 (3 inches) +local RANGE_COLORS = { + half = {1.0, 1.0, 1.0, 0.6}, -- 0.5 = 3" white + one = {1.0, 0.764, 0.0, 0.7}, -- R1 yellow + two = {1.0, 0.459, 0.0, 0.7}, -- R2 orange + three = {1.0, 0.079, 0.0, 0.7}, -- R3 red + four = {0.764, 0.0, 0.259, 0.7}, -- R4 magenta + five = {0.528, 0.0, 0.443, 0.7}, -- R5 violet +} + +-- Per-rangeKey configurations: list of {radius_inches, color_name} +local RANGE_CONFIGS = { + -- Fig leaders: half + 4 bands at SWL standard ranges 3/6/12/18/24" + small = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + medium = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + large = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + huge = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + laat = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + epic = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + long = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + snail = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + -- Token rangeKeys (1 to 2 bands depending on use) + smokeToken = {{r=1,c="one"}}, + token = {{r=1,c="one"}}, + tokenRangeTwo = {{r=1,c="one"},{r=2,c="two"}}, + poi = {{r=3,c="one"}}, + bombCart = {{r=1,c="one"},{r=2,c="two"}}, +} + +local COHESION_RADIUS_IN_MAC = { + small = 3.531496, -- 27mm (halfCohesion_27mm OrthographicSize) + medium = 3.984252, -- 50mm + large = 4.377953, -- 70mm +} + +-- Base radius (inches) from base diameter in mm. +local FIG_BASE_RADIUS_IN = { + small = 27 / 2 / 25.4, -- 0.5315" + medium = 50 / 2 / 25.4, -- 0.9842" + large = 70 / 2 / 25.4, -- 1.3779" + huge = 100 / 2 / 25.4, -- 1.9685" + laat = 120 / 2 / 25.4, -- 2.3622" + epic = 150 / 2 / 25.4, -- 2.9527" + long = 50 / 2 / 25.4, -- approx for oblong + snail = 50 / 2 / 25.4, -- ditto +} + +local function macFigBaseRadius(fig) + if not fig then return 0.5315 end + local data = fig.getTable("unitData") + if not data or not data.baseSize then return 0.5315 end + return FIG_BASE_RADIUS_IN[data.baseSize] or 0.5315 +end + +-- Token base radii (inches) — measured from token edge for range bands +local TOKEN_BASE_RADIUS = { + smokeToken = 18.8 / 2 / 25.4, -- 0.370" + token = 25.1 / 2 / 25.4, -- 0.494" + tokenRangeTwo = 25.1 / 2 / 25.4, -- 0.494" + poi = 50.8 / 2 / 25.4, -- 1.000" + bombCart = 50.0 / 2 / 25.4, -- 0.984" +} + +-- Maximum Move radii (inches), indexed by baseSize then speed 1..3 +-- Source: ProjectorRadius from BB_MovementProjector materials (single variants) +local MAX_MOVE_RADIUS = { + small = { 4.547244, 6.515748, 8.484252}, -- 27mm + medium = { 5.669292, 7.637795, 9.606299}, -- 50mm + large = { 6.850394, 8.818897, 10.787401}, -- 70mm + huge = { 8.622047, 10.590551, 12.559055}, -- 100mm + laat = { 9.803149, 11.771653, 13.740157}, -- 120mm + epic = {11.574803, 13.543307, 15.511811}, -- 150mm + long = {13.051181, 15.019685, 16.988190}, -- oblong + snail = {14.527559, 16.496063, 18.464567}, -- snail +} + +-- Deployment cell dimensions (width X, depth Z, inches). +-- All cells live on a 6"×6" grid; sub-cells are halves/quarters of that cell. +local DEPLOYMENT_CELL_SIZES = { + r = {6, 6}, b = {6, 6}, -- full cell + rl = {6, 6}, bl = {6, 6}, -- large variant (also full cell) + rh = {6, 3}, bh = {6, 3}, -- horizontal half (3" deep) + rs = {3, 6}, bs = {3, 6}, -- vertical half (3" wide), x-offset +1.5 + rss = {3, 6}, bss = {3, 6}, -- vertical half (3" wide), x-offset -1.5 + rc = {3, 3}, bc = {3, 3}, -- corner quarter + rcc = {3, 3}, bcc = {3, 3}, -- opposite corner quarter +} + +local function macRayGroundY(x, z, offset) + local hits = Physics.cast({ + origin = {x, 30, z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 50, + }) + for _, h in ipairs(hits) do + return h.point.y + (offset or 0.05) + end + return offset or 0.05 +end + +local function macBuildRect(cx, cz, hw, hd, color, thickness) + local segPerSide = 8 + local pts = {} + local corners = { + {cx - hw, cz + hd}, {cx + hw, cz + hd}, + {cx + hw, cz - hd}, {cx - hw, cz - hd}, + } + for i = 1, 4 do + local a = corners[i] + local b = corners[i % 4 + 1] + for s = 0, segPerSide - 1 do + local t = s / segPerSide + local x = a[1] + (b[1] - a[1]) * t + local z = a[2] + (b[2] - a[2]) * t + table.insert(pts, {x, macRayGroundY(x, z), z}) + end + end + local c1 = corners[1] + table.insert(pts, {c1[1], macRayGroundY(c1[1], c1[2]), c1[2]}) + return {points = pts, color = color, thickness = thickness or 0.06} +end + +local function macBuildRing(centerPos, radius, color, thickness, ignoreObj) + local nSeg = 64 + local pts = {} + for i = 0, nSeg do + local a = (i / nSeg) * 2 * math.pi + local x = centerPos.x + radius * math.cos(a) + local z = centerPos.z + radius * math.sin(a) + local hits = Physics.cast({ + origin = {x, centerPos.y + 10, z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local y = centerPos.y + 0.05 + for _, h in ipairs(hits) do + if h.hit_object ~= ignoreObj then + y = h.point.y + 0.05 + break + end + end + table.insert(pts, {x, y, z}) + end + return {points = pts, color = color, thickness = thickness or 0.05} +end + +local macBuilders = {} + +macBuilders.range = function(fig, params) + local pos = fig.getPosition() + -- Try to resolve rangeKey from params, then from the figure's script var + local rangeKey = (params and params.rangeKey) or fig.getVar("rangeKey") + local config, decalURL, maxR, br + if rangeKey and RANGE_CONFIGS[rangeKey] then + config = RANGE_CONFIGS[rangeKey] + decalURL = RANGE_DECAL_URLS[rangeKey] + maxR = RANGE_MAX_RADIUS[rangeKey] + br = TOKEN_BASE_RADIUS[rangeKey] or 0 + else + local data = fig.getTable("unitData") + local baseSize = (data and data.baseSize) or "small" + config = RANGE_CONFIGS[baseSize] or RANGE_CONFIGS.small + decalURL = RANGE_DECAL_URLS.fig_leader[baseSize] + or RANGE_DECAL_URLS.fig_leader.small + maxR = RANGE_MAX_RADIUS.fig_leader + br = macFigBaseRadius(fig) + end + + -- SWL convention: all distances are measured from the base/token edge. + + -- Vector lines (contour) + local lines = {} + for _, band in ipairs(config) do + table.insert(lines, macBuildRing(pos, band.r + br, RANGE_COLORS[band.c], 0.1, fig)) + end + + -- Decal halo (filled bands) — flat on the ground under the fig + local hits = Physics.cast({ + origin = {pos.x, pos.y + 10, pos.z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local groundY = pos.y + for _, h in ipairs(hits) do + if h.hit_object ~= fig then groundY = h.point.y; break end + end + + local effectiveMaxR = maxR + br + return { + lines = lines, + decals = { + { + name = "range_decal_" .. fig.getGUID(), + url = decalURL, + position = {pos.x, groundY + 0.02, pos.z}, -- below cohesion halo + rotation = {90, 0, 0}, + scale = {effectiveMaxR * 2, effectiveMaxR * 2, effectiveMaxR * 2}, + } + }, + } +end + +macBuilders.maxmove = function(fig, params) + local pos = fig.getPosition() + local baseSize = (params and params.baseSize) or "small" + local speed = (params and params.speed) or 1 + local radii = MAX_MOVE_RADIUS[baseSize] or MAX_MOVE_RADIUS.small + local r = radii[speed] or radii[1] + local color = {0.333, 0.800, 1.000, 0.7} + -- Base ring: small white outline matching the figure's base + -- (BB_MovementProjector renders a base ring on top of the cyan max-move). + local br = FIG_BASE_RADIUS_IN[baseSize] or FIG_BASE_RADIUS_IN.small + local baseColor = {1, 1, 1, 0.8} + + -- Ground raycast for decal position + local hits = Physics.cast({ + origin = {pos.x, pos.y + 10, pos.z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local groundY = pos.y + for _, h in ipairs(hits) do + if h.hit_object ~= fig then groundY = h.point.y; break end + end + + return { + lines = { + macBuildRing(pos, r, color, 0.1, fig), -- outer cyan max-move + macBuildRing(pos, br, baseColor, 0.06, fig), -- inner white base ring + }, + decals = { + { + name = "maxmove_decal_" .. fig.getGUID(), + url = MAX_MOVE_CYAN_URL, + position = {pos.x, groundY + 0.025, pos.z}, + rotation = {90, 0, 0}, + scale = {r * 2, r * 2, r * 2}, + } + }, + } +end + +macBuilders.deployment = function(_, params) + local pos = params.pos -- {x, y, z} cell center (after offset) + local cell = params.cell -- "r"/"b"/"rh"/... key + local size = DEPLOYMENT_CELL_SIZES[cell] or {6, 6} + local w, d = size[1], size[2] + local cx, cz = pos[1], pos[3] + local hw, hd = w / 2, d / 2 + + local isRed = cell:sub(1, 1) == "r" + local lineColor = isRed and {1, 0.15, 0.15, 0.9} or {0.15, 0.4, 1, 0.9} + local url = isRed and DEPLOYMENT_RED_URL or DEPLOYMENT_BLUE_URL + + local groundY = macRayGroundY(cx, cz, 0) + + return { + lines = { macBuildRect(cx, cz, hw, hd, lineColor, 0.08) }, + decals = { + { + name = "deployment_decal_" .. cell .. "_" .. tostring(cx) .. "_" .. tostring(cz), + url = url, + position = {cx, groundY + 0.03, cz}, + rotation = {90, 0, 0}, + scale = {w, d, 1}, + } + }, + } +end + +macBuilders.cohesion = function(fig, params) + local pos = fig.getPosition() + local data = fig.getTable("unitData") + local baseSize = (data and data.baseSize) or "small" + local r = COHESION_RADIUS_IN_MAC[baseSize] or COHESION_RADIUS_IN_MAC.small + if params and params.radius then r = params.radius end + + -- Find ground level under the fig so the flat decal sits on the table, + -- not at the fig pivot (which is above the base). + local hits = Physics.cast({ + origin = {pos.x, pos.y + 10, pos.z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local groundY = pos.y + for _, h in ipairs(hits) do + if h.hit_object ~= fig then + groundY = h.point.y + break + end + end + + return { + lines = { macBuildRing(pos, r, {1, 1, 1, 0.8}, 0.06, fig) }, + decals = { + { + name = "cohesion_halo_" .. fig.getGUID(), + url = COHESION_HALO_URL, + position = {pos.x, groundY + 0.04, pos.z}, + rotation = {90, 0, 0}, + scale = {r * 2, r * 2, r * 2}, + } + }, + } +end + +function macRedrawAll() + local lines, decals = {}, {} + -- Preload all texture URLs (invisible decals far below the table) so TTS + -- keeps them cached and we never get the white-square flash on respawn. + local preloadURLs = { + COHESION_HALO_URL, + RANGE_DECAL_URLS.smokeToken, + RANGE_DECAL_URLS.token, RANGE_DECAL_URLS.tokenRangeTwo, + RANGE_DECAL_URLS.poi, RANGE_DECAL_URLS.bombCart, + RANGE_DECAL_URLS.fig_leader.small, RANGE_DECAL_URLS.fig_leader.medium, + RANGE_DECAL_URLS.fig_leader.large, RANGE_DECAL_URLS.fig_leader.huge, + RANGE_DECAL_URLS.fig_leader.laat, RANGE_DECAL_URLS.fig_leader.epic, + RANGE_DECAL_URLS.fig_leader.long, RANGE_DECAL_URLS.fig_leader.snail, + MAX_MOVE_CYAN_URL, + DEPLOYMENT_RED_URL, DEPLOYMENT_BLUE_URL, + } + for i, url in ipairs(preloadURLs) do + table.insert(decals, { + name = "preload_" .. i, + url = url, + position = {0, -200 - i * 0.01, 0}, + rotation = {90, 0, 0}, + scale = {0.01, 0.01, 0.01}, + }) + end + local stale = {} + for key, entry in pairs(activeOverlays) do + local b = macBuilders[entry.type] + if b then + -- Deployment entries have no fig (params-driven). Others need a + -- live Object — drop them silently if destroyed/invalid. + local needsFig = (entry.type ~= "deployment") + if needsFig and (not entry.fig + or type(entry.fig.getPosition) ~= "function") then + stale[#stale + 1] = key + else + local ok, out = pcall(b, entry.fig, entry.params) + if ok and out then + for _, l in ipairs(out.lines or {}) do + table.insert(lines, l) + end + for _, d in ipairs(out.decals or {}) do + table.insert(decals, d) + end + else + stale[#stale + 1] = key + end + end + end + end + for _, k in ipairs(stale) do activeOverlays[k] = nil end + Global.setVectorLines(lines) + Global.setDecals(decals) +end + +function gSpawnCohesion(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":cohesion"] = { + type = "cohesion", fig = fig, params = params or {} + } + macRedrawAll() +end + +function gClearCohesion(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":cohesion"] = nil + macRedrawAll() +end + +function gToggleCohesion(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local key = fig.getGUID() .. ":cohesion" + if activeOverlays[key] then + gClearCohesion(params) + else + gSpawnCohesion(params) + end +end + +-- Range polling: a slow timer (5 frames = ~12 fps) that re-draws active +-- range rulers so they follow figures in real-time. Stops automatically when +-- no range overlay is active anymore — avoids the overnight memory leak. +macRangePollingActive = macRangePollingActive or false + +function macRangePoll() + local hasRange = false + for _, e in pairs(activeOverlays) do + if e.type == "range" then hasRange = true; break end + end + if hasRange then + macRedrawAll() + Wait.frames(macRangePoll, 5) + else + macRangePollingActive = false + end +end + +function gSpawnRange(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":range"] = { + type = "range", fig = fig, params = params or {} + } + macRedrawAll() + if not macRangePollingActive then + macRangePollingActive = true + Wait.frames(macRangePoll, 5) + end +end + +function gClearRange(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":range"] = nil + macRedrawAll() +end + +function gToggleRange(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local key = fig.getGUID() .. ":range" + if activeOverlays[key] then + gClearRange(params) + else + gSpawnRange(params) + end +end + +function gClearAllRange() + for k, e in pairs(activeOverlays) do + if e.type == "range" then activeOverlays[k] = nil end + end + macRedrawAll() +end + +function gSpawnDeployment(params) + -- params: { cell = "r" | "b" | ..., pos = {x, y, z} } + if not params or not params.cell or not params.pos then return end + local key = "deploy:" .. params.cell .. ":" + .. tostring(params.pos[1]) .. ":" .. tostring(params.pos[3]) + activeOverlays[key] = {type = "deployment", fig = nil, params = params} + macRedrawAll() +end + +function gClearAllDeployment() + for k, e in pairs(activeOverlays) do + if e.type == "deployment" then activeOverlays[k] = nil end + end + macRedrawAll() +end + +function gSpawnMaxMove(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":maxmove"] = { + type = "maxmove", fig = fig, params = params or {} + } + macRedrawAll() +end + +function gClearMaxMove(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":maxmove"] = nil + macRedrawAll() +end + +function gClearAllMaxMove() + for k, e in pairs(activeOverlays) do + if e.type == "maxmove" then activeOverlays[k] = nil end + end + macRedrawAll() +end + +function onObjectPickUp(player_color, obj) + if not obj or not obj.getGUID then return end + local guid = obj.getGUID() + local prefix = guid .. ":" + local plen = #prefix + local changed = false + for key, entry in pairs(activeOverlays) do + if key:sub(1, plen) == prefix then + -- Cohesion: static, hide during pickup, restore on drop. + -- Range: keep visible; the polling timer redraws each tick so + -- the ruler follows the figure in real-time during the drag. + if entry.type == "cohesion" then + hiddenWhilePickedUp[key] = entry + activeOverlays[key] = nil + changed = true + end + end + end + if changed then macRedrawAll() end +end + +function onObjectDrop(player_color, obj) + if not obj or not obj.getGUID then return end + local guid = obj.getGUID() + local prefix = guid .. ":" + local plen = #prefix + local changed = false + for key, entry in pairs(hiddenWhilePickedUp) do + if key:sub(1, plen) == prefix then + activeOverlays[key] = entry + hiddenWhilePickedUp[key] = nil + changed = true + end + end + if changed then macRedrawAll() end +end + +function onObjectDestroy(obj) + if not obj or not obj.getGUID then return end + local guid = obj.getGUID() + local prefix = guid .. ":" + local plen = #prefix + local changed = false + for key in pairs(activeOverlays) do + if key:sub(1, plen) == prefix then + activeOverlays[key] = nil + changed = true + end + end + for key in pairs(hiddenWhilePickedUp) do + if key:sub(1, plen) == prefix then + hiddenWhilePickedUp[key] = nil + end + end + if changed then macRedrawAll() end +end + +-- Texture preload is handled by macRedrawAll which always includes all PNG +-- URLs as invisible decals far below the table. No separate preload needed. + +-- ============================================ +-- PER-SEAT MODE TOGGLE (Cohesion + Range) +-- Each seated player picks their renderer: "mac" (this patch) or +-- "windows" (original bundle Projector). When a player triggers an overlay, +-- the router checks THEIR mode and uses that renderer. The rendered overlay +-- is visible to everyone (TTS engine limitation). +-- ============================================ + +playerOverlayMode = playerOverlayMode or {} -- color → "mac" | "windows" +deploymentMode = deploymentMode or "mac" -- table-wide setting + +function gGetMode(params) + return playerOverlayMode[params.color] or "mac" +end + +function gGetDeploymentMode() + return deploymentMode +end + +function gToggleDeploymentMode() + deploymentMode = (deploymentMode == "mac") and "windows" or "mac" + macRefreshModeUI() +end + +function gCohesionTrigger(params) + if not params or not params.figGUID then return end + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local mode = playerOverlayMode[params.playerColor] or "mac" + if mode == "windows" then + -- Object may not have the original alias (e.g., hovered non-fig object). + -- pcall guards against "no such function" errors that would otherwise + -- propagate out of the hotkey/button callback. + local ok = pcall(function() fig.call("spawnCohesionRulerOriginal", fig) end) + if not ok then gToggleCohesion({figGUID = params.figGUID}) end + else + gToggleCohesion({figGUID = params.figGUID}) + end +end + +function gRangeTrigger(params) + if not params or not params.figGUID then return end + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local mode = playerOverlayMode[params.playerColor] or "mac" + if mode == "windows" then + local ok = pcall(function() fig.call("spawnRangeRulerOriginal", fig) end) + if not ok then gToggleRange({figGUID = params.figGUID}) end + else + gToggleRange({figGUID = params.figGUID}) + end +end + +-- UI: floating panel with one toggle button per seated player +function macModeClick(player, _, id) + local color = id:sub(9) -- "macmode_Red" → "Red" + if player.color ~= color then + broadcastToColor("Only the player at this seat can toggle their mode.", + player.color, {1, 0.5, 0.5}) + return + end + local cur = playerOverlayMode[color] or "mac" + playerOverlayMode[color] = (cur == "mac") and "windows" or "mac" + -- Wipe any active Cohesion/Range overlays for this player so the next + -- trigger redraws in the chosen mode without stale visuals. + macRefreshModeUI() +end + +local function macFindNodeById(tree, id) + for _, node in ipairs(tree) do + if node.attributes and node.attributes.id == id then return node, tree end + if node.children then + local found, parent = macFindNodeById(node.children, id) + if found then return found, parent end + end + end + return nil, nil +end + +-- Tracks the panel's desired active state across refreshes. Persists so a +-- click on the close X before the initial refresh is honored. +macModePanelActive = (macModePanelActive == nil) and true or macModePanelActive + +function macModeToggleVisibility() + macModePanelActive = not macModePanelActive + local tree = UI.getXmlTable() or {} + local panel = macFindNodeById(tree, "macModePanel") + if panel then + panel.attributes.active = macModePanelActive and "true" or "false" + UI.setXmlTable(tree) + else + macRefreshModeUI() + end +end + +function macRefreshModeUI() + local seated = Player.getPlayers() + local rows = {{ + tag = "Panel", + attributes = { + color = "transparent", + preferredHeight = "24", + }, + children = { + { + tag = "Text", + attributes = { + text = "Mac TTS U6 Patch", + fontSize = "16", + color = "white", + alignment = "MiddleLeft", + rectAlignment = "MiddleLeft", + }, + }, + { + tag = "Button", + attributes = { + id = "macModePanelClose", + text = "X", + onClick = "macModeToggleVisibility", + color = "#3a1e1e", + textColor = "white", + fontSize = "12", + width = "24", + height = "20", + rectAlignment = "MiddleRight", + }, + }, + }, + }, { + tag = "Text", + attributes = { + text = "Cohesion & Range: pick your renderer", + fontSize = "12", + color = "#bbbbbb", + alignment = "MiddleCenter", + }, + }} + if #seated == 0 then + table.insert(rows, { + tag = "Text", + attributes = { + text = "(no seated players)", + fontSize = "12", + color = "#888888", + alignment = "MiddleCenter", + }, + }) + end + for _, p in ipairs(seated) do + local mode = playerOverlayMode[p.color] or "mac" + local label = p.color .. " - " .. + (mode == "mac" and "MAC FALLBACK" or "WINDOWS ORIGINAL") + local bg = (mode == "mac") and "#1e7a3a" or "#7a3a1e" + table.insert(rows, { + tag = "Button", + attributes = { + id = "macmode_" .. p.color, + text = label, + onClick = "macModeClick", + color = bg, + fontSize = "13", + textColor = "white", + preferredHeight = "28", + }, + }) + end + -- Deployment is table-wide (everyone sees the same zones); use a single + -- global toggle rather than per-seat. + table.insert(rows, { + tag = "Text", + attributes = { + text = "Deployment (table-wide)", + fontSize = "12", + color = "#bbbbbb", + alignment = "MiddleCenter", + }, + }) + table.insert(rows, { + tag = "Button", + attributes = { + id = "macmode_deployment", + text = (deploymentMode == "mac") and "DEPLOYMENT: MAC FALLBACK" + or "DEPLOYMENT: WINDOWS ORIGINAL", + onClick = "macDeploymentClick", + color = (deploymentMode == "mac") and "#1e3a7a" or "#7a3a1e", + fontSize = "13", + textColor = "white", + preferredHeight = "28", + }, + }) + -- Build my panel (will be merged into the existing UI tree below so we + -- don't clobber legionFloatingMenu / Welcome / Chess Clocks etc.). + local panel = { + tag = "Panel", + attributes = { + id = "macModePanel", + active = macModePanelActive and "true" or "false", + rectAlignment = "MiddleRight", + offsetXY = "-10 80", + width = "260", + height = "320", + color = "rgba(0.06,0.06,0.06,0.9)", + padding = "8 8 8 8", + outlineSize = "1 1", + outline = "#303030", + }, + children = {{ + tag = "VerticalLayout", + attributes = { + spacing = "4", + childForceExpandHeight = "false", + childForceExpandWidth = "true", + }, + children = rows, + }}, + } + + local tree = UI.getXmlTable() or {} + -- Remove any stale macModePanel before reinserting the fresh one. + for i = #tree, 1, -1 do + if tree[i].attributes and tree[i].attributes.id == "macModePanel" then + table.remove(tree, i) + end + end + table.insert(tree, panel) + + -- Inject a "Mac Patch" button into the bottom-right legionFloatingMenu + -- (replaces the first interactable=false placeholder button). + local menu = macFindNodeById(tree, "legionFloatingMenu") + if menu and menu.children then + local hasMine = false + for _, c in ipairs(menu.children) do + if c.attributes and c.attributes.id == "macModeMenuButton" then + hasMine = true; break + end + end + if not hasMine then + for i, c in ipairs(menu.children) do + if c.attributes and c.attributes.interactable == "false" then + menu.children[i] = { + tag = "Button", + attributes = { + id = "macModeMenuButton", + fontSize = "10", + onClick = "macModeToggleVisibility", + tooltip = "Toggle Mac TTS U6 Patch panel", + }, + value = "Mac Patch", + } + break + end + end + end + end + + UI.setXmlTable(tree) +end + +function macDeploymentClick(_, _, _) + gToggleDeploymentMode() +end + +function onPlayerChangeColor(_) macRefreshModeUI() end +function onPlayerConnect(_) macRefreshModeUI() end +function onPlayerDisconnect(_) macRefreshModeUI() end + +Wait.time(macRefreshModeUI, 2) + +-- Override hotkey init functions to capture playerColor and route through +-- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys +-- here SHADOWS the originals — when the original onLoad runs init*(), our +-- versions register the hotkey instead. + +function initCohesionHotkeys() + addHotkey("Show Cohesion On Hovered Model", + function(playerColor, hoverObject, _) + if not hoverObject or not hoverObject.interactable then return end + gCohesionTrigger({ + figGUID = hoverObject.getGUID(), + playerColor = playerColor, + }) + end) +end + +function initRangebandHotkeys() + addHotkey("Show Range On Hovered Model", + function(playerColor, hoverObject, _) + if not hoverObject or not hoverObject.interactable then return end + gRangeTrigger({ + figGUID = hoverObject.getGUID(), + playerColor = playerColor, + }) + end) +end + +-- Legacy global-scope wrappers (for any code that still calls these directly). +-- Default to mac mode when no color context is available. +function showCohesionOnHoveredModel(hoverObject) + if not hoverObject or not hoverObject.interactable then return end + gToggleCohesion({ figGUID = hoverObject.getGUID() }) +end + +function spawnCohesionRuler(cohesionSourceObject) + if not cohesionSourceObject then return end + gSpawnCohesion({ figGUID = cohesionSourceObject.getGUID() }) +end + +function clearCohesionRuler() + for key, entry in pairs(activeOverlays) do + if entry.type == "cohesion" then activeOverlays[key] = nil end + end + macRedrawAll() +end + +function showRangeOnHoveredModel(hoverObject) + if not hoverObject or not hoverObject.interactable then return end + gToggleRange({ figGUID = hoverObject.getGUID() }) +end + +function spawnRangeRuler(rangeSourceObject, _) + if not rangeSourceObject then return end + gSpawnRange({ figGUID = rangeSourceObject.getGUID() }) +end + +function clearRangeRulers() + gClearAllRange() +end + diff --git a/mod/src/includes/RangeRulers.ttslua b/mod/src/includes/RangeRulers.ttslua index 6df6e15a..c51676e0 100644 --- a/mod/src/includes/RangeRulers.ttslua +++ b/mod/src/includes/RangeRulers.ttslua @@ -80,3 +80,33 @@ function spawnRangeRuler(rangeSourceObject, projectorBundleOverride) noRulers = false end + +-- ============================================ +-- Mac fallback per-seat router (appended). +-- Mirrors the Cohesion pattern. See !/Overlays for the manager. +-- ============================================ +spawnRangeRulerOriginal = spawnRangeRuler +clearRangeRulersOriginal = clearRangeRulers + +function spawnRangeRuler(rangeSourceObject, _) + if not rangeSourceObject then return end + Global.call("gRangeTrigger", { + figGUID = rangeSourceObject.getGUID(), + playerColor = nil, + }) +end + +function clearRangeRulers() + Global.call("gClearAllRange", {}) + -- Also run the original (destroys the bundle Object spawned in Windows + -- mode). Skip if rangeRuler was never set (pure-Mac session). + if rangeRuler ~= nil then + pcall(clearRangeRulersOriginal) + end +end + +function clearRangeRuler() + if self and self.getGUID and self.getGUID() ~= "-1" then + Global.call("gClearRange", { figGUID = self.getGUID() }) + end +end From adec1721e1140c3892c3cac26b936f111c2cc476 Mon Sep 17 00:00:00 2001 From: Martin Pourrat Date: Sat, 16 May 2026 16:19:52 +0200 Subject: [PATCH 78/93] Strip non-ASCII chars from Overlays.ttslua comments TTS multiplayer sync uses an internal byte buffer that overflows when the synced LuaScript contains multi-byte UTF-8 characters, producing "output byte buffer is too small to contain the encoded data" Lua errors on the receiving client and breaking the synced script on Windows clients. Replace em dashes, arrows, and accented chars in comments with ASCII equivalents. No runtime behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) --- mod/src/includes/Overlays.ttslua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mod/src/includes/Overlays.ttslua b/mod/src/includes/Overlays.ttslua index d4849270..503a2375 100644 --- a/mod/src/includes/Overlays.ttslua +++ b/mod/src/includes/Overlays.ttslua @@ -1,4 +1,4 @@ --- MacFallback.ttslua — Mac fallback for the TTS Unity 6 Projector magenta bug. +-- MacFallback.ttslua - Mac fallback for the TTS Unity 6 Projector magenta bug. -- Replaces every Custom_AssetBundle Projector overlay (Cohesion / Range / -- Maximum Move / Deployment Boundary) with a vector-lines + decal hybrid -- rendered via Global.setVectorLines / Global.setDecals. Per-seat toggle so @@ -50,7 +50,7 @@ local RANGE_MAX_RADIUS = { } -- Range band colors (RGBA) extracted from BB_RangeProjector material defaults --- + demi-portée blanche pour SWL Range 0.5 (3 inches) +-- + demi-portee blanche pour SWL Range 0.5 (3 inches) local RANGE_COLORS = { half = {1.0, 1.0, 1.0, 0.6}, -- 0.5 = 3" white one = {1.0, 0.764, 0.0, 0.7}, -- R1 yellow @@ -104,7 +104,7 @@ local function macFigBaseRadius(fig) return FIG_BASE_RADIUS_IN[data.baseSize] or 0.5315 end --- Token base radii (inches) — measured from token edge for range bands +-- Token base radii (inches) - measured from token edge for range bands local TOKEN_BASE_RADIUS = { smokeToken = 18.8 / 2 / 25.4, -- 0.370" token = 25.1 / 2 / 25.4, -- 0.494" @@ -127,7 +127,7 @@ local MAX_MOVE_RADIUS = { } -- Deployment cell dimensions (width X, depth Z, inches). --- All cells live on a 6"×6" grid; sub-cells are halves/quarters of that cell. +-- All cells live on a 6"x6" grid; sub-cells are halves/quarters of that cell. local DEPLOYMENT_CELL_SIZES = { r = {6, 6}, b = {6, 6}, -- full cell rl = {6, 6}, bl = {6, 6}, -- large variant (also full cell) @@ -228,7 +228,7 @@ macBuilders.range = function(fig, params) table.insert(lines, macBuildRing(pos, band.r + br, RANGE_COLORS[band.c], 0.1, fig)) end - -- Decal halo (filled bands) — flat on the ground under the fig + -- Decal halo (filled bands) - flat on the ground under the fig local hits = Physics.cast({ origin = {pos.x, pos.y + 10, pos.z}, direction = {0, -1, 0}, @@ -391,7 +391,7 @@ function macRedrawAll() local b = macBuilders[entry.type] if b then -- Deployment entries have no fig (params-driven). Others need a - -- live Object — drop them silently if destroyed/invalid. + -- live Object - drop them silently if destroyed/invalid. local needsFig = (entry.type ~= "deployment") if needsFig and (not entry.fig or type(entry.fig.getPosition) ~= "function") then @@ -445,7 +445,7 @@ end -- Range polling: a slow timer (5 frames = ~12 fps) that re-draws active -- range rulers so they follow figures in real-time. Stops automatically when --- no range overlay is active anymore — avoids the overnight memory leak. +-- no range overlay is active anymore - avoids the overnight memory leak. macRangePollingActive = macRangePollingActive or false function macRangePoll() @@ -606,7 +606,7 @@ end -- is visible to everyone (TTS engine limitation). -- ============================================ -playerOverlayMode = playerOverlayMode or {} -- color → "mac" | "windows" +playerOverlayMode = playerOverlayMode or {} -- color -> "mac" | "windows" deploymentMode = deploymentMode or "mac" -- table-wide setting function gGetMode(params) @@ -653,7 +653,7 @@ end -- UI: floating panel with one toggle button per seated player function macModeClick(player, _, id) - local color = id:sub(9) -- "macmode_Red" → "Red" + local color = id:sub(9) -- "macmode_Red" -> "Red" if player.color ~= color then broadcastToColor("Only the player at this seat can toggle their mode.", player.color, {1, 0.5, 0.5}) @@ -869,7 +869,7 @@ Wait.time(macRefreshModeUI, 2) -- Override hotkey init functions to capture playerColor and route through -- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys --- here SHADOWS the originals — when the original onLoad runs init*(), our +-- here SHADOWS the originals - when the original onLoad runs init*(), our -- versions register the hotkey instead. function initCohesionHotkeys() From eefd0ba235d94fd566fa36b17497c3920773f803 Mon Sep 17 00:00:00 2001 From: Martin Pourrat Date: Sat, 16 May 2026 17:49:22 +0200 Subject: [PATCH 79/93] Add silhouette_top.png hologram cap Used by the Mac fallback hologram silhouette overlay to cap the top of the wireframe cylinder. Radial gradient green-cyan, matching the Star Wars briefing hologram aesthetic. Co-Authored-By: Claude Opus 4.7 (1M context) --- mod/data/mac-fallback-assets/silhouette_top.png | Bin 0 -> 30253 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 mod/data/mac-fallback-assets/silhouette_top.png diff --git a/mod/data/mac-fallback-assets/silhouette_top.png b/mod/data/mac-fallback-assets/silhouette_top.png new file mode 100644 index 0000000000000000000000000000000000000000..24b673050268d67f6c8c3057636a98040acc254d GIT binary patch literal 30253 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 Date: Sat, 16 May 2026 18:13:32 +0200 Subject: [PATCH 80/93] Add silhouette_cylinder.obj for Mac fallback Custom_Model Simple 48-segment cylinder mesh (radius=1, height=1, base at y=0) used to spawn a translucent volume for the Mac silhouette overlay. Custom_Model uses TTS's internal shader, so setColorTint alpha is respected (unlike the vanilla Custom_AssetBundle whose custom shader breaks under Unity 6). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../silhouette_cylinder.obj | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 mod/data/mac-fallback-assets/silhouette_cylinder.obj diff --git a/mod/data/mac-fallback-assets/silhouette_cylinder.obj b/mod/data/mac-fallback-assets/silhouette_cylinder.obj new file mode 100644 index 00000000..a333e5db --- /dev/null +++ b/mod/data/mac-fallback-assets/silhouette_cylinder.obj @@ -0,0 +1,244 @@ +# cylinder.obj - Mac silhouette overlay (radius=1, height=1, base at y=0) +o cylinder +v 1.000000 1.000000 0.000000 +v 0.991445 1.000000 0.130526 +v 0.965926 1.000000 0.258819 +v 0.923880 1.000000 0.382683 +v 0.866025 1.000000 0.500000 +v 0.793353 1.000000 0.608761 +v 0.707107 1.000000 0.707107 +v 0.608761 1.000000 0.793353 +v 0.500000 1.000000 0.866025 +v 0.382683 1.000000 0.923880 +v 0.258819 1.000000 0.965926 +v 0.130526 1.000000 0.991445 +v 0.000000 1.000000 1.000000 +v -0.130526 1.000000 0.991445 +v -0.258819 1.000000 0.965926 +v -0.382683 1.000000 0.923880 +v -0.500000 1.000000 0.866025 +v -0.608761 1.000000 0.793353 +v -0.707107 1.000000 0.707107 +v -0.793353 1.000000 0.608761 +v -0.866025 1.000000 0.500000 +v -0.923880 1.000000 0.382683 +v -0.965926 1.000000 0.258819 +v -0.991445 1.000000 0.130526 +v -1.000000 1.000000 0.000000 +v -0.991445 1.000000 -0.130526 +v -0.965926 1.000000 -0.258819 +v -0.923880 1.000000 -0.382683 +v -0.866025 1.000000 -0.500000 +v -0.793353 1.000000 -0.608761 +v -0.707107 1.000000 -0.707107 +v -0.608761 1.000000 -0.793353 +v -0.500000 1.000000 -0.866025 +v -0.382683 1.000000 -0.923880 +v -0.258819 1.000000 -0.965926 +v -0.130526 1.000000 -0.991445 +v -0.000000 1.000000 -1.000000 +v 0.130526 1.000000 -0.991445 +v 0.258819 1.000000 -0.965926 +v 0.382683 1.000000 -0.923880 +v 0.500000 1.000000 -0.866025 +v 0.608761 1.000000 -0.793353 +v 0.707107 1.000000 -0.707107 +v 0.793353 1.000000 -0.608761 +v 0.866025 1.000000 -0.500000 +v 0.923880 1.000000 -0.382683 +v 0.965926 1.000000 -0.258819 +v 0.991445 1.000000 -0.130526 +v 1.000000 0.000000 0.000000 +v 0.991445 0.000000 0.130526 +v 0.965926 0.000000 0.258819 +v 0.923880 0.000000 0.382683 +v 0.866025 0.000000 0.500000 +v 0.793353 0.000000 0.608761 +v 0.707107 0.000000 0.707107 +v 0.608761 0.000000 0.793353 +v 0.500000 0.000000 0.866025 +v 0.382683 0.000000 0.923880 +v 0.258819 0.000000 0.965926 +v 0.130526 0.000000 0.991445 +v 0.000000 0.000000 1.000000 +v -0.130526 0.000000 0.991445 +v -0.258819 0.000000 0.965926 +v -0.382683 0.000000 0.923880 +v -0.500000 0.000000 0.866025 +v -0.608761 0.000000 0.793353 +v -0.707107 0.000000 0.707107 +v -0.793353 0.000000 0.608761 +v -0.866025 0.000000 0.500000 +v -0.923880 0.000000 0.382683 +v -0.965926 0.000000 0.258819 +v -0.991445 0.000000 0.130526 +v -1.000000 0.000000 0.000000 +v -0.991445 0.000000 -0.130526 +v -0.965926 0.000000 -0.258819 +v -0.923880 0.000000 -0.382683 +v -0.866025 0.000000 -0.500000 +v -0.793353 0.000000 -0.608761 +v -0.707107 0.000000 -0.707107 +v -0.608761 0.000000 -0.793353 +v -0.500000 0.000000 -0.866025 +v -0.382683 0.000000 -0.923880 +v -0.258819 0.000000 -0.965926 +v -0.130526 0.000000 -0.991445 +v -0.000000 0.000000 -1.000000 +v 0.130526 0.000000 -0.991445 +v 0.258819 0.000000 -0.965926 +v 0.382683 0.000000 -0.923880 +v 0.500000 0.000000 -0.866025 +v 0.608761 0.000000 -0.793353 +v 0.707107 0.000000 -0.707107 +v 0.793353 0.000000 -0.608761 +v 0.866025 0.000000 -0.500000 +v 0.923880 0.000000 -0.382683 +v 0.965926 0.000000 -0.258819 +v 0.991445 0.000000 -0.130526 +v 0.000000 1.000000 0.000000 +v 0.000000 0.000000 0.000000 +f 1 49 50 2 +f 2 50 51 3 +f 3 51 52 4 +f 4 52 53 5 +f 5 53 54 6 +f 6 54 55 7 +f 7 55 56 8 +f 8 56 57 9 +f 9 57 58 10 +f 10 58 59 11 +f 11 59 60 12 +f 12 60 61 13 +f 13 61 62 14 +f 14 62 63 15 +f 15 63 64 16 +f 16 64 65 17 +f 17 65 66 18 +f 18 66 67 19 +f 19 67 68 20 +f 20 68 69 21 +f 21 69 70 22 +f 22 70 71 23 +f 23 71 72 24 +f 24 72 73 25 +f 25 73 74 26 +f 26 74 75 27 +f 27 75 76 28 +f 28 76 77 29 +f 29 77 78 30 +f 30 78 79 31 +f 31 79 80 32 +f 32 80 81 33 +f 33 81 82 34 +f 34 82 83 35 +f 35 83 84 36 +f 36 84 85 37 +f 37 85 86 38 +f 38 86 87 39 +f 39 87 88 40 +f 40 88 89 41 +f 41 89 90 42 +f 42 90 91 43 +f 43 91 92 44 +f 44 92 93 45 +f 45 93 94 46 +f 46 94 95 47 +f 47 95 96 48 +f 48 96 49 1 +f 97 2 1 +f 97 3 2 +f 97 4 3 +f 97 5 4 +f 97 6 5 +f 97 7 6 +f 97 8 7 +f 97 9 8 +f 97 10 9 +f 97 11 10 +f 97 12 11 +f 97 13 12 +f 97 14 13 +f 97 15 14 +f 97 16 15 +f 97 17 16 +f 97 18 17 +f 97 19 18 +f 97 20 19 +f 97 21 20 +f 97 22 21 +f 97 23 22 +f 97 24 23 +f 97 25 24 +f 97 26 25 +f 97 27 26 +f 97 28 27 +f 97 29 28 +f 97 30 29 +f 97 31 30 +f 97 32 31 +f 97 33 32 +f 97 34 33 +f 97 35 34 +f 97 36 35 +f 97 37 36 +f 97 38 37 +f 97 39 38 +f 97 40 39 +f 97 41 40 +f 97 42 41 +f 97 43 42 +f 97 44 43 +f 97 45 44 +f 97 46 45 +f 97 47 46 +f 97 48 47 +f 97 1 48 +f 98 49 50 +f 98 50 51 +f 98 51 52 +f 98 52 53 +f 98 53 54 +f 98 54 55 +f 98 55 56 +f 98 56 57 +f 98 57 58 +f 98 58 59 +f 98 59 60 +f 98 60 61 +f 98 61 62 +f 98 62 63 +f 98 63 64 +f 98 64 65 +f 98 65 66 +f 98 66 67 +f 98 67 68 +f 98 68 69 +f 98 69 70 +f 98 70 71 +f 98 71 72 +f 98 72 73 +f 98 73 74 +f 98 74 75 +f 98 75 76 +f 98 76 77 +f 98 77 78 +f 98 78 79 +f 98 79 80 +f 98 80 81 +f 98 81 82 +f 98 82 83 +f 98 83 84 +f 98 84 85 +f 98 85 86 +f 98 86 87 +f 98 87 88 +f 98 88 89 +f 98 89 90 +f 98 90 91 +f 98 91 92 +f 98 92 93 +f 98 93 94 +f 98 94 95 +f 98 95 96 +f 98 96 49 From 071d66479dcb3b0abd5db1a994392019aabb048b Mon Sep 17 00:00:00 2001 From: Martin Pourrat Date: Sat, 16 May 2026 18:44:03 +0200 Subject: [PATCH 81/93] Add silhouette_wireframe.obj: 3D wireframe cage mesh True 3D wireframe cylinder cage: each ring/vertical edge is modeled as a thin rectangular bar (40 bars total, ~320 verts, ~240 faces). Spawned as Custom_Model so the wireframe is a real 3D mesh on the table. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../silhouette_wireframe.obj | 562 ++++++++++++++++++ 1 file changed, 562 insertions(+) create mode 100644 mod/data/mac-fallback-assets/silhouette_wireframe.obj diff --git a/mod/data/mac-fallback-assets/silhouette_wireframe.obj b/mod/data/mac-fallback-assets/silhouette_wireframe.obj new file mode 100644 index 00000000..7e6fc13d --- /dev/null +++ b/mod/data/mac-fallback-assets/silhouette_wireframe.obj @@ -0,0 +1,562 @@ +# silhouette_wireframe.obj - 3D wireframe cage +o wireframe_cage +v 1.03923 1.04000 0.00780 +v 1.03923 0.96000 0.00780 +v 0.96077 1.04000 -0.00780 +v 0.96077 0.96000 -0.00780 +v 0.96311 1.04000 0.39049 +v 0.96311 0.96000 0.39049 +v 0.88465 1.04000 0.37488 +v 0.88465 0.96000 0.37488 +v 0.95714 1.04000 0.40491 +v 0.95714 0.96000 0.40491 +v 0.89062 1.04000 0.36046 +v 0.89062 0.96000 0.36046 +v 0.74037 1.04000 0.72933 +v 0.74037 0.96000 0.72933 +v 0.67385 1.04000 0.68488 +v 0.67385 0.96000 0.68488 +v 0.72933 1.04000 0.74037 +v 0.72933 0.96000 0.74037 +v 0.68488 1.04000 0.67385 +v 0.68488 0.96000 0.67385 +v 0.40491 1.04000 0.95714 +v 0.40491 0.96000 0.95714 +v 0.36046 1.04000 0.89062 +v 0.36046 0.96000 0.89062 +v 0.39049 1.04000 0.96311 +v 0.39049 0.96000 0.96311 +v 0.37488 1.04000 0.88465 +v 0.37488 0.96000 0.88465 +v 0.00780 1.04000 1.03923 +v 0.00780 0.96000 1.03923 +v -0.00780 1.04000 0.96077 +v -0.00780 0.96000 0.96077 +v -0.00780 1.04000 1.03923 +v -0.00780 0.96000 1.03923 +v 0.00780 1.04000 0.96077 +v 0.00780 0.96000 0.96077 +v -0.39049 1.04000 0.96311 +v -0.39049 0.96000 0.96311 +v -0.37488 1.04000 0.88465 +v -0.37488 0.96000 0.88465 +v -0.40491 1.04000 0.95714 +v -0.40491 0.96000 0.95714 +v -0.36046 1.04000 0.89062 +v -0.36046 0.96000 0.89062 +v -0.72933 1.04000 0.74037 +v -0.72933 0.96000 0.74037 +v -0.68488 1.04000 0.67385 +v -0.68488 0.96000 0.67385 +v -0.74037 1.04000 0.72933 +v -0.74037 0.96000 0.72933 +v -0.67385 1.04000 0.68488 +v -0.67385 0.96000 0.68488 +v -0.95714 1.04000 0.40491 +v -0.95714 0.96000 0.40491 +v -0.89062 1.04000 0.36046 +v -0.89062 0.96000 0.36046 +v -0.96311 1.04000 0.39049 +v -0.96311 0.96000 0.39049 +v -0.88465 1.04000 0.37488 +v -0.88465 0.96000 0.37488 +v -1.03923 1.04000 0.00780 +v -1.03923 0.96000 0.00780 +v -0.96077 1.04000 -0.00780 +v -0.96077 0.96000 -0.00780 +v -1.03923 1.04000 -0.00780 +v -1.03923 0.96000 -0.00780 +v -0.96077 1.04000 0.00780 +v -0.96077 0.96000 0.00780 +v -0.96311 1.04000 -0.39049 +v -0.96311 0.96000 -0.39049 +v -0.88465 1.04000 -0.37488 +v -0.88465 0.96000 -0.37488 +v -0.95714 1.04000 -0.40491 +v -0.95714 0.96000 -0.40491 +v -0.89062 1.04000 -0.36046 +v -0.89062 0.96000 -0.36046 +v -0.74037 1.04000 -0.72933 +v -0.74037 0.96000 -0.72933 +v -0.67385 1.04000 -0.68488 +v -0.67385 0.96000 -0.68488 +v -0.72933 1.04000 -0.74037 +v -0.72933 0.96000 -0.74037 +v -0.68488 1.04000 -0.67385 +v -0.68488 0.96000 -0.67385 +v -0.40491 1.04000 -0.95714 +v -0.40491 0.96000 -0.95714 +v -0.36046 1.04000 -0.89062 +v -0.36046 0.96000 -0.89062 +v -0.39049 1.04000 -0.96311 +v -0.39049 0.96000 -0.96311 +v -0.37488 1.04000 -0.88465 +v -0.37488 0.96000 -0.88465 +v -0.00780 1.04000 -1.03923 +v -0.00780 0.96000 -1.03923 +v 0.00780 1.04000 -0.96077 +v 0.00780 0.96000 -0.96077 +v 0.00780 1.04000 -1.03923 +v 0.00780 0.96000 -1.03923 +v -0.00780 1.04000 -0.96077 +v -0.00780 0.96000 -0.96077 +v 0.39049 1.04000 -0.96311 +v 0.39049 0.96000 -0.96311 +v 0.37488 1.04000 -0.88465 +v 0.37488 0.96000 -0.88465 +v 0.40491 1.04000 -0.95714 +v 0.40491 0.96000 -0.95714 +v 0.36046 1.04000 -0.89062 +v 0.36046 0.96000 -0.89062 +v 0.72933 1.04000 -0.74037 +v 0.72933 0.96000 -0.74037 +v 0.68488 1.04000 -0.67385 +v 0.68488 0.96000 -0.67385 +v 0.74037 1.04000 -0.72933 +v 0.74037 0.96000 -0.72933 +v 0.67385 1.04000 -0.68488 +v 0.67385 0.96000 -0.68488 +v 0.95714 1.04000 -0.40491 +v 0.95714 0.96000 -0.40491 +v 0.89062 1.04000 -0.36046 +v 0.89062 0.96000 -0.36046 +v 0.96311 1.04000 -0.39049 +v 0.96311 0.96000 -0.39049 +v 0.88465 1.04000 -0.37488 +v 0.88465 0.96000 -0.37488 +v 1.03923 1.04000 -0.00780 +v 1.03923 0.96000 -0.00780 +v 0.96077 1.04000 0.00780 +v 0.96077 0.96000 0.00780 +v 1.03923 0.04000 0.00780 +v 1.03923 -0.04000 0.00780 +v 0.96077 0.04000 -0.00780 +v 0.96077 -0.04000 -0.00780 +v 0.96311 0.04000 0.39049 +v 0.96311 -0.04000 0.39049 +v 0.88465 0.04000 0.37488 +v 0.88465 -0.04000 0.37488 +v 0.95714 0.04000 0.40491 +v 0.95714 -0.04000 0.40491 +v 0.89062 0.04000 0.36046 +v 0.89062 -0.04000 0.36046 +v 0.74037 0.04000 0.72933 +v 0.74037 -0.04000 0.72933 +v 0.67385 0.04000 0.68488 +v 0.67385 -0.04000 0.68488 +v 0.72933 0.04000 0.74037 +v 0.72933 -0.04000 0.74037 +v 0.68488 0.04000 0.67385 +v 0.68488 -0.04000 0.67385 +v 0.40491 0.04000 0.95714 +v 0.40491 -0.04000 0.95714 +v 0.36046 0.04000 0.89062 +v 0.36046 -0.04000 0.89062 +v 0.39049 0.04000 0.96311 +v 0.39049 -0.04000 0.96311 +v 0.37488 0.04000 0.88465 +v 0.37488 -0.04000 0.88465 +v 0.00780 0.04000 1.03923 +v 0.00780 -0.04000 1.03923 +v -0.00780 0.04000 0.96077 +v -0.00780 -0.04000 0.96077 +v -0.00780 0.04000 1.03923 +v -0.00780 -0.04000 1.03923 +v 0.00780 0.04000 0.96077 +v 0.00780 -0.04000 0.96077 +v -0.39049 0.04000 0.96311 +v -0.39049 -0.04000 0.96311 +v -0.37488 0.04000 0.88465 +v -0.37488 -0.04000 0.88465 +v -0.40491 0.04000 0.95714 +v -0.40491 -0.04000 0.95714 +v -0.36046 0.04000 0.89062 +v -0.36046 -0.04000 0.89062 +v -0.72933 0.04000 0.74037 +v -0.72933 -0.04000 0.74037 +v -0.68488 0.04000 0.67385 +v -0.68488 -0.04000 0.67385 +v -0.74037 0.04000 0.72933 +v -0.74037 -0.04000 0.72933 +v -0.67385 0.04000 0.68488 +v -0.67385 -0.04000 0.68488 +v -0.95714 0.04000 0.40491 +v -0.95714 -0.04000 0.40491 +v -0.89062 0.04000 0.36046 +v -0.89062 -0.04000 0.36046 +v -0.96311 0.04000 0.39049 +v -0.96311 -0.04000 0.39049 +v -0.88465 0.04000 0.37488 +v -0.88465 -0.04000 0.37488 +v -1.03923 0.04000 0.00780 +v -1.03923 -0.04000 0.00780 +v -0.96077 0.04000 -0.00780 +v -0.96077 -0.04000 -0.00780 +v -1.03923 0.04000 -0.00780 +v -1.03923 -0.04000 -0.00780 +v -0.96077 0.04000 0.00780 +v -0.96077 -0.04000 0.00780 +v -0.96311 0.04000 -0.39049 +v -0.96311 -0.04000 -0.39049 +v -0.88465 0.04000 -0.37488 +v -0.88465 -0.04000 -0.37488 +v -0.95714 0.04000 -0.40491 +v -0.95714 -0.04000 -0.40491 +v -0.89062 0.04000 -0.36046 +v -0.89062 -0.04000 -0.36046 +v -0.74037 0.04000 -0.72933 +v -0.74037 -0.04000 -0.72933 +v -0.67385 0.04000 -0.68488 +v -0.67385 -0.04000 -0.68488 +v -0.72933 0.04000 -0.74037 +v -0.72933 -0.04000 -0.74037 +v -0.68488 0.04000 -0.67385 +v -0.68488 -0.04000 -0.67385 +v -0.40491 0.04000 -0.95714 +v -0.40491 -0.04000 -0.95714 +v -0.36046 0.04000 -0.89062 +v -0.36046 -0.04000 -0.89062 +v -0.39049 0.04000 -0.96311 +v -0.39049 -0.04000 -0.96311 +v -0.37488 0.04000 -0.88465 +v -0.37488 -0.04000 -0.88465 +v -0.00780 0.04000 -1.03923 +v -0.00780 -0.04000 -1.03923 +v 0.00780 0.04000 -0.96077 +v 0.00780 -0.04000 -0.96077 +v 0.00780 0.04000 -1.03923 +v 0.00780 -0.04000 -1.03923 +v -0.00780 0.04000 -0.96077 +v -0.00780 -0.04000 -0.96077 +v 0.39049 0.04000 -0.96311 +v 0.39049 -0.04000 -0.96311 +v 0.37488 0.04000 -0.88465 +v 0.37488 -0.04000 -0.88465 +v 0.40491 0.04000 -0.95714 +v 0.40491 -0.04000 -0.95714 +v 0.36046 0.04000 -0.89062 +v 0.36046 -0.04000 -0.89062 +v 0.72933 0.04000 -0.74037 +v 0.72933 -0.04000 -0.74037 +v 0.68488 0.04000 -0.67385 +v 0.68488 -0.04000 -0.67385 +v 0.74037 0.04000 -0.72933 +v 0.74037 -0.04000 -0.72933 +v 0.67385 0.04000 -0.68488 +v 0.67385 -0.04000 -0.68488 +v 0.95714 0.04000 -0.40491 +v 0.95714 -0.04000 -0.40491 +v 0.89062 0.04000 -0.36046 +v 0.89062 -0.04000 -0.36046 +v 0.96311 0.04000 -0.39049 +v 0.96311 -0.04000 -0.39049 +v 0.88465 0.04000 -0.37488 +v 0.88465 -0.04000 -0.37488 +v 1.03923 0.04000 -0.00780 +v 1.03923 -0.04000 -0.00780 +v 0.96077 0.04000 0.00780 +v 0.96077 -0.04000 0.00780 +v 1.04000 0.00000 0.04000 +v 0.96000 0.00000 0.04000 +v 1.04000 0.00000 -0.04000 +v 0.96000 0.00000 -0.04000 +v 1.04000 1.00000 0.04000 +v 0.96000 1.00000 0.04000 +v 1.04000 1.00000 -0.04000 +v 0.96000 1.00000 -0.04000 +v 0.74711 0.00000 0.74711 +v 0.66711 0.00000 0.74711 +v 0.74711 0.00000 0.66711 +v 0.66711 0.00000 0.66711 +v 0.74711 1.00000 0.74711 +v 0.66711 1.00000 0.74711 +v 0.74711 1.00000 0.66711 +v 0.66711 1.00000 0.66711 +v 0.04000 0.00000 1.04000 +v -0.04000 0.00000 1.04000 +v 0.04000 0.00000 0.96000 +v -0.04000 0.00000 0.96000 +v 0.04000 1.00000 1.04000 +v -0.04000 1.00000 1.04000 +v 0.04000 1.00000 0.96000 +v -0.04000 1.00000 0.96000 +v -0.66711 0.00000 0.74711 +v -0.74711 0.00000 0.74711 +v -0.66711 0.00000 0.66711 +v -0.74711 0.00000 0.66711 +v -0.66711 1.00000 0.74711 +v -0.74711 1.00000 0.74711 +v -0.66711 1.00000 0.66711 +v -0.74711 1.00000 0.66711 +v -0.96000 0.00000 0.04000 +v -1.04000 0.00000 0.04000 +v -0.96000 0.00000 -0.04000 +v -1.04000 0.00000 -0.04000 +v -0.96000 1.00000 0.04000 +v -1.04000 1.00000 0.04000 +v -0.96000 1.00000 -0.04000 +v -1.04000 1.00000 -0.04000 +v -0.66711 0.00000 -0.66711 +v -0.74711 0.00000 -0.66711 +v -0.66711 0.00000 -0.74711 +v -0.74711 0.00000 -0.74711 +v -0.66711 1.00000 -0.66711 +v -0.74711 1.00000 -0.66711 +v -0.66711 1.00000 -0.74711 +v -0.74711 1.00000 -0.74711 +v 0.04000 0.00000 -0.96000 +v -0.04000 0.00000 -0.96000 +v 0.04000 0.00000 -1.04000 +v -0.04000 0.00000 -1.04000 +v 0.04000 1.00000 -0.96000 +v -0.04000 1.00000 -0.96000 +v 0.04000 1.00000 -1.04000 +v -0.04000 1.00000 -1.04000 +v 0.74711 0.00000 -0.66711 +v 0.66711 0.00000 -0.66711 +v 0.74711 0.00000 -0.74711 +v 0.66711 0.00000 -0.74711 +v 0.74711 1.00000 -0.66711 +v 0.66711 1.00000 -0.66711 +v 0.74711 1.00000 -0.74711 +v 0.66711 1.00000 -0.74711 +f 1 2 4 3 +f 5 6 8 7 +f 1 2 6 5 +f 2 4 8 6 +f 4 3 7 8 +f 3 1 5 7 +f 9 10 12 11 +f 13 14 16 15 +f 9 10 14 13 +f 10 12 16 14 +f 12 11 15 16 +f 11 9 13 15 +f 17 18 20 19 +f 21 22 24 23 +f 17 18 22 21 +f 18 20 24 22 +f 20 19 23 24 +f 19 17 21 23 +f 25 26 28 27 +f 29 30 32 31 +f 25 26 30 29 +f 26 28 32 30 +f 28 27 31 32 +f 27 25 29 31 +f 33 34 36 35 +f 37 38 40 39 +f 33 34 38 37 +f 34 36 40 38 +f 36 35 39 40 +f 35 33 37 39 +f 41 42 44 43 +f 45 46 48 47 +f 41 42 46 45 +f 42 44 48 46 +f 44 43 47 48 +f 43 41 45 47 +f 49 50 52 51 +f 53 54 56 55 +f 49 50 54 53 +f 50 52 56 54 +f 52 51 55 56 +f 51 49 53 55 +f 57 58 60 59 +f 61 62 64 63 +f 57 58 62 61 +f 58 60 64 62 +f 60 59 63 64 +f 59 57 61 63 +f 65 66 68 67 +f 69 70 72 71 +f 65 66 70 69 +f 66 68 72 70 +f 68 67 71 72 +f 67 65 69 71 +f 73 74 76 75 +f 77 78 80 79 +f 73 74 78 77 +f 74 76 80 78 +f 76 75 79 80 +f 75 73 77 79 +f 81 82 84 83 +f 85 86 88 87 +f 81 82 86 85 +f 82 84 88 86 +f 84 83 87 88 +f 83 81 85 87 +f 89 90 92 91 +f 93 94 96 95 +f 89 90 94 93 +f 90 92 96 94 +f 92 91 95 96 +f 91 89 93 95 +f 97 98 100 99 +f 101 102 104 103 +f 97 98 102 101 +f 98 100 104 102 +f 100 99 103 104 +f 99 97 101 103 +f 105 106 108 107 +f 109 110 112 111 +f 105 106 110 109 +f 106 108 112 110 +f 108 107 111 112 +f 107 105 109 111 +f 113 114 116 115 +f 117 118 120 119 +f 113 114 118 117 +f 114 116 120 118 +f 116 115 119 120 +f 115 113 117 119 +f 121 122 124 123 +f 125 126 128 127 +f 121 122 126 125 +f 122 124 128 126 +f 124 123 127 128 +f 123 121 125 127 +f 129 130 132 131 +f 133 134 136 135 +f 129 130 134 133 +f 130 132 136 134 +f 132 131 135 136 +f 131 129 133 135 +f 137 138 140 139 +f 141 142 144 143 +f 137 138 142 141 +f 138 140 144 142 +f 140 139 143 144 +f 139 137 141 143 +f 145 146 148 147 +f 149 150 152 151 +f 145 146 150 149 +f 146 148 152 150 +f 148 147 151 152 +f 147 145 149 151 +f 153 154 156 155 +f 157 158 160 159 +f 153 154 158 157 +f 154 156 160 158 +f 156 155 159 160 +f 155 153 157 159 +f 161 162 164 163 +f 165 166 168 167 +f 161 162 166 165 +f 162 164 168 166 +f 164 163 167 168 +f 163 161 165 167 +f 169 170 172 171 +f 173 174 176 175 +f 169 170 174 173 +f 170 172 176 174 +f 172 171 175 176 +f 171 169 173 175 +f 177 178 180 179 +f 181 182 184 183 +f 177 178 182 181 +f 178 180 184 182 +f 180 179 183 184 +f 179 177 181 183 +f 185 186 188 187 +f 189 190 192 191 +f 185 186 190 189 +f 186 188 192 190 +f 188 187 191 192 +f 187 185 189 191 +f 193 194 196 195 +f 197 198 200 199 +f 193 194 198 197 +f 194 196 200 198 +f 196 195 199 200 +f 195 193 197 199 +f 201 202 204 203 +f 205 206 208 207 +f 201 202 206 205 +f 202 204 208 206 +f 204 203 207 208 +f 203 201 205 207 +f 209 210 212 211 +f 213 214 216 215 +f 209 210 214 213 +f 210 212 216 214 +f 212 211 215 216 +f 211 209 213 215 +f 217 218 220 219 +f 221 222 224 223 +f 217 218 222 221 +f 218 220 224 222 +f 220 219 223 224 +f 219 217 221 223 +f 225 226 228 227 +f 229 230 232 231 +f 225 226 230 229 +f 226 228 232 230 +f 228 227 231 232 +f 227 225 229 231 +f 233 234 236 235 +f 237 238 240 239 +f 233 234 238 237 +f 234 236 240 238 +f 236 235 239 240 +f 235 233 237 239 +f 241 242 244 243 +f 245 246 248 247 +f 241 242 246 245 +f 242 244 248 246 +f 244 243 247 248 +f 243 241 245 247 +f 249 250 252 251 +f 253 254 256 255 +f 249 250 254 253 +f 250 252 256 254 +f 252 251 255 256 +f 251 249 253 255 +f 257 258 260 259 +f 261 262 264 263 +f 257 258 262 261 +f 258 260 264 262 +f 260 259 263 264 +f 259 257 261 263 +f 265 266 268 267 +f 269 270 272 271 +f 265 266 270 269 +f 266 268 272 270 +f 268 267 271 272 +f 267 265 269 271 +f 273 274 276 275 +f 277 278 280 279 +f 273 274 278 277 +f 274 276 280 278 +f 276 275 279 280 +f 275 273 277 279 +f 281 282 284 283 +f 285 286 288 287 +f 281 282 286 285 +f 282 284 288 286 +f 284 283 287 288 +f 283 281 285 287 +f 289 290 292 291 +f 293 294 296 295 +f 289 290 294 293 +f 290 292 296 294 +f 292 291 295 296 +f 291 289 293 295 +f 297 298 300 299 +f 301 302 304 303 +f 297 298 302 301 +f 298 300 304 302 +f 300 299 303 304 +f 299 297 301 303 +f 305 306 308 307 +f 309 310 312 311 +f 305 306 310 309 +f 306 308 312 310 +f 308 307 311 312 +f 307 305 309 311 +f 313 314 316 315 +f 317 318 320 319 +f 313 314 318 317 +f 314 316 320 318 +f 316 315 319 320 +f 315 313 317 319 From 36f99a0448f3012d47068d48ce47d016826f9b04 Mon Sep 17 00:00:00 2001 From: Martin Pourrat Date: Sat, 16 May 2026 19:00:24 +0200 Subject: [PATCH 82/93] Revert silhouette WIP: drop OBJ + PNG assets Five iterations on silhouette overlay this session hit a hard wall: TTS Custom_Model spawns an auto-generated collider that cannot be disabled in Lua, so any spawned cylinder breaks fig-on-fig collisions. The pure Lua approaches (vector lines, decals stack) miss the gameplay-mandatory volume cue (LoS / cover). Next attempt will use a Unity 6 AssetBundle (custom Prefab with explicit no-MeshCollider) under the same per-seat Mac fallback toggle. For now the branch stays focused on Projector + R POI fixes, which are validated solo. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../silhouette_cylinder.obj | 244 -------- .../mac-fallback-assets/silhouette_top.png | Bin 30253 -> 0 bytes .../silhouette_wireframe.obj | 562 ------------------ 3 files changed, 806 deletions(-) delete mode 100644 mod/data/mac-fallback-assets/silhouette_cylinder.obj delete mode 100644 mod/data/mac-fallback-assets/silhouette_top.png delete mode 100644 mod/data/mac-fallback-assets/silhouette_wireframe.obj diff --git a/mod/data/mac-fallback-assets/silhouette_cylinder.obj b/mod/data/mac-fallback-assets/silhouette_cylinder.obj deleted file mode 100644 index a333e5db..00000000 --- a/mod/data/mac-fallback-assets/silhouette_cylinder.obj +++ /dev/null @@ -1,244 +0,0 @@ -# cylinder.obj - Mac silhouette overlay (radius=1, height=1, base at y=0) -o cylinder -v 1.000000 1.000000 0.000000 -v 0.991445 1.000000 0.130526 -v 0.965926 1.000000 0.258819 -v 0.923880 1.000000 0.382683 -v 0.866025 1.000000 0.500000 -v 0.793353 1.000000 0.608761 -v 0.707107 1.000000 0.707107 -v 0.608761 1.000000 0.793353 -v 0.500000 1.000000 0.866025 -v 0.382683 1.000000 0.923880 -v 0.258819 1.000000 0.965926 -v 0.130526 1.000000 0.991445 -v 0.000000 1.000000 1.000000 -v -0.130526 1.000000 0.991445 -v -0.258819 1.000000 0.965926 -v -0.382683 1.000000 0.923880 -v -0.500000 1.000000 0.866025 -v -0.608761 1.000000 0.793353 -v -0.707107 1.000000 0.707107 -v -0.793353 1.000000 0.608761 -v -0.866025 1.000000 0.500000 -v -0.923880 1.000000 0.382683 -v -0.965926 1.000000 0.258819 -v -0.991445 1.000000 0.130526 -v -1.000000 1.000000 0.000000 -v -0.991445 1.000000 -0.130526 -v -0.965926 1.000000 -0.258819 -v -0.923880 1.000000 -0.382683 -v -0.866025 1.000000 -0.500000 -v -0.793353 1.000000 -0.608761 -v -0.707107 1.000000 -0.707107 -v -0.608761 1.000000 -0.793353 -v -0.500000 1.000000 -0.866025 -v -0.382683 1.000000 -0.923880 -v -0.258819 1.000000 -0.965926 -v -0.130526 1.000000 -0.991445 -v -0.000000 1.000000 -1.000000 -v 0.130526 1.000000 -0.991445 -v 0.258819 1.000000 -0.965926 -v 0.382683 1.000000 -0.923880 -v 0.500000 1.000000 -0.866025 -v 0.608761 1.000000 -0.793353 -v 0.707107 1.000000 -0.707107 -v 0.793353 1.000000 -0.608761 -v 0.866025 1.000000 -0.500000 -v 0.923880 1.000000 -0.382683 -v 0.965926 1.000000 -0.258819 -v 0.991445 1.000000 -0.130526 -v 1.000000 0.000000 0.000000 -v 0.991445 0.000000 0.130526 -v 0.965926 0.000000 0.258819 -v 0.923880 0.000000 0.382683 -v 0.866025 0.000000 0.500000 -v 0.793353 0.000000 0.608761 -v 0.707107 0.000000 0.707107 -v 0.608761 0.000000 0.793353 -v 0.500000 0.000000 0.866025 -v 0.382683 0.000000 0.923880 -v 0.258819 0.000000 0.965926 -v 0.130526 0.000000 0.991445 -v 0.000000 0.000000 1.000000 -v -0.130526 0.000000 0.991445 -v -0.258819 0.000000 0.965926 -v -0.382683 0.000000 0.923880 -v -0.500000 0.000000 0.866025 -v -0.608761 0.000000 0.793353 -v -0.707107 0.000000 0.707107 -v -0.793353 0.000000 0.608761 -v -0.866025 0.000000 0.500000 -v -0.923880 0.000000 0.382683 -v -0.965926 0.000000 0.258819 -v -0.991445 0.000000 0.130526 -v -1.000000 0.000000 0.000000 -v -0.991445 0.000000 -0.130526 -v -0.965926 0.000000 -0.258819 -v -0.923880 0.000000 -0.382683 -v -0.866025 0.000000 -0.500000 -v -0.793353 0.000000 -0.608761 -v -0.707107 0.000000 -0.707107 -v -0.608761 0.000000 -0.793353 -v -0.500000 0.000000 -0.866025 -v -0.382683 0.000000 -0.923880 -v -0.258819 0.000000 -0.965926 -v -0.130526 0.000000 -0.991445 -v -0.000000 0.000000 -1.000000 -v 0.130526 0.000000 -0.991445 -v 0.258819 0.000000 -0.965926 -v 0.382683 0.000000 -0.923880 -v 0.500000 0.000000 -0.866025 -v 0.608761 0.000000 -0.793353 -v 0.707107 0.000000 -0.707107 -v 0.793353 0.000000 -0.608761 -v 0.866025 0.000000 -0.500000 -v 0.923880 0.000000 -0.382683 -v 0.965926 0.000000 -0.258819 -v 0.991445 0.000000 -0.130526 -v 0.000000 1.000000 0.000000 -v 0.000000 0.000000 0.000000 -f 1 49 50 2 -f 2 50 51 3 -f 3 51 52 4 -f 4 52 53 5 -f 5 53 54 6 -f 6 54 55 7 -f 7 55 56 8 -f 8 56 57 9 -f 9 57 58 10 -f 10 58 59 11 -f 11 59 60 12 -f 12 60 61 13 -f 13 61 62 14 -f 14 62 63 15 -f 15 63 64 16 -f 16 64 65 17 -f 17 65 66 18 -f 18 66 67 19 -f 19 67 68 20 -f 20 68 69 21 -f 21 69 70 22 -f 22 70 71 23 -f 23 71 72 24 -f 24 72 73 25 -f 25 73 74 26 -f 26 74 75 27 -f 27 75 76 28 -f 28 76 77 29 -f 29 77 78 30 -f 30 78 79 31 -f 31 79 80 32 -f 32 80 81 33 -f 33 81 82 34 -f 34 82 83 35 -f 35 83 84 36 -f 36 84 85 37 -f 37 85 86 38 -f 38 86 87 39 -f 39 87 88 40 -f 40 88 89 41 -f 41 89 90 42 -f 42 90 91 43 -f 43 91 92 44 -f 44 92 93 45 -f 45 93 94 46 -f 46 94 95 47 -f 47 95 96 48 -f 48 96 49 1 -f 97 2 1 -f 97 3 2 -f 97 4 3 -f 97 5 4 -f 97 6 5 -f 97 7 6 -f 97 8 7 -f 97 9 8 -f 97 10 9 -f 97 11 10 -f 97 12 11 -f 97 13 12 -f 97 14 13 -f 97 15 14 -f 97 16 15 -f 97 17 16 -f 97 18 17 -f 97 19 18 -f 97 20 19 -f 97 21 20 -f 97 22 21 -f 97 23 22 -f 97 24 23 -f 97 25 24 -f 97 26 25 -f 97 27 26 -f 97 28 27 -f 97 29 28 -f 97 30 29 -f 97 31 30 -f 97 32 31 -f 97 33 32 -f 97 34 33 -f 97 35 34 -f 97 36 35 -f 97 37 36 -f 97 38 37 -f 97 39 38 -f 97 40 39 -f 97 41 40 -f 97 42 41 -f 97 43 42 -f 97 44 43 -f 97 45 44 -f 97 46 45 -f 97 47 46 -f 97 48 47 -f 97 1 48 -f 98 49 50 -f 98 50 51 -f 98 51 52 -f 98 52 53 -f 98 53 54 -f 98 54 55 -f 98 55 56 -f 98 56 57 -f 98 57 58 -f 98 58 59 -f 98 59 60 -f 98 60 61 -f 98 61 62 -f 98 62 63 -f 98 63 64 -f 98 64 65 -f 98 65 66 -f 98 66 67 -f 98 67 68 -f 98 68 69 -f 98 69 70 -f 98 70 71 -f 98 71 72 -f 98 72 73 -f 98 73 74 -f 98 74 75 -f 98 75 76 -f 98 76 77 -f 98 77 78 -f 98 78 79 -f 98 79 80 -f 98 80 81 -f 98 81 82 -f 98 82 83 -f 98 83 84 -f 98 84 85 -f 98 85 86 -f 98 86 87 -f 98 87 88 -f 98 88 89 -f 98 89 90 -f 98 90 91 -f 98 91 92 -f 98 92 93 -f 98 93 94 -f 98 94 95 -f 98 95 96 -f 98 96 49 diff --git a/mod/data/mac-fallback-assets/silhouette_top.png b/mod/data/mac-fallback-assets/silhouette_top.png deleted file mode 100644 index 24b673050268d67f6c8c3057636a98040acc254d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30253 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 Date: Sat, 16 May 2026 19:14:12 +0200 Subject: [PATCH 83/93] Add silhouette_mac_fallback.unity3d for Mac SIL transparent hologram Custom_AssetBundle replacement for the Mac SIL fallback. Unity 6 build with: - Primitive Cylinder, no collider component (Custom_AssetBundle won't auto- inject one like Custom_Model does, so it won't push minis around) - Standard shader, Rendering Mode = Transparent, cyan alpha 0.10 - Self-emission tint for the hologram-briefing look Built via Assets/Editor/BuildSilhouetteHologram.cs in UnityProject-U6 (Unity 6000.0.62f1 batchmode, target StandaloneWindows64). --- .../silhouette_mac_fallback.unity3d | Bin 0 -> 161817 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d diff --git a/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d b/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d new file mode 100644 index 0000000000000000000000000000000000000000..794827a2285a161744158b153004f45fb4c0b2a7 GIT binary patch literal 161817 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 literal 0 HcmV?d00001 From e202554ffebb233eadc9c180751233a0ad80a665 Mon Sep 17 00:00:00 2001 From: Martin Pourrat Date: Sat, 16 May 2026 20:57:33 +0200 Subject: [PATCH 84/93] Replace silhouette_mac_fallback.unity3d with shader-recompiled-U6 build The previous build (commit d1234b7) used a primitive Cylinder + Standard Transparent material, which didn't render the way the upstream silhouette does and also turned out to be unnecessary - the real bug is the Allen White BucketheadBits/Silhouette shader compiled under Unity 2019 failing to load on Mac TTS U6 (falls back to Standard, opaque). This new build reuses Assets/Sihl/Sihl.prefab as-is (same mesh, same material, same shader source), but the shader is compiled by Unity 6's toolchain. Identical visual contract to upstream Windows users; the only difference vs the vanilla Steam UGC bundle is the Unity compile target. Path A from Dicewrench's analysis. --- .../silhouette_mac_fallback.unity3d | Bin 161817 -> 10922 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d b/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d index 794827a2285a161744158b153004f45fb4c0b2a7..15bb9258a1c225572de9b686b6595455abfe184f 100644 GIT binary patch literal 10922 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 Mcz6b}|N5b`;!*7d0000000007b82|tPLI3~& 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 From 35285f04ab557977598233f2d405ee15e022cfd0 Mon Sep 17 00:00:00 2001 From: Martin Pourrat Date: Sun, 17 May 2026 17:02:48 +0200 Subject: [PATCH 85/93] Archive mac-patcher Python tooling Adds the Python patcher that injects Mac fallback into a vanilla SWL TTS save, alongside the helper scripts used during development (bundle inspection via UnityPy, overlay PNG generation, retrospective notes). Documents the full set of fixes shipped on this branch: cohesion/range/ movement/deployment overlays, silhouette nil-guard, SIL button rename (toggleSilhouettes -> macToggleSil to bypass the TTS engine intercept), Order Token Speed/Move inline bodies, firing arc lines for round and oblong bases >27mm. See mac-patcher/README.md for usage. Co-Authored-By: Claude Opus 4.7 (1M context) --- mac-patcher/.gitignore | 6 + mac-patcher/README.md | 74 + mac-patcher/SHADER_INVENTORY.md | 59 + mac-patcher/design-refactor.md | 330 ++++ mac-patcher/extract_projector_specs.py | 198 +++ mac-patcher/generate_overlay_assets.py | 282 ++++ mac-patcher/inspect_cohesion.py | 83 + mac-patcher/inspect_externals.py | 51 + mac-patcher/materials-reference.md | 170 ++ mac-patcher/patch_cohesion_rename.py | 81 + mac-patcher/patch_save_for_mac.py | 2133 ++++++++++++++++++++++++ mac-patcher/requirements.txt | 3 + mac-patcher/retro-cohesion.md | 170 ++ mac-patcher/retro-deployment.md | 76 + mac-patcher/retro-movement.md | 122 ++ mac-patcher/retro-range.md | 86 + mac-patcher/scan_bundles.py | 166 ++ 17 files changed, 4090 insertions(+) create mode 100644 mac-patcher/.gitignore create mode 100644 mac-patcher/README.md create mode 100644 mac-patcher/SHADER_INVENTORY.md create mode 100644 mac-patcher/design-refactor.md create mode 100644 mac-patcher/extract_projector_specs.py create mode 100644 mac-patcher/generate_overlay_assets.py create mode 100644 mac-patcher/inspect_cohesion.py create mode 100644 mac-patcher/inspect_externals.py create mode 100644 mac-patcher/materials-reference.md create mode 100644 mac-patcher/patch_cohesion_rename.py create mode 100644 mac-patcher/patch_save_for_mac.py create mode 100644 mac-patcher/requirements.txt create mode 100644 mac-patcher/retro-cohesion.md create mode 100644 mac-patcher/retro-deployment.md create mode 100644 mac-patcher/retro-movement.md create mode 100644 mac-patcher/retro-range.md create mode 100644 mac-patcher/scan_bundles.py diff --git a/mac-patcher/.gitignore b/mac-patcher/.gitignore new file mode 100644 index 00000000..34bac69b --- /dev/null +++ b/mac-patcher/.gitignore @@ -0,0 +1,6 @@ +venv/ +__pycache__/ +*.pyc +*.bak +*.bak-* +scan_bundles_*.out diff --git a/mac-patcher/README.md b/mac-patcher/README.md new file mode 100644 index 00000000..420bd388 --- /dev/null +++ b/mac-patcher/README.md @@ -0,0 +1,74 @@ +# mac-patcher + +Python patcher that injects Mac-compatibility fallbacks into a SWL TTS save +file. Works around the TTS Unity 6 (Berserk) shader bug where vanilla +AssetBundle Projectors (range rulers, cohesion halos, movement templates, +deployment zones, silhouettes) render as a magenta rectangle on macOS +because the custom shader bundled by Allen White (Dicewrench) fails to load +under Unity 6 Metal. + +## What it does + +For each affected object script in a `TS_Save_N.json`: + +- **Cohesion / Range / Maximum Move / Deployment / Silhouette**: routes the + visualization through a Mac-friendly path (vector lines + decal PNGs) + built into the Global script. The vanilla bundle path stays available + for per-seat opt-in (see "Mac TTS U6 Patch" floating panel). + +- **SIL button**: renames `click_function = "toggleSilhouettes"` to + `"macToggleSil"` + injects a forwarder. Empirically the engine silently + swallows the original click_function name in Mac mode (the button plays + its sound/animation but the function is never invoked). The rename + bypasses the issue entirely. + +- **clearSilhouette nil guard**: upstream `clearSilhouette()` derefs + `removeAttachments()[1]` which is nil when `silhouetteState` is true but + the physical silhouette attachment has been lost across save reloads. + Patched to guard the nil case + reset state in onload. + +- **Order Token Speed/Move button overrides**: inlines vanilla bodies of + `changeSpeed1/2/3` and `moveForward/Backwards/Left/Right` to capture + the clicking player's color (`macActivePlayerForMove`) for per-seat + routing. Inlined rather than wrapped because the List Builder copies the + block to Command Token Custom_Models which lack the vanilla + Order_Token function bodies (wrap+call_orig would crash with "attempt + to call a nil value"). + +## Usage + +```bash +# Setup once +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt + +# Patch a save +python3 patch_save_for_mac.py [--reload] +``` + +The patcher also mirrors the patched save into +`~/Library/Tabletop Simulator/Saves/TS_Save_N.json` (next free slot, or the +existing `SWL BETA - MAC PATCH` slot if present) so TTS picks it up in +Games -> Save & Load. + +`--reload` hot-pushes the patched scripts to a running TTS instance via the +External Editor API (no need to restart TTS). + +## Idempotence + +All patches are idempotent: a re-run on an already-patched save strips the +previous Mac patch block from Global (via marker regex) and re-injects +fresh. Per-object wrappers are also stripped before re-injection. + +## Files + +- `patch_save_for_mac.py` - main patcher +- `scan_bundles.py` - UnityPy scanner for the local TTS bundle cache +- `extract_projector_specs.py` - dump Projector material properties from a + bundle (used to discover the `_Arc` shader keyword for firing arc lines) +- `generate_overlay_assets.py` - regenerate the range/cohesion/etc. PNG + decals from authoritative SWL geometry +- `inspect_*.py` - debug helpers +- `retro-*.md` - retrospective notes on each subsystem rewrite +- `SHADER_INVENTORY.md` - list of custom shaders in the vanilla bundles diff --git a/mac-patcher/SHADER_INVENTORY.md b/mac-patcher/SHADER_INVENTORY.md new file mode 100644 index 00000000..926e86db --- /dev/null +++ b/mac-patcher/SHADER_INVENTORY.md @@ -0,0 +1,59 @@ +# SW Legion TTS Mod — Shader Inventory + +Scan effectué le 2026-05-07 sur `~/Library/Tabletop Simulator/Mods/Assetbundles/` (40 bundles `.unity3d`). +Outil : UnityPy 1.25.0, script `scan_bundles.py`. Output brut : `scan_bundles_2026-05-07.out`. + +## TL;DR +**9 shaders custom distincts**, **tous embarqués dans le `.unity3d` qui les utilise**. Donc : +- L'astuce "Always Included Shaders" du player TTS est **non-applicable** : ces shaders ne sont pas chargés depuis le pool du player, ils vivent dans les bundles. +- Le bug magenta vient du fait que le bytecode/code source de ces shaders custom ne fonctionne plus correctement sous Unity 6 + Metal sur Mac. +- Les overlays magenta listés par Martin (cohésion, range, deployment, movement) correspondent **exactement** aux 4 shaders `BucketheadBits/Projector/*`. + +## Inventaire complet (par catégorie) + +### Shaders Projector (overlays affectés par le bug magenta) +| Shader | Materials | Bundles | +|---|---|---| +| `BucketheadBits/Projector/Movement` | `ProjectorMaterial_27mm_speed1_single`, `ProjectorMaterial_27mm_speed2_single`, `ProjectorMaterial_50mm_speed2_single` | 3 | +| `BucketheadBits/Projector/Deployment` | `Projector_Deployment_Blue` (×2), `Projector_Deployment_Red` (×2) | 4 | +| `BucketheadBits/Projector/Range` | `ProjectorMaterial_25mm_token`, `ProjectorMaterial_27mm`, `ProjectorMaterial_50mm` | 3 | +| `BucketheadBits/Projector/Cohesion` | `Cohesion_27mm` | 1 | + +→ 11 materials projector au total, 4 shaders distincts à porter en priorité. + +### Outils / autres custom +| Shader | Materials | Bundles | +|---|---|---| +| `BucketheadBits/MoveTool` | `Speed1`, `Speed2` | 2 | +| `BucketheadBits/Tokens/Lambert Channel Mask` | `unitIDtoken_1` à `unitIDtoken_10` | 10 | +| `BucketheadBits/Silhouette` | `BucketheadBits_Silhouette` | 1 | +| `BucketheadBits/Units/Color Replacer` | `Ewok_SharedMat` | 1 | +| `DWD/LightenSkybox` | `Skybox` | 1 | + +→ Le shader `Tokens/Lambert Channel Mask` correspond aux 10 unitIDtokens loggés en console par TTS lors du bug : "Shader didn't load correctly for AssetBundle material unitIDtoken_X". Donc ce shader est **explicitement confirmé cassé**. + +## Auteurs +- Préfixe **`BucketheadBits/`** sur 8/9 shaders → ancien contributeur "Buckethead". Aucun repo GitHub public trouvé sous ce handle. Possible que ce soit un handle alternatif de Tieren (crédité ailleurs comme "original mod creator"). +- Préfixe **`DWD/`** sur 1 shader (LightenSkybox) → autre auteur, juste un tint skybox. + +## Implications pour la conversation avec Ben + +1. **L'angle "Always Included Shaders" n'est pas la bonne piste** pour ce bug, parce que les shaders custom sont embarqués dans les bundles. Si Ben a essayé cette piste, c'est cohérent que ça n'ait rien donné. + +2. **Le scope minimum réaliste** : porter / remplacer ces 9 shaders sous Unity 6 (compilation Metal-compatible). Soit en partant des sources s'il les a, soit en réécrivant des équivalents stock. + +3. **Materials = 11 projectors + 10 tokens + 4 outils + 1 skybox = 26 materials affectés au total** dans 27 bundles différents. Volumétrie raisonnable si les shaders sont swappable d'un coup. + +4. **Type technique des shaders** : + - Les `Projector/*` sont des shaders Unity Projector (transparent, projection avec bandes alternées) — historiquement tricky à porter Metal. + - Le `Tokens/Lambert Channel Mask` est un shader de coloration par canaux (R/G/B/A) — utilisé pour appliquer la couleur du joueur sur un mesh à canaux. Classique mais codé custom. + - Les autres (MoveTool, Silhouette, Color Replacer, LightenSkybox) sont des shaders d'effets simples. + +## Sources non-trouvées +- Aucun repo public sous `swlegion`, `swlegion-dev`, `matanlurey`, `halestom15`, `Buckethead` ne contient les sources Unity de ces shaders. +- Le projet Unity vit donc en privé sur la machine d'un contributeur (probablement Decaf ou Ben). + +## Prochaine étape +Attendre Ben pour confirmer si les sources sont disponibles. Cet inventaire répond déjà partiellement à ses questions 1 et 2 : +- Q1 ("custom shaders updated or just retargeted ?") : on sait maintenant exactement ce qu'il faut updater. +- Q2 ("which shaders are referenced ?") : liste complète ci-dessus. diff --git a/mac-patcher/design-refactor.md b/mac-patcher/design-refactor.md new file mode 100644 index 00000000..a94b2d03 --- /dev/null +++ b/mac-patcher/design-refactor.md @@ -0,0 +1,330 @@ +# Design refactor — Overlays Projector SWL TTS (Mac/Unity 6) + +**Status** : draft 15 mai 2026. Spec pour le patch event-driven hybride (decals plats + vector lines drapants). Base : 4 retro-*.md + materials-reference.md + shaders Allen White lus. + +## 1. Contexte (résumé exécutif) + +Le passage du player TTS à Unity 6 a strippé les variants Standard receiver des Projectors legacy. Sur Mac, **tous les overlays Projector du mod SWL s'affichent en magenta** : Cohesion Ruler, Range Ruler, Maximum Move, Deployment Boundary. Validation empirique session 14-15 mai : aucun fix côté bundle/shader ne marche (le strip est player-side TTS U6). Fix Berserk = ticket à ouvrir mais sans garantie de calendrier. + +**Solution mod-side validée** : remplacer le pattern Custom_AssetBundle (Projector) par un combo **Decal plat (PNG hébergé) + Vector lines (drapant via raycast)** orchestré par un manager Global event-driven. + +## 1bis. Fallback per-seat avec auto-test au boot + +Le refactor ne **remplace pas** le Projector legacy, il l'**augmente** d'un fallback per-seat. Chaque joueur a un compat flag détecté au boot, et le rendu s'adapte : + +- **Seats non-Mac** (TTS U6 OK) → voient le **Projector legacy** (rendu original préservé) +- **Seats Mac** (bug magenta) → voient le **patch hybride** (decal + vector lines) + +Quand Berserk corrigera le bug, l'auto-test au boot suivant désactivera le compat pour les seats Mac → retour automatique au comportement legacy, zéro action utilisateur. + +### 1bis.1 État par-seat (Global script) + +```lua +compatBySeat = {} -- {Red=true|false, Blue=true|false, ...} true = Mac/needs patch + +function onLoad(script_state) + if script_state ~= "" then + compatBySeat = JSON.decode(script_state) or {} + end + testCompatForAllSeated() +end + +function onSave() + return JSON.encode(compatBySeat) +end + +function getMacSeats() + local out = {} + for color, isMac in pairs(compatBySeat) do + if isMac then table.insert(out, color) end + end + return out +end + +function getNonMacSeats() + local out = {} + for color, isMac in pairs(compatBySeat) do + if not isMac then table.insert(out, color) end + end + return out +end +``` + +### 1bis.2 Test compat au boot + +```lua +function testCompatForAllSeated() + for _, player in pairs(Player.getPlayers()) do + if player.seated then + offerCompatTest(player) + end + end +end + +function offerCompatTest(player) + -- Spawn un canary Projector en zone hors-jeu (sous la table) + local canary = spawnObject({ + type="Custom_AssetBundle", + position={-50, -10, -50}, + scale={0,0,0} + }) + canary.setCustomObject({assetbundle = COHESION_27MM_URL}) + + -- Wait quelques secondes le temps que le bundle charge + Wait.time(function() + -- UI popup pour ce joueur uniquement + player.broadcast("Test rendu : cliquez sur ce que vous voyez sur le canary (Y=-10)") + -- Buttons "Couleurs OK" et "Magenta/rose" + -- onClick stocke compatBySeat[player.color] = true|false + -- destruct canary + end, 3) +end +``` + +### 1bis.3 Spawn d'un overlay (pattern unifié) + +```lua +function spawnOverlay(type, fig, params) + local macSeats = getMacSeats() + local nonMacSeats = getNonMacSeats() + local entry = {type=type, fig=fig, params=params, objects={}} + + -- 1. Projector legacy pour les seats non-Mac + if #nonMacSeats > 0 then + local projector = spawnObject({type="Custom_AssetBundle", ...}) + projector.setCustomObject({assetbundle = bundleURLs[type][fig.baseSize]}) + projector.setLock(true) + projector.setName(type .. "_legacy") + if #macSeats > 0 then projector.setInvisibleTo(macSeats) end + entry.objects.projector = projector + end + + -- 2. Custom_Tile (PNG plat) pour les seats Mac + if #macSeats > 0 then + local tile = spawnObject({type="Custom_Tile", position=fig.pos+Y0.1, scale=...}) + tile.setCustomObject({image = pngURLs[type][fig.baseSize]}) + tile.setLock(true) + tile.setName(type .. "_patch") + if #nonMacSeats > 0 then tile.setInvisibleTo(nonMacSeats) end + entry.objects.tile = tile + end + + -- 3. Vector lines (drape relief) pour les seats Mac + if #macSeats > 0 then + entry.lines = buildVectorLines(type, fig, params, macSeats) + -- ajoutées au batch global + end + + activeOverlays[fig.getGUID() .. ":" .. type] = entry + redrawAll() +end +``` + +### 1bis.4 Bug Berserk fix → reset auto + +Quand Berserk corrigera les variants Standard receiver dans le player TTS U6 : +- Au prochain boot de partie, `testCompatForAllSeated` re-run pour chaque seat +- Les Mac users verront maintenant le canary correctement → cliquent "Couleurs OK" → `compatBySeat[color] = false` +- Le patch ne s'active plus pour eux, comportement legacy restauré +- Aucun changement de code requis côté mod + +**Re-test manuel** : un bouton "Re-test compatibility" dans l'UI Notes du mod permet de forcer un nouveau test à tout moment (utile si le user veut vérifier après une mise à jour TTS). + +### 1bis.5 Incertitudes à valider empiriquement au POC + +| Incertitude | Plan B si KO | +|---|---| +| `Object.setInvisibleTo` cache-t-il le **rendu de la projection** d'un Projector ou seulement le GameObject parent ? | Si KO : si au moins 1 Mac seat présent, on ne spawn PAS le Projector du tout (tout le monde voit le patch — compromis : 1 Mac dans la partie = tout le monde adopte le rendu patch) | +| `Global.setDecals()` supporte-t-il un filtre `players` per entry ? | Si KO : utiliser `Custom_Tile` Objects (déjà ce qu'on a au-dessus, avec `setInvisibleTo`). Plus propre et déjà compatible per-seat | + +À tester en début de phase d'implémentation, avant de pousser tout le code. + +## 2. Architecture event-driven (manager Global) + +### 2.1 État global unique + +```lua +-- mod/src/includes/Overlays.ttslua (nouveau) +local activeOverlays = {} -- clé = "{guid}:{type}" → {type, fig, params} +local hiddenWhilePickedUp = {} -- buffer pour les overlays cachés temporairement +``` + +### 2.2 API publique unifiée + +```lua +-- Spawn / clear par type, scopé à une fig +function spawnOverlay(type, fig, params) +function clearOverlayForFig(type, fig) +function clearAllOverlays() -- reset complet + +-- Adapters pour conserver l'API existante des callers +function spawnCohesionRuler(fig) -- → spawnOverlay("cohesion", fig, {}) +function clearCohesionRuler() -- → clearOverlayForFig("cohesion", self) +function spawnRangeRuler(fig, override) -- → spawnOverlay("range", fig, {override=override}) +function clearRangeRulers() -- → clearOverlayForFig("range", selectedUnitObj) +-- etc. +``` + +### 2.3 Event handlers (Global) + +```lua +function onObjectPickUp(player_color, obj) + -- buffer tous les overlays attachés à obj puis les retirer du draw + hidePickedUp(obj) + redrawAll() +end + +function onObjectDrop(player_color, obj) + -- restaurer les overlays de obj depuis le buffer + restorePickedUp(obj) + redrawAll() +end + +function onObjectDestroy(obj) + -- nettoyage définitif + purgeForFig(obj) + redrawAll() +end +``` + +### 2.4 Rendu unique par tick d'event + +```lua +function redrawAll() + local lines, decals = {}, {} + for _, entry in pairs(activeOverlays) do + local l, d = builders[entry.type](entry.fig, entry.params) + for _, line in ipairs(l) do table.insert(lines, line) end + for _, decal in ipairs(d) do table.insert(decals, decal) end + end + Global.setVectorLines(lines) + Global.setDecals(decals) +end +``` + +**Important** : `setVectorLines` et `setDecals` sont des **singletons** côté TTS — chaque appel remplace toute la collection. D'où le manager global qui agrège. + +## 3. Spec par overlay + +### 3.1 Cohesion + +| Composant | Détail | +|---|---| +| Trigger spawn | Hotkey "Show Cohesion On Hovered Model" / bouton COHESION Order Token / dropCoroutine post-mouvement (`moveState=true`) | +| Trigger clear | Mêmes triggers (toggle) + onPickedUp (auto) + standby/reload globaux | +| Decal PNG | `cohesion_halo.png` — gradient radial blanc fade depuis le centre, fond transparent, 512×512px | +| Decal params | `position = fig.pos + Y0.1`, `rotation = (90, 0, 0)` (face vers le haut), `scale = (baseRadius+1, baseRadius+1, baseRadius+1) × 2` | +| Vector line | 1 cercle filaire blanc à `r = base_radius + 0.5"` du centre, drapé par raycast 32 segments | +| Couleur | #FFFFFF α=0.6 | + +API conservée : `showCohesionOnHoveredModel`, `spawnCohesionRuler`, `clearCohesionRuler`. Compatible avec les 3 sites de require existants (Global, Unit_Leader, Order_Token) car redirigés vers le manager global. + +### 3.2 Range Ruler + +| Composant | Détail | +|---|---| +| Trigger spawn | Hotkey "Show Range On Hovered Model" / bouton RANGE Order Token (targetingMode/attackMode) / bouton R sur POI/tokens | +| Trigger clear | Mêmes triggers (toggle) + clearTemplates (post-drop mouvement) + exit modes | +| Decal PNG | `range_bands.png` (universel) — 4 anneaux concentriques à 6/12/18/24 inches relatifs, couleurs jaune/orange/rouge/magenta foncé, fond transparent, 1024×1024px | +| Decal params | `position = fig.pos + Y0.1`, `scale = (60, 60, 60)` (couvre 24" de rayon + marge) | +| Vector lines | 4 cercles filaires aux rayons absolus **6", 12", 18", 24"** du centre, mêmes couleurs que les bandes du PNG | +| Couleur (par anneau) | R1 #FFC300, R2 #FF7500, R3 #FF1400, R4 #C20042 (alpha 0.6 sur les lignes) | +| Variantes | Tokens (smokeToken/token/tokenRangeTwo/bombCart/POI) → moins d'anneaux (1 ou 2), PNG distincts ou même PNG avec scale ajusté | + +API conservée : `showRangeOnHoveredModel`, `spawnRangeRuler(fig, override)`, `clearRangeRuler/clearRangeRulers`. + +Pour les variantes, le `override` permet de passer un type explicite (`"smokeToken"`, `"poi"`, etc.) au lieu du baseSize de la fig. + +### 3.3 Maximum Move + +| Composant | Détail | +|---|---| +| Trigger spawn | Lors du clic sur bouton speed (1/2/3) sur Order Token (flow mouvement) | +| Trigger clear | clearMovementTemplates (drop final) | +| Decal PNG | `max_move_disk.png` — disque plein bleu ciel #55CCFF α=0.48, fond transparent, 512×512px | +| Decal params | `position = fig.pos + Y0.1`, `scale = ProjectorRadius × 2` selon (baseSize, speed) | +| Vector line | 1 cercle filaire bleu ciel au rayon = ProjectorRadius (drape relief) + 1 anneau base blanc fin à r = baseRadius + 0.5" | +| Couleur | Cercle principal #55CCFF α=0.6, anneau base #FFFFFF α=0.5 | +| Rayons | Lookup table `maxMoveRadius[baseSize][speed]` (ex: `27mm.speed1=4.55", speed2=6.52", speed3=8.48"`) — extraits de `_ProjectorRadius` dans les materials Movement | + +API conservée : flow existant dans `Order_Token.a57c41.lua:552-574`, remplacement du `spawnObject Custom_AssetBundle` par `spawnOverlay("maxmove", fig, {speed=selectedSpeed, baseSize=unitData.baseSize})`. + +### 3.4 Deployment Boundary + +| Composant | Détail | +|---|---| +| Trigger spawn | `spawnDeploymentBoundary(matrix)` au setup (depuis menu UI Deployment) | +| Trigger clear | `clearDeploymentBoundary()` (menu UI Remove Overlay) + standby global | +| Decal PNG | `deployment_red.png` + `deployment_blue.png` — rectangle plein uniforme α=0.5, 256×256px | +| Decal params | Par cellule de la matrix, `position = grid pos + offset`, `scale = (6, 6, 6)` (taille standard d'une cellule SWL), rotation selon `deployRotations[cell]` | +| Vector lines | Contour rectangle drapant (4 côtés × N segments via raycast) par cellule, couleur match decal | +| Couleur | Rouge #FF0000 / Bleu #0000FF α=0.5 | +| Variantes | Half/L/Corner/Round → combinaisons de cellules dans la matrix, le manager gère chaque cellule individuellement (1 decal + 1 contour par cellule) | + +API conservée : `spawnDeploymentBoundary(matrix)`, `clearDeploymentBoundary()`. Internement, chaque cellule est enregistrée comme un overlay distinct avec un GUID synthétique (ex `"deployment:bs:5:3"`). + +## 4. Assets PNG à générer + +| Fichier | Dimensions | Description | Hosting | +|---|---|---|---| +| `cohesion_halo.png` | 512×512 | Gradient radial blanc centre → transparent bord, RGBA | iron-squadron.fr/tts-assets/ | +| `range_bands.png` | 1024×1024 | 4 anneaux concentriques aux rayons relatifs 0.25/0.5/0.75/1.0, gradient discret (pas blend), couleurs jaune/orange/rouge/magenta, alpha 0.6 | iron-squadron.fr/tts-assets/ | +| `range_smoke.png` | 256×256 | 1 anneau à rayon relatif 1.0, blanc opaque | idem | +| `range_token.png` | 256×256 | 1 anneau à rayon relatif 1.0, jaune | idem | +| `range_tokenRangeTwo.png` | 256×256 | 2 anneaux, jaune + orange | idem | +| `range_poi.png` | 256×256 | 1 anneau pour POI (range 0.5 / 3") | idem | +| `range_bombCart.png` | 256×256 | 2 anneaux | idem | +| `max_move_disk.png` | 512×512 | Disque plein bleu ciel #55CCFF α=0.48, RGBA | idem | +| `deployment_red.png` | 256×256 | Carré rouge plein #FF0000 α=0.5 | idem | +| `deployment_blue.png` | 256×256 | Carré bleu plein #0000FF α=0.5 | idem | + +**Génération** : script Python Pillow `generate_overlay_assets.py` qui produit tous les PNG d'un coup. Reproductible, versionnable dans `swlegion-tts/tool/`. + +**Upload** : `rsync` vers le VPS Iron Squadron, dossier `/var/www/iron-squadron/tts-assets/` (ou similaire). URL stable indépendante de Steam. + +## 5. Edge cases et invariants + +| Cas | Gestion | +|---|---| +| Multi-overlays simultanés (plusieurs figs avec cohesion + range) | Cohabitent dans la table `activeOverlays`, un `redrawAll()` au moindre changement | +| Fig détruite (mort en combat) | `onObjectDestroy` → `purgeForFig` → redraw | +| Pickup pendant overlay actif | `onObjectPickUp` → buffer dans `hiddenWhilePickedUp`, retire du rendu | +| Drop après pickup | `onObjectDrop` → restore depuis buffer, redraw (positions recalculées par les builders) | +| Pickup d'un objet sans overlay | Handler skip silencieux (test sur présence dans la table) | +| Reload de partie | `activeOverlays` se reset au boot (variable Lua locale du Global), OK | +| Filtres `standbyTokens` / `removeLockedRulers` (scans `getAllObjects()` par nom) | Deviennent caducs (plus d'Object physique). À supprimer ou laisser comme no-op | +| Test multi-joueurs réseau | Le manager Global est synchronisé par TTS (Lua sync), donc tous les joueurs voient les mêmes overlays | +| Drape impossible sur certains terrains custom | Limitation acceptable (déjà flag par Ben). Le decal plat sert de fallback visuel | + +## 6. Chemin de migration (séquentiel, testable étape par étape) + +1. **Setup Manager Global** : créer `mod/src/includes/Overlays.ttslua`. Aucun caller encore branché. Test : `npm run compile && load mod`, no-op confirmé +2. **Cohesion** (60 lignes, le plus simple) : réécrire `mod/src/includes/Cohesion.ttslua` pour rediriger vers le manager. Test : spawn via hotkey + bouton + dropCoroutine sur Mac +3. **Range Ruler** : réécrire `mod/src/includes/RangeRulers.ttslua` + adapter `POI_Token.lua` + `TokenWithRangeRuler.ttslua`. Test : 4 anneaux + tokens +4. **Maximum Move** : adapter `Order_Token.a57c41.lua:552-574` (spawn inline → call manager) + `clearMovementTemplates`. Test : flow mouvement complet sur Mac +5. **Deployment** : réécrire `SETUP_CONTROLLER.1cb552.lua:243-323`. Test : sélection scenario + spawn de la zone +6. **Cleanup** : supprimer les filtres `standbyTokens` / `removeLockedRulers` pour les types qui ne sont plus des Objects. Garder le filtre pour Movement Template (mesh, toujours Object) +7. **PR sur swlegion/tts** : branche `mac-overlays-refactor`, description du bug + solution + diff. Test communauté + +## 7. Compatibilité conservée + +- **Tous les sites de require existants** continuent à fonctionner sans modif (`require('!/Cohesion')`, `require('!/RangeRulers')`) +- **Toutes les fonctions globales conservent leur signature** (`spawnCohesionRuler(fig)`, `clearRangeRulers()`, etc.) +- **Pas de change d'API publique** → callers (Unit_Leader, Order_Token, POI_Token, etc.) inchangés +- **Variables d'état globales** comme `selectedUnitObj` conservées pour compat avec les filtres et la logique de toggle + +## 8. Risques connus à surveiller + +| Risque | Mitigation | +|---|---| +| Performance multi-overlays (N décals + N×64 vectors) | Tester empiriquement sur table chargée. Si lag, sparse raycast (32 segments) ou skip raycast si table plate détectée | +| Synchronisation decal/vector lines pas pixel-perfect | Vector lines au-dessus du decal (Y légèrement supérieur), tolérance visuelle | +| URL Iron Squadron offline | Hosting sur 2 destinations (VPS + GitHub Pages backup) ou inclure le CDN dans le repo si TTS supporte les data URIs | +| Bug U6 sur les decals aussi (improbable, POC validé le 14 mai) | Garder Path B = ticket Berserk en parallèle | +| Refactor casse un caller obscur | Test exhaustif des 5 sites de spawn + tous les triggers connus | + +## 9. Annexes + +- `materials-reference.md` : tous les params visuels extraits +- `retro-cohesion.md`, `retro-range.md`, `retro-deployment.md`, `retro-movement.md` : déclencheurs et call graphs +- `swlegion-tts/mod/src/includes/` : code source des modules à patcher diff --git a/mac-patcher/extract_projector_specs.py b/mac-patcher/extract_projector_specs.py new file mode 100644 index 00000000..e699f8f1 --- /dev/null +++ b/mac-patcher/extract_projector_specs.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +""" +Scan Unity .mat and .prefab YAML files in UnityProject-U6/Assets/Projectors/ +and extract critical parameters for the SWL TTS mod refactor. + +Output: materials-reference.md with structured tables per overlay. +""" +import os +import re +from pathlib import Path +from collections import defaultdict + +ROOT = Path("/Users/martinpourrat/MARTIN/Star Wars Legion/Mod TTS SWL/UnityProject-U6/Assets/Projectors") +OUT = Path("/Users/martinpourrat/MARTIN/Star Wars Legion/Mod TTS SWL/materials-reference.md") + +OVERLAYS = ["Cohesion", "Range", "Deployment", "Movement"] + + +def read(p: Path) -> str: + return p.read_text(encoding="utf-8", errors="replace") + + +def parse_material(path: Path) -> dict: + """Extract critical fields from a Unity .mat YAML file.""" + text = read(path) + out = {"path": str(path.relative_to(ROOT)), "name": path.stem} + + m = re.search(r"m_ShaderKeywords:\s*(.*)", text) + out["keywords"] = m.group(1).strip() if m else "" + + m = re.search(r"m_Shader:\s*\{fileID:\s*\d+,\s*guid:\s*([0-9a-f]+)", text) + out["shader_guid"] = m.group(1) if m else "" + + # Floats — capture key params + floats = {} + for fname in ["_BaseSize", "_ProjectorRadius", "_BandSize", "_BandContrast", + "_MaxRange", "_Arc", "_RangeSize", "_GradScaler", + "_OneTuner", "_TwoTuner", "_ThreeTuner", "_FourTuner", + "_FiveTuner", "_InfinityTuner"]: + m = re.search(rf"-\s*{re.escape(fname)}:\s*(-?[\d.]+)", text) + if m: + floats[fname] = m.group(1) + out["floats"] = floats + + # Colors — capture all _Color* / _Range* + colors = {} + for m in re.finditer(r"-\s*(_\w+):\s*\{r:\s*([\d.]+),\s*g:\s*([\d.]+),\s*b:\s*([\d.]+),\s*a:\s*([\d.]+)\}", text): + name, r, g, b, a = m.groups() + if name in ("_BumpScale",): # not a color + continue + colors[name] = (float(r), float(g), float(b), float(a)) + out["colors"] = colors + + return out + + +def parse_prefab(path: Path) -> dict: + """Extract critical fields from a Unity .prefab YAML file (Projector components).""" + text = read(path) + out = {"path": str(path.relative_to(ROOT)), "name": path.stem} + + # Count Projector components (Unity type id 119) + projectors = re.findall(r"---\s*!u!119\s*&\d+\s*\nProjector:(.*?)(?=\n---|\Z)", text, re.DOTALL) + out["n_projectors"] = len(projectors) + + proj_data = [] + for body in projectors: + d = {} + for fname in ["m_OrthographicSize", "m_FarClipPlane", "m_NearClipPlane", + "m_FieldOfView", "m_AspectRatio", "m_Orthographic"]: + m = re.search(rf"{fname}:\s*(-?[\d.]+)", body) + if m: + d[fname] = m.group(1) + m = re.search(r"m_Material:\s*\{fileID:\s*\d+,\s*guid:\s*([0-9a-f]+)", body) + if m: + d["material_guid"] = m.group(1) + proj_data.append(d) + out["projectors"] = proj_data + + # Transforms (LocalPosition Y, rotation) + transforms = [] + for body in re.findall(r"---\s*!u!4\s*&\d+\s*\nTransform:(.*?)(?=\n---|\Z)", text, re.DOTALL): + d = {} + m = re.search(r"m_LocalPosition:\s*\{x:\s*(-?[\d.]+),\s*y:\s*(-?[\d.]+),\s*z:\s*(-?[\d.]+)\}", body) + if m: + d["pos"] = (float(m.group(1)), float(m.group(2)), float(m.group(3))) + m = re.search(r"m_LocalEulerAnglesHint:\s*\{x:\s*(-?[\d.]+),\s*y:\s*(-?[\d.]+),\s*z:\s*(-?[\d.]+)\}", body) + if m: + d["rot"] = (float(m.group(1)), float(m.group(2)), float(m.group(3))) + transforms.append(d) + out["transforms"] = transforms + + return out + + +def rgba_to_hex(r, g, b, a): + """Convert 0-1 RGBA to hex + alpha note.""" + rh, gh, bh = int(r * 255), int(g * 255), int(b * 255) + return f"#{rh:02X}{gh:02X}{bh:02X} (α={a:.2f})" + + +def fmt_colors(colors): + """Format dict of colors as compact markdown.""" + if not colors: + return "—" + parts = [] + for name, (r, g, b, a) in colors.items(): + if a == 0 and r == 0 and g == 0 and b == 0: + parts.append(f"`{name}` transparent") + else: + parts.append(f"`{name}` {rgba_to_hex(r, g, b, a)}") + return "
".join(parts) + + +def fmt_floats(floats): + """Format dict of floats.""" + if not floats: + return "—" + return ", ".join(f"`{k}`={v}" for k, v in floats.items()) + + +def main(): + materials_by_overlay = defaultdict(list) + prefabs_by_overlay = defaultdict(list) + + for overlay in OVERLAYS: + for p in sorted((ROOT / overlay).rglob("*.mat")): + if p.suffix == ".meta": + continue + materials_by_overlay[overlay].append(parse_material(p)) + for p in sorted((ROOT / overlay).rglob("*.prefab")): + if p.suffix == ".meta": + continue + prefabs_by_overlay[overlay].append(parse_prefab(p)) + + # Build the master doc + lines = [] + lines.append("# Materials & Prefabs Reference — SWL TTS Projector Overlays") + lines.append("") + lines.append("Extraction directe des `.mat` et `.prefab` Unity (YAML) dans `UnityProject-U6/Assets/Projectors/`. Source de vérité pour reproduire le rendu en vector lines.") + lines.append("") + lines.append("Notation : couleurs en hex + alpha. Champs `Floats` clés du shader projector (paramètres procéduraux des anneaux).") + lines.append("") + + for overlay in OVERLAYS: + lines.append(f"## {overlay}") + lines.append("") + + # Materials table + lines.append("### Materials") + lines.append("") + lines.append("| Material | Shader keywords | Floats clés | Couleurs |") + lines.append("|---|---|---|---|") + for m in materials_by_overlay[overlay]: + kw = m["keywords"] or "—" + lines.append(f"| `{m['name']}` | `{kw}` | {fmt_floats(m['floats'])} | {fmt_colors(m['colors'])} |") + lines.append("") + + # Prefabs table + lines.append("### Prefabs") + lines.append("") + lines.append("| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid |") + lines.append("|---|---|---|---|---|") + for p in prefabs_by_overlay[overlay]: + n = p["n_projectors"] + proj_sizes = [pr.get("m_OrthographicSize", "?") for pr in p["projectors"]] + proj_far = [pr.get("m_FarClipPlane", "?") for pr in p["projectors"]] + proj_summary = "
".join(f"size={s}, far={f}" for s, f in zip(proj_sizes, proj_far)) if proj_sizes else "—" + + tr_summary = "
".join( + f"pos=({t.get('pos', ('?',)*3)[0]}, {t.get('pos', ('?',)*3)[1]}, {t.get('pos', ('?',)*3)[2]}) rot=({t.get('rot', ('?',)*3)[0]}, {t.get('rot', ('?',)*3)[1]}, {t.get('rot', ('?',)*3)[2]})" + for t in p["transforms"] + ) if p["transforms"] else "—" + + mat_guids = [pr.get("material_guid", "?") for pr in p["projectors"]] + mat_summary = "
".join(g[:12] + "…" for g in mat_guids) if mat_guids else "—" + + lines.append(f"| `{p['name']}` | {n} | {proj_summary} | {tr_summary} | {mat_summary} |") + lines.append("") + + # Final notes + lines.append("## Notes globales") + lines.append("") + lines.append("- Cohesion : double set `Materials/cohesion_*` et `_Revamp/halfCohesion_*`. Probable : `_Revamp/` est la version actuellement utilisée (le bundle live s'appelle `halfcohesion_27mm.unity3d` cf shader-inventory).") + lines.append("- Range : shader procédural avec jusqu'à 5 bandes (`_RangeOne` à `_RangeFive`) + `_RangeInfinity`. Le keyword `_MAXRANGE_RANGEFIVE` ou `_MAXRANGE_RANGEFOUR` détermine la dernière bande visible.") + lines.append("- Movement : un seul Projector par prefab, paramétré par `_BaseSize` + speed. Le `_Arc` doit contrôler la portion d'arc visible.") + lines.append("- Deployment : 6 materials uniques réutilisés par 12 prefabs (rotations différentes pour matérialiser corner/half/L/round/score).") + + OUT.write_text("\n".join(lines), encoding="utf-8") + print(f"Wrote {OUT}") + # Stats + total_mats = sum(len(v) for v in materials_by_overlay.values()) + total_prefabs = sum(len(v) for v in prefabs_by_overlay.values()) + print(f"Scanned {total_mats} materials and {total_prefabs} prefabs across {len(OVERLAYS)} overlays.") + + +if __name__ == "__main__": + main() diff --git a/mac-patcher/generate_overlay_assets.py b/mac-patcher/generate_overlay_assets.py new file mode 100644 index 00000000..d11b1932 --- /dev/null +++ b/mac-patcher/generate_overlay_assets.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +""" +Generate PNG assets for the Mac Cohesion + Range overlay fallback. +""" +from pathlib import Path +from PIL import Image, ImageDraw +import math + +OUT_DIR = Path(__file__).parent / "tts-assets" +OUT_DIR.mkdir(exist_ok=True) + + +# Range band colors (R, G, B in 0..255) — from BB_RangeProjector defaults +RANGE_COLORS = { + "half": (255, 255, 255), # 0.5 / 3" — white + "one": (255, 195, 0), # R1 / 6" — yellow + "two": (255, 117, 0), # R2 / 12" — orange + "three": (255, 20, 0), # R3 / 18" — red + "four": (195, 0, 66), # R4 / 24" — magenta + "five": (134, 0, 112), # R5 / 30" — violet +} + +# Configurations per overlay type (bands = list of (radius_inches, color_key)) +# Edge-based convention: each band's effective world radius is band_r + base_r. +RANGE_BAND_CONFIGS = { + "fig_leader": { + "bands": [(3, "half"), (6, "one"), (12, "two"), + (18, "three"), (24, "four"), (30, "five")], + "max_radius": 30, + }, + # SWL ranges in inches: Range 1 = 6", Range 2 = 12", Range 0.5 (poi) = 3". + "smokeToken": {"bands": [(6, "one")], "max_radius": 6}, + "token": {"bands": [(6, "one")], "max_radius": 6}, + "tokenRangeTwo": {"bands": [(6, "one"), (12, "two")], "max_radius": 12}, + "poi": {"bands": [(3, "one")], "max_radius": 3}, + "bombCart": {"bands": [(6, "one"), (12, "two")], "max_radius": 12}, +} + +# Base radii (inches, half-diameter) per ROUND fig base size. Oblong bases +# (long, snail) live in OBLONG_DIMENSIONS_MM below — they don't use a single +# radius and don't generate round PNGs. +FIG_BASE_RADIUS_IN = { + "small": 27 / 2 / 25.4, + "medium": 50 / 2 / 25.4, + "large": 70 / 2 / 25.4, + "huge": 100 / 2 / 25.4, + "laat": 120 / 2 / 25.4, + "epic": 150 / 2 / 25.4, +} + +# Token base radii (inches), only needed for multi-band tokens. +TOKEN_BASE_RADIUS_IN = { + "tokenRangeTwo": 25.1 / 2 / 25.4, + "bombCart": 50.0 / 2 / 25.4, +} + + +def _with_base_offset(config: dict, base_r: float) -> dict: + """Shift band radii outward by base_r and grow max_radius accordingly so + the PNG band proportions match the world positions of the vector-line + rings (which are drawn at band_r + base_r).""" + return { + "bands": [(r + base_r, c) for r, c in config["bands"]], + "max_radius": config["max_radius"] + base_r, + } + + +def generate_cohesion_halo(size: int = 512) -> Path: + """Halo radial blanc fade transparent — reproduit le rendu Cohesion bundle. + + Le shader BB_CohesionProjector fait `rangeOne = (1 - grad)` puis multiplie + par alpha global. On reproduit ce fade : opaque au centre, transparent au + bord. + """ + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + pixels = img.load() + cx, cy = size / 2, size / 2 + max_r = size / 2 + + # Alpha global du material Cohesion 27mm = 0.37 ; on multiplie par le fade + base_alpha = 0.37 + + for y in range(size): + for x in range(size): + dx, dy = x - cx, y - cy + dist = math.sqrt(dx * dx + dy * dy) + r_norm = dist / max_r # [0, 1+] depuis le centre + if r_norm > 1.0: + continue # transparent au-delà du rayon + + # Fade radial : (1 - r) avec courbe douce + # Le shader fait (1 - grad) linéaire, on garde la même chose + fade = max(0.0, 1.0 - r_norm) + alpha = int(base_alpha * fade * 255) + pixels[x, y] = (255, 255, 255, alpha) + + out = OUT_DIR / "cohesion_halo.png" + img.save(out) + return out + + +def generate_range_band_png(name: str, config: dict, size: int = 1024, + alpha: float = 0.2) -> Path: + """Generate a circular range overlay with concentric bands. + + Drawing from outer-to-inner: each filled ellipse overlays the previous, + producing clean rings. + """ + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + cx = cy = size / 2 + max_r_pix = size / 2 + a = int(alpha * 255) + + for band_r, color_key in reversed(config["bands"]): + r_pix = (band_r / config["max_radius"]) * max_r_pix + r, g, b = RANGE_COLORS[color_key] + draw.ellipse( + [(cx - r_pix, cy - r_pix), (cx + r_pix, cy + r_pix)], + fill=(r, g, b, a), + ) + + out = OUT_DIR / f"range_{name}.png" + img.save(out) + return out + + +# Oblong base dimensions (mm). Used to render stadium-shape range overlays +# whose 6 bands stay concentric to a rectangular footprint with rounded caps. +OBLONG_DIMENSIONS_MM = { + "long": {"width": 100, "length": 175}, + "snail": {"width": 100, "length": 200}, +} + + +def generate_range_band_stadium_png(base_size: str, dims_mm: dict, + config: dict, alpha: float = 0.2, + pixels_per_inch: int = 16) -> Path: + """Generate a stadium-shape PNG with 6 concentric range bands. + + The PNG dimensions encode the *outermost* band's bounding box (so the + decal can be stretched in TTS using scale = {shortSpan, longSpan, 1} and + the bands stay aligned with the matching vector stadium outlines). + + A stadium = rounded rectangle whose corner radius equals half the + short dimension, producing a pill shape with semicircular caps on the + long-axis ends. Each band is one rounded_rectangle of width + 2*(halfWid + band_r) and length 2*(halfLen + band_r), corner radius + halfWid + band_r. Drawn outer-to-inner so each band overpaints the next. + """ + halfWid_in = (dims_mm["width"] / 2) / 25.4 + halfLen_in = (dims_mm["length"] / 2) / 25.4 + max_dist = config["max_radius"] + + shortSpan_in = 2 * (halfWid_in + max_dist) + longSpan_in = 2 * (halfLen_in + max_dist) + + W = int(round(shortSpan_in * pixels_per_inch)) + H = int(round(longSpan_in * pixels_per_inch)) + cx, cy = W / 2, H / 2 + + img = Image.new("RGBA", (W, H), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + a = int(alpha * 255) + + for band_r, color_key in reversed(config["bands"]): + half_w_px = (halfWid_in + band_r) * pixels_per_inch + half_h_px = (halfLen_in + band_r) * pixels_per_inch + r, g, b = RANGE_COLORS[color_key] + draw.rounded_rectangle( + [(cx - half_w_px, cy - half_h_px), + (cx + half_w_px, cy + half_h_px)], + radius=half_w_px, # = half of the short side → semicircle caps + fill=(r, g, b, a), + ) + + out = OUT_DIR / f"range_fig_leader_{base_size}_stadium.png" + img.save(out) + return out + + +def generate_filled_disk(name: str, color_rgb: tuple, size: int = 512, + alpha: float = 0.2) -> Path: + """Generate a simple filled disk PNG (used for Maximum Move overlay).""" + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + cx = cy = size / 2 + r = size / 2 + a = int(alpha * 255) + draw.ellipse([(cx - r, cy - r), (cx + r, cy + r)], + fill=(color_rgb[0], color_rgb[1], color_rgb[2], a)) + out = OUT_DIR / f"{name}.png" + img.save(out) + return out + + +def generate_filled_square(name: str, color_rgb: tuple, size: int = 256, + alpha: float = 0.3) -> Path: + """Generate a filled square PNG (used for Deployment Boundary cells).""" + img = Image.new("RGBA", (size, size), + (color_rgb[0], color_rgb[1], color_rgb[2], int(alpha * 255))) + out = OUT_DIR / f"{name}.png" + img.save(out) + return out + + +def generate_silhouette_top_disc(size: int = 512) -> Path: + """Hologram-style top cap for the Mac silhouette cylinder. + + Radial gradient: bright green-cyan center fades to transparent at the + rim. Combined with the vector wireframe cylinder this gives the + Star Wars hologram briefing look (Leia "Help me Obi-Wan" projection). + """ + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + pixels = img.load() + cx, cy = size / 2, size / 2 + max_r = size / 2 + + r_center, g_center, b_center = 102, 255, 178 # bright hologram cyan-green + a_center = 0.85 + + for y in range(size): + for x in range(size): + dx, dy = x - cx, y - cy + dist = math.sqrt(dx * dx + dy * dy) + r_norm = dist / max_r + if r_norm > 1.0: + continue + # Smooth radial falloff (1-r squared) for a soft hologram glow + fade = (1.0 - r_norm) ** 1.5 + alpha = int(a_center * fade * 255) + pixels[x, y] = (r_center, g_center, b_center, alpha) + + out = OUT_DIR / "silhouette_top.png" + img.save(out) + return out + + +def main(): + p = generate_cohesion_halo() + print(f"Generated {p} ({p.stat().st_size} bytes)") + + # Fig leaders (round bases): one PNG per base size, bands shifted by base radius. + fig_cfg = RANGE_BAND_CONFIGS["fig_leader"] + for base_size, br in FIG_BASE_RADIUS_IN.items(): + out = generate_range_band_png(f"fig_leader_{base_size}", + _with_base_offset(fig_cfg, br)) + print(f"Generated {out} ({out.stat().st_size} bytes)") + + # Oblong bases: stadium-shape PNGs (rectangular footprint + rounded caps). + for base_size, dims in OBLONG_DIMENSIONS_MM.items(): + out = generate_range_band_stadium_png(base_size, dims, fig_cfg) + print(f"Generated {out} ({out.stat().st_size} bytes)") + + # Multi-band tokens: bake in their base radius for alignment. + for name, br in TOKEN_BASE_RADIUS_IN.items(): + cfg = RANGE_BAND_CONFIGS[name] + out = generate_range_band_png(name, _with_base_offset(cfg, br)) + print(f"Generated {out} ({out.stat().st_size} bytes)") + + # Single-band tokens: no offset adjustment needed (band sits at outer edge). + for name in ("smokeToken", "token", "poi"): + out = generate_range_band_png(name, RANGE_BAND_CONFIGS[name]) + print(f"Generated {out} ({out.stat().st_size} bytes)") + + # Maximum Move: cyan filled disk (#55CCFF) + p = generate_filled_disk("max_move_cyan", (85, 204, 255), alpha=0.2) + print(f"Generated {p} ({p.stat().st_size} bytes)") + + # Deployment Boundary: red and blue filled squares (alpha 0.5 = match bundle) + p = generate_filled_square("deployment_red", (255, 0, 0), alpha=0.3) + print(f"Generated {p} ({p.stat().st_size} bytes)") + p = generate_filled_square("deployment_blue", (0, 0, 255), alpha=0.3) + print(f"Generated {p} ({p.stat().st_size} bytes)") + + # Silhouette top cap: hologram-style green/cyan radial gradient. + p = generate_silhouette_top_disc() + print(f"Generated {p} ({p.stat().st_size} bytes)") + + +if __name__ == "__main__": + main() diff --git a/mac-patcher/inspect_cohesion.py b/mac-patcher/inspect_cohesion.py new file mode 100644 index 00000000..e26becbe --- /dev/null +++ b/mac-patcher/inspect_cohesion.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Inspect Cohesion bundle structure: list objects, materials, shaders, and the m_Shader PPtr.""" +import os +import sys +import UnityPy + +BUNDLE = os.path.expanduser( + "~/Library/Tabletop Simulator/Mods/Assetbundles/" + "httpssteamusercontentaakamaihdnetugc2482129948496305632EBBE2560D4336E6C96317EDDA787E225CF0E5B48.unity3d" +) + +env = UnityPy.load(BUNDLE) + +print("=" * 80) +print("BUNDLE METADATA") +print("=" * 80) +for f in env.files.values(): + print(f" file: {type(f).__name__}") + if hasattr(f, "unity_version"): + print(f" unity_version: {f.unity_version}") + if hasattr(f, "target_platform"): + print(f" target_platform: {f.target_platform}") + if hasattr(f, "externals"): + print(f" externals (cross-bundle refs):") + for ext in getattr(f, "externals", []): + print(f" - {ext}") + +print() +print("=" * 80) +print("ALL OBJECTS") +print("=" * 80) +for obj in env.objects: + path_id = getattr(obj, "path_id", "?") + type_name = obj.type.name + try: + data = obj.read() + name = getattr(data, "m_Name", "") or "" + except Exception as e: + name = f"" + print(f" PathID={path_id:<6} type={type_name:<15} name={name!r}") + +print() +print("=" * 80) +print("MATERIAL DETAILS (looking for Cohesion_27mm)") +print("=" * 80) +for obj in env.objects: + if obj.type.name != "Material": + continue + data = obj.read() + print(f"\nMaterial PathID={obj.path_id} name={data.m_Name!r}") + sref = data.m_Shader + print(f" m_Shader PPtr:") + print(f" file_id = {getattr(sref, 'file_id', '?')} " + f"(0 = internal to this bundle, !=0 = external file)") + print(f" path_id = {getattr(sref, 'path_id', '?')}") + # Try to resolve + try: + sobj = sref.deref() + if sobj: + sd = sobj.read() + sname = ( + getattr(getattr(sd, "m_ParsedForm", None), "m_Name", None) + or getattr(sd, "m_Name", None) + ) + print(f" resolved -> Shader name = {sname!r}") + else: + print(f" resolved -> None (external or missing)") + except Exception as e: + print(f" resolve error: {e!r}") + +print() +print("=" * 80) +print("SHADER DETAILS") +print("=" * 80) +for obj in env.objects: + if obj.type.name != "Shader": + continue + data = obj.read() + sname = ( + getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) + or getattr(data, "m_Name", None) + ) + print(f" PathID={obj.path_id} Shader name={sname!r}") diff --git a/mac-patcher/inspect_externals.py b/mac-patcher/inspect_externals.py new file mode 100644 index 00000000..c9307a21 --- /dev/null +++ b/mac-patcher/inspect_externals.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""For each bundle: dump its externals (referenced external files) and list materials with external m_Shader PPtr.""" +import os +import sys +import UnityPy + +BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") + +bundles = sorted(f for f in os.listdir(BUNDLE_DIR) if f.endswith(".unity3d")) + +# We are most interested in bundles whose materials use external shaders (file_id != 0) +print("=" * 90) +print("BUNDLES WITH EXTERNAL m_Shader REFERENCES") +print("=" * 90) + +for fname in bundles: + path = os.path.join(BUNDLE_DIR, fname) + short_id = fname.replace("httpssteamusercontentaakamaihdnetugc", "")[:20] + env = UnityPy.load(path) + + external_mats = [] + for obj in env.objects: + if obj.type.name != "Material": + continue + try: + data = obj.read() + sref = data.m_Shader + fid = getattr(sref, "file_id", None) + pid = getattr(sref, "path_id", None) + if fid and fid != 0: + external_mats.append((data.m_Name, fid, pid)) + except Exception: + pass + + if not external_mats: + continue + + print(f"\n{short_id}:") + # Dump externals table + for f in env.files.values(): + if hasattr(f, "externals"): + print(f" externals table:") + for i, ext in enumerate(getattr(f, "externals", [])): + # The externals entry usually has a 'path' and 'guid' + ext_path = getattr(ext, "path", "?") + ext_guid = getattr(ext, "guid", "?") + # index i+1 because file_id is 1-based for externals (file_id=0 means self) + print(f" file_id={i+1} path={ext_path!r} guid={ext_guid}") + print(f" materials with external m_Shader:") + for name, fid, pid in external_mats: + print(f" material={name!r} m_Shader=(file_id={fid}, path_id={pid})") diff --git a/mac-patcher/materials-reference.md b/mac-patcher/materials-reference.md new file mode 100644 index 00000000..eabaae36 --- /dev/null +++ b/mac-patcher/materials-reference.md @@ -0,0 +1,170 @@ +# Materials & Prefabs Reference — SWL TTS Projector Overlays + +Extraction directe des `.mat` et `.prefab` Unity (YAML) dans `UnityProject-U6/Assets/Projectors/`. Source de vérité pour reproduire le rendu en vector lines. + +Notation : couleurs en hex + alpha. Champs `Floats` clés du shader projector (paramètres procéduraux des anneaux). + +## Cohesion + +### Materials + +| Material | Shader keywords | Floats clés | Couleurs | +|---|---|---|---| +| `Cohesion_27mm` | `_ARC_OFF _MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=4.479488, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.37)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #000000 (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_50mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=5.470629, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_70mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=6.850394, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_27mm` | `_ARC_OFF _MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=3.531496, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.37)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #000000 (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_50mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=3.984252, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_70mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=4.3779526, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | + +### Prefabs + +| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | +|---|---|---|---|---| +| `cohesion_27mm` | 1 | size=4.547244, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | ebe2e870a59f… | +| `cohesion_50mm` | 1 | size=5.669291, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 406845c49cf4… | +| `cohesion_70mm` | 1 | size=6.850394, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3a57b0b27e32… | +| `halfCohesion_27mm` | 1 | size=3.531496, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 0e1c90ce1ef6… | +| `halfCohesion_50mm` | 1 | size=3.984252, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 2e05a24ab516… | +| `halfCohesion_70mm` | 1 | size=4.3779526, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 7cee44cc736f… | + +## Range + +### Materials + +| Material | Shader keywords | Floats clés | Couleurs | +|---|---|---|---| +| `ProjectorMaterial_100mm` | `_ARC_ON _MAXRANGE_RANGEFIVE _RANGE_FIVE` | `_BaseSize`=100, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.0183, `_TwoTuner`=0.0062, `_ThreeTuner`=0.0245, `_FourTuner`=0.0123, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_100mm_oblong` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=47.4, `_ProjectorRadius`=30, `_BandSize`=0.34, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=-0.022, `_TwoTuner`=-0.0421, `_ThreeTuner`=-0.0324, `_FourTuner`=-0.0525, `_FiveTuner`=0.073, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_100mm_snail` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=47.4, `_ProjectorRadius`=30, `_BandSize`=0.34, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=-0.0216, `_TwoTuner`=-0.0421, `_ThreeTuner`=-0.0324, `_FourTuner`=-0.0525, `_FiveTuner`=0.073, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_120mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=120, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.022, `_TwoTuner`=0.0074, `_ThreeTuner`=0.0292, `_FourTuner`=0.0146, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_150mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=150, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.027, `_TwoTuner`=0.009, `_ThreeTuner`=0.0358, `_FourTuner`=0.018, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_18mm_smokeToken` | `_ARC_OFF _MAXRANGE_RANGEONE` | `_BaseSize`=18.8, `_ProjectorRadius`=6, `_BandSize`=-0.11, `_BandContrast`=0.46, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #3A3145 (α=0.59)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_25mm_token` | `_ARC_OFF _MAXRANGE_RANGEONE` | `_BaseSize`=27, `_ProjectorRadius`=2.97, `_BandSize`=-0.45, `_BandContrast`=3, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_25mm_tokenRangeTwo` | `_ARC_OFF _MAXRANGE_RANGETWO` | `_BaseSize`=28.12, `_ProjectorRadius`=14.9, `_BandSize`=0.04, `_BandContrast`=3, `_MaxRange`=1, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0062, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_27mm` | `_ARC_OFF _MAXRANGE_RANGEFIVE` | `_BaseSize`=27, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=0, `_RangeSize`=-76.5, `_GradScaler`=0.0034, `_OneTuner`=0.0053, `_TwoTuner`=0.0019, `_ThreeTuner`=0.0072, `_FourTuner`=0.0037, `_FiveTuner`=0, `_InfinityTuner`=0.0034 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_50mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=50, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_GradScaler`=0, `_OneTuner`=0.0098, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0127, `_FourTuner`=0.0063, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_50mm_bombCart` | `_ARC_OFF _MAXRANGE_RANGETWO` | `_BaseSize`=50, `_ProjectorRadius`=12, `_BandSize`=0.01, `_BandContrast`=3, `_MaxRange`=1, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_70mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=70, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=2.87, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.0132, `_TwoTuner`=0.0045, `_ThreeTuner`=0.0175, `_FourTuner`=0.009, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.35)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `Projector_BoardGuide` | `m_LightmapFlags: 4` | — | `_CameraFadeParams` transparent
`_Color` #FFFFFF (α=1.00)
`_ColorAddSubDiff` transparent
`_EmissionColor` #000000 (α=1.00)
`_SoftParticleFadeParams` transparent
`_TintColor` #7F7F7F (α=0.50) | + +### Prefabs + +| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | +|---|---|---|---|---| +| `projector_100mm` | 1 | size=31.9685, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 4b37ff3d68c5… | +| `projector_100mm_oblong` | 1 | size=33.4, far=45 | pos=(0.0, -1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 6c0556b105e8… | +| `projector_120mm` | 1 | size=32.3622, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | ab767c454926… | +| `projector_150mm` | 1 | size=32.95276, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3a33cb678c17… | +| `projector_200mm_oblong` | 1 | size=33.4, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0)
pos=(-0.0, -0.26, -0.0) rot=(0.0, 0.0, 90.0) | cccc683b4b18… | +| `projector_27mm` | 1 | size=30.5314, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | b1c5ef617632… | +| `projector_50mm` | 1 | size=30.984253, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 20ce39a8e0e1… | +| `projector_70mm` | 1 | size=31.37795, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 977480df799a… | +| `projector_POIGuide` | 1 | size=36, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | a0fb1f64d52e… | +| `projector_bomb_cart` | 1 | size=12.98425, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 417ae88dafb1… | +| `projector_smokeToken` | 1 | size=6.3700786, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3ef1e0f12697… | +| `projector_token` | 1 | size=7.06299, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 94ad9a7a030f… | +| `projector_tokenRangeTwo` | 1 | size=12.6, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 0905fd93d0b7… | + +## Deployment + +### Materials + +| Material | Shader keywords | Floats clés | Couleurs | +|---|---|---|---| +| `Projector_Deployment_Blue` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Deployment_Blue_Round` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Deployment_Red` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Deployment_Red_Round` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Score_Blue` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Score_Red` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | + +### Prefabs + +| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | +|---|---|---|---|---| +| `projector_blue_score` | 1 | size=6, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | fe66dad2ac93… | +| `projector_blue_spawn` | 1 | size=3, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 23c778da5a02… | +| `projector_blue_spawn_L` | 2 | size=1.5, far=25
size=3, far=25 | pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0)
pos=(1.5, 1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(0.0, -1.5000002, 0.0) rot=(0.0, 0.0, 90.0) | 23c778da5a02…
23c778da5a02… | +| `projector_blue_spawn_corner` | 1 | size=1.5, far=25 | pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0)
pos=(-1.5, -1.5, 0.0) rot=(0.0, 0.0, 90.0) | 23c778da5a02… | +| `projector_blue_spawn_half` | 1 | size=3, far=25 | pos=(0.0, -1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0) | 23c778da5a02… | +| `projector_blue_spawn_round2` | 1 | size=12, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 34786d447192… | +| `projector_red_score` | 1 | size=6, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 90fdc53ff83c… | +| `projector_red_spawn` | 1 | size=3, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 93e068805588… | +| `projector_red_spawn_L` | 2 | size=3, far=25
size=1.5, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0)
pos=(0.0, 1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(-1.5, -1.5, -0.0) rot=(0.0, 0.0, 90.0) | 93e068805588…
93e068805588… | +| `projector_red_spawn_corner` | 1 | size=1.5, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0)
pos=(1.5, 1.5, -0.0) rot=(0.0, 0.0, 90.0) | 93e068805588… | +| `projector_red_spawn_half` | 1 | size=3, far=25 | pos=(0.0, 1.5, -0.0) rot=(0.0, 0.0, 90.0)
pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 93e068805588… | +| `projector_red_spawn_round2` | 1 | size=12, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 54824e17d05f… | + +## Movement + +### Materials + +| Material | Shader keywords | Floats clés | Couleurs | +|---|---|---|---| +| `ProjectorMaterial_100mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=8.622047, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_100mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=10.590551, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_100mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=12.559055, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_120mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=9.803149, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_120mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=11.771653, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_120mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.740157, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_150mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=11.574803, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_150mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.543307, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_150mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=15.511811, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed1_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=8.45, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=4.547244, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed2_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=12.42748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=6.515748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed3_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=16.42748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=8.484252, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_50mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=5.6692915, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_50mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=7.6377954, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_50mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=9.606299, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_70mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=6.850394, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_70mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=8.818897, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_70mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=10.787401, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_oblong_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.051181, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_oblong_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=15.019685, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_oblong_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=16.98819, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_snail_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=14.527559, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_snail_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=16.496063, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_snail_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=18.464567, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | + +### Prefabs + +| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | +|---|---|---|---|---| +| `movement_100mm_speed1_single` | 1 | size=8.622047, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 406c1ddb0995… | +| `movement_100mm_speed2_single` | 1 | size=10.590551, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 0aa471d50563… | +| `movement_100mm_speed3_single` | 1 | size=12.559055, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 8fe5008dea10… | +| `movement_120mm_speed1_single` | 1 | size=9.803149, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 21bc9fa53646… | +| `movement_120mm_speed2_single` | 1 | size=11.771653, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 6d02674694a9… | +| `movement_120mm_speed3_single` | 1 | size=13.740157, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 895a4d7a1f02… | +| `movement_150mm_speed1_single` | 1 | size=11.574803, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | fef40c5dadca… | +| `movement_150mm_speed2_single` | 1 | size=13.543307, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | c9d6b007a730… | +| `movement_150mm_speed3_single` | 1 | size=15.511811, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | d08a6fca8013… | +| `movement_27mm_speed1_double` | 1 | size=8.45, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 7037d7245d82… | +| `movement_27mm_speed1_single` | 1 | size=4.547244, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 61da0f6e009e… | +| `movement_27mm_speed2_double` | 1 | size=12.42748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 362084914234… | +| `movement_27mm_speed2_single` | 1 | size=6.515748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 6de57d74ffda… | +| `movement_27mm_speed3_double` | 1 | size=16.42748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | a0dce2ad51d5… | +| `movement_27mm_speed3_single` | 1 | size=8.484252, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | b65b690fde07… | +| `movement_50mm_speed1_single` | 1 | size=5.6692915, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 38f1f5f6a60c… | +| `movement_50mm_speed2_single` | 1 | size=7.6377954, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 3d2f279db06b… | +| `movement_50mm_speed3_single` | 1 | size=9.606299, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | ca54633a2d92… | +| `movement_70mm_speed1_single` | 1 | size=6.850394, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 3d2faeaaaaff… | +| `movement_70mm_speed2_single` | 1 | size=8.818897, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 948d0799a0c3… | +| `movement_70mm_speed3_single` | 1 | size=10.787401, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | a986001c0bc6… | +| `movement_oblong_speed1_single` | 1 | size=13.051181, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | b9540fc3dad6… | +| `movement_oblong_speed2_single` | 1 | size=15.019685, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 672e50dcd47c… | +| `movement_oblong_speed3_single` | 1 | size=16.98819, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | a2468c9fe07d… | +| `movement_snail_speed1_single` | 1 | size=14.527559, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | b7c44d9516fd… | +| `movement_snail_speed2_single` | 1 | size=16.496063, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | d27d5e30c83c… | +| `movement_snail_speed3_single` | 1 | size=18.464567, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | ecdda5f21c4c… | + +## Notes globales + +- Cohesion : double set `Materials/cohesion_*` et `_Revamp/halfCohesion_*`. Probable : `_Revamp/` est la version actuellement utilisée (le bundle live s'appelle `halfcohesion_27mm.unity3d` cf shader-inventory). +- Range : shader procédural avec jusqu'à 5 bandes (`_RangeOne` à `_RangeFive`) + `_RangeInfinity`. Le keyword `_MAXRANGE_RANGEFIVE` ou `_MAXRANGE_RANGEFOUR` détermine la dernière bande visible. +- Movement : un seul Projector par prefab, paramétré par `_BaseSize` + speed. Le `_Arc` doit contrôler la portion d'arc visible. +- Deployment : 6 materials uniques réutilisés par 12 prefabs (rotations différentes pour matérialiser corner/half/L/round/score). \ No newline at end of file diff --git a/mac-patcher/patch_cohesion_rename.py b/mac-patcher/patch_cohesion_rename.py new file mode 100644 index 00000000..065a14a9 --- /dev/null +++ b/mac-patcher/patch_cohesion_rename.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +PATCH ATTEMPT #1 — Rename embedded Shader m_Name from +'BucketheadBits/Projector/Cohesion' to 'Standard'. + +Theory: at AssetBundle.LoadAsset, Unity may resolve the shader by name via +Shader.Find() before falling back to the embedded bytecode. If so, TTS picks +up its own built-in Standard shader and renders the Cohesion projector with +it, ignoring the broken embedded shader. + +Writes to .patched.unity3d alongside the original. Does NOT touch +the live bundle in ~/Library/Tabletop Simulator/Mods/Assetbundles/ yet. +""" +import os +import sys +import shutil +import UnityPy + +BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") +BUNDLE_NAME = "httpssteamusercontentaakamaihdnetugc2482129948496305632EBBE2560D4336E6C96317EDDA787E225CF0E5B48.unity3d" +SRC = os.path.join(BUNDLE_DIR, BUNDLE_NAME) +DST = os.path.join("/tmp", "cohesion.patched.unity3d") + +print(f"Loading: {SRC}") +env = UnityPy.load(SRC) + +modified = False +for obj in env.objects: + if obj.type.name != "Shader": + continue + data = obj.read() + current = getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) or getattr(data, "m_Name", None) + print(f" Found Shader PathID={obj.path_id} current m_Name={current!r}") + if current != "BucketheadBits/Projector/Cohesion": + print(" -> Not our target, skipping.") + continue + + # Modify via typetree (safest cross-version path with UnityPy) + tree = obj.read_typetree() + # m_ParsedForm.m_Name is the canonical name for parsed shaders. + # m_Name may also exist for the asset itself; we try both. + if "m_ParsedForm" in tree and "m_Name" in tree["m_ParsedForm"]: + old = tree["m_ParsedForm"]["m_Name"] + tree["m_ParsedForm"]["m_Name"] = "Standard" + print(f" -> renamed m_ParsedForm.m_Name {old!r} -> 'Standard'") + modified = True + if "m_Name" in tree and tree.get("m_Name"): + old = tree["m_Name"] + tree["m_Name"] = "Standard" + print(f" -> renamed m_Name {old!r} -> 'Standard'") + modified = True + + obj.save_typetree(tree) + +if not modified: + print("ERROR: no shader matched. Aborting.") + sys.exit(1) + +# Write out the patched bundle +print(f"\nSaving patched bundle to: {DST}") +with open(DST, "wb") as f: + f.write(env.file.save(packer="lz4")) + +print(f" size: {os.path.getsize(DST)} bytes (original was {os.path.getsize(SRC)})") + +# Verify roundtrip +print("\nVerifying patched bundle is re-loadable...") +env2 = UnityPy.load(DST) +for obj in env2.objects: + if obj.type.name == "Shader": + d = obj.read() + n = getattr(getattr(d, "m_ParsedForm", None), "m_Name", None) or getattr(d, "m_Name", None) + print(f" After patch: Shader PathID={obj.path_id} m_Name={n!r}") + elif obj.type.name == "Material": + d = obj.read() + sref = d.m_Shader + print(f" Material {d.m_Name!r} m_Shader=(file_id={sref.file_id}, path_id={sref.path_id})") + +print("\nDone. Patched file ready at:", DST) +print("Run this command to replace the live bundle (only when TTS is closed):") +print(f" cp {DST!r} {SRC!r}") diff --git a/mac-patcher/patch_save_for_mac.py b/mac-patcher/patch_save_for_mac.py new file mode 100644 index 00000000..601d5b46 --- /dev/null +++ b/mac-patcher/patch_save_for_mac.py @@ -0,0 +1,2133 @@ +#!/usr/bin/env python3 +""" +Patch a TTS save JSON (SWL mod beta) to add Mac Cohesion fallback. + +Two-part patch: +1. Append manager + event handlers to the Global LuaScript. +2. Replace the inline Cohesion block in Unit_Leader (99f1c8) and Order_Token + (a57c41) with delegated stubs that call the Global manager via Global.call. + +The native hotkey "Show Cohesion On Hovered Model" and the Order Token +"COHESION" button automatically use the patched code afterwards - they +spawn vector-lines rings instead of legacy Custom_AssetBundle Projectors. + +Usage: + python3 patch_save_for_mac.py +""" +import json +import re +import socket +import sys +from pathlib import Path + +# ------------------------------------------------------------ +# 1) Code appended to Global LuaScript (manager + event handlers) +# ------------------------------------------------------------ +GLOBAL_PATCH_LUA = r""" + +-- ============================================ +-- MAC COHESION FALLBACK (auto-injected for TTS U6 magenta bug) +-- Manager Global + event handlers + vector-lines rendering. +-- Called by Unit_Leader / Order_Token via Global.call from their patched +-- Cohesion functions. +-- ============================================ + +-- Capture the ORIGINAL Global functions loaded by !/RangeRulers and +-- !/Cohesion BEFORE we overwrite them below. We also capture the inner +-- spawn/clear pair because the toggle wrapper resolves them by NAME at +-- call time - without aliasing those too, the original toggle would route +-- back into our Mac versions, defeating Windows mode entirely. +spawnRangeRulerOriginalGlobal = spawnRangeRuler +clearRangeRulersOriginalGlobal = clearRangeRulers +spawnCohesionRulerOriginalGlobal = spawnCohesionRuler +clearCohesionRulerOriginalGlobal = clearCohesionRuler + +activeOverlays = activeOverlays or {} +hiddenWhilePickedUp = hiddenWhilePickedUp or {} + +local ASSETS_BASE = "https://raw.githubusercontent.com/ironsquadronfr-hub/tts/mac-projector-fallback/mod/data/mac-fallback-assets/" +local COHESION_HALO_URL = ASSETS_BASE .. "cohesion_halo.png" +local MAX_MOVE_CYAN_URL = ASSETS_BASE .. "max_move_cyan.png" +local DEPLOYMENT_RED_URL = ASSETS_BASE .. "deployment_red.png" +local DEPLOYMENT_BLUE_URL = ASSETS_BASE .. "deployment_blue.png" +local RANGE_DECAL_URLS = { + smokeToken = ASSETS_BASE .. "range_smokeToken.png", + token = ASSETS_BASE .. "range_token.png", + tokenRangeTwo = ASSETS_BASE .. "range_tokenRangeTwo.png", + poi = ASSETS_BASE .. "range_poi.png", + bombCart = ASSETS_BASE .. "range_bombCart.png", + -- Fig leaders: per-base-size PNGs (bands pre-offset by the base radius so + -- the decal bands line up with the vector-line rings). + fig_leader = { + small = ASSETS_BASE .. "range_fig_leader_small.png", + medium = ASSETS_BASE .. "range_fig_leader_medium.png", + large = ASSETS_BASE .. "range_fig_leader_large.png", + huge = ASSETS_BASE .. "range_fig_leader_huge.png", + laat = ASSETS_BASE .. "range_fig_leader_laat.png", + epic = ASSETS_BASE .. "range_fig_leader_epic.png", + long = ASSETS_BASE .. "range_fig_leader_long_stadium.png", + snail = ASSETS_BASE .. "range_fig_leader_snail_stadium.png", + }, +} +local RANGE_MAX_RADIUS = { + fig_leader = 30, -- R5 max + -- SWL: Range 1 = 6", Range 2 = 12", Range 0.5 = 3". + smokeToken = 6, + token = 6, + tokenRangeTwo = 12, + poi = 3, + bombCart = 12, +} + +-- Range band colors (RGBA) extracted from BB_RangeProjector material defaults +-- + demi-portee blanche pour SWL Range 0.5 (3 inches) +local RANGE_COLORS = { + half = {1.0, 1.0, 1.0, 0.6}, -- 0.5 = 3" white + one = {1.0, 0.764, 0.0, 0.7}, -- R1 yellow + two = {1.0, 0.459, 0.0, 0.7}, -- R2 orange + three = {1.0, 0.079, 0.0, 0.7}, -- R3 red + four = {0.764, 0.0, 0.259, 0.7}, -- R4 magenta + five = {0.528, 0.0, 0.443, 0.7}, -- R5 violet +} + +-- Per-rangeKey configurations: list of {radius_inches, color_name} +local RANGE_CONFIGS = { + -- Fig leaders: half + 4 bands at SWL standard ranges 3/6/12/18/24" + small = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + medium = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + large = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + huge = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + laat = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + epic = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + long = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + snail = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + -- Token rangeKeys (1 to 2 bands depending on use). SWL ranges in inches: + -- Range 1 = 6", Range 2 = 12", Range 0.5 (poi) = 3". + smokeToken = {{r=6,c="one"}}, + token = {{r=6,c="one"}}, + tokenRangeTwo = {{r=6,c="one"},{r=12,c="two"}}, + poi = {{r=3,c="one"}}, + bombCart = {{r=6,c="one"},{r=12,c="two"}}, +} + +-- Base footprint dimensions in mm. width <= length; equal means round. +-- Used to drive round vs stadium overlay shape. +local BASE_DIMENSIONS = { + small = {width = 27, length = 27}, + medium = {width = 50, length = 50}, + large = {width = 70, length = 70}, + huge = {width = 100, length = 100}, + laat = {width = 120, length = 120}, + epic = {width = 150, length = 150}, + long = {width = 100, length = 175}, -- oblong + snail = {width = 100, length = 200}, -- oblong +} + +-- Base radius (inches) for round overlays. For oblong bases we use a +-- stadium shape instead (see macBuildStadium), so the radius here is the +-- half-width that drives the cap arcs. +local FIG_BASE_RADIUS_IN = {} +for k, d in pairs(BASE_DIMENSIONS) do + FIG_BASE_RADIUS_IN[k] = (d.width / 2) / 25.4 +end + +local function macIsOblong(baseSize) + local d = BASE_DIMENSIONS[baseSize] + return d ~= nil and d.length > d.width +end + +-- Returns the stadium half-dimensions (inches) and yaw for an oblong base, +-- or nil if the fig's base is round. Callers should branch on the return. +local function macOblongDims(fig, baseSize) + if not macIsOblong(baseSize) then return nil end + local d = BASE_DIMENSIONS[baseSize] + return { + halfLen = (d.length / 2) / 25.4, + halfWid = (d.width / 2) / 25.4, + rotY = fig.getRotation().y, + } +end + +-- Resolve baseSize for a fig. The mod stores it as a script-local Var on the +-- fig itself (set at spawn from the unit's container). unitData.baseSize is +-- only populated on the Unit Leader, not on minis/vehicles - so getVar is +-- the canonical read, with unitData as fallback for safety. +local function macGetBaseSize(fig) + if not fig then return nil end + local ok, bs = pcall(function() return fig.getVar("baseSize") end) + if ok and bs then return bs end + local data = fig.getTable("unitData") + if data and data.baseSize then return data.baseSize end + return nil +end + +local function macFigBaseRadius(fig) + if not fig then return 0.5315 end + local bs = macGetBaseSize(fig) + return (bs and FIG_BASE_RADIUS_IN[bs]) or 0.5315 +end + +-- Token base radii (inches) - measured from token edge for range bands +local TOKEN_BASE_RADIUS = { + smokeToken = 18.8 / 2 / 25.4, -- 0.370" + token = 25.1 / 2 / 25.4, -- 0.494" + tokenRangeTwo = 25.1 / 2 / 25.4, -- 0.494" + poi = 50.8 / 2 / 25.4, -- 1.000" + bombCart = 50.0 / 2 / 25.4, -- 0.984" +} + +-- Hotkey-driven range on tokens renders fig-leader-style bands using the +-- closest equivalent base size (so the visual matches what a unit of that +-- footprint would see). Token R button keeps the single ring (token-spec). +local TOKEN_TO_BASESIZE = { + smokeToken = "small", + token = "small", + tokenRangeTwo = "small", + poi = "medium", + bombCart = "medium", +} + +-- Maximum Move radii (inches), indexed by baseSize then speed 1..3 +-- Source: ProjectorRadius from BB_MovementProjector materials (single variants) +local MAX_MOVE_RADIUS = { + small = { 4.547244, 6.515748, 8.484252}, -- 27mm + medium = { 5.669292, 7.637795, 9.606299}, -- 50mm + large = { 6.850394, 8.818897, 10.787401}, -- 70mm + huge = { 8.622047, 10.590551, 12.559055}, -- 100mm + laat = { 9.803149, 11.771653, 13.740157}, -- 120mm + epic = {11.574803, 13.543307, 15.511811}, -- 150mm + long = {13.051181, 15.019685, 16.988190}, -- oblong + snail = {14.527559, 16.496063, 18.464567}, -- snail +} + +-- Deployment cell dimensions (width X, depth Z, inches). +-- All cells live on a 6"x6" grid; sub-cells are halves/quarters of that cell. +local DEPLOYMENT_CELL_SIZES = { + r = {6, 6}, b = {6, 6}, -- full cell + rl = {6, 6}, bl = {6, 6}, -- large variant (also full cell) + rh = {6, 3}, bh = {6, 3}, -- horizontal half (3" deep) + rs = {3, 6}, bs = {3, 6}, -- vertical half (3" wide), x-offset +1.5 + rss = {3, 6}, bss = {3, 6}, -- vertical half (3" wide), x-offset -1.5 + rc = {3, 3}, bc = {3, 3}, -- corner quarter + rcc = {3, 3}, bcc = {3, 3}, -- opposite corner quarter +} + +local function macRayGroundY(x, z, offset) + local hits = Physics.cast({ + origin = {x, 30, z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 50, + }) + for _, h in ipairs(hits) do + return h.point.y + (offset or 0.05) + end + return offset or 0.05 +end + +local function macBuildRect(cx, cz, hw, hd, color, thickness) + local segPerSide = 8 + local pts = {} + local corners = { + {cx - hw, cz + hd}, {cx + hw, cz + hd}, + {cx + hw, cz - hd}, {cx - hw, cz - hd}, + } + for i = 1, 4 do + local a = corners[i] + local b = corners[i % 4 + 1] + for s = 0, segPerSide - 1 do + local t = s / segPerSide + local x = a[1] + (b[1] - a[1]) * t + local z = a[2] + (b[2] - a[2]) * t + table.insert(pts, {x, macRayGroundY(x, z), z}) + end + end + local c1 = corners[1] + table.insert(pts, {c1[1], macRayGroundY(c1[1], c1[2]), c1[2]}) + return {points = pts, color = color, thickness = thickness or 0.06} +end + +local function macBuildRing(centerPos, radius, color, thickness, ignoreObj) + local nSeg = 64 + local pts = {} + for i = 0, nSeg do + local a = (i / nSeg) * 2 * math.pi + local x = centerPos.x + radius * math.cos(a) + local z = centerPos.z + radius * math.sin(a) + local hits = Physics.cast({ + origin = {x, centerPos.y + 10, z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local y = centerPos.y + 0.05 + for _, h in ipairs(hits) do + if h.hit_object ~= ignoreObj then + y = h.point.y + 0.05 + break + end + end + table.insert(pts, {x, y, z}) + end + return {points = pts, color = color, thickness = thickness or 0.05} +end + +-- Stadium = rectangle with two semicircular caps on the long-axis ends. +-- halfLen, halfWid in inches (halfLen >= halfWid). dist = expansion outwards +-- from the base edge in inches (so the overlay sits at dist outside the base). +-- rotYdeg rotates the stadium around Y to match the fig's orientation; the +-- long axis is local Z (front-to-back of the model). +local function macBuildStadium(centerPos, halfLen, halfWid, rotYdeg, dist, color, thickness, ignoreObj) + local rotRad = math.rad(rotYdeg or 0) + local cosR, sinR = math.cos(rotRad), math.sin(rotRad) + + local capCenter = halfLen - halfWid -- distance from center to cap arc center, along long axis + local capR = halfWid + dist -- cap arc radius (= straight-side offset from long axis) + + local nSegArc = 32 -- segments per semicircle cap + local pts = {} + + local function addLocal(lx, lz) + local wx = centerPos.x + lx * cosR + lz * sinR + local wz = centerPos.z - lx * sinR + lz * cosR + local hits = Physics.cast({ + origin = {wx, centerPos.y + 10, wz}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local y = centerPos.y + 0.05 + for _, h in ipairs(hits) do + if h.hit_object ~= ignoreObj then + y = h.point.y + 0.05 + break + end + end + table.insert(pts, {wx, y, wz}) + end + + -- Top cap: angle 0 to pi (right edge -> top -> left edge), centered at z=+capCenter + for i = 0, nSegArc do + local a = (i / nSegArc) * math.pi + addLocal(capR * math.cos(a), capCenter + capR * math.sin(a)) + end + -- The straight left side is implicit: the polyline jumps from + -- (-capR, +capCenter) directly to (-capR, -capCenter) - TTS draws a + -- straight segment between consecutive points. + -- Bottom cap: angle pi to 2pi, centered at z=-capCenter + for i = 0, nSegArc do + local a = math.pi + (i / nSegArc) * math.pi + addLocal(capR * math.cos(a), -capCenter + capR * math.sin(a)) + end + -- Close the loop back to the top-cap start point. + addLocal(capR, capCenter) + + return {points = pts, color = color, thickness = thickness or 0.05} +end + +local macBuilders = {} + +macBuilders.range = function(fig, params) + local pos = fig.getPosition() + -- Resolve rangeKey for token-specific single-ring rendering (R button on + -- a token). The hotkey path sets params.forceFigMode = true to bypass + -- this and always render the fig-leader 6-band overlay, using the token's + -- equivalent baseSize for sizing. + local forceFig = params and params.forceFigMode + local rangeKey = (not forceFig) and ((params and params.rangeKey) or fig.getVar("rangeKey")) or nil + local config, decalURL, maxR, br + local oblong, halfLen, halfWid, rotY + local baseSize -- hoisted: used below for firing-arc decision + if rangeKey and RANGE_CONFIGS[rangeKey] then + config = RANGE_CONFIGS[rangeKey] + decalURL = RANGE_DECAL_URLS[rangeKey] + maxR = RANGE_MAX_RADIUS[rangeKey] + br = TOKEN_BASE_RADIUS[rangeKey] or 0 + else + baseSize = macGetBaseSize(fig) + local hasBaseSize = baseSize ~= nil + if not hasBaseSize then + -- Token hit from hotkey: map rangeKey to base size for sizing. + local tk = fig.getVar("rangeKey") + baseSize = (tk and TOKEN_TO_BASESIZE[tk]) or "small" + end + config = RANGE_CONFIGS[baseSize] or RANGE_CONFIGS.small + decalURL = RANGE_DECAL_URLS.fig_leader[baseSize] + or RANGE_DECAL_URLS.fig_leader.small + maxR = RANGE_MAX_RADIUS.fig_leader + if hasBaseSize then + br = macFigBaseRadius(fig) + local od = macOblongDims(fig, baseSize) + if od then + oblong, halfLen, halfWid, rotY = true, od.halfLen, od.halfWid, od.rotY + end + else + -- Token in forceFig mode: use token's measured radius for offset. + local tk = fig.getVar("rangeKey") + br = (tk and TOKEN_BASE_RADIUS[tk]) or FIG_BASE_RADIUS_IN[baseSize] or 0.5315 + end + end + + -- SWL convention: all distances are measured from the base/token edge. + + -- Vector contours: 6 concentric rings for round bases, 6 concentric + -- stadium outlines for oblong bases (each band sits at band.r outwards + -- from the base edge - same rule, just a different shape). + local lines = {} + for _, band in ipairs(config) do + if oblong then + table.insert(lines, macBuildStadium(pos, halfLen, halfWid, rotY, band.r, + RANGE_COLORS[band.c], 0.1, fig)) + else + table.insert(lines, macBuildRing(pos, band.r + br, RANGE_COLORS[band.c], 0.1, fig)) + end + end + + -- Firing arc lines (4 radial lines dividing into Front/R/Rear/L quadrants). + -- Mirrors the vanilla Projector shader: ProjectorMaterial_ sets + -- _Arc=1 / shader keyword _ARC_ON for every non-27mm base (medium, large, + -- huge, laat, epic, long, snail). 27mm sets _Arc=0. + -- Round bases : lines emanate from the base edge, radial from center. + -- Oblong bases: lines emanate from the centers of the two end + -- semicircles (front cap + rear cap), front lines at +/-45 from the + -- long-axis forward, rear lines at +/-135. + local arcColor = {1, 1, 1, 0.6} + if baseSize and baseSize ~= "small" then + if oblong then + local capOffset = (halfLen or 0) - (halfWid or 0) + local rad = math.rad(rotY) + local fwdX, fwdZ = math.sin(rad), math.cos(rad) + local frontX = pos.x + capOffset * fwdX + local frontZ = pos.z + capOffset * fwdZ + local rearX = pos.x - capOffset * fwdX + local rearZ = pos.z - capOffset * fwdZ + local reach = (halfWid or 0) + (maxR or 6) + local arcs = { + {frontX, frontZ, 45}, -- front-right boundary + {frontX, frontZ, -45}, -- front-left boundary + {rearX, rearZ, 135}, -- rear-right boundary + {rearX, rearZ, -135}, -- rear-left boundary + } + for _, e in ipairs(arcs) do + local cx, cz, deg = e[1], e[2], e[3] + local a = math.rad(rotY + deg) + local sinA, cosA = math.sin(a), math.cos(a) + table.insert(lines, { + points = { + {cx, pos.y + 0.1, cz}, + {cx + reach * sinA, pos.y + 0.1, cz + reach * cosA}, + }, + color = arcColor, + thickness = 0.05, + }) + end + else + local figRotY = fig.getRotation().y + local outerR = (maxR or 6) + br + for _, deg in ipairs({45, 135, 225, 315}) do + local a = math.rad(deg + figRotY) + local sinA, cosA = math.sin(a), math.cos(a) + table.insert(lines, { + points = { + {pos.x + br * sinA, pos.y + 0.1, pos.z + br * cosA}, + {pos.x + outerR * sinA, pos.y + 0.1, pos.z + outerR * cosA}, + }, + color = arcColor, + thickness = 0.05, + }) + end + end + end + + -- Decal halo (filled bands) - flat on the ground under the fig + local hits = Physics.cast({ + origin = {pos.x, pos.y + 10, pos.z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local groundY = pos.y + for _, h in ipairs(hits) do + if h.hit_object ~= fig then groundY = h.point.y; break end + end + + if oblong then + -- Stadium-shape PNGs are authored per oblong base size: the 6 bands + -- inside the PNG already match the stadium vector outlines because + -- the PNG was generated with the same halfWid/halfLen + dist math. + -- Decal scale = footprint of the outermost band (2*(half + maxR)) + -- in each axis, so the PNG stretches to exactly the right size. + local longSpan = 2 * (halfLen + maxR) + local shortSpan = 2 * (halfWid + maxR) + return { + lines = lines, + decals = { + { + name = "range_decal_" .. fig.getGUID(), + url = decalURL, + position = {pos.x, groundY + 0.02, pos.z}, + rotation = {90, rotY, 0}, + scale = {shortSpan, longSpan, 1}, + } + }, + } + end + + local effectiveMaxR = maxR + br + return { + lines = lines, + decals = { + { + name = "range_decal_" .. fig.getGUID(), + url = decalURL, + position = {pos.x, groundY + 0.02, pos.z}, -- below cohesion halo + rotation = {90, 0, 0}, + scale = {effectiveMaxR * 2, effectiveMaxR * 2, effectiveMaxR * 2}, + } + }, + } +end + +macBuilders.maxmove = function(fig, params) + -- Use the anchor position stored at spawn time so the ring stays put + -- when the fig slides toward the destination, instead of tracking it. + local pos = (params and params.anchorPos) or fig.getPosition() + local baseSize = (params and params.baseSize) or "small" + local speed = (params and params.speed) or 1 + local radii = MAX_MOVE_RADIUS[baseSize] or MAX_MOVE_RADIUS.small + local r = radii[speed] or radii[1] + local color = {0.333, 0.800, 1.000, 0.7} + -- Base ring: small white outline matching the figure's base + -- (BB_MovementProjector renders a base ring on top of the cyan max-move). + local br = FIG_BASE_RADIUS_IN[baseSize] or FIG_BASE_RADIUS_IN.small + local baseColor = {1, 1, 1, 0.8} + -- Oblong base dimensions for the stadium base ring. Use the rotation + -- captured at spawn-time so the ring keeps its orientation even after + -- the fig pivots toward its destination. + local od = macOblongDims(fig, baseSize) + local oblong = od ~= nil + local halfLen, halfWid, rotY + if oblong then + halfLen, halfWid = od.halfLen, od.halfWid + rotY = (params and params.anchorRot) or od.rotY + end + + -- Ground raycast for decal position + local hits = Physics.cast({ + origin = {pos.x, pos.y + 10, pos.z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local groundY = pos.y + for _, h in ipairs(hits) do + if h.hit_object ~= fig then groundY = h.point.y; break end + end + + if oblong then + -- Oblong base: the cyan max-move envelope stays a round ring (isotropic + -- - any rotation reaches the same radius from the pivot), but the + -- inner white base ring follows the rectangular footprint as a + -- stadium hugging the actual base edge. + return { + lines = { + macBuildRing(pos, r, color, 0.1, fig), + macBuildStadium(pos, halfLen, halfWid, rotY, 0, + baseColor, 0.06, fig), + }, + decals = { + { + name = "maxmove_decal_" .. fig.getGUID(), + url = MAX_MOVE_CYAN_URL, + position = {pos.x, groundY + 0.025, pos.z}, + rotation = {90, 0, 0}, + scale = {r * 2, r * 2, r * 2}, + } + }, + } + end + + return { + lines = { + macBuildRing(pos, r, color, 0.1, fig), -- outer cyan max-move + macBuildRing(pos, br, baseColor, 0.06, fig), -- inner white base ring + }, + decals = { + { + name = "maxmove_decal_" .. fig.getGUID(), + url = MAX_MOVE_CYAN_URL, + position = {pos.x, groundY + 0.025, pos.z}, + rotation = {90, 0, 0}, + scale = {r * 2, r * 2, r * 2}, + } + }, + } +end + +macBuilders.deployment = function(_, params) + local pos = params.pos -- {x, y, z} cell center (after offset) + local cell = params.cell -- "r"/"b"/"rh"/... key + local size = DEPLOYMENT_CELL_SIZES[cell] or {6, 6} + local w, d = size[1], size[2] + local cx, cz = pos[1], pos[3] + local hw, hd = w / 2, d / 2 + + local isRed = cell:sub(1, 1) == "r" + local lineColor = isRed and {1, 0.15, 0.15, 0.9} or {0.15, 0.4, 1, 0.9} + local url = isRed and DEPLOYMENT_RED_URL or DEPLOYMENT_BLUE_URL + + local groundY = macRayGroundY(cx, cz, 0) + + return { + lines = { macBuildRect(cx, cz, hw, hd, lineColor, 0.08) }, + decals = { + { + name = "deployment_decal_" .. cell .. "_" .. tostring(cx) .. "_" .. tostring(cz), + url = url, + position = {cx, groundY + 0.03, cz}, + rotation = {90, 0, 0}, + scale = {w, d, 1}, + } + }, + } +end + +macBuilders.cohesion = function(fig, params) + local pos = fig.getPosition() + -- Cohesion overlay = base edge + 3 inches (half Range 1). Round bases get + -- a ring; oblong bases (long/snail) get a stadium-shape concentric to the + -- footprint so the offset stays a constant 3" from the edge. + local rangeKey = fig.getVar("rangeKey") + local oblong, halfLen, halfWid, rotY, r + if rangeKey and TOKEN_BASE_RADIUS[rangeKey] then + r = TOKEN_BASE_RADIUS[rangeKey] + 3.0 + else + local baseSize = macGetBaseSize(fig) or "small" + local od = macOblongDims(fig, baseSize) + if od then + oblong, halfLen, halfWid, rotY = true, od.halfLen, od.halfWid, od.rotY + else + r = (FIG_BASE_RADIUS_IN[baseSize] or FIG_BASE_RADIUS_IN.small) + 3.0 + end + end + if params and params.radius then + oblong, r = false, params.radius + end + + -- Find ground level under the fig so the flat decal sits on the table, + -- not at the fig pivot (which is above the base). + local hits = Physics.cast({ + origin = {pos.x, pos.y + 10, pos.z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local groundY = pos.y + for _, h in ipairs(hits) do + if h.hit_object ~= fig then + groundY = h.point.y + break + end + end + + if oblong then + -- Halo decal: stretch the round halo PNG into an ellipse aligned with + -- the fig's long axis. Approximates the stadium fade (a true stadium + -- halo would need a custom PNG); good enough visually since the + -- vector stadium outline carries the precise boundary. + local longSpan = 2 * (halfLen + 3.0) + local shortSpan = 2 * (halfWid + 3.0) + return { + lines = { macBuildStadium(pos, halfLen, halfWid, rotY, 3.0, + {1, 1, 1, 0.8}, 0.06, fig) }, + decals = { + { + name = "cohesion_halo_" .. fig.getGUID(), + url = COHESION_HALO_URL, + position = {pos.x, groundY + 0.04, pos.z}, + -- 90 deg pitch lays the decal flat on the table; rotY (yaw) + -- aligns its local Y with the fig's long axis. + rotation = {90, rotY, 0}, + scale = {shortSpan, longSpan, 1}, + } + }, + } + end + + return { + lines = { macBuildRing(pos, r, {1, 1, 1, 0.8}, 0.06, fig) }, + decals = { + { + name = "cohesion_halo_" .. fig.getGUID(), + url = COHESION_HALO_URL, + position = {pos.x, groundY + 0.04, pos.z}, + rotation = {90, 0, 0}, + scale = {r * 2, r * 2, r * 2}, + } + }, + } +end + +function macRedrawAll() + local lines, decals = {}, {} + -- Preload all texture URLs (invisible decals far below the table) so TTS + -- keeps them cached and we never get the white-square flash on respawn. + local preloadURLs = { + COHESION_HALO_URL, + RANGE_DECAL_URLS.smokeToken, + RANGE_DECAL_URLS.token, RANGE_DECAL_URLS.tokenRangeTwo, + RANGE_DECAL_URLS.poi, RANGE_DECAL_URLS.bombCart, + RANGE_DECAL_URLS.fig_leader.small, RANGE_DECAL_URLS.fig_leader.medium, + RANGE_DECAL_URLS.fig_leader.large, RANGE_DECAL_URLS.fig_leader.huge, + RANGE_DECAL_URLS.fig_leader.laat, RANGE_DECAL_URLS.fig_leader.epic, + RANGE_DECAL_URLS.fig_leader.long, RANGE_DECAL_URLS.fig_leader.snail, + MAX_MOVE_CYAN_URL, + DEPLOYMENT_RED_URL, DEPLOYMENT_BLUE_URL, + } + for i, url in ipairs(preloadURLs) do + table.insert(decals, { + name = "preload_" .. i, + url = url, + position = {0, -200 - i * 0.01, 0}, + rotation = {90, 0, 0}, + scale = {0.01, 0.01, 0.01}, + }) + end + local stale = {} + for key, entry in pairs(activeOverlays) do + local b = macBuilders[entry.type] + if b then + -- Deployment entries have no fig (params-driven). Others need a + -- live Object - drop them silently if destroyed/invalid. + local needsFig = (entry.type ~= "deployment") + if needsFig and (not entry.fig + or type(entry.fig.getPosition) ~= "function") then + stale[#stale + 1] = key + else + local ok, out = pcall(b, entry.fig, entry.params) + if ok and out then + for _, l in ipairs(out.lines or {}) do + table.insert(lines, l) + end + for _, d in ipairs(out.decals or {}) do + table.insert(decals, d) + end + else + stale[#stale + 1] = key + end + end + end + end + for _, k in ipairs(stale) do activeOverlays[k] = nil end + Global.setVectorLines(lines) + Global.setDecals(decals) +end + +function gSpawnCohesion(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":cohesion"] = { + type = "cohesion", fig = fig, params = params or {} + } + macRedrawAll() +end + +function gClearCohesion(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":cohesion"] = nil + macRedrawAll() +end + +function gToggleCohesion(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local key = fig.getGUID() .. ":cohesion" + if activeOverlays[key] then + gClearCohesion(params) + else + gSpawnCohesion(params) + end +end + +-- Range polling: a slow timer (5 frames = ~12 fps) that re-draws active +-- range rulers so they follow figures in real-time. Stops automatically when +-- no range overlay is active anymore - avoids the overnight memory leak. +macRangePollingActive = macRangePollingActive or false + +function macRangePoll() + local hasRange = false + for _, e in pairs(activeOverlays) do + if e.type == "range" then hasRange = true; break end + end + if hasRange then + macRedrawAll() + Wait.frames(macRangePoll, 5) + else + macRangePollingActive = false + end +end + +function gSpawnRange(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":range"] = { + type = "range", fig = fig, params = params or {} + } + macRedrawAll() + if not macRangePollingActive then + macRangePollingActive = true + Wait.frames(macRangePoll, 5) + end +end + +function gClearRange(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":range"] = nil + macRedrawAll() +end + +function gToggleRange(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local key = fig.getGUID() .. ":range" + if activeOverlays[key] then + gClearRange(params) + else + gSpawnRange(params) + end +end + +function gSpawnDeployment(params) + -- params: { cell = "r" | "b" | ..., pos = {x, y, z} } + if not params or not params.cell or not params.pos then return end + local key = "deploy:" .. params.cell .. ":" + .. tostring(params.pos[1]) .. ":" .. tostring(params.pos[3]) + activeOverlays[key] = {type = "deployment", fig = nil, params = params} + macRedrawAll() +end + +function gClearAllDeployment() + for k, e in pairs(activeOverlays) do + if e.type == "deployment" then activeOverlays[k] = nil end + end + macRedrawAll() +end + +function gSpawnMaxMove(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + -- Capture the spawn-time position + yaw. MaxMove is anchored to where + -- the move STARTED - it must not follow the fig as it slides toward + -- the destination. The yaw matters for oblong stadium overlays so the + -- white base ring keeps its orientation while the fig pivots. + local p = (params and params.params) or params or {} + p.anchorPos = fig.getPosition() + p.anchorRot = fig.getRotation().y + activeOverlays[fig.getGUID() .. ":maxmove"] = { + type = "maxmove", fig = fig, params = p + } + macRedrawAll() +end + +function gClearMaxMove(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":maxmove"] = nil + macRedrawAll() +end + +function onObjectPickUp(player_color, obj) + if not obj or not obj.getGUID then return end + local guid = obj.getGUID() + local prefix = guid .. ":" + local plen = #prefix + local changed = false + for key, entry in pairs(activeOverlays) do + if key:sub(1, plen) == prefix then + -- Cohesion: static, hide during pickup, restore on drop. + -- Range: keep visible; the polling timer redraws each tick so + -- the ruler follows the figure in real-time during the drag. + if entry.type == "cohesion" then + hiddenWhilePickedUp[key] = entry + activeOverlays[key] = nil + changed = true + end + end + end + if changed then macRedrawAll() end +end + +function onObjectDrop(player_color, obj) + if not obj or not obj.getGUID then return end + local guid = obj.getGUID() + local prefix = guid .. ":" + local plen = #prefix + + -- Defer the overlay restore until the fig has come to rest. Without this, + -- a flick-drop with residual inertia leaves the overlay anchored at the + -- release position while the fig continues to slide across the table. + local function tryRestore() + local fig = getObjectFromGUID(guid) + if not fig then return end -- destroyed during slide + if fig.held_by_color then return end -- picked up again, hold off + local v = fig.getVelocity and fig.getVelocity() + if v then + local speed = math.sqrt((v.x or 0)^2 + (v.y or 0)^2 + (v.z or 0)^2) + if speed > 0.05 then + Wait.frames(tryRestore, 3) + return + end + end + local changed = false + for key, entry in pairs(hiddenWhilePickedUp) do + if key:sub(1, plen) == prefix then + activeOverlays[key] = entry + hiddenWhilePickedUp[key] = nil + changed = true + end + end + if changed then macRedrawAll() end + end + tryRestore() +end + +function onObjectDestroy(obj) + if not obj or not obj.getGUID then return end + local guid = obj.getGUID() + local prefix = guid .. ":" + local plen = #prefix + local changed = false + for key in pairs(activeOverlays) do + if key:sub(1, plen) == prefix then + activeOverlays[key] = nil + changed = true + end + end + for key in pairs(hiddenWhilePickedUp) do + if key:sub(1, plen) == prefix then + hiddenWhilePickedUp[key] = nil + end + end + if changed then macRedrawAll() end +end + +-- Texture preload is handled by macRedrawAll which always includes all PNG +-- URLs as invisible decals far below the table. No separate preload needed. + +-- ============================================ +-- PER-SEAT MODE TOGGLE (Cohesion + Range) +-- Each seated player picks their renderer: "mac" (this patch) or +-- "windows" (original bundle Projector). When a player triggers an overlay, +-- the router checks THEIR mode and uses that renderer. The rendered overlay +-- is visible to everyone (TTS engine limitation). +-- ============================================ + +playerOverlayMode = playerOverlayMode or {} -- color -> "mac" | "windows" +-- Default = "windows" (majority of TTS users). Mac users toggle their seat +-- via the floating panel. Same for deployment (table-wide setting). +deploymentMode = deploymentMode or "windows" + +function gGetMode(params) + return playerOverlayMode[params.color] or "windows" +end + +function gGetDeploymentMode() + return deploymentMode +end + +function gToggleDeploymentMode() + deploymentMode = (deploymentMode == "mac") and "windows" or "mac" + -- Wipe any active deployment zones so the next setup uses the new mode. + gClearAllDeployment() + macRefreshModeUI() +end + +-- Replicates the original showRangeOnHoveredModel toggle, calling the +-- aliased Global originals so our Mac overrides don't recapture the path. +function gWindowsRangeToggle(fig) + if not fig then return end + if rangeRuler ~= nil then + pcall(clearRangeRulersOriginalGlobal) + if selectedUnitObj == fig then + selectedUnitObj = nil + return + end + end + if fig.interactable then + pcall(function() spawnRangeRulerOriginalGlobal(fig) end) + selectedUnitObj = fig + end +end + +-- Replicates the original showCohesionOnHoveredModel toggle. Uses Global +-- aliased originals + Global cohesionRuler state (set by the original). +function gWindowsCohesionToggle(fig) + if not fig then return end + if fig.interactable and selectedUnitObj == fig and cohesionRuler ~= nil then + pcall(clearCohesionRulerOriginalGlobal) + selectedUnitObj = nil + return + end + pcall(clearCohesionRulerOriginalGlobal) + pcall(function() spawnCohesionRulerOriginalGlobal(fig) end) + selectedUnitObj = fig +end + +function gCohesionTrigger(params) + if not params or not params.figGUID then return end + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local mode = playerOverlayMode[params.playerColor] or "windows" + if mode == "windows" then + gWindowsCohesionToggle(fig) + else + gToggleCohesion({figGUID = params.figGUID}) + end +end + +function gRangeTrigger(params) + if not params or not params.figGUID then return end + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local mode = playerOverlayMode[params.playerColor] or "windows" + if mode == "windows" then + gWindowsRangeToggle(fig) + else + gToggleRange({ + figGUID = params.figGUID, + forceFigMode = params.forceFigMode, + rangeKey = params.rangeKey, + }) + end +end + +-- UI: floating panel with one toggle button per seated player +function macModeClick(player, _, id) + local color = id:sub(9) -- "macmode_Red" -> "Red" + if player.color ~= color then + broadcastToColor("Only the player at this seat can toggle their mode.", + player.color, {1, 0.5, 0.5}) + return + end + local cur = playerOverlayMode[color] or "windows" + playerOverlayMode[color] = (cur == "mac") and "windows" or "mac" + -- Wipe Mac-side overlays so stale visuals don't linger after the toggle. + -- We don't track per-color ownership, so this clears Cohesion+Range for + -- everyone - acceptable since each player can re-trigger their hotkey. + -- Windows-side overlays (fig-scoped cohesionRuler/rangeRuler) are + -- cleared by iterating objects with active state. + for k, e in pairs(activeOverlays) do + if e.type == "cohesion" or e.type == "range" then + activeOverlays[k] = nil + end + end + macRedrawAll() + for _, obj in ipairs(getAllObjects()) do + if obj.getVar and obj.getVar("cohesionRuler") then + pcall(function() obj.call("clearCohesionRulerOriginal", obj) end) + end + if obj.getVar and obj.getVar("rangeRuler") then + pcall(function() obj.call("clearRangeRulersOriginal", obj) end) + end + end + macRefreshModeUI() +end + +local function macFindNodeById(tree, id) + for _, node in ipairs(tree) do + if node.attributes and node.attributes.id == id then return node, tree end + if node.children then + local found, parent = macFindNodeById(node.children, id) + if found then return found, parent end + end + end + return nil, nil +end + +-- Tracks the panel's desired active state across refreshes. Persists so a +-- click on the close X before the initial refresh is honored. Hidden at first +-- load (default = false); the floating "Mac Patch" menu button reveals it. +macModePanelActive = (macModePanelActive == nil) and false or macModePanelActive + +function macModeToggleVisibility() + macModePanelActive = not macModePanelActive + local tree = UI.getXmlTable() or {} + local panel = macFindNodeById(tree, "macModePanel") + if panel then + panel.attributes.active = macModePanelActive and "true" or "false" + UI.setXmlTable(tree) + else + macRefreshModeUI() + end +end + +function macRefreshModeUI() + local seated = Player.getPlayers() + local rows = {{ + tag = "Panel", + attributes = { + color = "transparent", + preferredHeight = "24", + }, + children = { + { + tag = "Text", + attributes = { + text = "Mac TTS U6 Patch", + fontSize = "16", + color = "white", + alignment = "MiddleLeft", + rectAlignment = "MiddleLeft", + }, + }, + { + tag = "Button", + attributes = { + id = "macModePanelClose", + text = "X", + onClick = "macModeToggleVisibility", + color = "#3a1e1e", + textColor = "white", + fontSize = "12", + width = "24", + height = "20", + rectAlignment = "MiddleRight", + }, + }, + }, + }, { + tag = "Text", + attributes = { + text = "Cohesion & Range: pick your renderer", + fontSize = "12", + color = "#bbbbbb", + alignment = "MiddleCenter", + }, + }} + if #seated == 0 then + table.insert(rows, { + tag = "Text", + attributes = { + text = "(no seated players)", + fontSize = "12", + color = "#888888", + alignment = "MiddleCenter", + }, + }) + end + for _, p in ipairs(seated) do + local mode = playerOverlayMode[p.color] or "windows" + local label = p.color .. " - " .. + (mode == "mac" and "MAC FALLBACK" or "WINDOWS ORIGINAL") + local bg = (mode == "mac") and "#1e7a3a" or "#7a3a1e" + table.insert(rows, { + tag = "Button", + attributes = { + id = "macmode_" .. p.color, + text = label, + onClick = "macModeClick", + color = bg, + fontSize = "13", + textColor = "white", + preferredHeight = "28", + }, + }) + end + -- Deployment is table-wide (everyone sees the same zones); use a single + -- global toggle rather than per-seat. + table.insert(rows, { + tag = "Text", + attributes = { + text = "Deployment (table-wide)", + fontSize = "12", + color = "#bbbbbb", + alignment = "MiddleCenter", + }, + }) + table.insert(rows, { + tag = "Button", + attributes = { + id = "macmode_deployment", + text = (deploymentMode == "mac") and "DEPLOYMENT: MAC FALLBACK" + or "DEPLOYMENT: WINDOWS ORIGINAL", + onClick = "macDeploymentClick", + color = (deploymentMode == "mac") and "#1e3a7a" or "#7a3a1e", + fontSize = "13", + textColor = "white", + preferredHeight = "28", + }, + }) + -- Build my panel (will be merged into the existing UI tree below so we + -- don't clobber legionFloatingMenu / Welcome / Chess Clocks etc.). + local panel = { + tag = "Panel", + attributes = { + id = "macModePanel", + active = macModePanelActive and "true" or "false", + rectAlignment = "MiddleRight", + offsetXY = "-10 80", + width = "260", + height = "320", + color = "rgba(0.06,0.06,0.06,0.9)", + padding = "8 8 8 8", + outlineSize = "1 1", + outline = "#303030", + }, + children = {{ + tag = "VerticalLayout", + attributes = { + spacing = "4", + childForceExpandHeight = "false", + childForceExpandWidth = "true", + }, + children = rows, + }}, + } + + local tree = UI.getXmlTable() or {} + -- Remove any stale macModePanel before reinserting the fresh one. + for i = #tree, 1, -1 do + if tree[i].attributes and tree[i].attributes.id == "macModePanel" then + table.remove(tree, i) + end + end + table.insert(tree, panel) + + -- Inject a "Mac Patch" button into the bottom-right legionFloatingMenu + -- (replaces the first interactable=false placeholder button). + local menu = macFindNodeById(tree, "legionFloatingMenu") + if menu and menu.children then + local hasMine = false + for _, c in ipairs(menu.children) do + if c.attributes and c.attributes.id == "macModeMenuButton" then + hasMine = true; break + end + end + if not hasMine then + for i, c in ipairs(menu.children) do + if c.attributes and c.attributes.interactable == "false" then + menu.children[i] = { + tag = "Button", + attributes = { + id = "macModeMenuButton", + fontSize = "10", + onClick = "macModeToggleVisibility", + tooltip = "Toggle Mac TTS U6 Patch panel", + }, + value = "Mac Patch", + } + break + end + end + end + end + + UI.setXmlTable(tree) +end + +function macDeploymentClick(_, _, _) + gToggleDeploymentMode() +end + +-- Defer one frame: TTS throws a UTF-8 buffer encoding error if we rebuild +-- the XML synchronously inside these handlers (engine race during seat +-- change finalization). pcall guards against any residual issue. +local function macDeferRefresh() + Wait.frames(function() pcall(macRefreshModeUI) end, 1) +end +function onPlayerChangeColor(_) macDeferRefresh() end +function onPlayerConnect(_) macDeferRefresh() end +function onPlayerDisconnect(_) macDeferRefresh() end + +Wait.time(macRefreshModeUI, 2) + +-- Override hotkey init functions to capture playerColor and route through +-- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys +-- here SHADOWS the originals - when the original onLoad runs init*(), our +-- versions register the hotkey instead. + +function initCohesionHotkeys() + addHotkey("Show Cohesion On Hovered Model", + function(playerColor, hoverObject, _) + if not hoverObject or not hoverObject.interactable then return end + gCohesionTrigger({ + figGUID = hoverObject.getGUID(), + playerColor = playerColor, + }) + end) +end + +function initRangebandHotkeys() + addHotkey("Show Range On Hovered Model", + function(playerColor, hoverObject, _) + if not hoverObject or not hoverObject.interactable then return end + gRangeTrigger({ + figGUID = hoverObject.getGUID(), + playerColor = playerColor, + forceFigMode = true, -- hotkey always shows 6-band fig overlay + }) + end) +end + +-- The vanilla showRangeOnHoveredModel calls spawnRangeRuler(fig) which spawns +-- the Range AssetBundle directly. On Mac, the "Oblong Range" shader used by +-- long/snail bundles isn't supported (magenta). Override the global function +-- to route through gToggleRange so our stadium overlay renders instead. +-- Vanilla Range bundle still works for round bases via gWindowsRangeToggle +-- when the player explicitly chose Windows mode. +function showRangeOnHoveredModel(hoverObject) + if not hoverObject or not hoverObject.interactable then return end + gToggleRange({figGUID = hoverObject.getGUID(), forceFigMode = true}) +end + +-- Note: we deliberately do NOT override the global spawnRangeRuler / +-- clearRangeRulers / showRangeOnHoveredModel chain at the Lua scope where +-- the vanilla mod defines it. Overriding spawnRangeRuler from a token +-- script reproducibly SIGSEGV'd TTS on Mac in May 2026 (Mono GC stack +-- corruption). Mac routing happens via the g* functions called from our +-- own hotkey/button wrappers; the originals stay reachable for +-- Windows-mode players via spawnRangeRulerOriginalGlobal aliases above. + +-- END MAC COHESION FALLBACK +""" + + +# ------------------------------------------------------------ +# 2) Wrapper appended AFTER the original Cohesion include (per-seat router) +# ------------------------------------------------------------ +# Strategy: keep the ORIGINAL Cohesion/RangeRulers expansion intact, then +# append wrappers that alias originals + route through the Global router. +# This preserves the original Custom_AssetBundle Projector spawn for +# Windows-mode players (called back as spawnCohesionRulerOriginal). + +COHESION_WRAPPER = r""" +-- MAC PATCH per-seat router (Cohesion) +-- Aliases the original spawn so Windows-mode players still get their bundle. +spawnCohesionRulerOriginal = spawnCohesionRuler +clearCohesionRulerOriginal = clearCohesionRuler + +function spawnCohesionRuler(cohesionSourceObject) + if not cohesionSourceObject then return end + Global.call("gCohesionTrigger", { + figGUID = cohesionSourceObject.getGUID(), + playerColor = nil, + }) +end + +function clearCohesionRuler() + if self and self.getGUID and self.getGUID() ~= "-1" then + Global.call("gClearCohesion", { figGUID = self.getGUID() }) + end + if cohesionRuler ~= nil then + pcall(clearCohesionRulerOriginal) + end +end + +-- Vanilla Order Token's clearTemplates() calls clearCohesionRulers (plural), +-- but the save was built from an older mod version that ships only the +-- singular clearCohesionRuler. Provide the plural as a delegating stub so +-- standby/clearTemplates/etc. don't nil-error on click. +function clearCohesionRulers() + if selectedUnitObj then + pcall(function() selectedUnitObj.setVar("moveState", false) end) + pcall(function() selectedUnitObj.call("clearCohesionRuler") end) + end +end + +-- Same root cause as clearCohesionRulers: newer Order Token code paths in +-- the live save (e.g. moveUnit -> stopAttack) call helpers the older bundled +-- script doesn't ship. Stub them so MOVE/AIM/etc don't nil-error. The bodies +-- mirror the current vanilla source but pcall every dependency so we don't +-- chain-error if anything else is also missing. +function clearAttackLine() + if attackLine then + for k, v in pairs(attackLine) do + pcall(destroyObject, v) + end + attackLine = nil + end +end + +function exitTargetingMode() + enemyHighlighted = false + attackModeOn = false + pcall(clearRangeRulers) + pcall(unhighlightEnemies) + pcall(clearAttackLine) +end + +function exitAttackMode() + enemyHighlighted = false + attackModeOn = false + pcall(clearRangeRulers) + pcall(unhighlightEnemies) +end +-- END MAC PATCH per-seat router (Cohesion) +""" + + +COHESION_BLOCK_RE = re.compile( + r"----#include !/Cohesion\r?\n.*?----#include !/Cohesion\r?\n", + re.DOTALL +) + +RANGE_BLOCK_RE = re.compile( + r"----#include !/RangeRulers\r?\n.*?----#include !/RangeRulers\r?\n", + re.DOTALL +) + +# Maximum Move spawn block in Order_Token (a57c41), inline (not an include). +# Captures from `local maxMoveBundles = getMovementLinks()` up to and +# including the two closing ends of the original `if isDeploy / if ... ~= nil` +# scaffold. The replacement is self-contained (opens 3 ifs, closes all 3), +# so re-applying yields the same canonical block - idempotent. (Earlier +# versions only matched up to setName("Maximum Move") and re-matched their +# own output, accumulating orphan else/end copies on each re-run.) +MAXMOVE_SPAWN_RE = re.compile( + r"local maxMoveBundles = getMovementLinks\(\).*?" + r"\r?\n end\r?\n end\r?\n", + re.DOTALL +) + +MAXMOVE_SPAWN_REPLACEMENT = r"""local maxMoveBundles = getMovementLinks() + local baseSizeMoveBundles = maxMoveBundles[unitData.baseSize] + local maxMoveTemplateBundleToSpawn = baseSizeMoveBundles and baseSizeMoveBundles[unitData.selectedSpeed] + + -- MAC PATCH: permissive condition (covers nil + false) so the overlay + -- also fires when changeSpeed2/3 calls moveUnit() with no isDeploy arg. + if isDeploy ~= true then + if maxMoveTemplateBundleToSpawn ~= nil then + local _macMode = Global.call("gGetMode", {color = macActivePlayerForMove}) + if _macMode == "windows" then + -- WINDOWS ORIGINAL: spawn Custom_AssetBundle Projector + maxMoveTemplate = spawnObject({ + type = "Custom_AssetBundle", + position = {basePos.x, basePos.y + 20, basePos.z}, + rotation = {0, basePos.y, 0}, + scale = {0,0,0} + }) + maxMoveTemplate.setCustomObject({ + type = 0, + assetbundle = maxMoveTemplateBundleToSpawn + }) + maxMoveTemplate.setLock(true) + maxMoveTemplate.use_gravity = false + maxMoveTemplate.setName("Maximum Move") + else + -- MAC FALLBACK: route through the Global Overlays manager + Global.call("gSpawnMaxMove", { + figGUID = selectedUnitObj.getGUID(), + baseSize = unitData.baseSize, + speed = unitData.selectedSpeed, + }) + maxMoveTemplate = nil + end + end + end +""" + +# clearMovementTemplates function: needs to also clear our managed maxmove +# overlay. We replace the whole function body. +# Match up to the first un-indented `end` (= the function's closing end), so +# the regex is idempotent - it matches both the vanilla body and any +# previously patched body shape (which has extra lines past the inner `end`). +CLEAR_MOVEMENT_RE = re.compile( + r"function clearMovementTemplates\(\).*?\r?\nend\b", + re.DOTALL +) + +CLEAR_MOVEMENT_REPLACEMENT = r"""function clearMovementTemplates() + if templateA ~= nil then + destroyObject(templateA) + end + if templateB ~= nil then + destroyObject(templateB) + end + -- MAC PATCH per-seat: destroy bundle Object if present (Windows-mode + -- spawn) AND clear the manager entry (Mac fallback). Either may be set. + if maxMoveTemplate ~= nil then + pcall(destroyObject, maxMoveTemplate) + end + -- Clear only THIS token's MaxMove (keyed by the attached unit), not + -- everyone's - gClearAllMaxMove was killing other tokens' rings when + -- two players had MOVE open at the same time. + if selectedUnitObj then + Global.call("gClearMaxMove", { figGUID = selectedUnitObj.getGUID() }) + end + maxMoveTemplate = nil +end""" + +# Deployment Boundary: replace the Custom_AssetBundle Projector spawn inside +# spawnBoundaryCell (SETUP_CONTROLLER 1cb552) with a Global.call to the manager. +# Idempotent: matches both the vanilla form AND a previously-injected patched +# form (between the MAC PATCH dual-path marker and the closing ` end`). The +# patched alternative is listed first so re-runs prefer it. +DEPLOYMENT_SPAWN_RE = re.compile( + r"(?:" + r"-- MAC PATCH dual-path:.*?\r?\n end" + r"|" + r"local projector = spawnObject\(\{\r?\n" + r" type = \"Custom_AssetBundle\",.*?" + r"projector\.setCustomObject\(\{\r?\n" + r" assetbundle = asset,\r?\n" + r" \}\)" + r")", + re.DOTALL +) + +DEPLOYMENT_SPAWN_REPLACEMENT = r"""-- MAC PATCH dual-path: branch on the table-wide deploymentMode toggle. + local _macDeployMode = Global.call("gGetDeploymentMode") + local projector = nil + if _macDeployMode == "windows" then + projector = spawnObject({ + type = "Custom_AssetBundle", + position = pos, + scale = {0, 0, 0}, + rotation = {0, deployRotations[cell], 0} + }) + projector.setName("Deployment Boundary") + projector.setLock(true) + projector.setCustomObject({ + assetbundle = asset, + }) + else + Global.call("gSpawnDeployment", { cell = cell, pos = pos }) + end""" + +# clearDeploymentBoundary: also clear manager entries. +CLEAR_DEPLOYMENT_RE = re.compile( + r"function clearDeploymentBoundary\(\)\r?\n" + r" local battlefieldObjs = battlefieldZone\.getObjects\(\).*?\r?\n" + r" end\r?\nend", + re.DOTALL +) + +CLEAR_DEPLOYMENT_REPLACEMENT = r"""function clearDeploymentBoundary() + -- MAC PATCH: clear Global Overlays manager entries first. + Global.call("gClearAllDeployment", {}) + local battlefieldObjs = battlefieldZone.getObjects() + for _, obj in pairs(battlefieldObjs) do + if obj.getName() == "Deployment Boundary" then + destroyObject(obj) + end + end +end""" + +# Silhouette URL swap to our Unity-6-compiled bundle. +# +# The upstream BucketheadBits_Silhouette bundle uses Allen White's custom +# Silhouette shader compiled under Unity 2019.4. TTS U6 on Mac fails to +# load that shader at runtime (console: "Shader didn't load correctly for +# AssetBundle material BucketheadBits_Silhouette. Assigning Standard +# shader.") and falls back to Standard, which renders the cylinder opaque. +# +# Our fork-hosted bundle uses the SAME prefab + material + shader source +# (Assets/Sihl/), only the compile target differs (Unity 6 instead of +# Unity 2019.4). Path A from Dicewrench's analysis. +# +# We swap ALL silhouette URLs (default cylinder, snail, long) to the same +# fork URL. Snail/long visual fidelity is acceptable for now (cylinder +# placeholder); per-base meshes are a separate follow-up. +SILHOUETTE_BUNDLE_URL = ( + "https://raw.githubusercontent.com/ironsquadronfr-hub/tts/" + "mac-projector-fallback/mod/data/mac-fallback-assets/" + "silhouette_mac_fallback.unity3d" +) +SILHOUETTE_URL_RE = re.compile( + r'(silhouetteData\s*=\s*)"https?://[^"]*/ugc/[^"]+"' +) +SILHOUETTE_URL_REPLACEMENT = r'\1"' + SILHOUETTE_BUNDLE_URL + '"' + + +# Cleanup of debug prints and corrupted signatures from earlier sessions. +# Safe no-op on a clean save. Matches both the legacy `print("[SIL DEBUG] ...)` +# form AND the chat-visible `printToAll("[SIL] ...", {...})` form. +SIL_DEBUG_STRIP_RE = re.compile( + # Match leading horizontal whitespace only (NOT \s* which would also eat + # the newline before, mashing the function signature against the next + # statement - that was a real bug we hit earlier this session). + r'[ \t]*(?:print\("\[SIL DEBUG\][^\n]*\)|' + r'printToAll\("\[SIL\][^"]*",\s*\{[^}]*\}\))\r?\n' +) +# Repair function signatures that were mashed by the previous \s*-glob bug. +# Idempotent: target the specific mashed patterns; matches are zero on a +# clean save. +SIL_REPAIR_TOGGLE_RE = re.compile( + r'(function toggleSilhouettes\(\))( if silhouetteState then)' +) +SIL_REPAIR_SHOW_RE = re.compile( + r'(function showSilhouette\(\))( for k, guid in pairs\(miniGUIDs)' +) +SIL_REPAIR_SPAWN_RE = re.compile( + r'(function spawnSilhouette\(obj, pos, rot\))( local globals)' +) + + +# Silhouette state-desync fix (upstream bug). +# +# Upstream clearSilhouette assumes removeAttachments()[1] always returns an +# object, but the state variable `silhouetteState` is persisted across saves +# while the physical attachments are not - so a save made with silhouettes +# visible reloads with state=true but no attachments. First SIL click then +# calls clearSilhouette which dereferences nil and crashes. +# +# Two-part patch: +# (1) clearSilhouette: guard the nil case (skip the destruct if no +# attachment), so re-clicks don't crash. +# (2) onload: force silhouetteState=false on every load, since the load +# cannot restore the physical silhouettes anyway. +# +# Also fixes the "two silhouettes stacked" symptom: that happens when +# showSilhouette runs while state is desynced, spawning a second set on top +# of one whose handles have been forgotten. +CLEAR_SILHOUETTE_RE = re.compile( + r"function clearSilhouette\(\).*?silhouetteState = false\r?\nend", + re.DOTALL +) +CLEAR_SILHOUETTE_REPLACEMENT = r"""function clearSilhouette() + -- MAC PATCH: guard nil silToDestroy. silhouetteState can be persisted as + -- true across saves while the physical attachments are not, so a load + -- with state=true + no attachments would otherwise crash here. + for k, guid in pairs(miniGUIDs or {}) do + local obj = getObjectFromGUID(guid) + if obj then + local silToDestroy = obj.removeAttachments()[1] + if silToDestroy then silToDestroy.destruct() end + end + end + silhouetteState = false +end""" + +# Match the existing `function onload()` body (one-line empty or actual +# code). Idempotent: re-runs detect the marker `MAC PATCH: silhouette reset` +# and skip. +SIL_ONLOAD_RESET_LINE = ( + ' -- MAC PATCH: silhouette reset on load (attachments lost across saves)\n' + ' silhouetteState = false\n' +) +ONLOAD_OPEN_RE = re.compile(r"function onload\(\)\r?\n") + + +# SIL button rename (root cause: TTS engine intercepts the click_function +# name "toggleSilhouettes" silently in Mac mode; LCK/R buttons unaffected +# because their click_function names differ. Confirmed empirically 17 mai +# 2026: renaming to "macToggleSil" + forwarder unblocks Mac mode entirely). +# +# We swap the click_function name on the SIL button definition AND inject a +# forwarder function that simply calls toggleSilhouettes(). The original +# toggleSilhouettes is untouched, so Windows mode behavior is identical. +# Idempotent: re-runs detect both markers and skip. +SIL_BUTTON_CLICKFN_OLD = 'click_function = "toggleSilhouettes"' +SIL_BUTTON_CLICKFN_NEW = 'click_function = "macToggleSil"' +SIL_FORWARDER = ''' +-- MAC PATCH: rebuilt SIL button click handler. The original click_function +-- name "toggleSilhouettes" is silently intercepted by TTS in Mac mode (the +-- SIL button plays its click sound/animation but the function is never +-- called). Renaming the click_function on the button definition + adding +-- this forwarder restores the routing. +function macToggleSil() + toggleSilhouettes() +end +-- END MAC PATCH SIL forwarder +''' +SIL_FORWARDER_MARKER = 'function macToggleSil()' + + +RANGE_WRAPPER = r""" +-- MAC PATCH per-seat router (Range) - ALIAS ONLY. +-- +-- HISTORY (do not re-introduce): we previously overrode spawnRangeRuler / +-- clearRangeRulers / clearRangeRuler on the ~20 token scripts. That +-- reproducibly SIGSEGV'd TTS Mac (Mono GC_clear_stack_inner) when a fig +-- hotkey spawned the vanilla Range bundle. Overrides REMOVED - per-seat +-- Range routing for fig hotkey lives in Global (gRangeTrigger via our +-- initRangebandHotkeys shadow). Tokens' R buttons keep vanilla behavior +-- (magenta on Mac, native on Windows) until a per-token Mac fallback +-- ships - see TOKEN_BUTTON_WRAPPER for the per-token override path. +-- +-- The single alias below is REQUIRED: Global macModeClick calls +-- obj.call("clearRangeRulersOriginal", obj) on every object holding a +-- rangeRuler state, to wipe the vanilla ruler when toggling modes. +clearRangeRulersOriginal = clearRangeRulers +-- END MAC PATCH per-seat router (Range) +""" + +# Order_Token-only overrides: intercept the UI button click handlers +# (toggleCohesionRuler, targetingMode, changeSpeedN, moveX) to capture +# playerColor passed by TTS for per-seat routing. +ORDER_TOKEN_BUTTON_OVERRIDES = r""" +-- MAC PATCH per-seat router (Order_Token button click overrides) +function toggleCohesionRuler(_, playerColor) + if not rulerOn then + Global.call("gCohesionTrigger", { + figGUID = selectedUnitObj.getGUID(), + playerColor = playerColor, + }) + rulerOn = true + else + selectedUnitObj.call("clearCohesionRuler") + rulerOn = false + end +end + +function targetingMode(_, playerColor) + if not enemyHighlighted then + exitAttackMode() + highlightEnemies() + Global.call("gRangeTrigger", { + figGUID = selectedUnitObj.getGUID(), + playerColor = playerColor, + }) + enemyHighlighted = true + resetRangeButtons() + else + exitTargetingMode() + end +end + +-- Capture playerColor for Maximum Move per-seat routing. moveUnit() doesn't +-- receive the click color, so each entry-point button stashes it in a +-- script-global var that the spawn block reads. Defaults to nil -> Mac mode. + +-- initMove / initDeploy: redefine the body INLINE rather than wrap, because +-- wrapping (capturing the original via a local + calling it) reproducibly +-- broke every Order Token button click in testing (root cause unknown; +-- suspected Lua chunk-level upvalue interaction with TTS engine click +-- dispatch). The inline body mirrors the vanilla logic + the playerColor +-- capture so the MAXMOVE_SPAWN block can route to Mac/Windows per seat. +function initMove(obj, playerColor) + if not selectedUnitObj then return end + macActivePlayerForMove = playerColor + initPos = selectedUnitObj.getPosition() + initRot = selectedUnitObj.getRotation() + selectedUnitObj.call("setStartPos") + moveUnit(false) +end +function initDeploy(obj, playerColor) + if not selectedUnitObj then return end + macActivePlayerForMove = playerColor + initPos = selectedUnitObj.getPosition() + initRot = selectedUnitObj.getRotation() + selectedUnitObj.call("setStartPos") + moveUnit(true) +end + +-- INLINE the vanilla bodies (mirror Order_Token.a57c41.lua) rather than +-- wrap. The List Builder copies this block to Command Token Custom_Models +-- which DO NOT carry the vanilla Order_Token function bodies - so a +-- wrap-then-call pattern hits `_macOrigChangeSpeed1 = nil` and crashes the +-- moment a Speed/Move button is pressed. Inlining keeps the behavior +-- identical on both Order Tokens (vanilla body re-defined) and Command +-- Tokens (helper fns setTemplateVariables/clearTemplates/moveUnit exist +-- on both because they're part of the include set the List Builder emits). +function changeSpeed1(_, playerColor) + macActivePlayerForMove = playerColor + unitData.selectedSpeed = 1 + setTemplateVariables() + clearTemplates() + moveUnit() +end +function changeSpeed2(_, playerColor) + macActivePlayerForMove = playerColor + unitData.selectedSpeed = 2 + setTemplateVariables() + clearTemplates() + moveUnit() +end +function changeSpeed3(_, playerColor) + macActivePlayerForMove = playerColor + unitData.selectedSpeed = 3 + setTemplateVariables() + clearTemplates() + moveUnit() +end +function moveForward(_, playerColor) + macActivePlayerForMove = playerColor + self.editButton({ + index = 11, click_function = "moveBackwards", + label = "B", tooltip = "Move Backwards" + }) + moveDirection = "forward" + moveUnit() +end +function moveBackwards(_, playerColor) + macActivePlayerForMove = playerColor + self.editButton({ + index = 11, click_function = "moveForward", + label = "F", tooltip = "Move Forward" + }) + moveDirection = "backwards" + moveUnit() +end +function moveLeft(_, playerColor) + macActivePlayerForMove = playerColor + moveDirection = "left" + moveUnit() +end +function moveRight(_, playerColor) + macActivePlayerForMove = playerColor + moveDirection = "right" + moveUnit() +end +-- END MAC PATCH per-seat router (Order_Token button click overrides) +""" + +# Wrapper appended to objects that have `function toggleRangeRuler()` - +# the R button on tokens (POI, Smoke, Bomb Cart, Objective). Per-seat +# routing: Mac players get our vector bands (TOKEN-spec via rangeKey), +# Windows players get the vanilla token bundle (spawnTokenRangeRuler). +# The vanilla function is aliased BEFORE override so Windows-mode clicks +# can fall through to it. +TOKEN_BUTTON_WRAPPER = r""" +-- MAC PATCH per-seat router (token R button) +-- Aliases the vanilla toggleRangeRuler before overriding so Windows-mode +-- players keep spawning the token-specific Range bundle (POI / Smoke / +-- bomb_cart / objective). Mac-mode players get our vector overlay via +-- gRangeTrigger which dispatches to macBuilders.range with the token's +-- rangeKey var. +local _macToggleRangeRulerOriginal = toggleRangeRuler +function toggleRangeRuler(_, playerColor) + local mode = Global.call("gGetMode", { color = playerColor }) + if mode == "windows" then + if _macToggleRangeRulerOriginal then + return _macToggleRangeRulerOriginal() + end + return + end + if rangeOn then + Global.call("gClearRange", { figGUID = self.getGUID() }) + rangeOn = false + else + Global.call("gRangeTrigger", { + figGUID = self.getGUID(), + playerColor = playerColor, + }) + rangeOn = true + end +end +-- END MAC PATCH per-seat router (token R button) +""" + +TOKEN_BUTTON_WRAPPER_RE = re.compile( + r"\r?\n-- MAC PATCH per-seat router \(token R button\).*?" + r"-- END MAC PATCH per-seat router \(token R button\)\r?\n", + re.DOTALL +) + +# Idempotent marker - used to detect and remove an existing Mac patch in the +# Global LuaScript before applying a fresh one. +GLOBAL_PATCH_MARKER_RE = re.compile( + r"\r?\n-- ={5,}\r?\n-- MAC COHESION FALLBACK.*?-- END MAC COHESION FALLBACK\r?\n", + re.DOTALL +) + +# Idempotent markers for the per-seat wrappers added to object scripts. +# Strip these before re-applying to avoid double-wrapping. +COHESION_WRAPPER_RE = re.compile( + r"\r?\n-- MAC PATCH per-seat router \(Cohesion\).*?" + r"-- END MAC PATCH per-seat router \(Cohesion\)\r?\n", + re.DOTALL +) +RANGE_WRAPPER_RE = re.compile( + r"\r?\n-- MAC PATCH per-seat router \(Range\).*?" + r"-- END MAC PATCH per-seat router \(Range\)\r?\n", + re.DOTALL +) +ORDER_TOKEN_OVERRIDES_RE = re.compile( + r"\r?\n-- MAC PATCH per-seat router \(Order_Token button click overrides\).*?" + r"-- END MAC PATCH per-seat router \(Order_Token button click overrides\)\r?\n", + re.DOTALL +) + + +TARGET_GUIDS = {"99f1c8", "a57c41"} # Objects with the Cohesion block to replace + + +def collect_patched_scripts(data: dict) -> list: + """Return script_states for the TTS External Editor API (Save & Play). + + Includes the Global script and any object script we patched (matching + TARGET_GUIDS). Format: list of dicts with name, guid, script. + """ + states = [ + {"name": "Global", "guid": "-1", "script": data.get("LuaScript", "")} + ] + + def walk(o): + if isinstance(o, dict): + guid = o.get("GUID", "").lower() + if guid in TARGET_GUIDS and "LuaScript" in o: + states.append({ + "name": o.get("Name", guid), + "guid": guid, + "script": o["LuaScript"], + }) + for v in o.values(): + walk(v) + elif isinstance(o, list): + for x in o: + walk(x) + + walk(data) + return states + + +def send_to_tts(script_states: list) -> bool: + """Send a Save & Play (messageID 1) to TTS via the External Editor API. + + Returns True on success, False if TTS isn't listening (likely the API + is disabled in TTS Configuration). + """ + msg = {"messageID": 1, "scriptStates": script_states} + payload = json.dumps(msg).encode("utf-8") + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(3) + s.connect(("127.0.0.1", 39999)) + s.sendall(payload) + s.close() + return True + except (ConnectionRefusedError, socket.timeout, OSError) as e: + print(f" WARN: could not connect to TTS on port 39999 ({e}).") + print(f" -> Enable Configuration -> Game Tweaks -> 'External Editor API' in TTS.") + return False + + +def patch_object_scripts(data: dict) -> tuple: + """Walk the JSON tree, replace Cohesion and Range blocks in object scripts. + + Cohesion block: only on TARGET_GUIDS (Unit_Leader, Order_Token). + Range block: on every object containing the !/RangeRulers include + (Order_Token, Tokens, POI, bomb_cart, etc. - ~20 objects). + + Returns (n_cohesion_patched, n_range_patched, n_dep_patched). + """ + n_coh, n_rng, n_dep = 0, 0, 0 + + def walk(o): + nonlocal n_coh, n_rng, n_dep + if isinstance(o, dict): + guid = o.get("GUID", "").lower() + if "LuaScript" in o: + ls = o["LuaScript"] + changed = False + + # Strip any previous per-seat wrappers so re-runs are idempotent. + for rx in (COHESION_WRAPPER_RE, RANGE_WRAPPER_RE, + ORDER_TOKEN_OVERRIDES_RE, TOKEN_BUTTON_WRAPPER_RE): + new_ls, n = rx.subn("", ls) + if n > 0: + ls = new_ls + changed = True + + # Cleanup: strip stale debug prints + repair any signatures + # mashed by an earlier (overzealous) strip pass. Both are + # no-ops on a clean save. + new_ls, n = SIL_DEBUG_STRIP_RE.subn("", ls) + if n > 0: + ls = new_ls + changed = True + for rx in (SIL_REPAIR_TOGGLE_RE, SIL_REPAIR_SHOW_RE, SIL_REPAIR_SPAWN_RE): + new_ls, n = rx.subn(r'\1\n\2', ls) + if n > 0: + ls = new_ls + changed = True + + # (Diagnostic SIL prints removed 17 mai 2026 once mute Mac + # mode confirmed. SIL_DEBUG_STRIP_RE above stays as a cleanup + # pass for older saves that still carry the prints.) + + # Silhouette URL swap to our Unity-6-compiled bundle. + # Applies to any object that defines spawnSilhouette + # (Unit_Leader 99f1c8, Bomb Cart b497e1, POI Token 761483). + # Idempotent: the regex only matches Steam UGC URLs (/ugc/), + # not our fork URL, so re-runs are no-ops. + new_ls, n = SILHOUETTE_URL_RE.subn(SILHOUETTE_URL_REPLACEMENT, ls) + if n > 0: + ls = new_ls + changed = True + + # SIL button rename + forwarder. Bypasses the TTS engine + # bug where the click_function name "toggleSilhouettes" is + # silently muted in Mac mode. Idempotent: replace() is no-op + # once swap is done, marker check prevents double-injection. + if SIL_BUTTON_CLICKFN_OLD in ls: + ls = ls.replace(SIL_BUTTON_CLICKFN_OLD, SIL_BUTTON_CLICKFN_NEW) + changed = True + if SIL_BUTTON_CLICKFN_NEW in ls and SIL_FORWARDER_MARKER not in ls: + ls = ls.rstrip() + '\n' + SIL_FORWARDER + changed = True + + # Cohesion: KEEP the original include block, append per-seat + # wrapper after it. Only Unit_Leader / Order_Token have it. + if guid in TARGET_GUIDS: + def coh_repl(m): + return m.group(0) + COHESION_WRAPPER + new_ls, n = COHESION_BLOCK_RE.subn(coh_repl, ls, count=1) + if n > 0: + ls = new_ls + n_coh += 1 + changed = True + + # Silhouette desync fix (any object with clearSilhouette). + # Guard the nil-attachment case and reset silhouetteState in + # onload. Idempotent: the regex matches both the vanilla + # body and our previously-patched body (same shape). + if "function clearSilhouette()" in ls: + new_ls, n = CLEAR_SILHOUETTE_RE.subn(CLEAR_SILHOUETTE_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + changed = True + # Inject the state reset at the top of onload. + if "MAC PATCH: silhouette reset on load" not in ls: + new_ls, n = ONLOAD_OPEN_RE.subn( + "function onload()\n" + SIL_ONLOAD_RESET_LINE, + ls, count=1, + ) + if n > 0: + ls = new_ls + changed = True + + # Range: KEEP block, append wrapper. ~21 objects. + def rng_repl(m): + return m.group(0) + RANGE_WRAPPER + new_ls, n = RANGE_BLOCK_RE.subn(rng_repl, ls, count=1) + if n > 0: + ls = new_ls + n_rng += 1 + changed = True + + # Maximum Move spawn (only in Order_Token a57c41) - Mac only + if guid == "a57c41": + new_ls, n = MAXMOVE_SPAWN_RE.subn(MAXMOVE_SPAWN_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + changed = True + new_ls, n = CLEAR_MOVEMENT_RE.subn(CLEAR_MOVEMENT_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + changed = True + # Button click overrides (per-seat capture of playerColor) + ls = ls + ORDER_TOKEN_BUTTON_OVERRIDES + changed = True + + # Token R button: append toggleRangeRuler override on any + # object that defines that function (POI, Smoke, BombCart, + # Objective, etc.). Routes the click to the Mac fallback + # Range overlay sized via TOKEN_BASE_RADIUS. (Already-applied + # case is handled by the strip loop above.) + if "function toggleRangeRuler()" in ls: + ls = ls + TOKEN_BUTTON_WRAPPER + changed = True + + # Deployment Boundary (only in SETUP_CONTROLLER 1cb552) - Mac only + if guid == "1cb552": + new_ls, n = DEPLOYMENT_SPAWN_RE.subn(DEPLOYMENT_SPAWN_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + n_dep += 1 + changed = True + new_ls, n = CLEAR_DEPLOYMENT_RE.subn(CLEAR_DEPLOYMENT_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + changed = True + + if changed: + o["LuaScript"] = ls + + for v in o.values(): + walk(v) + elif isinstance(o, list): + for x in o: + walk(x) + + walk(data) + return n_coh, n_rng, n_dep + + +def main(): + args = [a for a in sys.argv[1:] if not a.startswith("--")] + flags = [a for a in sys.argv[1:] if a.startswith("--")] + if len(args) != 2: + print(f"Usage: {sys.argv[0]} [--reload]") + sys.exit(1) + src = Path(args[0]) + dst = Path(args[1]) + reload_tts = "--reload" in flags + + with src.open("r") as f: + data = json.load(f) + + # Rename so the patched save is recognizable in TTS load list. + data["SaveName"] = "SWL BETA - MAC PATCH" + + # 1. Append manager + handlers to Global LuaScript (idempotent: remove + # any previous Mac patch block first, then append fresh). + original_global_len = len(data.get("LuaScript", "")) + existing = data.get("LuaScript", "") + cleaned, n_removed = GLOBAL_PATCH_MARKER_RE.subn("", existing) + data["LuaScript"] = cleaned + GLOBAL_PATCH_LUA + new_global_len = len(data["LuaScript"]) + note = " (replaced existing patch)" if n_removed else "" + print(f"Global LuaScript: {original_global_len} -> {new_global_len} bytes{note}") + print() + + # 2. Replace Cohesion + Range + Deployment blocks in object scripts. + print("Object script patches:") + n_coh, n_rng, n_dep = patch_object_scripts(data) + print(f" Cohesion blocks replaced: {n_coh}") + print(f" Range blocks replaced: {n_rng}") + print(f" Deployment blocks replaced: {n_dep}") + print() + + dst.parent.mkdir(parents=True, exist_ok=True) + with dst.open("w") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + + print(f"Written: {dst}") + print(f" Save Name: {data.get('SaveName', '?').strip()}") + print(f" Version: {data.get('VersionNumber', '?')}") + + # Mirror to TTS Saves folder so it appears in Games -> Save & Load. + # Picks the next free TS_Save_N.json slot (TTS scans these on load). + saves_dir = Path.home() / "Library" / "Tabletop Simulator" / "Saves" + if saves_dir.exists(): + existing = sorted( + int(p.stem.split("_")[-1]) + for p in saves_dir.glob("TS_Save_*.json") + if p.stem.split("_")[-1].isdigit() + ) + # Reuse the highest existing slot if its content matches the prefix + # "SWL BETA - MAC PATCH"; else pick next free number. + target = None + for n in reversed(existing): + p = saves_dir / f"TS_Save_{n}.json" + try: + with p.open("r") as f: + if '"SWL BETA - MAC PATCH"' in f.read(512): + target = p + break + except OSError: + continue + if target is None: + next_n = (existing[-1] if existing else 0) + 1 + target = saves_dir / f"TS_Save_{next_n}.json" + with target.open("w") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + print(f" Mirrored to: {target}") + + if reload_tts: + print() + print("Hot-reloading into TTS via External Editor API...") + states = collect_patched_scripts(data) + print(f" Sending {len(states)} script(s): " + + ", ".join(f"{s['name']}({s['guid']})" for s in states)) + if send_to_tts(states): + print(" OK Sent. TTS should restart Lua with the new code " + "(table/objects state preserved).") + + +if __name__ == "__main__": + main() diff --git a/mac-patcher/requirements.txt b/mac-patcher/requirements.txt new file mode 100644 index 00000000..fd6ee21e --- /dev/null +++ b/mac-patcher/requirements.txt @@ -0,0 +1,3 @@ +UnityPy>=1.10 +Pillow>=10.0 +requests>=2.31 diff --git a/mac-patcher/retro-cohesion.md b/mac-patcher/retro-cohesion.md new file mode 100644 index 00000000..a67ea58c --- /dev/null +++ b/mac-patcher/retro-cohesion.md @@ -0,0 +1,170 @@ +# Rétro-ingé Cohesion Ruler + +Rédigé en mode prod, base pour le refactor event-driven vector lines (15 mai 2026, post-finding session 14-15 mai). + +## 1. Définition fonctionnelle (rappel règles SWL) + +Le **Cohesion Ruler** matérialise la **distance de cohésion d'unité** : tous les minis d'une même unité doivent rester à une distance maximale du leader d'unité (sinon malus / dispersion). Le ruler aide à vérifier visuellement cette contrainte pendant le tour. + +Distance de cohésion : **0.5 inch** (1,27 cm) entre les bords des socles, règle officielle SWL. + +## 2. Visuel attendu + +3 tailles, indexées par `baseSize` du leader : + +| Clé | Base size physique | Bundle | +|---|---|---| +| `small` | 27 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305632/...` | +| `medium` | 50 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305877/...` | +| `large` | 70 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305957/...` | + +Rendu côté Windows (TTS Unity 2019.4) : cercle semi-transparent vert/cyan projeté au sol via Unity Projector, centré sur la fig. **À extraire empiriquement** via UnityPy + référence Workshop pour la couleur/alpha exacts (en TODO ci-dessous). + +Comportement Projector : drape sur le relief de la table (rochers, élévations) grâce au Projector legacy. + +## 3. Déclencheurs (qui peut spawn/clear le ruler ?) + +### Spawn + +1. **Hotkey global "Show Cohesion On Hovered Model"** — `StarWarsLegion.lua:611-619` + - Bind via `addHotkey()` côté Global + - Appelle `showCohesionOnHoveredModel(hoverObject)` qui : + - Si déjà selected → clear + reset `selectedUnitObj` + - Sinon → `clearCohesionRuler() + spawnCohesionRuler(hoverObject)` + - **État global** `selectedUnitObj` tracké côté Global (variable du script `StarWarsLegion.lua` via `require('!/Cohesion')`) + +2. **Bouton "COHESION" sur Order Token** — `Order_Token.a57c41.lua:347-352, 369-378` + - Bouton créé pendant l'activation d'une unité + - `toggleCohesionRuler()` toggle via flag `rulerOn` local au Order Token + - Appelle `selectedUnitObj.call("spawnCohesionRuler", selectedUnitObj)` → s'exécute dans le script de la **fig sélectionnée** (Unit_Leader.99f1c8) + +3. **Re-spawn automatique après mouvement** — `Unit_Leader.99f1c8.lua:278-286` + - `dropCoroutine()` attend que `getVelocity().y == 0` (fig au sol) + - Si `moveState == true` (= en cours de mesure de mouvement) → `spawnCohesionRuler(self)` + +### Clear + +1. **Hotkey re-press** — toggle via `showCohesionOnHoveredModel` (idem spawn, second appel) + +2. **Bouton COHESION re-press** — toggle via `toggleCohesionRuler` (idem) + +3. **Bouton invisible "unitID" sur Unit_Leader** — `Unit_Leader.99f1c8.lua:45` + - Bouton au centre de la fig avec label = numéro d'unité, couleur alpha 0.01 (quasi-invisible) + - `click_function = "clearCohesionRuler"` + - C'est l'overlay cliquable du numéro affiché sur chaque fig + +4. **onPickedUp sur Unit_Leader** — `Unit_Leader.99f1c8.lua:274-276` + - **Event-driven déjà existant** : pickup la fig → clear automatique + +5. **`standbyTokens()` global** — `StarWarsLegion.lua:562-571` + - Boucle sur `getAllObjects()` et destroy tout objet nommé `"Cohesion Ruler"` (et "Range Ruler", "Movement Template", "Deployment Boundary") + - Déclenché par... à tracer (probablement reset de partie ou hotkey debug) + +6. **`removeLockedRulers()` GAME_CONTROLLER** — `GAME_CONTROLLER.623b03.lua:148-155` + - Boucle sur `getAllObjects()` et destroy "Cohesion Ruler" / "Range Ruler" + - Appelé par la fonction de reload (`reloadObj` voisine) + +7. **`clearCohesionRulers()` (pluriel) Order Token** — `Order_Token.a57c41.lua:788-793` + - Appelé par `clearTemplates()` lors du reset après mouvement + - `selectedUnitObj.setVar("moveState", false)` + `selectedUnitObj.call("clearCohesionRuler")` + +## 4. Lifecycle complet + +``` +[Spawn] + showCohesionOnHoveredModel(fig) [hotkey hover] + toggleCohesionRuler() [via Order Token bouton] + dropCoroutine() [auto si moveState] + ↓ + spawnCohesionRuler(fig): + 1. unitData = fig.getTable("unitData") + 2. bundleURL = getCohesionLinks()[unitData.baseSize] + 3. spawnObject(Custom_AssetBundle, pos = fig.pos + Y+20, scale = 0, rot = (0, fig.rot.y, 0)) + 4. setCustomObject({type=0, assetbundle=bundleURL}) + 5. setLock(true), use_gravity=false, setName("Cohesion Ruler") + ↓ +[Ruler affiché — STATIQUE, ne suit pas la fig] + Note: pas de onFixedUpdate. Si fig bouge sans pickup (impossible normalement + car les figs sont lockables), le ruler reste à l'ancienne position. + +[Clear] + showCohesionOnHoveredModel(same fig) [hotkey re-press toggle] + toggleCohesionRuler() [re-press toggle] + onPickedUp(fig) [pickup auto] + clearCohesionRuler() [click invisible "unitID" button] + standbyTokens() [global reset] + removeLockedRulers() [reload] + clearCohesionRulers() [via Order Token clearTemplates] + ↓ + clearCohesionRuler(): + 1. destroyObject(cohesionRuler) + 2. cohesionRuler = nil + +[Re-spawn auto post-drop] + onDropped → checkVelocity → si moveState → startLuaCoroutine(dropCoroutine) + ↓ + dropCoroutine: + while velocity.y ~= 0: yield + if moveState == true: spawnCohesionRuler(self) +``` + +## 5. État scopé : ATTENTION pattern hétérogène + +`cohesionRuler` est une variable Lua **scopée au script Lua qui fait le require**. Chaque objet qui require `!/Cohesion` a SA propre `cohesionRuler`. Trois scopes coexistent : + +| Site require | Scope `cohesionRuler` | +|---|---| +| `StarWarsLegion.lua:8` (Global) | Script Global | +| `Unit_Leader.99f1c8.lua:1` (chaque fig leader) | Par-fig | +| `Order_Token.a57c41.lua:4` (chaque Order Token) | Par-Order-Token | + +**Conséquence pratique** : +- Hotkey hover → spawn dans le scope Global → `cohesionRuler` du Global mis à jour, **pas celui de la fig** +- Bouton COHESION → `selectedUnitObj.call("spawnCohesionRuler", selectedUnitObj)` → exécute dans le scope de la fig → `cohesionRuler` de la fig mis à jour +- `onPickedUp(fig)` → clear dans le scope de la fig → ne clear PAS un ruler spawn via hotkey hover (mais le bouton invisible "unitID" est sur la fig, donc dans le scope fig...) + +**Bug latent connu (non reproduit)** : si tu spawn via hotkey hover, puis tu pickup une autre fig, le ruler du Global subsiste. À garder en tête pour le refactor (à corriger ou pas selon scope). + +## 6. Filtres de cleanup global + +Deux fonctions scannent `getAllObjects()` et destroy par nom : + +| Fonction | Fichier | Cible | +|---|---|---| +| `standbyTokens()` | StarWarsLegion.lua:562 | "Cohesion Ruler" + "Range Ruler" + "Movement Template" + "Deployment Boundary" | +| `removeLockedRulers()` | GAME_CONTROLLER.623b03.lua:148 | "Cohesion Ruler" + "Range Ruler" | + +**Impact refactor** : si on remplace l'Object Custom_AssetBundle par des vector lines (qui ne sont pas des Objects), ces deux filtres deviennent **caducs**. Il faudra leur substituer un clear de la table d'état globale + `Global.setVectorLines({})`. + +## 7. Paramètres visuels exacts — TODO + +À extraire empiriquement via UnityPy sur les bundles small/medium/large : + +- [ ] Bundle `halfcohesion_27mm.unity3d` (ou équivalent small) : material color, alpha, orthographicSize du Projector, animation éventuelle +- [ ] Bundle medium (50mm) : idem +- [ ] Bundle large (70mm) : idem +- [ ] Screenshot référence côté Workshop (rendu attendu côté Windows TTS 2019.4 / U6 non-magenta) + +Scripts UnityPy disponibles dans `Mod TTS SWL/` (`inspect_cohesion.py`, `inspect_externals.py`). À adapter pour extraire les params materials. + +## 8. Implications pour le refactor + +### Bonnes nouvelles +- API publique petite et conservable : `showCohesionOnHoveredModel`, `spawnCohesionRuler`, `clearCohesionRuler` +- 80% du pattern event-driven déjà en place (`onPickedUp`, `dropCoroutine`) +- Lifecycle bien défini, déclencheurs énumérés exhaustivement + +### Décisions à prendre dans le design doc +1. **Unifier les scopes** : faire que `cohesionRuler` soit une table globale indexée par GUID (Variante A) plutôt que par-script. Résout le bug latent (§5) et permet le pattern multi-figs simultanées. +2. **Remplacer les filtres de cleanup** (§6) par des appels à la fonction de clear globale. +3. **Reproduire le drape sur relief** via `Physics.cast()` par segment, comme le POC validé du 14-15 mai. +4. **Compatibilité Order Token bouton** : conserver la sémantique toggle `rulerOn` ou simplifier. + +(Note : esthétique du rendu vector lines hors specs — on traitera une fois le dossier refactor clos.) + +## 9. Références + +- Code : `swlegion-tts/mod/src/includes/Cohesion.ttslua`, `data/CohesionLinks.ttslua`, `StarWarsLegion.lua`, `StarWarsLegion/Unit_Leader.99f1c8.lua`, `StarWarsLegion/Order_Token.a57c41.lua`, `StarWarsLegion/GAME_CONTROLLER.623b03.lua` +- Bundles : cache TTS local, sources Unity dans `Mod TTS SWL/UnityProject-U6/Assets/` +- Session 14-15 mai validation empirique : `~/.claude/projects/-Users-martinpourrat-MARTIN-Star-Wars-Legion/memory/mod-tts-swl/session-14-mai.md` +- Plan B identifié : `~/.claude/projects/-Users-martinpourrat-MARTIN-Star-Wars-Legion/memory/mod-tts-swl/paths-forward.md` diff --git a/mac-patcher/retro-deployment.md b/mac-patcher/retro-deployment.md new file mode 100644 index 00000000..20d1741e --- /dev/null +++ b/mac-patcher/retro-deployment.md @@ -0,0 +1,76 @@ +# Rétro-ingé Deployment Boundary + +## Visuel attendu + +Zones de déploiement rouge/bleu peintes au sol au début de la partie selon le scenario. Pattern matriciel avec 14 codes de cellule : + +| Code | Sens | +|---|---| +| `r` / `b` | Base red / blue | +| `rh` / `bh` | Home (zone arrière) | +| `rs` / `bs` | Side (zone latérale) | +| `rss` / `bss` | Side stretched | +| `rl` / `bl` | Long (zone allongée, spawn 2 cellules : sX + ccX) | +| `rc` / `bc` | Corner | +| `rcc` / `bcc` | Corner-corner (autre orientation) | + +URLs bundles dans `deployLinks` (`SETUP_CONTROLLER.1cb552.lua:188-202`). + +Plusieurs codes partagent la même URL (e.g. `rh = rs = rss` → même bundle, rotation différente). Distinct rotations : `r=0, rh=0, rs=90, rss=90, ...` (`deployRotations` ligne 204). + +Rendu côté Windows : grand rectangle/zone projeté au sol via Projector. Couleurs rouge/bleu. **À extraire via UnityPy** (task #13). + +## Spawn + +**1 seul déclencheur** : `spawnDeploymentBoundary(matrix)` — `SETUP_CONTROLLER.1cb552.lua:267-314` + +- Reçoit une matrice 12×N (codes par cellule) inversée puis lue ligne par ligne +- Pour chaque cellule non-vide : `spawnBoundaryCell(cell, x, z)` qui spawn 1 Custom_AssetBundle au pos calculé `{xStart + 6*(x-1), yValue, zStart - 6*(z-1)}` + `deployOffset[cell]` +- Cas spécial `bl`/`rl` : spawn 2 cellules (sX + ccX) pour matérialiser le L + +Appelé par `SETUP_CONTROLLER.1cb552.lua:369` (probablement après sélection de la carte Deployment via UI menu). + +## Clear + +**1 seul déclencheur** : `clearDeploymentBoundary()` — ligne 316-323 +- Boucle sur `battlefieldZone.getObjects()` et `destroyObject` chaque objet nommé `"Deployment Boundary"` + +Aussi via : +- **`standbyTokens()`** — `StarWarsLegion.lua:567` — destroy global par nom + +(Pas dans `removeLockedRulers` de GAME_CONTROLLER : Deployment n'est pas dans la liste.) + +## Lifecycle + +``` +[Setup partie] + Menu UI "Mount Deployment" → checkDeployment() → spawnDeploymentBoundary(matrix) + ↓ + Loop sur matrix → pour chaque cell : spawnBoundaryCell + ↓ + Custom_AssetBundle spawn statique (scale 0, locked, no gravity) + +[Clear] + Menu UI "Remove Overlay" → clearDeploymentBoundary() + ↓ + Boucle battlefieldZone → destroyObject par nom + +[Aussi : standbyTokens() global au reset] +``` + +**Pattern : 100% statique.** Pas de suivi, pas d'event. Spawn au setup, clear quand on remove. Plus simple que Cohesion et Range. + +## Différences clés vs Cohesion/Range + +1. **Pas de fig source** — c'est un overlay de zone, pas un overlay attaché à un objet mobile. Pas besoin de drape sur relief si la table est plate (à confirmer : sur certaines tables custom avec relief, est-ce que la zone de déploiement doit draper ? probablement oui pour cohérence visuelle). +2. **Multi-cellules** par scenario — peut-être 10-20 Custom_AssetBundle spawn simultanément (vs 1 pour Cohesion/Range). +3. **Pas de variable d'état scopée** — pas de `deploymentRuler = nil` ; le code itère sur `battlefieldZone.getObjects()` filtré par nom pour le cleanup. + +## Implications refactor + +- API publique à conserver : `spawnDeploymentBoundary(matrix)`, `clearDeploymentBoundary()` +- 14 bundle URLs (mais visuellement ~6 zones distinctes vu les doublons URL) → 6 sets de params visuels à extraire (task #13) +- Pas d'event-driven nécessaire — statique simple +- En vector lines : chaque cellule = un polygone fermé `setVectorLines({{points={p1,p2,p3,p4,p1}, ...}, ...})`. Pas de Physics.cast si table plate, sinon raycast par segment du contour +- Filtre cleanup `standbyTokens` à adapter pour clear la table d'état globale (ou laisser un `Global.setVectorLines({})` de la collection deployment) +- **Décision design** : Deployment partage-t-il la même collection `Global.setVectorLines()` que Cohesion/Range, ou faut-il un canal séparé ? `Global.setVectorLines` est singleton donc tout doit cohabiter dans la même collection. Implication : la table d'état globale doit indexer par type (`cohesion` / `range` / `deployment`) pour gérer les clears partiels. diff --git a/mac-patcher/retro-movement.md b/mac-patcher/retro-movement.md new file mode 100644 index 00000000..579aaca3 --- /dev/null +++ b/mac-patcher/retro-movement.md @@ -0,0 +1,122 @@ +# Rétro-ingé Movement Template + Maximum Move + +**Correction** : Movement implique **2 objets visuels** spawn ensemble lors de l'activation de mouvement. Un seul des deux est cassé magenta. + +## Composant 1 : Movement Template A/B (templates 3D courbes) — NON AFFECTÉ + +Code : `Order_Token.a57c41.lua:480-529` + +```lua +templateA = spawnObject({ type = "Custom_AssetBundle", scale = {1,1,1} ... }) +templateA.setCustomObject({ assetbundle = ..., material = 1 }) +templateA.setColorTint(...) +templateA.setName("Movement Template (A)") +``` + +- **scale = {1,1,1}** → mesh visible (pas un Projector caché) +- **`material = 1`** + **`setColorTint()`** → material standard avec colorTint TTS exposé +- Bundles `longBundle` / `shortBundle` / `sharedBundle` par speed (templateInfo) +- **Pas affecté par bug magenta** : c'est un mesh classique, pas un Projector receiver + +## Composant 2 : Maximum Move (cercle de portée) — CASSÉ MAGENTA ✅ + +Code : `Order_Token.a57c41.lua:552-574` + +```lua +maxMoveTemplate = spawnObject({ + type = "Custom_AssetBundle", + position = {basePos.x, basePos.y + 20, basePos.z}, + scale = {0,0,0} -- ← scale 0, pattern Projector +}) +maxMoveTemplate.setCustomObject({ + type = 0, + assetbundle = maxMoveTemplateBundleToSpawn +}) +maxMoveTemplate.setLock(true) +maxMoveTemplate.use_gravity = false +maxMoveTemplate.setName("Maximum Move") -- ← nom différent de "Movement Template" +``` + +- **scale = {0,0,0}** → Projector legacy (même pattern que Cohesion/Range/Deployment) +- **Pas de setColorTint** sur ce spawn +- **CASSÉ MAGENTA** sur Mac confirmé par Martin + +### Bundles (`includes/data/MovementLinks.ttslua`) + +`getMovementLinks()` retourne table indexée `[baseSize][selectedSpeed]` : + +| baseSize | Taille | Speeds | +|---|---|---| +| `small` | 27mm | 3 bundles (speed 1/2/3) | +| `medium` | 50mm | 3 | +| `large` | 70mm | 3 | +| `huge` | 100mm | 3 | +| `laat` | 120mm | 3 | +| `epic` | 150mm | 3 | +| `long` | 100×175mm oblong | 3 | +| `snail` | 100×200mm oblong | 3 | + +**Total : 24 bundles uniques** pour le cercle de portée max. + +Rendu visuel attendu côté Windows : cercle/anneau projeté au sol matérialisant la portée maximale de mouvement à cette vitesse. **Couleur/alpha à extraire via UnityPy** (task #13). + +## Déclencheurs + +### Spawn +**1 seul déclencheur** : flow d'activation mouvement via Order Token +- Bouton speed (1/2/3) cliqué sur Order Token → set `unitData.selectedSpeed` +- Une fonction de mouvement (autour de `Order_Token.a57c41.lua:480+`) spawn `templateA`, `templateB`, ET `maxMoveTemplate` ensemble +- Conditional : `if isDeploy == false` → pas de Maximum Move pendant la phase de déploiement (ligne 556) + +### Clear +**Fonction `clearMovementTemplates()`** — `Order_Token.a57c41.lua:776-786` +```lua +function clearMovementTemplates() + if templateA ~= nil then destroyObject(templateA) end + if templateB ~= nil then destroyObject(templateB) end + if maxMoveTemplate ~= nil then destroyObject(maxMoveTemplate) end +end +``` + +Appelé par : +- `clearTemplates()` (ligne 770-774) → appelé après le drop final du mouvement +- Aucun cleanup global (Maximum Move PAS dans `standbyTokens()` ni `removeLockedRulers()`) + +## Lifecycle + +``` +[Spawn] + Order Token bouton speed (1/2/3) → spawn flow + ↓ + templateA, templateB (mesh visible) + maxMoveTemplate (Projector) + ↓ + L'utilisateur déplace templateA jusqu'à la position cible + → contrôle visuel "ma fig peut-elle aller là" avec le cercle Maximum Move + +[Clear] + Drop final (fig placée) → clearTemplates() → destruct 3 objets ensemble +``` + +**Pattern : statique au spawn, pas de suivi de fig**. Le cercle Maximum Move est positionné une fois à `basePos + Y20` et reste là pendant que tu manipules les templates de mouvement. + +## État scopé + +`maxMoveTemplate` est une variable par Order Token (`require('!/Cohesion')` ligne 4 mais maxMoveTemplate vient du flow Order_Token directement, pas d'include). Pas de partage Global / Unit_Leader. + +## Implications refactor + +- API publique à conserver : le spawn et `clearMovementTemplates()` (mais cette dernière mélange templates A/B + maxMoveTemplate) +- 24 bundles cercle de portée → 8 sets de params visuels distincts par baseSize (les 3 speeds partagent probablement le même material avec rayon différent ; à extraire task #13) +- Pattern statique simple : spawn une fois, clear ensemble avec templates A/B +- En vector lines : 1 cercle par maxMoveTemplate, dessiné via setVectorLines + Physics.cast par segment pour drape relief +- Cleanup propre : `clearMovementTemplates` doit clear la table d'état globale spécifique au maxMove de cet Order Token +- **Templates A/B (mesh) restent inchangés** — pas dans le scope du refactor + +## Couleur attendue + +Le cercle Maximum Move devrait visuellement matcher la couleur du template de speed correspondant : +- Speed 1 : couleur 1 (vert ?) +- Speed 2 : couleur 2 (jaune ?) +- Speed 3 : couleur 3 (rouge ?) + +Note `templateInfo.moveTemplate[selectedSpeed].colorTint` — c'est le tint des templates A/B, mais le cercle Maximum Move utilise un bundle distinct sans colorTint (le bundle embarque sa propre couleur). À vérifier empiriquement / via UnityPy quelle est la couleur baked-in de chaque bundle Maximum Move par speed. diff --git a/mac-patcher/retro-range.md b/mac-patcher/retro-range.md new file mode 100644 index 00000000..a4569069 --- /dev/null +++ b/mac-patcher/retro-range.md @@ -0,0 +1,86 @@ +# Rétro-ingé Range Ruler + +## Visuel attendu + +12 tailles dans `getRangeRulerLinks()` (`mod/src/includes/data/RangeRulerLinks.ttslua`) : + +| Clé | Cible | Range | +|---|---|---| +| `small` | base 27mm | range mini | +| `medium` | base 50mm | range mini | +| `large` | base 70mm | range mini | +| `huge` | base 100mm | range véhicule | +| `laat` | base 120mm | range LAAT | +| `epic` | base 150mm | range épique | +| `long` | base 100mm oblong | range véhicule long | +| `snail` | base 100x200mm | range escargot | +| `bombCart` | bomb cart | range spécifique | +| `smokeToken` | jeton fumée 18.8mm | range 1 | +| `token` | jeton charge/objectif/condition 25.1mm | range 1 | +| `tokenRangeTwo` | jeton graffiti 25.1mm | range 2 | +| `poi` | POI token 50.8mm | range 0.5 (3") | + +Rendu côté Windows : **4 cercles concentriques** projetés au sol via Unity Projector legacy (1 bundle = N Projectors embarqués pour les bandes de portée 1/2/3/4). Couleurs/alphas/rayons par bande **à extraire via UnityPy** (task #13). + +**Configurations par overlay** : +- Rulers de fig (small/med/large/huge/laat/epic/long/snail) : **4 cercles** (range 1/2/3/4) +- `smokeToken` : 1 cercle (range 1, effet smoke 1 pouce) +- `token` (charge/obj/cond) : 1 cercle (range 1) +- `tokenRangeTwo` (graffiti) : 2 cercles (range 1 + 2) +- `poi` : 1 cercle (range 0.5 = 3") +- `bombCart` : à confirmer empiriquement + +## Déclencheurs + +### Spawn +1. **Hotkey "Show Range On Hovered Model"** — `StarWarsLegion.lua:601-608` → `showRangeOnHoveredModel(hoverObject)` +2. **Bouton "RANGE" sur Order Token** — `Order_Token.a57c41.lua:354-365` → `targetingMode()` ligne 922 → `spawnRangeRuler(selectedUnitObj)` +3. **Bouton "RANGE" via attackMode** — `Order_Token.a57c41.lua:934` → `spawnRangeRuler(selectedUnitObj)` +4. **Bouton "R" sur POI Token** — `POI_Tokens/POI_Token.761483.lua:23,41-46` → `toggleRangeRuler()` → `spawnTokenRangeRuler()` → `spawnRangeRuler(self, tokenRulerBundle)` avec override +5. **Bouton "R" sur autres tokens** — `includes/TokenWithRangeRuler.ttslua:37-43,52-56` (smokeToken, token, tokenRangeTwo via `rangeKey` scopé par-token) + +### Clear +1. **Hotkey re-press** — toggle via `showRangeOnHoveredModel` +2. **`clearRangeRulers()` (pluriel)** — Order Token : appelé par `clearTemplates`, `exitTargetingMode`, `exitAttackMode`, `attackMenu` +3. **`clearRangeRuler()` (singulier)** — POI/Token : appelé par `toggleRangeRuler` et `onDestroy` +4. **`standbyTokens()` global** — `StarWarsLegion.lua:567` — destroy tout objet nommé `"Range Ruler"` +5. **`removeLockedRulers()`** — `GAME_CONTROLLER.623b03.lua:151` — destroy par nom + +## Différence clé vs Cohesion : suivi temps réel + +`RangeRulers.ttslua:78-79` : + +```lua +luaScript = "targetGUID = '"..rangeSourceObject.getGUID().."'\n" + .. "function onFixedUpdate()\n" + .. " if targetGUID ~= nil then\n" + .. " targetObj = getObjectFromGUID(targetGUID)\n" + .. " local targetPosition = targetObj.getPosition()\n" + .. " self.setPosition({targetPosition.x, targetPosition.y + 20, targetPosition.z})\n" + .. " self.setRotation({0,targetObj.getRotation().y,0})\n" + .. " end\n" + .. "end" +rangeRuler.setLuaScript(luaScript) +``` + +→ Le ruler **se repositionne 60 fois/sec** côté objet ruler (pas côté Lua de la fig). Différent de Cohesion qui est statique. + +**Implication refactor** : pour Range, le suivi temps réel est utile gameplay (tu mesures pendant que tu déplaces la fig pour évaluer si tu vas être en range). On ne peut pas simplement passer en pur event-driven sans perte UX. Le drape sur relief recalculé doit suivre — soit redraw à chaque tick visible (avec mémoization de position pour éviter le leak), soit accepter une dégradation UX (redraw au drop seulement). + +## État scopé (même problème que Cohesion §5) + +Variable `rangeRuler` scopée par script-objet (Global, chaque fig leader, chaque POI Token, chaque token générique). Pattern identique à Cohesion : 3+ scopes peuvent désaccorder. + +`selectedUnitObj` global aussi tracké côté Range (`showRangeOnHoveredModel` ligne 7-17 de RangeRulers.ttslua). + +## Override bundle (paramètre `projectorBundleOverride`) + +`spawnRangeRuler(rangeSourceObject, projectorBundleOverride)` — le 2e paramètre permet de fournir une URL bundle au lieu de la lookuper via `unitData.baseSize`. Utilisé par POI Token et TokenWithRangeRuler qui passent `rangeRulerTable[rangeKey]` directement. À conserver dans le refactor. + +## Implications refactor + +- API publique à conserver : `showRangeOnHoveredModel`, `spawnRangeRuler` (avec override), `clearRangeRulers` (Order Token), `clearRangeRuler` (POI/Token) +- 12 bundle URLs → 12 sets de params visuels à extraire (chacun avec **1 à 4 cercles concentriques** selon le type) et reproduire en vector lines (task #13) +- Pattern suivi temps réel → décision design (redraw par tick avec mémoization vs redraw event-driven uniquement) +- Filtres cleanup global (`standbyTokens`, `removeLockedRulers`) à adapter pour la table d'état +- Override `projectorBundleOverride` → s'inscrit naturellement dans une table d'état `{fig=obj, rangeKey="poi"}` ou similaire diff --git a/mac-patcher/scan_bundles.py b/mac-patcher/scan_bundles.py new file mode 100644 index 00000000..eb5edfd2 --- /dev/null +++ b/mac-patcher/scan_bundles.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +"""Scan TTS AssetBundles for material -> shader references.""" +import UnityPy +import os +import sys +from collections import defaultdict, Counter + +BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") +STOCK_PREFIXES = ( + "Standard", + "Hidden/", + "Mobile/", + "Particles/", + "UI/", + "Sprites/", + "Skybox/", + "Legacy Shaders/", + "Universal Render Pipeline/", + "URP/", + "Unlit/", + "GUI/", + "Lightmap", + "VertexLit", + "Reflective/", + "Self-Illumin/", + "Transparent/", + "FX/", + "Nature/", + "TextMeshPro/", + "TextMesh Pro/", +) + +def is_stock(name: str) -> bool: + return any(name.startswith(p) for p in STOCK_PREFIXES) + +shader_to_bundles = defaultdict(set) +shader_material_count = Counter() +bundle_to_shaders = defaultdict(set) +embedded_shaders_per_bundle = defaultdict(set) +errors = [] +bundle_summary = [] + +bundles = sorted(f for f in os.listdir(BUNDLE_DIR) if f.endswith(".unity3d")) +print(f"Scanning {len(bundles)} bundles in {BUNDLE_DIR}\n", file=sys.stderr) + +for fname in bundles: + path = os.path.join(BUNDLE_DIR, fname) + short_id = fname.replace("httpssteamusercontentaakamaihdnetugc", "")[:20] + fsize = os.path.getsize(path) + n_mat = 0 + n_shader = 0 + n_tex = 0 + try: + env = UnityPy.load(path) + for obj in env.objects: + t = obj.type.name + if t == "Material": + n_mat += 1 + try: + data = obj.read() + shader_name = None + try: + sref = data.m_Shader + # Try multiple ways to resolve PPtr + try: + shader_obj = sref.deref() + except Exception: + shader_obj = None + if shader_obj is None: + try: + shader_obj_read = sref.read() + if shader_obj_read is not None: + shader_name = ( + getattr(getattr(shader_obj_read, "m_ParsedForm", None), "m_Name", None) + or getattr(shader_obj_read, "m_Name", None) + ) + except Exception: + pass + else: + sd = shader_obj.read() + shader_name = ( + getattr(getattr(sd, "m_ParsedForm", None), "m_Name", None) + or getattr(sd, "m_Name", None) + ) + except Exception as e: + errors.append(f"{short_id} mat shader resolve: {e!r}") + if not shader_name: + # PPtr fields + try: + file_id = getattr(data.m_Shader, "file_id", "?") + path_id = getattr(data.m_Shader, "path_id", "?") + shader_name = f"" + except Exception: + shader_name = "" + # Material name + mat_name = getattr(data, "m_Name", "") or "" + shader_to_bundles[shader_name].add(short_id) + shader_material_count[shader_name] += 1 + bundle_to_shaders[short_id].add((shader_name, mat_name)) + except Exception as e: + errors.append(f"{short_id} mat read err: {e!r}") + elif t == "Shader": + n_shader += 1 + try: + data = obj.read() + sname = ( + getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) + or getattr(data, "m_Name", None) + ) + if sname: + embedded_shaders_per_bundle[short_id].add(sname) + except Exception as e: + errors.append(f"{short_id} shader read: {e!r}") + elif t in ("Texture2D", "Texture"): + n_tex += 1 + except Exception as e: + errors.append(f"{short_id}: load failed: {e!r}") + + bundle_summary.append((short_id, fsize, n_mat, n_shader, n_tex)) + +# === REPORT === +print("=" * 90) +print("BUNDLE SUMMARY (id_prefix, size_bytes, n_materials, n_shaders_embedded, n_textures)") +print("=" * 90) +for short_id, fsize, n_mat, n_shader, n_tex in bundle_summary: + print(f" {short_id} {fsize:>10} mats={n_mat:>3} shaders={n_shader:>2} tex={n_tex:>3}") + +print() +print("=" * 90) +print("SHADER FREQUENCY ACROSS ALL MATERIALS (sorted by usage)") +print("=" * 90) +for sname, count in shader_material_count.most_common(): + flag = " [CUSTOM?]" if not is_stock(sname) and not sname.startswith("4} mats / {n_bundles:>2} bundles {sname}{flag}") + +print() +print("=" * 90) +print("EMBEDDED SHADERS (shaders bundled INSIDE a .unity3d, sorted)") +print("=" * 90) +all_embedded = set() +for shaders in embedded_shaders_per_bundle.values(): + all_embedded.update(shaders) +print(f"\nDistinct embedded shaders: {len(all_embedded)}") +for s in sorted(all_embedded): + bundles_with = [b for b, ss in embedded_shaders_per_bundle.items() if s in ss] + print(f" - {s} ({len(bundles_with)} bundle(s): {','.join(bundles_with[:3])}{'...' if len(bundles_with) > 3 else ''})") + +print() +print("=" * 90) +print("BUNDLES REFERENCING NON-STOCK SHADERS (with material names)") +print("=" * 90) +for short_id, items in sorted(bundle_to_shaders.items()): + custom = [(s, m) for (s, m) in items if not is_stock(s) and not s.startswith(" Date: Sun, 17 May 2026 17:14:39 +0200 Subject: [PATCH 86/93] Defer UI.setXmlTable in macModeClick/macModeToggleVisibility/gToggleDeploymentMode TTS occasionally throws "byte buffer too small to contain encoded data, encoding UTF-8 fallback EncoderReplacementFallback" when UI.setXmlTable runs synchronously inside a click handler or right after a deployment toggle. Symptom seen on the stable build: clicking the per-seat mode toggle prints the error in chat and the panel doesn't refresh. The fix mirrors what we already do for onPlayerChangeColor / Connect / Disconnect: defer one frame + wrap in pcall. Same Wait.frames pattern, just applied to the three remaining synchronous call sites. macDeferRefresh promoted from local to global function so handlers defined earlier in the chunk can reach it via name lookup at call time. Co-Authored-By: Claude Opus 4.7 (1M context) --- mac-patcher/patch_save_for_mac.py | 40 +++++++++++++++++++------------ 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/mac-patcher/patch_save_for_mac.py b/mac-patcher/patch_save_for_mac.py index 601d5b46..d31e8fb6 100644 --- a/mac-patcher/patch_save_for_mac.py +++ b/mac-patcher/patch_save_for_mac.py @@ -941,7 +941,7 @@ deploymentMode = (deploymentMode == "mac") and "windows" or "mac" -- Wipe any active deployment zones so the next setup uses the new mode. gClearAllDeployment() - macRefreshModeUI() + macDeferRefresh() end -- Replicates the original showRangeOnHoveredModel toggle, calling the @@ -1032,7 +1032,7 @@ pcall(function() obj.call("clearRangeRulersOriginal", obj) end) end end - macRefreshModeUI() + macDeferRefresh() end local function macFindNodeById(tree, id) @@ -1053,14 +1053,21 @@ function macModeToggleVisibility() macModePanelActive = not macModePanelActive - local tree = UI.getXmlTable() or {} - local panel = macFindNodeById(tree, "macModePanel") - if panel then - panel.attributes.active = macModePanelActive and "true" or "false" - UI.setXmlTable(tree) - else - macRefreshModeUI() - end + -- Defer the XML rebuild: TTS occasionally throws a UTF-8 byte-buffer + -- encoding error if UI.setXmlTable runs synchronously inside a UI + -- click handler. Deferring one frame + pcall has been reliable. + Wait.frames(function() + pcall(function() + local tree = UI.getXmlTable() or {} + local panel = macFindNodeById(tree, "macModePanel") + if panel then + panel.attributes.active = macModePanelActive and "true" or "false" + UI.setXmlTable(tree) + else + macRefreshModeUI() + end + end) + end, 1) end function macRefreshModeUI() @@ -1231,17 +1238,20 @@ gToggleDeploymentMode() end --- Defer one frame: TTS throws a UTF-8 buffer encoding error if we rebuild --- the XML synchronously inside these handlers (engine race during seat --- change finalization). pcall guards against any residual issue. -local function macDeferRefresh() +-- Defer one frame: TTS throws a UTF-8 byte-buffer encoding error if we +-- rebuild the UI XML synchronously inside a click handler / seat change. +-- pcall guards against any residual race. Promoted to a global function +-- so macModeClick / macModeToggleVisibility / gToggleDeploymentMode (all +-- defined before this point) can call it via name lookup at call time. +function macDeferRefresh() Wait.frames(function() pcall(macRefreshModeUI) end, 1) end function onPlayerChangeColor(_) macDeferRefresh() end function onPlayerConnect(_) macDeferRefresh() end function onPlayerDisconnect(_) macDeferRefresh() end -Wait.time(macRefreshModeUI, 2) +-- Initial UI build, deferred + pcalled like the rest. +Wait.time(function() pcall(macRefreshModeUI) end, 2) -- Override hotkey init functions to capture playerColor and route through -- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys From e2869267eb29ddccb7a6013763a5318a4ba4358a Mon Sep 17 00:00:00 2001 From: Martin Pourrat Date: Sun, 17 May 2026 17:42:19 +0200 Subject: [PATCH 87/93] Guard _loadArmyFromJson against nil JSON.decode result Pre-existing upstream crash in BLUE/RED LIST BUILDER: importFromText fires onEndEdit whenever the import InputField loses focus, even with empty/invalid text. JSON.decode("") returns nil, then _loadArmyFromJson indexes data.armyFaction and crashes. Repro: open Import JSON panel, focus the textarea, click anywhere outside the textarea. Crash fires on every blur. Fix: nil-guard at the top of _loadArmyFromJson. Covers both callers (importFromText and _loadArmy at line 2062). Idempotent via negative lookahead on the marker comment. Upstream-able if halestom wants it. Co-Authored-By: Claude Opus 4.7 (1M context) --- mac-patcher/patch_save_for_mac.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/mac-patcher/patch_save_for_mac.py b/mac-patcher/patch_save_for_mac.py index d31e8fb6..24bd5e22 100644 --- a/mac-patcher/patch_save_for_mac.py +++ b/mac-patcher/patch_save_for_mac.py @@ -1826,6 +1826,20 @@ re.DOTALL ) +# List Builder _loadArmyFromJson nil-guard. Stock SWL never nil-checks the +# result of JSON.decode(text) inside importFromText, so any blur of the +# import InputField with empty/invalid text crashes _loadArmyFromJson at +# `data.armyFaction`. The guard is upstream-able. Idempotent via the +# negative lookahead on the marker comment. +LIST_BUILDER_GUARD_RE = re.compile( + r"(function _loadArmyFromJson\(data\)\r?\n)(?! -- MAC PATCH: guard )" +) +LIST_BUILDER_GUARD_REPLACEMENT = ( + r"\1" + " -- MAC PATCH: guard nil/invalid JSON from onEndEdit blur (upstream bug)\r\n" + " if not data then return end\r\n" +) + # Idempotent marker - used to detect and remove an existing Mac patch in the # Global LuaScript before applying a fresh one. GLOBAL_PATCH_MARKER_RE = re.compile( @@ -2009,6 +2023,20 @@ def rng_repl(m): n_rng += 1 changed = True + # List Builder import guard. Applies to BLUE/RED LIST + # BUILDER (any object that defines _loadArmyFromJson). + # Fixes a pre-existing upstream crash where blurring the + # import InputField with empty/invalid text fires + # onEndEdit -> importFromText -> JSON.decode("") -> nil -> + # data.armyFaction crash. Idempotent. + if "function _loadArmyFromJson(data)" in ls: + new_ls, n = LIST_BUILDER_GUARD_RE.subn( + LIST_BUILDER_GUARD_REPLACEMENT, ls, count=1 + ) + if n > 0: + ls = new_ls + changed = True + # Maximum Move spawn (only in Order_Token a57c41) - Mac only if guid == "a57c41": new_ls, n = MAXMOVE_SPAWN_RE.subn(MAXMOVE_SPAWN_REPLACEMENT, ls, count=1) From 8af1ff91715cf5bb7a5d9cf1723322aef9cb48a5 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 20 May 2026 00:24:12 -0400 Subject: [PATCH 88/93] Mercenary + Errata Change from ShadowCollective nomenclature to Mercenary to add Ohnaka Gang. Add Geo Engineers. Delete in mod maps. Add Heavy Droid Trooper unit type --- contrib/cards/Empire/EmpireUnits.json | 22 ++++---- .../BlackSunEnforcersUnit.json | 0 .../BlackSunVigoUnit.json | 0 .../BosskUnit.json | 0 .../CadBaneUnit.json | 0 .../DinUnit.json | 0 .../HondoUnit.json | 0 .../IG11Unit.json | 0 .../MercenaryUnits.json} | 21 ++++---- .../PykeSyndicateCapoUnit.json | 0 .../PykeSyndicateFootSoldiersUnit.json | 0 .../SwoopBikeRidersUnit.json | 0 .../WLO5SpeederTankUnit.json | 0 .../WeequayPiratesUnit.json | 0 contrib/cards/Rebel/RebelUnits.json | 16 +++--- contrib/cards/Republic/RepublicUnits.json | 12 ++--- contrib/cards/Separatist/SeparatistUnits.json | 50 +++++++++++++++---- contrib/cards/official.json | 23 +++++++-- contrib/maps/skirmish.json | 23 +-------- mod/schema/deps/UnitTypes.json | 2 + mod/src/StarWarsLegion.lua | 48 +++++++++--------- mod/src/includes/Deck.ttslua | 2 +- mod/src/includes/SpawnList.ttslua | 2 +- mod/src/includes/data/ListBuilderNew.ttslua | 2 +- mod/src/includes/ui/Welcome.xml | 6 +-- 25 files changed, 127 insertions(+), 102 deletions(-) rename contrib/cards/{ShadowCollective => Mercenary}/BlackSunEnforcersUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/BlackSunVigoUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/BosskUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/CadBaneUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/DinUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/HondoUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/IG11Unit.json (100%) rename contrib/cards/{ShadowCollective/ShadowCollectiveUnits.json => Mercenary/MercenaryUnits.json} (89%) rename contrib/cards/{ShadowCollective => Mercenary}/PykeSyndicateCapoUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/PykeSyndicateFootSoldiersUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/SwoopBikeRidersUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/WLO5SpeederTankUnit.json (100%) rename contrib/cards/{ShadowCollective => Mercenary}/WeequayPiratesUnit.json (100%) diff --git a/contrib/cards/Empire/EmpireUnits.json b/contrib/cards/Empire/EmpireUnits.json index 4ab0a774..7194d350 100644 --- a/contrib/cards/Empire/EmpireUnits.json +++ b/contrib/cards/Empire/EmpireUnits.json @@ -331,10 +331,10 @@ ] }, { - "content": "contrib/cards/ShadowCollective/PykeSyndicateCapoUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateCapoUnit.json" }, { - "content": "contrib/cards/ShadowCollective/BlackSunVigoUnit.json" + "content": "contrib/cards/Mercenary/BlackSunVigoUnit.json" } ], "Operative": [ @@ -387,10 +387,10 @@ "additionalObjects": ["Victory Token"] }, { - "content": "contrib/cards/ShadowCollective/BosskUnit.json" + "content": "contrib/cards/Mercenary/BosskUnit.json" }, { - "content": "contrib/cards/ShadowCollective/CadBaneUnit.json" + "content": "contrib/cards/Mercenary/CadBaneUnit.json" }, { "name": "Darth Vader", @@ -481,13 +481,13 @@ "additionalObjects": ["Victory Token"] }, { - "content": "contrib/cards/ShadowCollective/DinUnit.json" + "content": "contrib/cards/Mercenary/DinUnit.json" }, { - "content": "contrib/cards/ShadowCollective/IG11Unit.json" + "content": "contrib/cards/Mercenary/IG11Unit.json" }, { - "content": "contrib/cards/ShadowCollective/HondoUnit.json" + "content": "contrib/cards/Mercenary/HondoUnit.json" }, { "name": "Fifth Brother", @@ -790,13 +790,13 @@ ] }, { - "content": "contrib/cards/ShadowCollective/PykeSyndicateFootSoldiersUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateFootSoldiersUnit.json" }, { - "content": "contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json" + "content": "contrib/cards/Mercenary/BlackSunEnforcersUnit.json" }, { - "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" + "content": "contrib/cards/Mercenary/WeequayPiratesUnit.json" } ], "Special Forces": [ @@ -1197,7 +1197,7 @@ ] }, { - "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" + "content": "contrib/cards/Mercenary/WLO5SpeederTankUnit.json" } ] } diff --git a/contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json b/contrib/cards/Mercenary/BlackSunEnforcersUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json rename to contrib/cards/Mercenary/BlackSunEnforcersUnit.json diff --git a/contrib/cards/ShadowCollective/BlackSunVigoUnit.json b/contrib/cards/Mercenary/BlackSunVigoUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/BlackSunVigoUnit.json rename to contrib/cards/Mercenary/BlackSunVigoUnit.json diff --git a/contrib/cards/ShadowCollective/BosskUnit.json b/contrib/cards/Mercenary/BosskUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/BosskUnit.json rename to contrib/cards/Mercenary/BosskUnit.json diff --git a/contrib/cards/ShadowCollective/CadBaneUnit.json b/contrib/cards/Mercenary/CadBaneUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/CadBaneUnit.json rename to contrib/cards/Mercenary/CadBaneUnit.json diff --git a/contrib/cards/ShadowCollective/DinUnit.json b/contrib/cards/Mercenary/DinUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/DinUnit.json rename to contrib/cards/Mercenary/DinUnit.json diff --git a/contrib/cards/ShadowCollective/HondoUnit.json b/contrib/cards/Mercenary/HondoUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/HondoUnit.json rename to contrib/cards/Mercenary/HondoUnit.json diff --git a/contrib/cards/ShadowCollective/IG11Unit.json b/contrib/cards/Mercenary/IG11Unit.json similarity index 100% rename from contrib/cards/ShadowCollective/IG11Unit.json rename to contrib/cards/Mercenary/IG11Unit.json diff --git a/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json b/contrib/cards/Mercenary/MercenaryUnits.json similarity index 89% rename from contrib/cards/ShadowCollective/ShadowCollectiveUnits.json rename to contrib/cards/Mercenary/MercenaryUnits.json index 79dda81c..4a2d32e4 100644 --- a/contrib/cards/ShadowCollective/ShadowCollectiveUnits.json +++ b/contrib/cards/Mercenary/MercenaryUnits.json @@ -38,10 +38,10 @@ ] }, { - "content": "contrib/cards/ShadowCollective/BlackSunVigoUnit.json" + "content": "contrib/cards/Mercenary/BlackSunVigoUnit.json" }, { - "content": "contrib/cards/ShadowCollective/PykeSyndicateCapoUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateCapoUnit.json" } ], "Operative": [ @@ -121,21 +121,24 @@ ] }, { - "content": "contrib/cards/ShadowCollective/CadBaneUnit.json" + "content": "contrib/cards/Mercenary/CadBaneUnit.json" }, { - "content": "contrib/cards/ShadowCollective/BosskUnit.json" + "content": "contrib/cards/Mercenary/BosskUnit.json" + }, + { + "content": "contrib/cards/Mercenary/HondoUnit.json" } ], "Corps": [ { - "content": "contrib/cards/ShadowCollective/PykeSyndicateFootSoldiersUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateFootSoldiersUnit.json" }, { - "content": "contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json" + "content": "contrib/cards/Mercenary/BlackSunEnforcersUnit.json" }, { - "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" + "content": "contrib/cards/Mercenary/WeequayPiratesUnit.json" } ], "Special Forces": [ @@ -172,7 +175,7 @@ ], "Support": [ { - "content": "contrib/cards/ShadowCollective/SwoopBikeRidersUnit.json" + "content": "contrib/cards/Mercenary/SwoopBikeRidersUnit.json" } ], "Heavy": [ @@ -200,7 +203,7 @@ ] }, { - "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" + "content": "contrib/cards/Mercenary/WLO5SpeederTankUnit.json" } ] } diff --git a/contrib/cards/ShadowCollective/PykeSyndicateCapoUnit.json b/contrib/cards/Mercenary/PykeSyndicateCapoUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/PykeSyndicateCapoUnit.json rename to contrib/cards/Mercenary/PykeSyndicateCapoUnit.json diff --git a/contrib/cards/ShadowCollective/PykeSyndicateFootSoldiersUnit.json b/contrib/cards/Mercenary/PykeSyndicateFootSoldiersUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/PykeSyndicateFootSoldiersUnit.json rename to contrib/cards/Mercenary/PykeSyndicateFootSoldiersUnit.json diff --git a/contrib/cards/ShadowCollective/SwoopBikeRidersUnit.json b/contrib/cards/Mercenary/SwoopBikeRidersUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/SwoopBikeRidersUnit.json rename to contrib/cards/Mercenary/SwoopBikeRidersUnit.json diff --git a/contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json b/contrib/cards/Mercenary/WLO5SpeederTankUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json rename to contrib/cards/Mercenary/WLO5SpeederTankUnit.json diff --git a/contrib/cards/ShadowCollective/WeequayPiratesUnit.json b/contrib/cards/Mercenary/WeequayPiratesUnit.json similarity index 100% rename from contrib/cards/ShadowCollective/WeequayPiratesUnit.json rename to contrib/cards/Mercenary/WeequayPiratesUnit.json diff --git a/contrib/cards/Rebel/RebelUnits.json b/contrib/cards/Rebel/RebelUnits.json index 85c3ba24..4ddc021e 100644 --- a/contrib/cards/Rebel/RebelUnits.json +++ b/contrib/cards/Rebel/RebelUnits.json @@ -242,7 +242,7 @@ ] }, { - "content": "contrib/cards/ShadowCollective/PykeSyndicateCapoUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateCapoUnit.json" }, { "name": "Rebel Officer", @@ -345,7 +345,7 @@ ], "Operative": [ { - "content": "contrib/cards/ShadowCollective/HondoUnit.json" + "content": "contrib/cards/Mercenary/HondoUnit.json" }, { "name": "Han Solo", @@ -721,10 +721,10 @@ ] }, { - "content": "contrib/cards/ShadowCollective/DinUnit.json" + "content": "contrib/cards/Mercenary/DinUnit.json" }, { - "content": "contrib/cards/ShadowCollective/IG11Unit.json" + "content": "contrib/cards/Mercenary/IG11Unit.json" }, { "name": "Ahsoka Tano", @@ -908,10 +908,10 @@ ] }, { - "content": "contrib/cards/ShadowCollective/PykeSyndicateFootSoldiersUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateFootSoldiersUnit.json" }, { - "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" + "content": "contrib/cards/Mercenary/WeequayPiratesUnit.json" }, { "name": "Ewok Skirmishers", @@ -1298,7 +1298,7 @@ ] }, { - "content": "contrib/cards/ShadowCollective/SwoopBikeRidersUnit.json" + "content": "contrib/cards/Mercenary/SwoopBikeRidersUnit.json" } ], "Heavy": [ @@ -1410,7 +1410,7 @@ ] }, { - "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" + "content": "contrib/cards/Mercenary/WLO5SpeederTankUnit.json" } ] } diff --git a/contrib/cards/Republic/RepublicUnits.json b/contrib/cards/Republic/RepublicUnits.json index 99f61bd2..c837017f 100644 --- a/contrib/cards/Republic/RepublicUnits.json +++ b/contrib/cards/Republic/RepublicUnits.json @@ -330,7 +330,7 @@ ] }, { - "content": "contrib/cards/ShadowCollective/PykeSyndicateCapoUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateCapoUnit.json" }, { "name": "Clone Commander Cody", @@ -371,7 +371,7 @@ ], "Operative": [ { - "content": "contrib/cards/ShadowCollective/HondoUnit.json" + "content": "contrib/cards/Mercenary/HondoUnit.json" }, { "name": "Shaak Ti", @@ -618,10 +618,10 @@ ] }, { - "content": "contrib/cards/ShadowCollective/PykeSyndicateFootSoldiersUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateFootSoldiersUnit.json" }, { - "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" + "content": "contrib/cards/Mercenary/WeequayPiratesUnit.json" } ], "Special Forces": [ @@ -876,7 +876,7 @@ ] }, { - "content": "contrib/cards/ShadowCollective/SwoopBikeRidersUnit.json" + "content": "contrib/cards/Mercenary/SwoopBikeRidersUnit.json" }, { "name": "Raddaugh Gnasp Fluttercraft", @@ -1042,7 +1042,7 @@ ] }, { - "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" + "content": "contrib/cards/Mercenary/WLO5SpeederTankUnit.json" } ] } diff --git a/contrib/cards/Separatist/SeparatistUnits.json b/contrib/cards/Separatist/SeparatistUnits.json index 49740196..cb363bbc 100644 --- a/contrib/cards/Separatist/SeparatistUnits.json +++ b/contrib/cards/Separatist/SeparatistUnits.json @@ -282,10 +282,10 @@ ] }, { - "content": "contrib/cards/ShadowCollective/PykeSyndicateCapoUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateCapoUnit.json" }, { - "content": "contrib/cards/ShadowCollective/BlackSunVigoUnit.json" + "content": "contrib/cards/Mercenary/BlackSunVigoUnit.json" }, { "name": "Poggle the Lesser", @@ -383,13 +383,13 @@ ] }, { - "content": "contrib/cards/ShadowCollective/HondoUnit.json" + "content": "contrib/cards/Mercenary/HondoUnit.json" }, { - "content": "contrib/cards/ShadowCollective/CadBaneUnit.json" + "content": "contrib/cards/Mercenary/CadBaneUnit.json" }, { - "content": "contrib/cards/ShadowCollective/BosskUnit.json" + "content": "contrib/cards/Mercenary/BosskUnit.json" }, { "name": "Maul", @@ -556,13 +556,13 @@ ] }, { - "content": "contrib/cards/ShadowCollective/PykeSyndicateFootSoldiersUnit.json" + "content": "contrib/cards/Mercenary/PykeSyndicateFootSoldiersUnit.json" }, { - "content": "contrib/cards/ShadowCollective/BlackSunEnforcersUnit.json" + "content": "contrib/cards/Mercenary/BlackSunEnforcersUnit.json" }, { - "content": "contrib/cards/ShadowCollective/WeequayPiratesUnit.json" + "content": "contrib/cards/Mercenary/WeequayPiratesUnit.json" }, { "name": "Geonosian Warriors", @@ -594,6 +594,34 @@ "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" } ] + }, + { + "name": "Geonosian Warriors", + "title": "Geonosian Engineers", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11895799947387687406/705B68582845528AF5FD5EEED97415BB9B36F748/", + "size": "small", + "type": "Trooper", + "points": 45, + "speed": 2, + "upgrades": {}, + "minis": [ + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177202081/37F68E146C49B315AA46E15BE88DDB56AE97646C/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177201554/7AEA5B0A98A298731F6BE51591401BBF095A5797/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177201933/E0DBF0D812FFF4F7F3A77CF1E4EAB84F78B8C49E/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" + }, + { + "bundle": "http://cloud-3.steamusercontent.com/ugc/2058763770177202012/76CB0371503304D5AAF562E5E2F04961CD206090/", + "secondary": "http://cloud-3.steamusercontent.com/ugc/2058763770177195881/84020A8311904050DC9309FE9109D47BC41FE4AA/" + } + ] } ], "Special Forces": [ @@ -769,7 +797,7 @@ }, { "name": "Droidekas", - "image": "https://steamusercontent-a.akamaihd.net/ugc/17494420314363813844/3FE04FCFAF3815AA2DE9E9C52101B5038AB6F892/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15998077387230471508/D99F8B4E4B7AF5FE30C23F4528F776797333B602/", "size": "medium", "type": "Emplacement Trooper", "points": 67, @@ -813,7 +841,7 @@ }, { "name": "STAP Riders", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131433789/32FE5B587A5DD8D208101FCE74CD2BE690483F37/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/15661631464886192384/E464BADECADC111B8100A6E02301597C467FDD72/", "size": "medium", "type": "Repulsor Vehicle", "points": 80, @@ -958,7 +986,7 @@ ] }, { - "content": "contrib/cards/ShadowCollective/WLO5SpeederTankUnit.json" + "content": "contrib/cards/Mercenary/WLO5SpeederTankUnit.json" } ] } diff --git a/contrib/cards/official.json b/contrib/cards/official.json index aa0c69d6..2800596f 100644 --- a/contrib/cards/official.json +++ b/contrib/cards/official.json @@ -4,7 +4,7 @@ "Empire": "Empire/EmpireUnits.json", "Republic": "Republic/RepublicUnits.json", "Separatist": "Separatist/SeparatistUnits.json", - "ShadowCollective": "ShadowCollective/ShadowCollectiveUnits.json" + "Mercenary": "Mercenary/MercenaryUnits.json" }, "upgrades": { @@ -437,7 +437,7 @@ }, { "name": "Trusted Agent", - "image": "https://steamusercontent-a.akamaihd.net/ugc/17954061666704112600/9401A70D88A98BA9FC5B5D8E56B0FA7F3B95961B/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/13647228434712340827/31340C9F16017CEC33FF34C97256BFBEE7AB7AAF/", "points": 5 }, { @@ -4764,7 +4764,7 @@ }, { "name": "Insatiable Curiosity", - "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081140100414/E3E6C5563CD533DC0560C50590B8A989E382E7BC/", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17077726437706151926/A1FAE61CD17F3A228082987AF90699FBEDF7F7E3/", "points": 8, "restrictions": { "include": { @@ -5040,6 +5040,23 @@ "image": "https://steamusercontent-a.akamaihd.net/ugc/59218081131527719/02ACE80E8D14CF7DC017156A128F34A09F725CF2/", "pip": 3 } + ], + "Mercenary": [ + { + "name": "Insolence?! We are Pirates!", + "image": "https://steamusercontent-a.akamaihd.net/ugc/17838958063167701144/B10D335A59FFE4CEB954585E0DB0E42DB67DE339/", + "pip": 1 + }, + { + "name": "Speak Softly and Drive a Big Tank", + "image": "https://steamusercontent-a.akamaihd.net/ugc/10689439603598113404/87E51EEC4B2D75528FA0277BA78C5F52DAF9886A/", + "pip": 2 + }, + { + "name": "I Smell Profit", + "image": "https://steamusercontent-a.akamaihd.net/ugc/11974419819645442132/21863ABAA56F91ACFBEDC358ECC81A9D01D80BAE/", + "pip": 3 + } ] }, diff --git a/contrib/maps/skirmish.json b/contrib/maps/skirmish.json index 96726aa2..93d51406 100644 --- a/contrib/maps/skirmish.json +++ b/contrib/maps/skirmish.json @@ -1,22 +1 @@ -[ - { - "name": "Bespin Traffic Stop", - "url": "http://cloud-3.steamusercontent.com/ugc/1754683015340767532/E7561682A2D59D5A4B2B1BFB991A1814CAF03756/" - }, - { - "name": "Desert Crossroads", - "url": "http://cloud-3.steamusercontent.com/ugc/1754683015340768205/3722DCA70A4EA409752FA48547017EE6DF6E5CE4/" - }, - { - "name": "Imperial Checkpoint", - "url": "http://cloud-3.steamusercontent.com/ugc/1754683015340768629/101233D6B377074CD1ECFC0A77D9AAED275A9737/" - }, - { - "name": "Woodland Battle", - "url": "http://cloud-3.steamusercontent.com/ugc/1754683015340768989/2F91C4693A43C4D4F5940A58FB414C84FC74B8DF/" - }, - { - "name": "Woodland Landing Pad", - "url": "http://cloud-3.steamusercontent.com/ugc/1754683015340769304/E7C01A86D916B57FF2189D15E864EA085913FBDF/" - } -] +[{}] diff --git a/mod/schema/deps/UnitTypes.json b/mod/schema/deps/UnitTypes.json index aff4f3db..3330ac0e 100644 --- a/mod/schema/deps/UnitTypes.json +++ b/mod/schema/deps/UnitTypes.json @@ -11,6 +11,8 @@ "Creature Trooper", "Emplacement Trooper", "Ground Vehicle", + "Heavy Trooper", + "Heavy Droid Trooper", "Repulsor Vehicle", "Trooper", "Wookiee Trooper" diff --git a/mod/src/StarWarsLegion.lua b/mod/src/StarWarsLegion.lua index 9f41cc30..58659bb7 100644 --- a/mod/src/StarWarsLegion.lua +++ b/mod/src/StarWarsLegion.lua @@ -435,12 +435,12 @@ function onload(saveData) commandTokenData.red.republicSupport = commandTokenData.red.sup commandTokenData.red.republicHeavy = commandTokenData.red.heavy - commandTokenData.red.shadowcollectiveCommander = commandTokenData.red.com - commandTokenData.red.shadowcollectiveOperative = commandTokenData.red.op - commandTokenData.red.shadowcollectiveCorps = commandTokenData.red.corps - commandTokenData.red.shadowcollectiveSpecialforces = commandTokenData.red.sf - commandTokenData.red.shadowcollectiveSupport = commandTokenData.red.sup - commandTokenData.red.shadowcollectiveHeavy = commandTokenData.red.heavy + commandTokenData.red.mercenaryCommander = commandTokenData.red.com + commandTokenData.red.mercenaryOperative = commandTokenData.red.op + commandTokenData.red.mercenaryCorps = commandTokenData.red.corps + commandTokenData.red.mercenarySpecialforces = commandTokenData.red.sf + commandTokenData.red.mercenarySupport = commandTokenData.red.sup + commandTokenData.red.mercenaryHeavy = commandTokenData.red.heavy commandTokenData.blue = {} @@ -479,12 +479,12 @@ function onload(saveData) commandTokenData.blue.republicSupport = commandTokenData.blue.sup commandTokenData.blue.republicHeavy = commandTokenData.blue.heavy - commandTokenData.blue.shadowcollectiveCommander = commandTokenData.blue.com - commandTokenData.blue.shadowcollectiveOperative = commandTokenData.blue.op - commandTokenData.blue.shadowcollectiveCorps = commandTokenData.blue.corps - commandTokenData.blue.shadowcollectiveSpecialforces = commandTokenData.blue.sf - commandTokenData.blue.shadowcollectiveSupport = commandTokenData.blue.sup - commandTokenData.blue.shadowcollectiveHeavy = commandTokenData.blue.heavy + commandTokenData.blue.mercenaryCommander = commandTokenData.blue.com + commandTokenData.blue.mercenaryOperative = commandTokenData.blue.op + commandTokenData.blue.mercenaryCorps = commandTokenData.blue.corps + commandTokenData.blue.mercenarySpecialforces = commandTokenData.blue.sf + commandTokenData.blue.mercenarySupport = commandTokenData.blue.sup + commandTokenData.blue.mercenaryHeavy = commandTokenData.blue.heavy commandTokenTrayData = {} commandTokenTrayData.red = {} @@ -513,12 +513,12 @@ function onload(saveData) commandTokenTrayData.red.separatistSpecialforces = "abb909" commandTokenTrayData.red.separatistSupport = "ce9be9" commandTokenTrayData.red.separatistHeavy = "f68963" - commandTokenTrayData.red.shadowcollectiveCommander = "071592" - commandTokenTrayData.red.shadowcollectiveOperative = "369fa9" - commandTokenTrayData.red.shadowcollectiveCorps = "af4c28" - commandTokenTrayData.red.shadowcollectiveSpecialforces = "abb909" - commandTokenTrayData.red.shadowcollectiveSupport = "ce9be9" - commandTokenTrayData.red.shadowcollectiveHeavy = "f68963" + commandTokenTrayData.red.mercenaryCommander = "071592" + commandTokenTrayData.red.mercenaryOperative = "369fa9" + commandTokenTrayData.red.mercenaryCorps = "af4c28" + commandTokenTrayData.red.mercenarySpecialforces = "abb909" + commandTokenTrayData.red.mercenarySupport = "ce9be9" + commandTokenTrayData.red.mercenaryHeavy = "f68963" commandTokenTrayData.red.stack = "e2202d" commandTokenTrayData.blue = {} @@ -547,12 +547,12 @@ function onload(saveData) commandTokenTrayData.blue.separatistSpecialforces = "3832c6" commandTokenTrayData.blue.separatistSupport = "d2c857" commandTokenTrayData.blue.separatistHeavy = "6d221d" - commandTokenTrayData.blue.shadowcollectiveCommander = "57ab78" - commandTokenTrayData.blue.shadowcollectiveOperative = "b30914" - commandTokenTrayData.blue.shadowcollectiveCorps = "e5e8df" - commandTokenTrayData.blue.shadowcollectiveSpecialforces = "3832c6" - commandTokenTrayData.blue.shadowcollectiveSupport = "d2c857" - commandTokenTrayData.blue.shadowcollectiveHeavy = "6d221d" + commandTokenTrayData.blue.mercenaryCommander = "57ab78" + commandTokenTrayData.blue.mercenaryOperative = "b30914" + commandTokenTrayData.blue.mercenaryCorps = "e5e8df" + commandTokenTrayData.blue.mercenarySpecialforces = "3832c6" + commandTokenTrayData.blue.mercenarySupport = "d2c857" + commandTokenTrayData.blue.mercenaryHeavy = "6d221d" commandTokenTrayData.blue.stack = "c9f9d0" -- standby tokens diff --git a/mod/src/includes/Deck.ttslua b/mod/src/includes/Deck.ttslua index 79953fc9..a51b79db 100644 --- a/mod/src/includes/Deck.ttslua +++ b/mod/src/includes/Deck.ttslua @@ -484,7 +484,7 @@ function Deck:_isUpgradeValidFor(upgrade, unit) if unitAlignment == "LIGHT" then return unit.faction:upper() == "REBEL" or unit.faction:upper() == "REPUBLIC" elseif unitAlignment == "DARK" then - return unit.faction:upper() == "EMPIRE" or unit.faction:upper() == "SEPARATIST" or unit.faction:upper() == "SHADOWCOLLECTIVE" + return unit.faction:upper() == "EMPIRE" or unit.faction:upper() == "SEPARATIST" or unit.faction:upper() == "MERCENARY" end end end diff --git a/mod/src/includes/SpawnList.ttslua b/mod/src/includes/SpawnList.ttslua index e361cb7b..33902579 100644 --- a/mod/src/includes/SpawnList.ttslua +++ b/mod/src/includes/SpawnList.ttslua @@ -317,7 +317,7 @@ local function initListSpawner(params) rebel = 1, separatist = 1, republic = 1, - shadowcollective = 1, + mercenary = 1, })[faction:lower()] then faction = "Imperial" end diff --git a/mod/src/includes/data/ListBuilderNew.ttslua b/mod/src/includes/data/ListBuilderNew.ttslua index 411ea83a..064943a3 100644 --- a/mod/src/includes/data/ListBuilderNew.ttslua +++ b/mod/src/includes/data/ListBuilderNew.ttslua @@ -524,7 +524,7 @@ end function _loadArmyFromJson(data) _clearZone() if data.armyFaction == "" then - data.armyFaction = "shadowcollective" + data.armyFaction = "mercenary" end _setSelectedScenario(data.battlefieldDeck.scenario) local spawn = initListSpawner({ diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 3c9abccd..47e68d21 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -46,11 +46,7 @@ CHANGELOG (Since last Beta) - - Add: Jedi Council - - Add: New April Upgrades - - Add: Strike Team Changes - - Add: Savage Opress - - Fix: Logray, Sabine, Wicket Models + - Merc Test 1 From 00f1fa075e532dca165bb6572ce5e7dea645976e Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 20 May 2026 00:52:02 -0400 Subject: [PATCH 89/93] Revert "Mac projector fallback" --- mac-patcher/.gitignore | 6 - mac-patcher/README.md | 74 - mac-patcher/SHADER_INVENTORY.md | 59 - mac-patcher/design-refactor.md | 330 --- mac-patcher/extract_projector_specs.py | 198 -- mac-patcher/generate_overlay_assets.py | 282 --- mac-patcher/inspect_cohesion.py | 83 - mac-patcher/inspect_externals.py | 51 - mac-patcher/materials-reference.md | 170 -- mac-patcher/patch_cohesion_rename.py | 81 - mac-patcher/patch_save_for_mac.py | 2171 ----------------- mac-patcher/requirements.txt | 3 - mac-patcher/retro-cohesion.md | 170 -- mac-patcher/retro-deployment.md | 76 - mac-patcher/retro-movement.md | 122 - mac-patcher/retro-range.md | 86 - mac-patcher/scan_bundles.py | 166 -- .../mac-fallback-assets/cohesion_halo.png | Bin 22585 -> 0 bytes .../mac-fallback-assets/deployment_blue.png | Bin 856 -> 0 bytes .../mac-fallback-assets/deployment_red.png | Bin 856 -> 0 bytes .../mac-fallback-assets/max_move_cyan.png | Bin 4124 -> 0 bytes .../mac-fallback-assets/range_bombCart.png | Bin 12426 -> 0 bytes .../range_fig_leader_epic.png | Bin 19173 -> 0 bytes .../range_fig_leader_huge.png | Bin 18811 -> 0 bytes .../range_fig_leader_laat.png | Bin 19058 -> 0 bytes .../range_fig_leader_large.png | Bin 18753 -> 0 bytes .../range_fig_leader_long_stadium.png | Bin 19025 -> 0 bytes .../range_fig_leader_medium.png | Bin 18599 -> 0 bytes .../range_fig_leader_small.png | Bin 18475 -> 0 bytes .../range_fig_leader_snail_stadium.png | Bin 19127 -> 0 bytes mod/data/mac-fallback-assets/range_poi.png | Bin 10364 -> 0 bytes .../mac-fallback-assets/range_smokeToken.png | Bin 10364 -> 0 bytes mod/data/mac-fallback-assets/range_token.png | Bin 10364 -> 0 bytes .../range_tokenRangeTwo.png | Bin 12344 -> 0 bytes .../silhouette_mac_fallback.unity3d | Bin 10922 -> 0 bytes mod/src/StarWarsLegion.lua | 1 - mod/src/StarWarsLegion/Order_Token.a57c41.lua | 97 +- .../SETUP_CONTROLLER.1cb552.lua | 32 +- mod/src/includes/Cohesion.ttslua | 28 +- mod/src/includes/Overlays.ttslua | 929 ------- mod/src/includes/RangeRulers.ttslua | 30 - src/index.ts | 26 +- 42 files changed, 53 insertions(+), 5218 deletions(-) delete mode 100644 mac-patcher/.gitignore delete mode 100644 mac-patcher/README.md delete mode 100644 mac-patcher/SHADER_INVENTORY.md delete mode 100644 mac-patcher/design-refactor.md delete mode 100644 mac-patcher/extract_projector_specs.py delete mode 100644 mac-patcher/generate_overlay_assets.py delete mode 100644 mac-patcher/inspect_cohesion.py delete mode 100644 mac-patcher/inspect_externals.py delete mode 100644 mac-patcher/materials-reference.md delete mode 100644 mac-patcher/patch_cohesion_rename.py delete mode 100644 mac-patcher/patch_save_for_mac.py delete mode 100644 mac-patcher/requirements.txt delete mode 100644 mac-patcher/retro-cohesion.md delete mode 100644 mac-patcher/retro-deployment.md delete mode 100644 mac-patcher/retro-movement.md delete mode 100644 mac-patcher/retro-range.md delete mode 100644 mac-patcher/scan_bundles.py delete mode 100644 mod/data/mac-fallback-assets/cohesion_halo.png delete mode 100644 mod/data/mac-fallback-assets/deployment_blue.png delete mode 100644 mod/data/mac-fallback-assets/deployment_red.png delete mode 100644 mod/data/mac-fallback-assets/max_move_cyan.png delete mode 100644 mod/data/mac-fallback-assets/range_bombCart.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_epic.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_huge.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_laat.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_large.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_long_stadium.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_medium.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_small.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png delete mode 100644 mod/data/mac-fallback-assets/range_poi.png delete mode 100644 mod/data/mac-fallback-assets/range_smokeToken.png delete mode 100644 mod/data/mac-fallback-assets/range_token.png delete mode 100644 mod/data/mac-fallback-assets/range_tokenRangeTwo.png delete mode 100644 mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d delete mode 100644 mod/src/includes/Overlays.ttslua diff --git a/mac-patcher/.gitignore b/mac-patcher/.gitignore deleted file mode 100644 index 34bac69b..00000000 --- a/mac-patcher/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -venv/ -__pycache__/ -*.pyc -*.bak -*.bak-* -scan_bundles_*.out diff --git a/mac-patcher/README.md b/mac-patcher/README.md deleted file mode 100644 index 420bd388..00000000 --- a/mac-patcher/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# mac-patcher - -Python patcher that injects Mac-compatibility fallbacks into a SWL TTS save -file. Works around the TTS Unity 6 (Berserk) shader bug where vanilla -AssetBundle Projectors (range rulers, cohesion halos, movement templates, -deployment zones, silhouettes) render as a magenta rectangle on macOS -because the custom shader bundled by Allen White (Dicewrench) fails to load -under Unity 6 Metal. - -## What it does - -For each affected object script in a `TS_Save_N.json`: - -- **Cohesion / Range / Maximum Move / Deployment / Silhouette**: routes the - visualization through a Mac-friendly path (vector lines + decal PNGs) - built into the Global script. The vanilla bundle path stays available - for per-seat opt-in (see "Mac TTS U6 Patch" floating panel). - -- **SIL button**: renames `click_function = "toggleSilhouettes"` to - `"macToggleSil"` + injects a forwarder. Empirically the engine silently - swallows the original click_function name in Mac mode (the button plays - its sound/animation but the function is never invoked). The rename - bypasses the issue entirely. - -- **clearSilhouette nil guard**: upstream `clearSilhouette()` derefs - `removeAttachments()[1]` which is nil when `silhouetteState` is true but - the physical silhouette attachment has been lost across save reloads. - Patched to guard the nil case + reset state in onload. - -- **Order Token Speed/Move button overrides**: inlines vanilla bodies of - `changeSpeed1/2/3` and `moveForward/Backwards/Left/Right` to capture - the clicking player's color (`macActivePlayerForMove`) for per-seat - routing. Inlined rather than wrapped because the List Builder copies the - block to Command Token Custom_Models which lack the vanilla - Order_Token function bodies (wrap+call_orig would crash with "attempt - to call a nil value"). - -## Usage - -```bash -# Setup once -python3 -m venv venv -source venv/bin/activate -pip install -r requirements.txt - -# Patch a save -python3 patch_save_for_mac.py [--reload] -``` - -The patcher also mirrors the patched save into -`~/Library/Tabletop Simulator/Saves/TS_Save_N.json` (next free slot, or the -existing `SWL BETA - MAC PATCH` slot if present) so TTS picks it up in -Games -> Save & Load. - -`--reload` hot-pushes the patched scripts to a running TTS instance via the -External Editor API (no need to restart TTS). - -## Idempotence - -All patches are idempotent: a re-run on an already-patched save strips the -previous Mac patch block from Global (via marker regex) and re-injects -fresh. Per-object wrappers are also stripped before re-injection. - -## Files - -- `patch_save_for_mac.py` - main patcher -- `scan_bundles.py` - UnityPy scanner for the local TTS bundle cache -- `extract_projector_specs.py` - dump Projector material properties from a - bundle (used to discover the `_Arc` shader keyword for firing arc lines) -- `generate_overlay_assets.py` - regenerate the range/cohesion/etc. PNG - decals from authoritative SWL geometry -- `inspect_*.py` - debug helpers -- `retro-*.md` - retrospective notes on each subsystem rewrite -- `SHADER_INVENTORY.md` - list of custom shaders in the vanilla bundles diff --git a/mac-patcher/SHADER_INVENTORY.md b/mac-patcher/SHADER_INVENTORY.md deleted file mode 100644 index 926e86db..00000000 --- a/mac-patcher/SHADER_INVENTORY.md +++ /dev/null @@ -1,59 +0,0 @@ -# SW Legion TTS Mod — Shader Inventory - -Scan effectué le 2026-05-07 sur `~/Library/Tabletop Simulator/Mods/Assetbundles/` (40 bundles `.unity3d`). -Outil : UnityPy 1.25.0, script `scan_bundles.py`. Output brut : `scan_bundles_2026-05-07.out`. - -## TL;DR -**9 shaders custom distincts**, **tous embarqués dans le `.unity3d` qui les utilise**. Donc : -- L'astuce "Always Included Shaders" du player TTS est **non-applicable** : ces shaders ne sont pas chargés depuis le pool du player, ils vivent dans les bundles. -- Le bug magenta vient du fait que le bytecode/code source de ces shaders custom ne fonctionne plus correctement sous Unity 6 + Metal sur Mac. -- Les overlays magenta listés par Martin (cohésion, range, deployment, movement) correspondent **exactement** aux 4 shaders `BucketheadBits/Projector/*`. - -## Inventaire complet (par catégorie) - -### Shaders Projector (overlays affectés par le bug magenta) -| Shader | Materials | Bundles | -|---|---|---| -| `BucketheadBits/Projector/Movement` | `ProjectorMaterial_27mm_speed1_single`, `ProjectorMaterial_27mm_speed2_single`, `ProjectorMaterial_50mm_speed2_single` | 3 | -| `BucketheadBits/Projector/Deployment` | `Projector_Deployment_Blue` (×2), `Projector_Deployment_Red` (×2) | 4 | -| `BucketheadBits/Projector/Range` | `ProjectorMaterial_25mm_token`, `ProjectorMaterial_27mm`, `ProjectorMaterial_50mm` | 3 | -| `BucketheadBits/Projector/Cohesion` | `Cohesion_27mm` | 1 | - -→ 11 materials projector au total, 4 shaders distincts à porter en priorité. - -### Outils / autres custom -| Shader | Materials | Bundles | -|---|---|---| -| `BucketheadBits/MoveTool` | `Speed1`, `Speed2` | 2 | -| `BucketheadBits/Tokens/Lambert Channel Mask` | `unitIDtoken_1` à `unitIDtoken_10` | 10 | -| `BucketheadBits/Silhouette` | `BucketheadBits_Silhouette` | 1 | -| `BucketheadBits/Units/Color Replacer` | `Ewok_SharedMat` | 1 | -| `DWD/LightenSkybox` | `Skybox` | 1 | - -→ Le shader `Tokens/Lambert Channel Mask` correspond aux 10 unitIDtokens loggés en console par TTS lors du bug : "Shader didn't load correctly for AssetBundle material unitIDtoken_X". Donc ce shader est **explicitement confirmé cassé**. - -## Auteurs -- Préfixe **`BucketheadBits/`** sur 8/9 shaders → ancien contributeur "Buckethead". Aucun repo GitHub public trouvé sous ce handle. Possible que ce soit un handle alternatif de Tieren (crédité ailleurs comme "original mod creator"). -- Préfixe **`DWD/`** sur 1 shader (LightenSkybox) → autre auteur, juste un tint skybox. - -## Implications pour la conversation avec Ben - -1. **L'angle "Always Included Shaders" n'est pas la bonne piste** pour ce bug, parce que les shaders custom sont embarqués dans les bundles. Si Ben a essayé cette piste, c'est cohérent que ça n'ait rien donné. - -2. **Le scope minimum réaliste** : porter / remplacer ces 9 shaders sous Unity 6 (compilation Metal-compatible). Soit en partant des sources s'il les a, soit en réécrivant des équivalents stock. - -3. **Materials = 11 projectors + 10 tokens + 4 outils + 1 skybox = 26 materials affectés au total** dans 27 bundles différents. Volumétrie raisonnable si les shaders sont swappable d'un coup. - -4. **Type technique des shaders** : - - Les `Projector/*` sont des shaders Unity Projector (transparent, projection avec bandes alternées) — historiquement tricky à porter Metal. - - Le `Tokens/Lambert Channel Mask` est un shader de coloration par canaux (R/G/B/A) — utilisé pour appliquer la couleur du joueur sur un mesh à canaux. Classique mais codé custom. - - Les autres (MoveTool, Silhouette, Color Replacer, LightenSkybox) sont des shaders d'effets simples. - -## Sources non-trouvées -- Aucun repo public sous `swlegion`, `swlegion-dev`, `matanlurey`, `halestom15`, `Buckethead` ne contient les sources Unity de ces shaders. -- Le projet Unity vit donc en privé sur la machine d'un contributeur (probablement Decaf ou Ben). - -## Prochaine étape -Attendre Ben pour confirmer si les sources sont disponibles. Cet inventaire répond déjà partiellement à ses questions 1 et 2 : -- Q1 ("custom shaders updated or just retargeted ?") : on sait maintenant exactement ce qu'il faut updater. -- Q2 ("which shaders are referenced ?") : liste complète ci-dessus. diff --git a/mac-patcher/design-refactor.md b/mac-patcher/design-refactor.md deleted file mode 100644 index a94b2d03..00000000 --- a/mac-patcher/design-refactor.md +++ /dev/null @@ -1,330 +0,0 @@ -# Design refactor — Overlays Projector SWL TTS (Mac/Unity 6) - -**Status** : draft 15 mai 2026. Spec pour le patch event-driven hybride (decals plats + vector lines drapants). Base : 4 retro-*.md + materials-reference.md + shaders Allen White lus. - -## 1. Contexte (résumé exécutif) - -Le passage du player TTS à Unity 6 a strippé les variants Standard receiver des Projectors legacy. Sur Mac, **tous les overlays Projector du mod SWL s'affichent en magenta** : Cohesion Ruler, Range Ruler, Maximum Move, Deployment Boundary. Validation empirique session 14-15 mai : aucun fix côté bundle/shader ne marche (le strip est player-side TTS U6). Fix Berserk = ticket à ouvrir mais sans garantie de calendrier. - -**Solution mod-side validée** : remplacer le pattern Custom_AssetBundle (Projector) par un combo **Decal plat (PNG hébergé) + Vector lines (drapant via raycast)** orchestré par un manager Global event-driven. - -## 1bis. Fallback per-seat avec auto-test au boot - -Le refactor ne **remplace pas** le Projector legacy, il l'**augmente** d'un fallback per-seat. Chaque joueur a un compat flag détecté au boot, et le rendu s'adapte : - -- **Seats non-Mac** (TTS U6 OK) → voient le **Projector legacy** (rendu original préservé) -- **Seats Mac** (bug magenta) → voient le **patch hybride** (decal + vector lines) - -Quand Berserk corrigera le bug, l'auto-test au boot suivant désactivera le compat pour les seats Mac → retour automatique au comportement legacy, zéro action utilisateur. - -### 1bis.1 État par-seat (Global script) - -```lua -compatBySeat = {} -- {Red=true|false, Blue=true|false, ...} true = Mac/needs patch - -function onLoad(script_state) - if script_state ~= "" then - compatBySeat = JSON.decode(script_state) or {} - end - testCompatForAllSeated() -end - -function onSave() - return JSON.encode(compatBySeat) -end - -function getMacSeats() - local out = {} - for color, isMac in pairs(compatBySeat) do - if isMac then table.insert(out, color) end - end - return out -end - -function getNonMacSeats() - local out = {} - for color, isMac in pairs(compatBySeat) do - if not isMac then table.insert(out, color) end - end - return out -end -``` - -### 1bis.2 Test compat au boot - -```lua -function testCompatForAllSeated() - for _, player in pairs(Player.getPlayers()) do - if player.seated then - offerCompatTest(player) - end - end -end - -function offerCompatTest(player) - -- Spawn un canary Projector en zone hors-jeu (sous la table) - local canary = spawnObject({ - type="Custom_AssetBundle", - position={-50, -10, -50}, - scale={0,0,0} - }) - canary.setCustomObject({assetbundle = COHESION_27MM_URL}) - - -- Wait quelques secondes le temps que le bundle charge - Wait.time(function() - -- UI popup pour ce joueur uniquement - player.broadcast("Test rendu : cliquez sur ce que vous voyez sur le canary (Y=-10)") - -- Buttons "Couleurs OK" et "Magenta/rose" - -- onClick stocke compatBySeat[player.color] = true|false - -- destruct canary - end, 3) -end -``` - -### 1bis.3 Spawn d'un overlay (pattern unifié) - -```lua -function spawnOverlay(type, fig, params) - local macSeats = getMacSeats() - local nonMacSeats = getNonMacSeats() - local entry = {type=type, fig=fig, params=params, objects={}} - - -- 1. Projector legacy pour les seats non-Mac - if #nonMacSeats > 0 then - local projector = spawnObject({type="Custom_AssetBundle", ...}) - projector.setCustomObject({assetbundle = bundleURLs[type][fig.baseSize]}) - projector.setLock(true) - projector.setName(type .. "_legacy") - if #macSeats > 0 then projector.setInvisibleTo(macSeats) end - entry.objects.projector = projector - end - - -- 2. Custom_Tile (PNG plat) pour les seats Mac - if #macSeats > 0 then - local tile = spawnObject({type="Custom_Tile", position=fig.pos+Y0.1, scale=...}) - tile.setCustomObject({image = pngURLs[type][fig.baseSize]}) - tile.setLock(true) - tile.setName(type .. "_patch") - if #nonMacSeats > 0 then tile.setInvisibleTo(nonMacSeats) end - entry.objects.tile = tile - end - - -- 3. Vector lines (drape relief) pour les seats Mac - if #macSeats > 0 then - entry.lines = buildVectorLines(type, fig, params, macSeats) - -- ajoutées au batch global - end - - activeOverlays[fig.getGUID() .. ":" .. type] = entry - redrawAll() -end -``` - -### 1bis.4 Bug Berserk fix → reset auto - -Quand Berserk corrigera les variants Standard receiver dans le player TTS U6 : -- Au prochain boot de partie, `testCompatForAllSeated` re-run pour chaque seat -- Les Mac users verront maintenant le canary correctement → cliquent "Couleurs OK" → `compatBySeat[color] = false` -- Le patch ne s'active plus pour eux, comportement legacy restauré -- Aucun changement de code requis côté mod - -**Re-test manuel** : un bouton "Re-test compatibility" dans l'UI Notes du mod permet de forcer un nouveau test à tout moment (utile si le user veut vérifier après une mise à jour TTS). - -### 1bis.5 Incertitudes à valider empiriquement au POC - -| Incertitude | Plan B si KO | -|---|---| -| `Object.setInvisibleTo` cache-t-il le **rendu de la projection** d'un Projector ou seulement le GameObject parent ? | Si KO : si au moins 1 Mac seat présent, on ne spawn PAS le Projector du tout (tout le monde voit le patch — compromis : 1 Mac dans la partie = tout le monde adopte le rendu patch) | -| `Global.setDecals()` supporte-t-il un filtre `players` per entry ? | Si KO : utiliser `Custom_Tile` Objects (déjà ce qu'on a au-dessus, avec `setInvisibleTo`). Plus propre et déjà compatible per-seat | - -À tester en début de phase d'implémentation, avant de pousser tout le code. - -## 2. Architecture event-driven (manager Global) - -### 2.1 État global unique - -```lua --- mod/src/includes/Overlays.ttslua (nouveau) -local activeOverlays = {} -- clé = "{guid}:{type}" → {type, fig, params} -local hiddenWhilePickedUp = {} -- buffer pour les overlays cachés temporairement -``` - -### 2.2 API publique unifiée - -```lua --- Spawn / clear par type, scopé à une fig -function spawnOverlay(type, fig, params) -function clearOverlayForFig(type, fig) -function clearAllOverlays() -- reset complet - --- Adapters pour conserver l'API existante des callers -function spawnCohesionRuler(fig) -- → spawnOverlay("cohesion", fig, {}) -function clearCohesionRuler() -- → clearOverlayForFig("cohesion", self) -function spawnRangeRuler(fig, override) -- → spawnOverlay("range", fig, {override=override}) -function clearRangeRulers() -- → clearOverlayForFig("range", selectedUnitObj) --- etc. -``` - -### 2.3 Event handlers (Global) - -```lua -function onObjectPickUp(player_color, obj) - -- buffer tous les overlays attachés à obj puis les retirer du draw - hidePickedUp(obj) - redrawAll() -end - -function onObjectDrop(player_color, obj) - -- restaurer les overlays de obj depuis le buffer - restorePickedUp(obj) - redrawAll() -end - -function onObjectDestroy(obj) - -- nettoyage définitif - purgeForFig(obj) - redrawAll() -end -``` - -### 2.4 Rendu unique par tick d'event - -```lua -function redrawAll() - local lines, decals = {}, {} - for _, entry in pairs(activeOverlays) do - local l, d = builders[entry.type](entry.fig, entry.params) - for _, line in ipairs(l) do table.insert(lines, line) end - for _, decal in ipairs(d) do table.insert(decals, decal) end - end - Global.setVectorLines(lines) - Global.setDecals(decals) -end -``` - -**Important** : `setVectorLines` et `setDecals` sont des **singletons** côté TTS — chaque appel remplace toute la collection. D'où le manager global qui agrège. - -## 3. Spec par overlay - -### 3.1 Cohesion - -| Composant | Détail | -|---|---| -| Trigger spawn | Hotkey "Show Cohesion On Hovered Model" / bouton COHESION Order Token / dropCoroutine post-mouvement (`moveState=true`) | -| Trigger clear | Mêmes triggers (toggle) + onPickedUp (auto) + standby/reload globaux | -| Decal PNG | `cohesion_halo.png` — gradient radial blanc fade depuis le centre, fond transparent, 512×512px | -| Decal params | `position = fig.pos + Y0.1`, `rotation = (90, 0, 0)` (face vers le haut), `scale = (baseRadius+1, baseRadius+1, baseRadius+1) × 2` | -| Vector line | 1 cercle filaire blanc à `r = base_radius + 0.5"` du centre, drapé par raycast 32 segments | -| Couleur | #FFFFFF α=0.6 | - -API conservée : `showCohesionOnHoveredModel`, `spawnCohesionRuler`, `clearCohesionRuler`. Compatible avec les 3 sites de require existants (Global, Unit_Leader, Order_Token) car redirigés vers le manager global. - -### 3.2 Range Ruler - -| Composant | Détail | -|---|---| -| Trigger spawn | Hotkey "Show Range On Hovered Model" / bouton RANGE Order Token (targetingMode/attackMode) / bouton R sur POI/tokens | -| Trigger clear | Mêmes triggers (toggle) + clearTemplates (post-drop mouvement) + exit modes | -| Decal PNG | `range_bands.png` (universel) — 4 anneaux concentriques à 6/12/18/24 inches relatifs, couleurs jaune/orange/rouge/magenta foncé, fond transparent, 1024×1024px | -| Decal params | `position = fig.pos + Y0.1`, `scale = (60, 60, 60)` (couvre 24" de rayon + marge) | -| Vector lines | 4 cercles filaires aux rayons absolus **6", 12", 18", 24"** du centre, mêmes couleurs que les bandes du PNG | -| Couleur (par anneau) | R1 #FFC300, R2 #FF7500, R3 #FF1400, R4 #C20042 (alpha 0.6 sur les lignes) | -| Variantes | Tokens (smokeToken/token/tokenRangeTwo/bombCart/POI) → moins d'anneaux (1 ou 2), PNG distincts ou même PNG avec scale ajusté | - -API conservée : `showRangeOnHoveredModel`, `spawnRangeRuler(fig, override)`, `clearRangeRuler/clearRangeRulers`. - -Pour les variantes, le `override` permet de passer un type explicite (`"smokeToken"`, `"poi"`, etc.) au lieu du baseSize de la fig. - -### 3.3 Maximum Move - -| Composant | Détail | -|---|---| -| Trigger spawn | Lors du clic sur bouton speed (1/2/3) sur Order Token (flow mouvement) | -| Trigger clear | clearMovementTemplates (drop final) | -| Decal PNG | `max_move_disk.png` — disque plein bleu ciel #55CCFF α=0.48, fond transparent, 512×512px | -| Decal params | `position = fig.pos + Y0.1`, `scale = ProjectorRadius × 2` selon (baseSize, speed) | -| Vector line | 1 cercle filaire bleu ciel au rayon = ProjectorRadius (drape relief) + 1 anneau base blanc fin à r = baseRadius + 0.5" | -| Couleur | Cercle principal #55CCFF α=0.6, anneau base #FFFFFF α=0.5 | -| Rayons | Lookup table `maxMoveRadius[baseSize][speed]` (ex: `27mm.speed1=4.55", speed2=6.52", speed3=8.48"`) — extraits de `_ProjectorRadius` dans les materials Movement | - -API conservée : flow existant dans `Order_Token.a57c41.lua:552-574`, remplacement du `spawnObject Custom_AssetBundle` par `spawnOverlay("maxmove", fig, {speed=selectedSpeed, baseSize=unitData.baseSize})`. - -### 3.4 Deployment Boundary - -| Composant | Détail | -|---|---| -| Trigger spawn | `spawnDeploymentBoundary(matrix)` au setup (depuis menu UI Deployment) | -| Trigger clear | `clearDeploymentBoundary()` (menu UI Remove Overlay) + standby global | -| Decal PNG | `deployment_red.png` + `deployment_blue.png` — rectangle plein uniforme α=0.5, 256×256px | -| Decal params | Par cellule de la matrix, `position = grid pos + offset`, `scale = (6, 6, 6)` (taille standard d'une cellule SWL), rotation selon `deployRotations[cell]` | -| Vector lines | Contour rectangle drapant (4 côtés × N segments via raycast) par cellule, couleur match decal | -| Couleur | Rouge #FF0000 / Bleu #0000FF α=0.5 | -| Variantes | Half/L/Corner/Round → combinaisons de cellules dans la matrix, le manager gère chaque cellule individuellement (1 decal + 1 contour par cellule) | - -API conservée : `spawnDeploymentBoundary(matrix)`, `clearDeploymentBoundary()`. Internement, chaque cellule est enregistrée comme un overlay distinct avec un GUID synthétique (ex `"deployment:bs:5:3"`). - -## 4. Assets PNG à générer - -| Fichier | Dimensions | Description | Hosting | -|---|---|---|---| -| `cohesion_halo.png` | 512×512 | Gradient radial blanc centre → transparent bord, RGBA | iron-squadron.fr/tts-assets/ | -| `range_bands.png` | 1024×1024 | 4 anneaux concentriques aux rayons relatifs 0.25/0.5/0.75/1.0, gradient discret (pas blend), couleurs jaune/orange/rouge/magenta, alpha 0.6 | iron-squadron.fr/tts-assets/ | -| `range_smoke.png` | 256×256 | 1 anneau à rayon relatif 1.0, blanc opaque | idem | -| `range_token.png` | 256×256 | 1 anneau à rayon relatif 1.0, jaune | idem | -| `range_tokenRangeTwo.png` | 256×256 | 2 anneaux, jaune + orange | idem | -| `range_poi.png` | 256×256 | 1 anneau pour POI (range 0.5 / 3") | idem | -| `range_bombCart.png` | 256×256 | 2 anneaux | idem | -| `max_move_disk.png` | 512×512 | Disque plein bleu ciel #55CCFF α=0.48, RGBA | idem | -| `deployment_red.png` | 256×256 | Carré rouge plein #FF0000 α=0.5 | idem | -| `deployment_blue.png` | 256×256 | Carré bleu plein #0000FF α=0.5 | idem | - -**Génération** : script Python Pillow `generate_overlay_assets.py` qui produit tous les PNG d'un coup. Reproductible, versionnable dans `swlegion-tts/tool/`. - -**Upload** : `rsync` vers le VPS Iron Squadron, dossier `/var/www/iron-squadron/tts-assets/` (ou similaire). URL stable indépendante de Steam. - -## 5. Edge cases et invariants - -| Cas | Gestion | -|---|---| -| Multi-overlays simultanés (plusieurs figs avec cohesion + range) | Cohabitent dans la table `activeOverlays`, un `redrawAll()` au moindre changement | -| Fig détruite (mort en combat) | `onObjectDestroy` → `purgeForFig` → redraw | -| Pickup pendant overlay actif | `onObjectPickUp` → buffer dans `hiddenWhilePickedUp`, retire du rendu | -| Drop après pickup | `onObjectDrop` → restore depuis buffer, redraw (positions recalculées par les builders) | -| Pickup d'un objet sans overlay | Handler skip silencieux (test sur présence dans la table) | -| Reload de partie | `activeOverlays` se reset au boot (variable Lua locale du Global), OK | -| Filtres `standbyTokens` / `removeLockedRulers` (scans `getAllObjects()` par nom) | Deviennent caducs (plus d'Object physique). À supprimer ou laisser comme no-op | -| Test multi-joueurs réseau | Le manager Global est synchronisé par TTS (Lua sync), donc tous les joueurs voient les mêmes overlays | -| Drape impossible sur certains terrains custom | Limitation acceptable (déjà flag par Ben). Le decal plat sert de fallback visuel | - -## 6. Chemin de migration (séquentiel, testable étape par étape) - -1. **Setup Manager Global** : créer `mod/src/includes/Overlays.ttslua`. Aucun caller encore branché. Test : `npm run compile && load mod`, no-op confirmé -2. **Cohesion** (60 lignes, le plus simple) : réécrire `mod/src/includes/Cohesion.ttslua` pour rediriger vers le manager. Test : spawn via hotkey + bouton + dropCoroutine sur Mac -3. **Range Ruler** : réécrire `mod/src/includes/RangeRulers.ttslua` + adapter `POI_Token.lua` + `TokenWithRangeRuler.ttslua`. Test : 4 anneaux + tokens -4. **Maximum Move** : adapter `Order_Token.a57c41.lua:552-574` (spawn inline → call manager) + `clearMovementTemplates`. Test : flow mouvement complet sur Mac -5. **Deployment** : réécrire `SETUP_CONTROLLER.1cb552.lua:243-323`. Test : sélection scenario + spawn de la zone -6. **Cleanup** : supprimer les filtres `standbyTokens` / `removeLockedRulers` pour les types qui ne sont plus des Objects. Garder le filtre pour Movement Template (mesh, toujours Object) -7. **PR sur swlegion/tts** : branche `mac-overlays-refactor`, description du bug + solution + diff. Test communauté - -## 7. Compatibilité conservée - -- **Tous les sites de require existants** continuent à fonctionner sans modif (`require('!/Cohesion')`, `require('!/RangeRulers')`) -- **Toutes les fonctions globales conservent leur signature** (`spawnCohesionRuler(fig)`, `clearRangeRulers()`, etc.) -- **Pas de change d'API publique** → callers (Unit_Leader, Order_Token, POI_Token, etc.) inchangés -- **Variables d'état globales** comme `selectedUnitObj` conservées pour compat avec les filtres et la logique de toggle - -## 8. Risques connus à surveiller - -| Risque | Mitigation | -|---|---| -| Performance multi-overlays (N décals + N×64 vectors) | Tester empiriquement sur table chargée. Si lag, sparse raycast (32 segments) ou skip raycast si table plate détectée | -| Synchronisation decal/vector lines pas pixel-perfect | Vector lines au-dessus du decal (Y légèrement supérieur), tolérance visuelle | -| URL Iron Squadron offline | Hosting sur 2 destinations (VPS + GitHub Pages backup) ou inclure le CDN dans le repo si TTS supporte les data URIs | -| Bug U6 sur les decals aussi (improbable, POC validé le 14 mai) | Garder Path B = ticket Berserk en parallèle | -| Refactor casse un caller obscur | Test exhaustif des 5 sites de spawn + tous les triggers connus | - -## 9. Annexes - -- `materials-reference.md` : tous les params visuels extraits -- `retro-cohesion.md`, `retro-range.md`, `retro-deployment.md`, `retro-movement.md` : déclencheurs et call graphs -- `swlegion-tts/mod/src/includes/` : code source des modules à patcher diff --git a/mac-patcher/extract_projector_specs.py b/mac-patcher/extract_projector_specs.py deleted file mode 100644 index e699f8f1..00000000 --- a/mac-patcher/extract_projector_specs.py +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env python3 -""" -Scan Unity .mat and .prefab YAML files in UnityProject-U6/Assets/Projectors/ -and extract critical parameters for the SWL TTS mod refactor. - -Output: materials-reference.md with structured tables per overlay. -""" -import os -import re -from pathlib import Path -from collections import defaultdict - -ROOT = Path("/Users/martinpourrat/MARTIN/Star Wars Legion/Mod TTS SWL/UnityProject-U6/Assets/Projectors") -OUT = Path("/Users/martinpourrat/MARTIN/Star Wars Legion/Mod TTS SWL/materials-reference.md") - -OVERLAYS = ["Cohesion", "Range", "Deployment", "Movement"] - - -def read(p: Path) -> str: - return p.read_text(encoding="utf-8", errors="replace") - - -def parse_material(path: Path) -> dict: - """Extract critical fields from a Unity .mat YAML file.""" - text = read(path) - out = {"path": str(path.relative_to(ROOT)), "name": path.stem} - - m = re.search(r"m_ShaderKeywords:\s*(.*)", text) - out["keywords"] = m.group(1).strip() if m else "" - - m = re.search(r"m_Shader:\s*\{fileID:\s*\d+,\s*guid:\s*([0-9a-f]+)", text) - out["shader_guid"] = m.group(1) if m else "" - - # Floats — capture key params - floats = {} - for fname in ["_BaseSize", "_ProjectorRadius", "_BandSize", "_BandContrast", - "_MaxRange", "_Arc", "_RangeSize", "_GradScaler", - "_OneTuner", "_TwoTuner", "_ThreeTuner", "_FourTuner", - "_FiveTuner", "_InfinityTuner"]: - m = re.search(rf"-\s*{re.escape(fname)}:\s*(-?[\d.]+)", text) - if m: - floats[fname] = m.group(1) - out["floats"] = floats - - # Colors — capture all _Color* / _Range* - colors = {} - for m in re.finditer(r"-\s*(_\w+):\s*\{r:\s*([\d.]+),\s*g:\s*([\d.]+),\s*b:\s*([\d.]+),\s*a:\s*([\d.]+)\}", text): - name, r, g, b, a = m.groups() - if name in ("_BumpScale",): # not a color - continue - colors[name] = (float(r), float(g), float(b), float(a)) - out["colors"] = colors - - return out - - -def parse_prefab(path: Path) -> dict: - """Extract critical fields from a Unity .prefab YAML file (Projector components).""" - text = read(path) - out = {"path": str(path.relative_to(ROOT)), "name": path.stem} - - # Count Projector components (Unity type id 119) - projectors = re.findall(r"---\s*!u!119\s*&\d+\s*\nProjector:(.*?)(?=\n---|\Z)", text, re.DOTALL) - out["n_projectors"] = len(projectors) - - proj_data = [] - for body in projectors: - d = {} - for fname in ["m_OrthographicSize", "m_FarClipPlane", "m_NearClipPlane", - "m_FieldOfView", "m_AspectRatio", "m_Orthographic"]: - m = re.search(rf"{fname}:\s*(-?[\d.]+)", body) - if m: - d[fname] = m.group(1) - m = re.search(r"m_Material:\s*\{fileID:\s*\d+,\s*guid:\s*([0-9a-f]+)", body) - if m: - d["material_guid"] = m.group(1) - proj_data.append(d) - out["projectors"] = proj_data - - # Transforms (LocalPosition Y, rotation) - transforms = [] - for body in re.findall(r"---\s*!u!4\s*&\d+\s*\nTransform:(.*?)(?=\n---|\Z)", text, re.DOTALL): - d = {} - m = re.search(r"m_LocalPosition:\s*\{x:\s*(-?[\d.]+),\s*y:\s*(-?[\d.]+),\s*z:\s*(-?[\d.]+)\}", body) - if m: - d["pos"] = (float(m.group(1)), float(m.group(2)), float(m.group(3))) - m = re.search(r"m_LocalEulerAnglesHint:\s*\{x:\s*(-?[\d.]+),\s*y:\s*(-?[\d.]+),\s*z:\s*(-?[\d.]+)\}", body) - if m: - d["rot"] = (float(m.group(1)), float(m.group(2)), float(m.group(3))) - transforms.append(d) - out["transforms"] = transforms - - return out - - -def rgba_to_hex(r, g, b, a): - """Convert 0-1 RGBA to hex + alpha note.""" - rh, gh, bh = int(r * 255), int(g * 255), int(b * 255) - return f"#{rh:02X}{gh:02X}{bh:02X} (α={a:.2f})" - - -def fmt_colors(colors): - """Format dict of colors as compact markdown.""" - if not colors: - return "—" - parts = [] - for name, (r, g, b, a) in colors.items(): - if a == 0 and r == 0 and g == 0 and b == 0: - parts.append(f"`{name}` transparent") - else: - parts.append(f"`{name}` {rgba_to_hex(r, g, b, a)}") - return "
".join(parts) - - -def fmt_floats(floats): - """Format dict of floats.""" - if not floats: - return "—" - return ", ".join(f"`{k}`={v}" for k, v in floats.items()) - - -def main(): - materials_by_overlay = defaultdict(list) - prefabs_by_overlay = defaultdict(list) - - for overlay in OVERLAYS: - for p in sorted((ROOT / overlay).rglob("*.mat")): - if p.suffix == ".meta": - continue - materials_by_overlay[overlay].append(parse_material(p)) - for p in sorted((ROOT / overlay).rglob("*.prefab")): - if p.suffix == ".meta": - continue - prefabs_by_overlay[overlay].append(parse_prefab(p)) - - # Build the master doc - lines = [] - lines.append("# Materials & Prefabs Reference — SWL TTS Projector Overlays") - lines.append("") - lines.append("Extraction directe des `.mat` et `.prefab` Unity (YAML) dans `UnityProject-U6/Assets/Projectors/`. Source de vérité pour reproduire le rendu en vector lines.") - lines.append("") - lines.append("Notation : couleurs en hex + alpha. Champs `Floats` clés du shader projector (paramètres procéduraux des anneaux).") - lines.append("") - - for overlay in OVERLAYS: - lines.append(f"## {overlay}") - lines.append("") - - # Materials table - lines.append("### Materials") - lines.append("") - lines.append("| Material | Shader keywords | Floats clés | Couleurs |") - lines.append("|---|---|---|---|") - for m in materials_by_overlay[overlay]: - kw = m["keywords"] or "—" - lines.append(f"| `{m['name']}` | `{kw}` | {fmt_floats(m['floats'])} | {fmt_colors(m['colors'])} |") - lines.append("") - - # Prefabs table - lines.append("### Prefabs") - lines.append("") - lines.append("| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid |") - lines.append("|---|---|---|---|---|") - for p in prefabs_by_overlay[overlay]: - n = p["n_projectors"] - proj_sizes = [pr.get("m_OrthographicSize", "?") for pr in p["projectors"]] - proj_far = [pr.get("m_FarClipPlane", "?") for pr in p["projectors"]] - proj_summary = "
".join(f"size={s}, far={f}" for s, f in zip(proj_sizes, proj_far)) if proj_sizes else "—" - - tr_summary = "
".join( - f"pos=({t.get('pos', ('?',)*3)[0]}, {t.get('pos', ('?',)*3)[1]}, {t.get('pos', ('?',)*3)[2]}) rot=({t.get('rot', ('?',)*3)[0]}, {t.get('rot', ('?',)*3)[1]}, {t.get('rot', ('?',)*3)[2]})" - for t in p["transforms"] - ) if p["transforms"] else "—" - - mat_guids = [pr.get("material_guid", "?") for pr in p["projectors"]] - mat_summary = "
".join(g[:12] + "…" for g in mat_guids) if mat_guids else "—" - - lines.append(f"| `{p['name']}` | {n} | {proj_summary} | {tr_summary} | {mat_summary} |") - lines.append("") - - # Final notes - lines.append("## Notes globales") - lines.append("") - lines.append("- Cohesion : double set `Materials/cohesion_*` et `_Revamp/halfCohesion_*`. Probable : `_Revamp/` est la version actuellement utilisée (le bundle live s'appelle `halfcohesion_27mm.unity3d` cf shader-inventory).") - lines.append("- Range : shader procédural avec jusqu'à 5 bandes (`_RangeOne` à `_RangeFive`) + `_RangeInfinity`. Le keyword `_MAXRANGE_RANGEFIVE` ou `_MAXRANGE_RANGEFOUR` détermine la dernière bande visible.") - lines.append("- Movement : un seul Projector par prefab, paramétré par `_BaseSize` + speed. Le `_Arc` doit contrôler la portion d'arc visible.") - lines.append("- Deployment : 6 materials uniques réutilisés par 12 prefabs (rotations différentes pour matérialiser corner/half/L/round/score).") - - OUT.write_text("\n".join(lines), encoding="utf-8") - print(f"Wrote {OUT}") - # Stats - total_mats = sum(len(v) for v in materials_by_overlay.values()) - total_prefabs = sum(len(v) for v in prefabs_by_overlay.values()) - print(f"Scanned {total_mats} materials and {total_prefabs} prefabs across {len(OVERLAYS)} overlays.") - - -if __name__ == "__main__": - main() diff --git a/mac-patcher/generate_overlay_assets.py b/mac-patcher/generate_overlay_assets.py deleted file mode 100644 index d11b1932..00000000 --- a/mac-patcher/generate_overlay_assets.py +++ /dev/null @@ -1,282 +0,0 @@ -#!/usr/bin/env python3 -""" -Generate PNG assets for the Mac Cohesion + Range overlay fallback. -""" -from pathlib import Path -from PIL import Image, ImageDraw -import math - -OUT_DIR = Path(__file__).parent / "tts-assets" -OUT_DIR.mkdir(exist_ok=True) - - -# Range band colors (R, G, B in 0..255) — from BB_RangeProjector defaults -RANGE_COLORS = { - "half": (255, 255, 255), # 0.5 / 3" — white - "one": (255, 195, 0), # R1 / 6" — yellow - "two": (255, 117, 0), # R2 / 12" — orange - "three": (255, 20, 0), # R3 / 18" — red - "four": (195, 0, 66), # R4 / 24" — magenta - "five": (134, 0, 112), # R5 / 30" — violet -} - -# Configurations per overlay type (bands = list of (radius_inches, color_key)) -# Edge-based convention: each band's effective world radius is band_r + base_r. -RANGE_BAND_CONFIGS = { - "fig_leader": { - "bands": [(3, "half"), (6, "one"), (12, "two"), - (18, "three"), (24, "four"), (30, "five")], - "max_radius": 30, - }, - # SWL ranges in inches: Range 1 = 6", Range 2 = 12", Range 0.5 (poi) = 3". - "smokeToken": {"bands": [(6, "one")], "max_radius": 6}, - "token": {"bands": [(6, "one")], "max_radius": 6}, - "tokenRangeTwo": {"bands": [(6, "one"), (12, "two")], "max_radius": 12}, - "poi": {"bands": [(3, "one")], "max_radius": 3}, - "bombCart": {"bands": [(6, "one"), (12, "two")], "max_radius": 12}, -} - -# Base radii (inches, half-diameter) per ROUND fig base size. Oblong bases -# (long, snail) live in OBLONG_DIMENSIONS_MM below — they don't use a single -# radius and don't generate round PNGs. -FIG_BASE_RADIUS_IN = { - "small": 27 / 2 / 25.4, - "medium": 50 / 2 / 25.4, - "large": 70 / 2 / 25.4, - "huge": 100 / 2 / 25.4, - "laat": 120 / 2 / 25.4, - "epic": 150 / 2 / 25.4, -} - -# Token base radii (inches), only needed for multi-band tokens. -TOKEN_BASE_RADIUS_IN = { - "tokenRangeTwo": 25.1 / 2 / 25.4, - "bombCart": 50.0 / 2 / 25.4, -} - - -def _with_base_offset(config: dict, base_r: float) -> dict: - """Shift band radii outward by base_r and grow max_radius accordingly so - the PNG band proportions match the world positions of the vector-line - rings (which are drawn at band_r + base_r).""" - return { - "bands": [(r + base_r, c) for r, c in config["bands"]], - "max_radius": config["max_radius"] + base_r, - } - - -def generate_cohesion_halo(size: int = 512) -> Path: - """Halo radial blanc fade transparent — reproduit le rendu Cohesion bundle. - - Le shader BB_CohesionProjector fait `rangeOne = (1 - grad)` puis multiplie - par alpha global. On reproduit ce fade : opaque au centre, transparent au - bord. - """ - img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) - pixels = img.load() - cx, cy = size / 2, size / 2 - max_r = size / 2 - - # Alpha global du material Cohesion 27mm = 0.37 ; on multiplie par le fade - base_alpha = 0.37 - - for y in range(size): - for x in range(size): - dx, dy = x - cx, y - cy - dist = math.sqrt(dx * dx + dy * dy) - r_norm = dist / max_r # [0, 1+] depuis le centre - if r_norm > 1.0: - continue # transparent au-delà du rayon - - # Fade radial : (1 - r) avec courbe douce - # Le shader fait (1 - grad) linéaire, on garde la même chose - fade = max(0.0, 1.0 - r_norm) - alpha = int(base_alpha * fade * 255) - pixels[x, y] = (255, 255, 255, alpha) - - out = OUT_DIR / "cohesion_halo.png" - img.save(out) - return out - - -def generate_range_band_png(name: str, config: dict, size: int = 1024, - alpha: float = 0.2) -> Path: - """Generate a circular range overlay with concentric bands. - - Drawing from outer-to-inner: each filled ellipse overlays the previous, - producing clean rings. - """ - img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) - draw = ImageDraw.Draw(img) - cx = cy = size / 2 - max_r_pix = size / 2 - a = int(alpha * 255) - - for band_r, color_key in reversed(config["bands"]): - r_pix = (band_r / config["max_radius"]) * max_r_pix - r, g, b = RANGE_COLORS[color_key] - draw.ellipse( - [(cx - r_pix, cy - r_pix), (cx + r_pix, cy + r_pix)], - fill=(r, g, b, a), - ) - - out = OUT_DIR / f"range_{name}.png" - img.save(out) - return out - - -# Oblong base dimensions (mm). Used to render stadium-shape range overlays -# whose 6 bands stay concentric to a rectangular footprint with rounded caps. -OBLONG_DIMENSIONS_MM = { - "long": {"width": 100, "length": 175}, - "snail": {"width": 100, "length": 200}, -} - - -def generate_range_band_stadium_png(base_size: str, dims_mm: dict, - config: dict, alpha: float = 0.2, - pixels_per_inch: int = 16) -> Path: - """Generate a stadium-shape PNG with 6 concentric range bands. - - The PNG dimensions encode the *outermost* band's bounding box (so the - decal can be stretched in TTS using scale = {shortSpan, longSpan, 1} and - the bands stay aligned with the matching vector stadium outlines). - - A stadium = rounded rectangle whose corner radius equals half the - short dimension, producing a pill shape with semicircular caps on the - long-axis ends. Each band is one rounded_rectangle of width - 2*(halfWid + band_r) and length 2*(halfLen + band_r), corner radius - halfWid + band_r. Drawn outer-to-inner so each band overpaints the next. - """ - halfWid_in = (dims_mm["width"] / 2) / 25.4 - halfLen_in = (dims_mm["length"] / 2) / 25.4 - max_dist = config["max_radius"] - - shortSpan_in = 2 * (halfWid_in + max_dist) - longSpan_in = 2 * (halfLen_in + max_dist) - - W = int(round(shortSpan_in * pixels_per_inch)) - H = int(round(longSpan_in * pixels_per_inch)) - cx, cy = W / 2, H / 2 - - img = Image.new("RGBA", (W, H), (0, 0, 0, 0)) - draw = ImageDraw.Draw(img) - a = int(alpha * 255) - - for band_r, color_key in reversed(config["bands"]): - half_w_px = (halfWid_in + band_r) * pixels_per_inch - half_h_px = (halfLen_in + band_r) * pixels_per_inch - r, g, b = RANGE_COLORS[color_key] - draw.rounded_rectangle( - [(cx - half_w_px, cy - half_h_px), - (cx + half_w_px, cy + half_h_px)], - radius=half_w_px, # = half of the short side → semicircle caps - fill=(r, g, b, a), - ) - - out = OUT_DIR / f"range_fig_leader_{base_size}_stadium.png" - img.save(out) - return out - - -def generate_filled_disk(name: str, color_rgb: tuple, size: int = 512, - alpha: float = 0.2) -> Path: - """Generate a simple filled disk PNG (used for Maximum Move overlay).""" - img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) - draw = ImageDraw.Draw(img) - cx = cy = size / 2 - r = size / 2 - a = int(alpha * 255) - draw.ellipse([(cx - r, cy - r), (cx + r, cy + r)], - fill=(color_rgb[0], color_rgb[1], color_rgb[2], a)) - out = OUT_DIR / f"{name}.png" - img.save(out) - return out - - -def generate_filled_square(name: str, color_rgb: tuple, size: int = 256, - alpha: float = 0.3) -> Path: - """Generate a filled square PNG (used for Deployment Boundary cells).""" - img = Image.new("RGBA", (size, size), - (color_rgb[0], color_rgb[1], color_rgb[2], int(alpha * 255))) - out = OUT_DIR / f"{name}.png" - img.save(out) - return out - - -def generate_silhouette_top_disc(size: int = 512) -> Path: - """Hologram-style top cap for the Mac silhouette cylinder. - - Radial gradient: bright green-cyan center fades to transparent at the - rim. Combined with the vector wireframe cylinder this gives the - Star Wars hologram briefing look (Leia "Help me Obi-Wan" projection). - """ - img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) - pixels = img.load() - cx, cy = size / 2, size / 2 - max_r = size / 2 - - r_center, g_center, b_center = 102, 255, 178 # bright hologram cyan-green - a_center = 0.85 - - for y in range(size): - for x in range(size): - dx, dy = x - cx, y - cy - dist = math.sqrt(dx * dx + dy * dy) - r_norm = dist / max_r - if r_norm > 1.0: - continue - # Smooth radial falloff (1-r squared) for a soft hologram glow - fade = (1.0 - r_norm) ** 1.5 - alpha = int(a_center * fade * 255) - pixels[x, y] = (r_center, g_center, b_center, alpha) - - out = OUT_DIR / "silhouette_top.png" - img.save(out) - return out - - -def main(): - p = generate_cohesion_halo() - print(f"Generated {p} ({p.stat().st_size} bytes)") - - # Fig leaders (round bases): one PNG per base size, bands shifted by base radius. - fig_cfg = RANGE_BAND_CONFIGS["fig_leader"] - for base_size, br in FIG_BASE_RADIUS_IN.items(): - out = generate_range_band_png(f"fig_leader_{base_size}", - _with_base_offset(fig_cfg, br)) - print(f"Generated {out} ({out.stat().st_size} bytes)") - - # Oblong bases: stadium-shape PNGs (rectangular footprint + rounded caps). - for base_size, dims in OBLONG_DIMENSIONS_MM.items(): - out = generate_range_band_stadium_png(base_size, dims, fig_cfg) - print(f"Generated {out} ({out.stat().st_size} bytes)") - - # Multi-band tokens: bake in their base radius for alignment. - for name, br in TOKEN_BASE_RADIUS_IN.items(): - cfg = RANGE_BAND_CONFIGS[name] - out = generate_range_band_png(name, _with_base_offset(cfg, br)) - print(f"Generated {out} ({out.stat().st_size} bytes)") - - # Single-band tokens: no offset adjustment needed (band sits at outer edge). - for name in ("smokeToken", "token", "poi"): - out = generate_range_band_png(name, RANGE_BAND_CONFIGS[name]) - print(f"Generated {out} ({out.stat().st_size} bytes)") - - # Maximum Move: cyan filled disk (#55CCFF) - p = generate_filled_disk("max_move_cyan", (85, 204, 255), alpha=0.2) - print(f"Generated {p} ({p.stat().st_size} bytes)") - - # Deployment Boundary: red and blue filled squares (alpha 0.5 = match bundle) - p = generate_filled_square("deployment_red", (255, 0, 0), alpha=0.3) - print(f"Generated {p} ({p.stat().st_size} bytes)") - p = generate_filled_square("deployment_blue", (0, 0, 255), alpha=0.3) - print(f"Generated {p} ({p.stat().st_size} bytes)") - - # Silhouette top cap: hologram-style green/cyan radial gradient. - p = generate_silhouette_top_disc() - print(f"Generated {p} ({p.stat().st_size} bytes)") - - -if __name__ == "__main__": - main() diff --git a/mac-patcher/inspect_cohesion.py b/mac-patcher/inspect_cohesion.py deleted file mode 100644 index e26becbe..00000000 --- a/mac-patcher/inspect_cohesion.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python3 -"""Inspect Cohesion bundle structure: list objects, materials, shaders, and the m_Shader PPtr.""" -import os -import sys -import UnityPy - -BUNDLE = os.path.expanduser( - "~/Library/Tabletop Simulator/Mods/Assetbundles/" - "httpssteamusercontentaakamaihdnetugc2482129948496305632EBBE2560D4336E6C96317EDDA787E225CF0E5B48.unity3d" -) - -env = UnityPy.load(BUNDLE) - -print("=" * 80) -print("BUNDLE METADATA") -print("=" * 80) -for f in env.files.values(): - print(f" file: {type(f).__name__}") - if hasattr(f, "unity_version"): - print(f" unity_version: {f.unity_version}") - if hasattr(f, "target_platform"): - print(f" target_platform: {f.target_platform}") - if hasattr(f, "externals"): - print(f" externals (cross-bundle refs):") - for ext in getattr(f, "externals", []): - print(f" - {ext}") - -print() -print("=" * 80) -print("ALL OBJECTS") -print("=" * 80) -for obj in env.objects: - path_id = getattr(obj, "path_id", "?") - type_name = obj.type.name - try: - data = obj.read() - name = getattr(data, "m_Name", "") or "" - except Exception as e: - name = f"" - print(f" PathID={path_id:<6} type={type_name:<15} name={name!r}") - -print() -print("=" * 80) -print("MATERIAL DETAILS (looking for Cohesion_27mm)") -print("=" * 80) -for obj in env.objects: - if obj.type.name != "Material": - continue - data = obj.read() - print(f"\nMaterial PathID={obj.path_id} name={data.m_Name!r}") - sref = data.m_Shader - print(f" m_Shader PPtr:") - print(f" file_id = {getattr(sref, 'file_id', '?')} " - f"(0 = internal to this bundle, !=0 = external file)") - print(f" path_id = {getattr(sref, 'path_id', '?')}") - # Try to resolve - try: - sobj = sref.deref() - if sobj: - sd = sobj.read() - sname = ( - getattr(getattr(sd, "m_ParsedForm", None), "m_Name", None) - or getattr(sd, "m_Name", None) - ) - print(f" resolved -> Shader name = {sname!r}") - else: - print(f" resolved -> None (external or missing)") - except Exception as e: - print(f" resolve error: {e!r}") - -print() -print("=" * 80) -print("SHADER DETAILS") -print("=" * 80) -for obj in env.objects: - if obj.type.name != "Shader": - continue - data = obj.read() - sname = ( - getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) - or getattr(data, "m_Name", None) - ) - print(f" PathID={obj.path_id} Shader name={sname!r}") diff --git a/mac-patcher/inspect_externals.py b/mac-patcher/inspect_externals.py deleted file mode 100644 index c9307a21..00000000 --- a/mac-patcher/inspect_externals.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -"""For each bundle: dump its externals (referenced external files) and list materials with external m_Shader PPtr.""" -import os -import sys -import UnityPy - -BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") - -bundles = sorted(f for f in os.listdir(BUNDLE_DIR) if f.endswith(".unity3d")) - -# We are most interested in bundles whose materials use external shaders (file_id != 0) -print("=" * 90) -print("BUNDLES WITH EXTERNAL m_Shader REFERENCES") -print("=" * 90) - -for fname in bundles: - path = os.path.join(BUNDLE_DIR, fname) - short_id = fname.replace("httpssteamusercontentaakamaihdnetugc", "")[:20] - env = UnityPy.load(path) - - external_mats = [] - for obj in env.objects: - if obj.type.name != "Material": - continue - try: - data = obj.read() - sref = data.m_Shader - fid = getattr(sref, "file_id", None) - pid = getattr(sref, "path_id", None) - if fid and fid != 0: - external_mats.append((data.m_Name, fid, pid)) - except Exception: - pass - - if not external_mats: - continue - - print(f"\n{short_id}:") - # Dump externals table - for f in env.files.values(): - if hasattr(f, "externals"): - print(f" externals table:") - for i, ext in enumerate(getattr(f, "externals", [])): - # The externals entry usually has a 'path' and 'guid' - ext_path = getattr(ext, "path", "?") - ext_guid = getattr(ext, "guid", "?") - # index i+1 because file_id is 1-based for externals (file_id=0 means self) - print(f" file_id={i+1} path={ext_path!r} guid={ext_guid}") - print(f" materials with external m_Shader:") - for name, fid, pid in external_mats: - print(f" material={name!r} m_Shader=(file_id={fid}, path_id={pid})") diff --git a/mac-patcher/materials-reference.md b/mac-patcher/materials-reference.md deleted file mode 100644 index eabaae36..00000000 --- a/mac-patcher/materials-reference.md +++ /dev/null @@ -1,170 +0,0 @@ -# Materials & Prefabs Reference — SWL TTS Projector Overlays - -Extraction directe des `.mat` et `.prefab` Unity (YAML) dans `UnityProject-U6/Assets/Projectors/`. Source de vérité pour reproduire le rendu en vector lines. - -Notation : couleurs en hex + alpha. Champs `Floats` clés du shader projector (paramètres procéduraux des anneaux). - -## Cohesion - -### Materials - -| Material | Shader keywords | Floats clés | Couleurs | -|---|---|---|---| -| `Cohesion_27mm` | `_ARC_OFF _MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=4.479488, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.37)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #000000 (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_50mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=5.470629, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_70mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=6.850394, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_27mm` | `_ARC_OFF _MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=3.531496, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.37)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #000000 (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_50mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=3.984252, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_70mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=4.3779526, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | - -### Prefabs - -| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | -|---|---|---|---|---| -| `cohesion_27mm` | 1 | size=4.547244, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | ebe2e870a59f… | -| `cohesion_50mm` | 1 | size=5.669291, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 406845c49cf4… | -| `cohesion_70mm` | 1 | size=6.850394, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3a57b0b27e32… | -| `halfCohesion_27mm` | 1 | size=3.531496, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 0e1c90ce1ef6… | -| `halfCohesion_50mm` | 1 | size=3.984252, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 2e05a24ab516… | -| `halfCohesion_70mm` | 1 | size=4.3779526, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 7cee44cc736f… | - -## Range - -### Materials - -| Material | Shader keywords | Floats clés | Couleurs | -|---|---|---|---| -| `ProjectorMaterial_100mm` | `_ARC_ON _MAXRANGE_RANGEFIVE _RANGE_FIVE` | `_BaseSize`=100, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.0183, `_TwoTuner`=0.0062, `_ThreeTuner`=0.0245, `_FourTuner`=0.0123, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_100mm_oblong` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=47.4, `_ProjectorRadius`=30, `_BandSize`=0.34, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=-0.022, `_TwoTuner`=-0.0421, `_ThreeTuner`=-0.0324, `_FourTuner`=-0.0525, `_FiveTuner`=0.073, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_100mm_snail` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=47.4, `_ProjectorRadius`=30, `_BandSize`=0.34, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=-0.0216, `_TwoTuner`=-0.0421, `_ThreeTuner`=-0.0324, `_FourTuner`=-0.0525, `_FiveTuner`=0.073, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_120mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=120, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.022, `_TwoTuner`=0.0074, `_ThreeTuner`=0.0292, `_FourTuner`=0.0146, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_150mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=150, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.027, `_TwoTuner`=0.009, `_ThreeTuner`=0.0358, `_FourTuner`=0.018, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_18mm_smokeToken` | `_ARC_OFF _MAXRANGE_RANGEONE` | `_BaseSize`=18.8, `_ProjectorRadius`=6, `_BandSize`=-0.11, `_BandContrast`=0.46, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #3A3145 (α=0.59)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_25mm_token` | `_ARC_OFF _MAXRANGE_RANGEONE` | `_BaseSize`=27, `_ProjectorRadius`=2.97, `_BandSize`=-0.45, `_BandContrast`=3, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_25mm_tokenRangeTwo` | `_ARC_OFF _MAXRANGE_RANGETWO` | `_BaseSize`=28.12, `_ProjectorRadius`=14.9, `_BandSize`=0.04, `_BandContrast`=3, `_MaxRange`=1, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0062, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_27mm` | `_ARC_OFF _MAXRANGE_RANGEFIVE` | `_BaseSize`=27, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=0, `_RangeSize`=-76.5, `_GradScaler`=0.0034, `_OneTuner`=0.0053, `_TwoTuner`=0.0019, `_ThreeTuner`=0.0072, `_FourTuner`=0.0037, `_FiveTuner`=0, `_InfinityTuner`=0.0034 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_50mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=50, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_GradScaler`=0, `_OneTuner`=0.0098, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0127, `_FourTuner`=0.0063, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_50mm_bombCart` | `_ARC_OFF _MAXRANGE_RANGETWO` | `_BaseSize`=50, `_ProjectorRadius`=12, `_BandSize`=0.01, `_BandContrast`=3, `_MaxRange`=1, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_70mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=70, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=2.87, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.0132, `_TwoTuner`=0.0045, `_ThreeTuner`=0.0175, `_FourTuner`=0.009, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.35)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `Projector_BoardGuide` | `m_LightmapFlags: 4` | — | `_CameraFadeParams` transparent
`_Color` #FFFFFF (α=1.00)
`_ColorAddSubDiff` transparent
`_EmissionColor` #000000 (α=1.00)
`_SoftParticleFadeParams` transparent
`_TintColor` #7F7F7F (α=0.50) | - -### Prefabs - -| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | -|---|---|---|---|---| -| `projector_100mm` | 1 | size=31.9685, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 4b37ff3d68c5… | -| `projector_100mm_oblong` | 1 | size=33.4, far=45 | pos=(0.0, -1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 6c0556b105e8… | -| `projector_120mm` | 1 | size=32.3622, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | ab767c454926… | -| `projector_150mm` | 1 | size=32.95276, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3a33cb678c17… | -| `projector_200mm_oblong` | 1 | size=33.4, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0)
pos=(-0.0, -0.26, -0.0) rot=(0.0, 0.0, 90.0) | cccc683b4b18… | -| `projector_27mm` | 1 | size=30.5314, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | b1c5ef617632… | -| `projector_50mm` | 1 | size=30.984253, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 20ce39a8e0e1… | -| `projector_70mm` | 1 | size=31.37795, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 977480df799a… | -| `projector_POIGuide` | 1 | size=36, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | a0fb1f64d52e… | -| `projector_bomb_cart` | 1 | size=12.98425, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 417ae88dafb1… | -| `projector_smokeToken` | 1 | size=6.3700786, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3ef1e0f12697… | -| `projector_token` | 1 | size=7.06299, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 94ad9a7a030f… | -| `projector_tokenRangeTwo` | 1 | size=12.6, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 0905fd93d0b7… | - -## Deployment - -### Materials - -| Material | Shader keywords | Floats clés | Couleurs | -|---|---|---|---| -| `Projector_Deployment_Blue` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Deployment_Blue_Round` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Deployment_Red` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Deployment_Red_Round` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Score_Blue` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Score_Red` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | - -### Prefabs - -| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | -|---|---|---|---|---| -| `projector_blue_score` | 1 | size=6, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | fe66dad2ac93… | -| `projector_blue_spawn` | 1 | size=3, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 23c778da5a02… | -| `projector_blue_spawn_L` | 2 | size=1.5, far=25
size=3, far=25 | pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0)
pos=(1.5, 1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(0.0, -1.5000002, 0.0) rot=(0.0, 0.0, 90.0) | 23c778da5a02…
23c778da5a02… | -| `projector_blue_spawn_corner` | 1 | size=1.5, far=25 | pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0)
pos=(-1.5, -1.5, 0.0) rot=(0.0, 0.0, 90.0) | 23c778da5a02… | -| `projector_blue_spawn_half` | 1 | size=3, far=25 | pos=(0.0, -1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0) | 23c778da5a02… | -| `projector_blue_spawn_round2` | 1 | size=12, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 34786d447192… | -| `projector_red_score` | 1 | size=6, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 90fdc53ff83c… | -| `projector_red_spawn` | 1 | size=3, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 93e068805588… | -| `projector_red_spawn_L` | 2 | size=3, far=25
size=1.5, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0)
pos=(0.0, 1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(-1.5, -1.5, -0.0) rot=(0.0, 0.0, 90.0) | 93e068805588…
93e068805588… | -| `projector_red_spawn_corner` | 1 | size=1.5, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0)
pos=(1.5, 1.5, -0.0) rot=(0.0, 0.0, 90.0) | 93e068805588… | -| `projector_red_spawn_half` | 1 | size=3, far=25 | pos=(0.0, 1.5, -0.0) rot=(0.0, 0.0, 90.0)
pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 93e068805588… | -| `projector_red_spawn_round2` | 1 | size=12, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 54824e17d05f… | - -## Movement - -### Materials - -| Material | Shader keywords | Floats clés | Couleurs | -|---|---|---|---| -| `ProjectorMaterial_100mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=8.622047, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_100mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=10.590551, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_100mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=12.559055, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_120mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=9.803149, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_120mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=11.771653, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_120mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.740157, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_150mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=11.574803, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_150mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.543307, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_150mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=15.511811, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed1_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=8.45, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=4.547244, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed2_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=12.42748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=6.515748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed3_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=16.42748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=8.484252, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_50mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=5.6692915, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_50mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=7.6377954, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_50mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=9.606299, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_70mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=6.850394, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_70mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=8.818897, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_70mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=10.787401, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_oblong_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.051181, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_oblong_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=15.019685, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_oblong_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=16.98819, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_snail_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=14.527559, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_snail_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=16.496063, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_snail_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=18.464567, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | - -### Prefabs - -| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | -|---|---|---|---|---| -| `movement_100mm_speed1_single` | 1 | size=8.622047, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 406c1ddb0995… | -| `movement_100mm_speed2_single` | 1 | size=10.590551, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 0aa471d50563… | -| `movement_100mm_speed3_single` | 1 | size=12.559055, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 8fe5008dea10… | -| `movement_120mm_speed1_single` | 1 | size=9.803149, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 21bc9fa53646… | -| `movement_120mm_speed2_single` | 1 | size=11.771653, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 6d02674694a9… | -| `movement_120mm_speed3_single` | 1 | size=13.740157, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 895a4d7a1f02… | -| `movement_150mm_speed1_single` | 1 | size=11.574803, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | fef40c5dadca… | -| `movement_150mm_speed2_single` | 1 | size=13.543307, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | c9d6b007a730… | -| `movement_150mm_speed3_single` | 1 | size=15.511811, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | d08a6fca8013… | -| `movement_27mm_speed1_double` | 1 | size=8.45, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 7037d7245d82… | -| `movement_27mm_speed1_single` | 1 | size=4.547244, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 61da0f6e009e… | -| `movement_27mm_speed2_double` | 1 | size=12.42748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 362084914234… | -| `movement_27mm_speed2_single` | 1 | size=6.515748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 6de57d74ffda… | -| `movement_27mm_speed3_double` | 1 | size=16.42748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | a0dce2ad51d5… | -| `movement_27mm_speed3_single` | 1 | size=8.484252, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | b65b690fde07… | -| `movement_50mm_speed1_single` | 1 | size=5.6692915, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 38f1f5f6a60c… | -| `movement_50mm_speed2_single` | 1 | size=7.6377954, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 3d2f279db06b… | -| `movement_50mm_speed3_single` | 1 | size=9.606299, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | ca54633a2d92… | -| `movement_70mm_speed1_single` | 1 | size=6.850394, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 3d2faeaaaaff… | -| `movement_70mm_speed2_single` | 1 | size=8.818897, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 948d0799a0c3… | -| `movement_70mm_speed3_single` | 1 | size=10.787401, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | a986001c0bc6… | -| `movement_oblong_speed1_single` | 1 | size=13.051181, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | b9540fc3dad6… | -| `movement_oblong_speed2_single` | 1 | size=15.019685, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 672e50dcd47c… | -| `movement_oblong_speed3_single` | 1 | size=16.98819, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | a2468c9fe07d… | -| `movement_snail_speed1_single` | 1 | size=14.527559, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | b7c44d9516fd… | -| `movement_snail_speed2_single` | 1 | size=16.496063, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | d27d5e30c83c… | -| `movement_snail_speed3_single` | 1 | size=18.464567, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | ecdda5f21c4c… | - -## Notes globales - -- Cohesion : double set `Materials/cohesion_*` et `_Revamp/halfCohesion_*`. Probable : `_Revamp/` est la version actuellement utilisée (le bundle live s'appelle `halfcohesion_27mm.unity3d` cf shader-inventory). -- Range : shader procédural avec jusqu'à 5 bandes (`_RangeOne` à `_RangeFive`) + `_RangeInfinity`. Le keyword `_MAXRANGE_RANGEFIVE` ou `_MAXRANGE_RANGEFOUR` détermine la dernière bande visible. -- Movement : un seul Projector par prefab, paramétré par `_BaseSize` + speed. Le `_Arc` doit contrôler la portion d'arc visible. -- Deployment : 6 materials uniques réutilisés par 12 prefabs (rotations différentes pour matérialiser corner/half/L/round/score). \ No newline at end of file diff --git a/mac-patcher/patch_cohesion_rename.py b/mac-patcher/patch_cohesion_rename.py deleted file mode 100644 index 065a14a9..00000000 --- a/mac-patcher/patch_cohesion_rename.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python3 -""" -PATCH ATTEMPT #1 — Rename embedded Shader m_Name from -'BucketheadBits/Projector/Cohesion' to 'Standard'. - -Theory: at AssetBundle.LoadAsset, Unity may resolve the shader by name via -Shader.Find() before falling back to the embedded bytecode. If so, TTS picks -up its own built-in Standard shader and renders the Cohesion projector with -it, ignoring the broken embedded shader. - -Writes to .patched.unity3d alongside the original. Does NOT touch -the live bundle in ~/Library/Tabletop Simulator/Mods/Assetbundles/ yet. -""" -import os -import sys -import shutil -import UnityPy - -BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") -BUNDLE_NAME = "httpssteamusercontentaakamaihdnetugc2482129948496305632EBBE2560D4336E6C96317EDDA787E225CF0E5B48.unity3d" -SRC = os.path.join(BUNDLE_DIR, BUNDLE_NAME) -DST = os.path.join("/tmp", "cohesion.patched.unity3d") - -print(f"Loading: {SRC}") -env = UnityPy.load(SRC) - -modified = False -for obj in env.objects: - if obj.type.name != "Shader": - continue - data = obj.read() - current = getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) or getattr(data, "m_Name", None) - print(f" Found Shader PathID={obj.path_id} current m_Name={current!r}") - if current != "BucketheadBits/Projector/Cohesion": - print(" -> Not our target, skipping.") - continue - - # Modify via typetree (safest cross-version path with UnityPy) - tree = obj.read_typetree() - # m_ParsedForm.m_Name is the canonical name for parsed shaders. - # m_Name may also exist for the asset itself; we try both. - if "m_ParsedForm" in tree and "m_Name" in tree["m_ParsedForm"]: - old = tree["m_ParsedForm"]["m_Name"] - tree["m_ParsedForm"]["m_Name"] = "Standard" - print(f" -> renamed m_ParsedForm.m_Name {old!r} -> 'Standard'") - modified = True - if "m_Name" in tree and tree.get("m_Name"): - old = tree["m_Name"] - tree["m_Name"] = "Standard" - print(f" -> renamed m_Name {old!r} -> 'Standard'") - modified = True - - obj.save_typetree(tree) - -if not modified: - print("ERROR: no shader matched. Aborting.") - sys.exit(1) - -# Write out the patched bundle -print(f"\nSaving patched bundle to: {DST}") -with open(DST, "wb") as f: - f.write(env.file.save(packer="lz4")) - -print(f" size: {os.path.getsize(DST)} bytes (original was {os.path.getsize(SRC)})") - -# Verify roundtrip -print("\nVerifying patched bundle is re-loadable...") -env2 = UnityPy.load(DST) -for obj in env2.objects: - if obj.type.name == "Shader": - d = obj.read() - n = getattr(getattr(d, "m_ParsedForm", None), "m_Name", None) or getattr(d, "m_Name", None) - print(f" After patch: Shader PathID={obj.path_id} m_Name={n!r}") - elif obj.type.name == "Material": - d = obj.read() - sref = d.m_Shader - print(f" Material {d.m_Name!r} m_Shader=(file_id={sref.file_id}, path_id={sref.path_id})") - -print("\nDone. Patched file ready at:", DST) -print("Run this command to replace the live bundle (only when TTS is closed):") -print(f" cp {DST!r} {SRC!r}") diff --git a/mac-patcher/patch_save_for_mac.py b/mac-patcher/patch_save_for_mac.py deleted file mode 100644 index 24bd5e22..00000000 --- a/mac-patcher/patch_save_for_mac.py +++ /dev/null @@ -1,2171 +0,0 @@ -#!/usr/bin/env python3 -""" -Patch a TTS save JSON (SWL mod beta) to add Mac Cohesion fallback. - -Two-part patch: -1. Append manager + event handlers to the Global LuaScript. -2. Replace the inline Cohesion block in Unit_Leader (99f1c8) and Order_Token - (a57c41) with delegated stubs that call the Global manager via Global.call. - -The native hotkey "Show Cohesion On Hovered Model" and the Order Token -"COHESION" button automatically use the patched code afterwards - they -spawn vector-lines rings instead of legacy Custom_AssetBundle Projectors. - -Usage: - python3 patch_save_for_mac.py -""" -import json -import re -import socket -import sys -from pathlib import Path - -# ------------------------------------------------------------ -# 1) Code appended to Global LuaScript (manager + event handlers) -# ------------------------------------------------------------ -GLOBAL_PATCH_LUA = r""" - --- ============================================ --- MAC COHESION FALLBACK (auto-injected for TTS U6 magenta bug) --- Manager Global + event handlers + vector-lines rendering. --- Called by Unit_Leader / Order_Token via Global.call from their patched --- Cohesion functions. --- ============================================ - --- Capture the ORIGINAL Global functions loaded by !/RangeRulers and --- !/Cohesion BEFORE we overwrite them below. We also capture the inner --- spawn/clear pair because the toggle wrapper resolves them by NAME at --- call time - without aliasing those too, the original toggle would route --- back into our Mac versions, defeating Windows mode entirely. -spawnRangeRulerOriginalGlobal = spawnRangeRuler -clearRangeRulersOriginalGlobal = clearRangeRulers -spawnCohesionRulerOriginalGlobal = spawnCohesionRuler -clearCohesionRulerOriginalGlobal = clearCohesionRuler - -activeOverlays = activeOverlays or {} -hiddenWhilePickedUp = hiddenWhilePickedUp or {} - -local ASSETS_BASE = "https://raw.githubusercontent.com/ironsquadronfr-hub/tts/mac-projector-fallback/mod/data/mac-fallback-assets/" -local COHESION_HALO_URL = ASSETS_BASE .. "cohesion_halo.png" -local MAX_MOVE_CYAN_URL = ASSETS_BASE .. "max_move_cyan.png" -local DEPLOYMENT_RED_URL = ASSETS_BASE .. "deployment_red.png" -local DEPLOYMENT_BLUE_URL = ASSETS_BASE .. "deployment_blue.png" -local RANGE_DECAL_URLS = { - smokeToken = ASSETS_BASE .. "range_smokeToken.png", - token = ASSETS_BASE .. "range_token.png", - tokenRangeTwo = ASSETS_BASE .. "range_tokenRangeTwo.png", - poi = ASSETS_BASE .. "range_poi.png", - bombCart = ASSETS_BASE .. "range_bombCart.png", - -- Fig leaders: per-base-size PNGs (bands pre-offset by the base radius so - -- the decal bands line up with the vector-line rings). - fig_leader = { - small = ASSETS_BASE .. "range_fig_leader_small.png", - medium = ASSETS_BASE .. "range_fig_leader_medium.png", - large = ASSETS_BASE .. "range_fig_leader_large.png", - huge = ASSETS_BASE .. "range_fig_leader_huge.png", - laat = ASSETS_BASE .. "range_fig_leader_laat.png", - epic = ASSETS_BASE .. "range_fig_leader_epic.png", - long = ASSETS_BASE .. "range_fig_leader_long_stadium.png", - snail = ASSETS_BASE .. "range_fig_leader_snail_stadium.png", - }, -} -local RANGE_MAX_RADIUS = { - fig_leader = 30, -- R5 max - -- SWL: Range 1 = 6", Range 2 = 12", Range 0.5 = 3". - smokeToken = 6, - token = 6, - tokenRangeTwo = 12, - poi = 3, - bombCart = 12, -} - --- Range band colors (RGBA) extracted from BB_RangeProjector material defaults --- + demi-portee blanche pour SWL Range 0.5 (3 inches) -local RANGE_COLORS = { - half = {1.0, 1.0, 1.0, 0.6}, -- 0.5 = 3" white - one = {1.0, 0.764, 0.0, 0.7}, -- R1 yellow - two = {1.0, 0.459, 0.0, 0.7}, -- R2 orange - three = {1.0, 0.079, 0.0, 0.7}, -- R3 red - four = {0.764, 0.0, 0.259, 0.7}, -- R4 magenta - five = {0.528, 0.0, 0.443, 0.7}, -- R5 violet -} - --- Per-rangeKey configurations: list of {radius_inches, color_name} -local RANGE_CONFIGS = { - -- Fig leaders: half + 4 bands at SWL standard ranges 3/6/12/18/24" - small = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - medium = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - large = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - huge = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - laat = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - epic = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - long = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - snail = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - -- Token rangeKeys (1 to 2 bands depending on use). SWL ranges in inches: - -- Range 1 = 6", Range 2 = 12", Range 0.5 (poi) = 3". - smokeToken = {{r=6,c="one"}}, - token = {{r=6,c="one"}}, - tokenRangeTwo = {{r=6,c="one"},{r=12,c="two"}}, - poi = {{r=3,c="one"}}, - bombCart = {{r=6,c="one"},{r=12,c="two"}}, -} - --- Base footprint dimensions in mm. width <= length; equal means round. --- Used to drive round vs stadium overlay shape. -local BASE_DIMENSIONS = { - small = {width = 27, length = 27}, - medium = {width = 50, length = 50}, - large = {width = 70, length = 70}, - huge = {width = 100, length = 100}, - laat = {width = 120, length = 120}, - epic = {width = 150, length = 150}, - long = {width = 100, length = 175}, -- oblong - snail = {width = 100, length = 200}, -- oblong -} - --- Base radius (inches) for round overlays. For oblong bases we use a --- stadium shape instead (see macBuildStadium), so the radius here is the --- half-width that drives the cap arcs. -local FIG_BASE_RADIUS_IN = {} -for k, d in pairs(BASE_DIMENSIONS) do - FIG_BASE_RADIUS_IN[k] = (d.width / 2) / 25.4 -end - -local function macIsOblong(baseSize) - local d = BASE_DIMENSIONS[baseSize] - return d ~= nil and d.length > d.width -end - --- Returns the stadium half-dimensions (inches) and yaw for an oblong base, --- or nil if the fig's base is round. Callers should branch on the return. -local function macOblongDims(fig, baseSize) - if not macIsOblong(baseSize) then return nil end - local d = BASE_DIMENSIONS[baseSize] - return { - halfLen = (d.length / 2) / 25.4, - halfWid = (d.width / 2) / 25.4, - rotY = fig.getRotation().y, - } -end - --- Resolve baseSize for a fig. The mod stores it as a script-local Var on the --- fig itself (set at spawn from the unit's container). unitData.baseSize is --- only populated on the Unit Leader, not on minis/vehicles - so getVar is --- the canonical read, with unitData as fallback for safety. -local function macGetBaseSize(fig) - if not fig then return nil end - local ok, bs = pcall(function() return fig.getVar("baseSize") end) - if ok and bs then return bs end - local data = fig.getTable("unitData") - if data and data.baseSize then return data.baseSize end - return nil -end - -local function macFigBaseRadius(fig) - if not fig then return 0.5315 end - local bs = macGetBaseSize(fig) - return (bs and FIG_BASE_RADIUS_IN[bs]) or 0.5315 -end - --- Token base radii (inches) - measured from token edge for range bands -local TOKEN_BASE_RADIUS = { - smokeToken = 18.8 / 2 / 25.4, -- 0.370" - token = 25.1 / 2 / 25.4, -- 0.494" - tokenRangeTwo = 25.1 / 2 / 25.4, -- 0.494" - poi = 50.8 / 2 / 25.4, -- 1.000" - bombCart = 50.0 / 2 / 25.4, -- 0.984" -} - --- Hotkey-driven range on tokens renders fig-leader-style bands using the --- closest equivalent base size (so the visual matches what a unit of that --- footprint would see). Token R button keeps the single ring (token-spec). -local TOKEN_TO_BASESIZE = { - smokeToken = "small", - token = "small", - tokenRangeTwo = "small", - poi = "medium", - bombCart = "medium", -} - --- Maximum Move radii (inches), indexed by baseSize then speed 1..3 --- Source: ProjectorRadius from BB_MovementProjector materials (single variants) -local MAX_MOVE_RADIUS = { - small = { 4.547244, 6.515748, 8.484252}, -- 27mm - medium = { 5.669292, 7.637795, 9.606299}, -- 50mm - large = { 6.850394, 8.818897, 10.787401}, -- 70mm - huge = { 8.622047, 10.590551, 12.559055}, -- 100mm - laat = { 9.803149, 11.771653, 13.740157}, -- 120mm - epic = {11.574803, 13.543307, 15.511811}, -- 150mm - long = {13.051181, 15.019685, 16.988190}, -- oblong - snail = {14.527559, 16.496063, 18.464567}, -- snail -} - --- Deployment cell dimensions (width X, depth Z, inches). --- All cells live on a 6"x6" grid; sub-cells are halves/quarters of that cell. -local DEPLOYMENT_CELL_SIZES = { - r = {6, 6}, b = {6, 6}, -- full cell - rl = {6, 6}, bl = {6, 6}, -- large variant (also full cell) - rh = {6, 3}, bh = {6, 3}, -- horizontal half (3" deep) - rs = {3, 6}, bs = {3, 6}, -- vertical half (3" wide), x-offset +1.5 - rss = {3, 6}, bss = {3, 6}, -- vertical half (3" wide), x-offset -1.5 - rc = {3, 3}, bc = {3, 3}, -- corner quarter - rcc = {3, 3}, bcc = {3, 3}, -- opposite corner quarter -} - -local function macRayGroundY(x, z, offset) - local hits = Physics.cast({ - origin = {x, 30, z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 50, - }) - for _, h in ipairs(hits) do - return h.point.y + (offset or 0.05) - end - return offset or 0.05 -end - -local function macBuildRect(cx, cz, hw, hd, color, thickness) - local segPerSide = 8 - local pts = {} - local corners = { - {cx - hw, cz + hd}, {cx + hw, cz + hd}, - {cx + hw, cz - hd}, {cx - hw, cz - hd}, - } - for i = 1, 4 do - local a = corners[i] - local b = corners[i % 4 + 1] - for s = 0, segPerSide - 1 do - local t = s / segPerSide - local x = a[1] + (b[1] - a[1]) * t - local z = a[2] + (b[2] - a[2]) * t - table.insert(pts, {x, macRayGroundY(x, z), z}) - end - end - local c1 = corners[1] - table.insert(pts, {c1[1], macRayGroundY(c1[1], c1[2]), c1[2]}) - return {points = pts, color = color, thickness = thickness or 0.06} -end - -local function macBuildRing(centerPos, radius, color, thickness, ignoreObj) - local nSeg = 64 - local pts = {} - for i = 0, nSeg do - local a = (i / nSeg) * 2 * math.pi - local x = centerPos.x + radius * math.cos(a) - local z = centerPos.z + radius * math.sin(a) - local hits = Physics.cast({ - origin = {x, centerPos.y + 10, z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local y = centerPos.y + 0.05 - for _, h in ipairs(hits) do - if h.hit_object ~= ignoreObj then - y = h.point.y + 0.05 - break - end - end - table.insert(pts, {x, y, z}) - end - return {points = pts, color = color, thickness = thickness or 0.05} -end - --- Stadium = rectangle with two semicircular caps on the long-axis ends. --- halfLen, halfWid in inches (halfLen >= halfWid). dist = expansion outwards --- from the base edge in inches (so the overlay sits at dist outside the base). --- rotYdeg rotates the stadium around Y to match the fig's orientation; the --- long axis is local Z (front-to-back of the model). -local function macBuildStadium(centerPos, halfLen, halfWid, rotYdeg, dist, color, thickness, ignoreObj) - local rotRad = math.rad(rotYdeg or 0) - local cosR, sinR = math.cos(rotRad), math.sin(rotRad) - - local capCenter = halfLen - halfWid -- distance from center to cap arc center, along long axis - local capR = halfWid + dist -- cap arc radius (= straight-side offset from long axis) - - local nSegArc = 32 -- segments per semicircle cap - local pts = {} - - local function addLocal(lx, lz) - local wx = centerPos.x + lx * cosR + lz * sinR - local wz = centerPos.z - lx * sinR + lz * cosR - local hits = Physics.cast({ - origin = {wx, centerPos.y + 10, wz}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local y = centerPos.y + 0.05 - for _, h in ipairs(hits) do - if h.hit_object ~= ignoreObj then - y = h.point.y + 0.05 - break - end - end - table.insert(pts, {wx, y, wz}) - end - - -- Top cap: angle 0 to pi (right edge -> top -> left edge), centered at z=+capCenter - for i = 0, nSegArc do - local a = (i / nSegArc) * math.pi - addLocal(capR * math.cos(a), capCenter + capR * math.sin(a)) - end - -- The straight left side is implicit: the polyline jumps from - -- (-capR, +capCenter) directly to (-capR, -capCenter) - TTS draws a - -- straight segment between consecutive points. - -- Bottom cap: angle pi to 2pi, centered at z=-capCenter - for i = 0, nSegArc do - local a = math.pi + (i / nSegArc) * math.pi - addLocal(capR * math.cos(a), -capCenter + capR * math.sin(a)) - end - -- Close the loop back to the top-cap start point. - addLocal(capR, capCenter) - - return {points = pts, color = color, thickness = thickness or 0.05} -end - -local macBuilders = {} - -macBuilders.range = function(fig, params) - local pos = fig.getPosition() - -- Resolve rangeKey for token-specific single-ring rendering (R button on - -- a token). The hotkey path sets params.forceFigMode = true to bypass - -- this and always render the fig-leader 6-band overlay, using the token's - -- equivalent baseSize for sizing. - local forceFig = params and params.forceFigMode - local rangeKey = (not forceFig) and ((params and params.rangeKey) or fig.getVar("rangeKey")) or nil - local config, decalURL, maxR, br - local oblong, halfLen, halfWid, rotY - local baseSize -- hoisted: used below for firing-arc decision - if rangeKey and RANGE_CONFIGS[rangeKey] then - config = RANGE_CONFIGS[rangeKey] - decalURL = RANGE_DECAL_URLS[rangeKey] - maxR = RANGE_MAX_RADIUS[rangeKey] - br = TOKEN_BASE_RADIUS[rangeKey] or 0 - else - baseSize = macGetBaseSize(fig) - local hasBaseSize = baseSize ~= nil - if not hasBaseSize then - -- Token hit from hotkey: map rangeKey to base size for sizing. - local tk = fig.getVar("rangeKey") - baseSize = (tk and TOKEN_TO_BASESIZE[tk]) or "small" - end - config = RANGE_CONFIGS[baseSize] or RANGE_CONFIGS.small - decalURL = RANGE_DECAL_URLS.fig_leader[baseSize] - or RANGE_DECAL_URLS.fig_leader.small - maxR = RANGE_MAX_RADIUS.fig_leader - if hasBaseSize then - br = macFigBaseRadius(fig) - local od = macOblongDims(fig, baseSize) - if od then - oblong, halfLen, halfWid, rotY = true, od.halfLen, od.halfWid, od.rotY - end - else - -- Token in forceFig mode: use token's measured radius for offset. - local tk = fig.getVar("rangeKey") - br = (tk and TOKEN_BASE_RADIUS[tk]) or FIG_BASE_RADIUS_IN[baseSize] or 0.5315 - end - end - - -- SWL convention: all distances are measured from the base/token edge. - - -- Vector contours: 6 concentric rings for round bases, 6 concentric - -- stadium outlines for oblong bases (each band sits at band.r outwards - -- from the base edge - same rule, just a different shape). - local lines = {} - for _, band in ipairs(config) do - if oblong then - table.insert(lines, macBuildStadium(pos, halfLen, halfWid, rotY, band.r, - RANGE_COLORS[band.c], 0.1, fig)) - else - table.insert(lines, macBuildRing(pos, band.r + br, RANGE_COLORS[band.c], 0.1, fig)) - end - end - - -- Firing arc lines (4 radial lines dividing into Front/R/Rear/L quadrants). - -- Mirrors the vanilla Projector shader: ProjectorMaterial_ sets - -- _Arc=1 / shader keyword _ARC_ON for every non-27mm base (medium, large, - -- huge, laat, epic, long, snail). 27mm sets _Arc=0. - -- Round bases : lines emanate from the base edge, radial from center. - -- Oblong bases: lines emanate from the centers of the two end - -- semicircles (front cap + rear cap), front lines at +/-45 from the - -- long-axis forward, rear lines at +/-135. - local arcColor = {1, 1, 1, 0.6} - if baseSize and baseSize ~= "small" then - if oblong then - local capOffset = (halfLen or 0) - (halfWid or 0) - local rad = math.rad(rotY) - local fwdX, fwdZ = math.sin(rad), math.cos(rad) - local frontX = pos.x + capOffset * fwdX - local frontZ = pos.z + capOffset * fwdZ - local rearX = pos.x - capOffset * fwdX - local rearZ = pos.z - capOffset * fwdZ - local reach = (halfWid or 0) + (maxR or 6) - local arcs = { - {frontX, frontZ, 45}, -- front-right boundary - {frontX, frontZ, -45}, -- front-left boundary - {rearX, rearZ, 135}, -- rear-right boundary - {rearX, rearZ, -135}, -- rear-left boundary - } - for _, e in ipairs(arcs) do - local cx, cz, deg = e[1], e[2], e[3] - local a = math.rad(rotY + deg) - local sinA, cosA = math.sin(a), math.cos(a) - table.insert(lines, { - points = { - {cx, pos.y + 0.1, cz}, - {cx + reach * sinA, pos.y + 0.1, cz + reach * cosA}, - }, - color = arcColor, - thickness = 0.05, - }) - end - else - local figRotY = fig.getRotation().y - local outerR = (maxR or 6) + br - for _, deg in ipairs({45, 135, 225, 315}) do - local a = math.rad(deg + figRotY) - local sinA, cosA = math.sin(a), math.cos(a) - table.insert(lines, { - points = { - {pos.x + br * sinA, pos.y + 0.1, pos.z + br * cosA}, - {pos.x + outerR * sinA, pos.y + 0.1, pos.z + outerR * cosA}, - }, - color = arcColor, - thickness = 0.05, - }) - end - end - end - - -- Decal halo (filled bands) - flat on the ground under the fig - local hits = Physics.cast({ - origin = {pos.x, pos.y + 10, pos.z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local groundY = pos.y - for _, h in ipairs(hits) do - if h.hit_object ~= fig then groundY = h.point.y; break end - end - - if oblong then - -- Stadium-shape PNGs are authored per oblong base size: the 6 bands - -- inside the PNG already match the stadium vector outlines because - -- the PNG was generated with the same halfWid/halfLen + dist math. - -- Decal scale = footprint of the outermost band (2*(half + maxR)) - -- in each axis, so the PNG stretches to exactly the right size. - local longSpan = 2 * (halfLen + maxR) - local shortSpan = 2 * (halfWid + maxR) - return { - lines = lines, - decals = { - { - name = "range_decal_" .. fig.getGUID(), - url = decalURL, - position = {pos.x, groundY + 0.02, pos.z}, - rotation = {90, rotY, 0}, - scale = {shortSpan, longSpan, 1}, - } - }, - } - end - - local effectiveMaxR = maxR + br - return { - lines = lines, - decals = { - { - name = "range_decal_" .. fig.getGUID(), - url = decalURL, - position = {pos.x, groundY + 0.02, pos.z}, -- below cohesion halo - rotation = {90, 0, 0}, - scale = {effectiveMaxR * 2, effectiveMaxR * 2, effectiveMaxR * 2}, - } - }, - } -end - -macBuilders.maxmove = function(fig, params) - -- Use the anchor position stored at spawn time so the ring stays put - -- when the fig slides toward the destination, instead of tracking it. - local pos = (params and params.anchorPos) or fig.getPosition() - local baseSize = (params and params.baseSize) or "small" - local speed = (params and params.speed) or 1 - local radii = MAX_MOVE_RADIUS[baseSize] or MAX_MOVE_RADIUS.small - local r = radii[speed] or radii[1] - local color = {0.333, 0.800, 1.000, 0.7} - -- Base ring: small white outline matching the figure's base - -- (BB_MovementProjector renders a base ring on top of the cyan max-move). - local br = FIG_BASE_RADIUS_IN[baseSize] or FIG_BASE_RADIUS_IN.small - local baseColor = {1, 1, 1, 0.8} - -- Oblong base dimensions for the stadium base ring. Use the rotation - -- captured at spawn-time so the ring keeps its orientation even after - -- the fig pivots toward its destination. - local od = macOblongDims(fig, baseSize) - local oblong = od ~= nil - local halfLen, halfWid, rotY - if oblong then - halfLen, halfWid = od.halfLen, od.halfWid - rotY = (params and params.anchorRot) or od.rotY - end - - -- Ground raycast for decal position - local hits = Physics.cast({ - origin = {pos.x, pos.y + 10, pos.z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local groundY = pos.y - for _, h in ipairs(hits) do - if h.hit_object ~= fig then groundY = h.point.y; break end - end - - if oblong then - -- Oblong base: the cyan max-move envelope stays a round ring (isotropic - -- - any rotation reaches the same radius from the pivot), but the - -- inner white base ring follows the rectangular footprint as a - -- stadium hugging the actual base edge. - return { - lines = { - macBuildRing(pos, r, color, 0.1, fig), - macBuildStadium(pos, halfLen, halfWid, rotY, 0, - baseColor, 0.06, fig), - }, - decals = { - { - name = "maxmove_decal_" .. fig.getGUID(), - url = MAX_MOVE_CYAN_URL, - position = {pos.x, groundY + 0.025, pos.z}, - rotation = {90, 0, 0}, - scale = {r * 2, r * 2, r * 2}, - } - }, - } - end - - return { - lines = { - macBuildRing(pos, r, color, 0.1, fig), -- outer cyan max-move - macBuildRing(pos, br, baseColor, 0.06, fig), -- inner white base ring - }, - decals = { - { - name = "maxmove_decal_" .. fig.getGUID(), - url = MAX_MOVE_CYAN_URL, - position = {pos.x, groundY + 0.025, pos.z}, - rotation = {90, 0, 0}, - scale = {r * 2, r * 2, r * 2}, - } - }, - } -end - -macBuilders.deployment = function(_, params) - local pos = params.pos -- {x, y, z} cell center (after offset) - local cell = params.cell -- "r"/"b"/"rh"/... key - local size = DEPLOYMENT_CELL_SIZES[cell] or {6, 6} - local w, d = size[1], size[2] - local cx, cz = pos[1], pos[3] - local hw, hd = w / 2, d / 2 - - local isRed = cell:sub(1, 1) == "r" - local lineColor = isRed and {1, 0.15, 0.15, 0.9} or {0.15, 0.4, 1, 0.9} - local url = isRed and DEPLOYMENT_RED_URL or DEPLOYMENT_BLUE_URL - - local groundY = macRayGroundY(cx, cz, 0) - - return { - lines = { macBuildRect(cx, cz, hw, hd, lineColor, 0.08) }, - decals = { - { - name = "deployment_decal_" .. cell .. "_" .. tostring(cx) .. "_" .. tostring(cz), - url = url, - position = {cx, groundY + 0.03, cz}, - rotation = {90, 0, 0}, - scale = {w, d, 1}, - } - }, - } -end - -macBuilders.cohesion = function(fig, params) - local pos = fig.getPosition() - -- Cohesion overlay = base edge + 3 inches (half Range 1). Round bases get - -- a ring; oblong bases (long/snail) get a stadium-shape concentric to the - -- footprint so the offset stays a constant 3" from the edge. - local rangeKey = fig.getVar("rangeKey") - local oblong, halfLen, halfWid, rotY, r - if rangeKey and TOKEN_BASE_RADIUS[rangeKey] then - r = TOKEN_BASE_RADIUS[rangeKey] + 3.0 - else - local baseSize = macGetBaseSize(fig) or "small" - local od = macOblongDims(fig, baseSize) - if od then - oblong, halfLen, halfWid, rotY = true, od.halfLen, od.halfWid, od.rotY - else - r = (FIG_BASE_RADIUS_IN[baseSize] or FIG_BASE_RADIUS_IN.small) + 3.0 - end - end - if params and params.radius then - oblong, r = false, params.radius - end - - -- Find ground level under the fig so the flat decal sits on the table, - -- not at the fig pivot (which is above the base). - local hits = Physics.cast({ - origin = {pos.x, pos.y + 10, pos.z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local groundY = pos.y - for _, h in ipairs(hits) do - if h.hit_object ~= fig then - groundY = h.point.y - break - end - end - - if oblong then - -- Halo decal: stretch the round halo PNG into an ellipse aligned with - -- the fig's long axis. Approximates the stadium fade (a true stadium - -- halo would need a custom PNG); good enough visually since the - -- vector stadium outline carries the precise boundary. - local longSpan = 2 * (halfLen + 3.0) - local shortSpan = 2 * (halfWid + 3.0) - return { - lines = { macBuildStadium(pos, halfLen, halfWid, rotY, 3.0, - {1, 1, 1, 0.8}, 0.06, fig) }, - decals = { - { - name = "cohesion_halo_" .. fig.getGUID(), - url = COHESION_HALO_URL, - position = {pos.x, groundY + 0.04, pos.z}, - -- 90 deg pitch lays the decal flat on the table; rotY (yaw) - -- aligns its local Y with the fig's long axis. - rotation = {90, rotY, 0}, - scale = {shortSpan, longSpan, 1}, - } - }, - } - end - - return { - lines = { macBuildRing(pos, r, {1, 1, 1, 0.8}, 0.06, fig) }, - decals = { - { - name = "cohesion_halo_" .. fig.getGUID(), - url = COHESION_HALO_URL, - position = {pos.x, groundY + 0.04, pos.z}, - rotation = {90, 0, 0}, - scale = {r * 2, r * 2, r * 2}, - } - }, - } -end - -function macRedrawAll() - local lines, decals = {}, {} - -- Preload all texture URLs (invisible decals far below the table) so TTS - -- keeps them cached and we never get the white-square flash on respawn. - local preloadURLs = { - COHESION_HALO_URL, - RANGE_DECAL_URLS.smokeToken, - RANGE_DECAL_URLS.token, RANGE_DECAL_URLS.tokenRangeTwo, - RANGE_DECAL_URLS.poi, RANGE_DECAL_URLS.bombCart, - RANGE_DECAL_URLS.fig_leader.small, RANGE_DECAL_URLS.fig_leader.medium, - RANGE_DECAL_URLS.fig_leader.large, RANGE_DECAL_URLS.fig_leader.huge, - RANGE_DECAL_URLS.fig_leader.laat, RANGE_DECAL_URLS.fig_leader.epic, - RANGE_DECAL_URLS.fig_leader.long, RANGE_DECAL_URLS.fig_leader.snail, - MAX_MOVE_CYAN_URL, - DEPLOYMENT_RED_URL, DEPLOYMENT_BLUE_URL, - } - for i, url in ipairs(preloadURLs) do - table.insert(decals, { - name = "preload_" .. i, - url = url, - position = {0, -200 - i * 0.01, 0}, - rotation = {90, 0, 0}, - scale = {0.01, 0.01, 0.01}, - }) - end - local stale = {} - for key, entry in pairs(activeOverlays) do - local b = macBuilders[entry.type] - if b then - -- Deployment entries have no fig (params-driven). Others need a - -- live Object - drop them silently if destroyed/invalid. - local needsFig = (entry.type ~= "deployment") - if needsFig and (not entry.fig - or type(entry.fig.getPosition) ~= "function") then - stale[#stale + 1] = key - else - local ok, out = pcall(b, entry.fig, entry.params) - if ok and out then - for _, l in ipairs(out.lines or {}) do - table.insert(lines, l) - end - for _, d in ipairs(out.decals or {}) do - table.insert(decals, d) - end - else - stale[#stale + 1] = key - end - end - end - end - for _, k in ipairs(stale) do activeOverlays[k] = nil end - Global.setVectorLines(lines) - Global.setDecals(decals) -end - -function gSpawnCohesion(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":cohesion"] = { - type = "cohesion", fig = fig, params = params or {} - } - macRedrawAll() -end - -function gClearCohesion(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":cohesion"] = nil - macRedrawAll() -end - -function gToggleCohesion(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local key = fig.getGUID() .. ":cohesion" - if activeOverlays[key] then - gClearCohesion(params) - else - gSpawnCohesion(params) - end -end - --- Range polling: a slow timer (5 frames = ~12 fps) that re-draws active --- range rulers so they follow figures in real-time. Stops automatically when --- no range overlay is active anymore - avoids the overnight memory leak. -macRangePollingActive = macRangePollingActive or false - -function macRangePoll() - local hasRange = false - for _, e in pairs(activeOverlays) do - if e.type == "range" then hasRange = true; break end - end - if hasRange then - macRedrawAll() - Wait.frames(macRangePoll, 5) - else - macRangePollingActive = false - end -end - -function gSpawnRange(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":range"] = { - type = "range", fig = fig, params = params or {} - } - macRedrawAll() - if not macRangePollingActive then - macRangePollingActive = true - Wait.frames(macRangePoll, 5) - end -end - -function gClearRange(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":range"] = nil - macRedrawAll() -end - -function gToggleRange(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local key = fig.getGUID() .. ":range" - if activeOverlays[key] then - gClearRange(params) - else - gSpawnRange(params) - end -end - -function gSpawnDeployment(params) - -- params: { cell = "r" | "b" | ..., pos = {x, y, z} } - if not params or not params.cell or not params.pos then return end - local key = "deploy:" .. params.cell .. ":" - .. tostring(params.pos[1]) .. ":" .. tostring(params.pos[3]) - activeOverlays[key] = {type = "deployment", fig = nil, params = params} - macRedrawAll() -end - -function gClearAllDeployment() - for k, e in pairs(activeOverlays) do - if e.type == "deployment" then activeOverlays[k] = nil end - end - macRedrawAll() -end - -function gSpawnMaxMove(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - -- Capture the spawn-time position + yaw. MaxMove is anchored to where - -- the move STARTED - it must not follow the fig as it slides toward - -- the destination. The yaw matters for oblong stadium overlays so the - -- white base ring keeps its orientation while the fig pivots. - local p = (params and params.params) or params or {} - p.anchorPos = fig.getPosition() - p.anchorRot = fig.getRotation().y - activeOverlays[fig.getGUID() .. ":maxmove"] = { - type = "maxmove", fig = fig, params = p - } - macRedrawAll() -end - -function gClearMaxMove(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":maxmove"] = nil - macRedrawAll() -end - -function onObjectPickUp(player_color, obj) - if not obj or not obj.getGUID then return end - local guid = obj.getGUID() - local prefix = guid .. ":" - local plen = #prefix - local changed = false - for key, entry in pairs(activeOverlays) do - if key:sub(1, plen) == prefix then - -- Cohesion: static, hide during pickup, restore on drop. - -- Range: keep visible; the polling timer redraws each tick so - -- the ruler follows the figure in real-time during the drag. - if entry.type == "cohesion" then - hiddenWhilePickedUp[key] = entry - activeOverlays[key] = nil - changed = true - end - end - end - if changed then macRedrawAll() end -end - -function onObjectDrop(player_color, obj) - if not obj or not obj.getGUID then return end - local guid = obj.getGUID() - local prefix = guid .. ":" - local plen = #prefix - - -- Defer the overlay restore until the fig has come to rest. Without this, - -- a flick-drop with residual inertia leaves the overlay anchored at the - -- release position while the fig continues to slide across the table. - local function tryRestore() - local fig = getObjectFromGUID(guid) - if not fig then return end -- destroyed during slide - if fig.held_by_color then return end -- picked up again, hold off - local v = fig.getVelocity and fig.getVelocity() - if v then - local speed = math.sqrt((v.x or 0)^2 + (v.y or 0)^2 + (v.z or 0)^2) - if speed > 0.05 then - Wait.frames(tryRestore, 3) - return - end - end - local changed = false - for key, entry in pairs(hiddenWhilePickedUp) do - if key:sub(1, plen) == prefix then - activeOverlays[key] = entry - hiddenWhilePickedUp[key] = nil - changed = true - end - end - if changed then macRedrawAll() end - end - tryRestore() -end - -function onObjectDestroy(obj) - if not obj or not obj.getGUID then return end - local guid = obj.getGUID() - local prefix = guid .. ":" - local plen = #prefix - local changed = false - for key in pairs(activeOverlays) do - if key:sub(1, plen) == prefix then - activeOverlays[key] = nil - changed = true - end - end - for key in pairs(hiddenWhilePickedUp) do - if key:sub(1, plen) == prefix then - hiddenWhilePickedUp[key] = nil - end - end - if changed then macRedrawAll() end -end - --- Texture preload is handled by macRedrawAll which always includes all PNG --- URLs as invisible decals far below the table. No separate preload needed. - --- ============================================ --- PER-SEAT MODE TOGGLE (Cohesion + Range) --- Each seated player picks their renderer: "mac" (this patch) or --- "windows" (original bundle Projector). When a player triggers an overlay, --- the router checks THEIR mode and uses that renderer. The rendered overlay --- is visible to everyone (TTS engine limitation). --- ============================================ - -playerOverlayMode = playerOverlayMode or {} -- color -> "mac" | "windows" --- Default = "windows" (majority of TTS users). Mac users toggle their seat --- via the floating panel. Same for deployment (table-wide setting). -deploymentMode = deploymentMode or "windows" - -function gGetMode(params) - return playerOverlayMode[params.color] or "windows" -end - -function gGetDeploymentMode() - return deploymentMode -end - -function gToggleDeploymentMode() - deploymentMode = (deploymentMode == "mac") and "windows" or "mac" - -- Wipe any active deployment zones so the next setup uses the new mode. - gClearAllDeployment() - macDeferRefresh() -end - --- Replicates the original showRangeOnHoveredModel toggle, calling the --- aliased Global originals so our Mac overrides don't recapture the path. -function gWindowsRangeToggle(fig) - if not fig then return end - if rangeRuler ~= nil then - pcall(clearRangeRulersOriginalGlobal) - if selectedUnitObj == fig then - selectedUnitObj = nil - return - end - end - if fig.interactable then - pcall(function() spawnRangeRulerOriginalGlobal(fig) end) - selectedUnitObj = fig - end -end - --- Replicates the original showCohesionOnHoveredModel toggle. Uses Global --- aliased originals + Global cohesionRuler state (set by the original). -function gWindowsCohesionToggle(fig) - if not fig then return end - if fig.interactable and selectedUnitObj == fig and cohesionRuler ~= nil then - pcall(clearCohesionRulerOriginalGlobal) - selectedUnitObj = nil - return - end - pcall(clearCohesionRulerOriginalGlobal) - pcall(function() spawnCohesionRulerOriginalGlobal(fig) end) - selectedUnitObj = fig -end - -function gCohesionTrigger(params) - if not params or not params.figGUID then return end - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local mode = playerOverlayMode[params.playerColor] or "windows" - if mode == "windows" then - gWindowsCohesionToggle(fig) - else - gToggleCohesion({figGUID = params.figGUID}) - end -end - -function gRangeTrigger(params) - if not params or not params.figGUID then return end - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local mode = playerOverlayMode[params.playerColor] or "windows" - if mode == "windows" then - gWindowsRangeToggle(fig) - else - gToggleRange({ - figGUID = params.figGUID, - forceFigMode = params.forceFigMode, - rangeKey = params.rangeKey, - }) - end -end - --- UI: floating panel with one toggle button per seated player -function macModeClick(player, _, id) - local color = id:sub(9) -- "macmode_Red" -> "Red" - if player.color ~= color then - broadcastToColor("Only the player at this seat can toggle their mode.", - player.color, {1, 0.5, 0.5}) - return - end - local cur = playerOverlayMode[color] or "windows" - playerOverlayMode[color] = (cur == "mac") and "windows" or "mac" - -- Wipe Mac-side overlays so stale visuals don't linger after the toggle. - -- We don't track per-color ownership, so this clears Cohesion+Range for - -- everyone - acceptable since each player can re-trigger their hotkey. - -- Windows-side overlays (fig-scoped cohesionRuler/rangeRuler) are - -- cleared by iterating objects with active state. - for k, e in pairs(activeOverlays) do - if e.type == "cohesion" or e.type == "range" then - activeOverlays[k] = nil - end - end - macRedrawAll() - for _, obj in ipairs(getAllObjects()) do - if obj.getVar and obj.getVar("cohesionRuler") then - pcall(function() obj.call("clearCohesionRulerOriginal", obj) end) - end - if obj.getVar and obj.getVar("rangeRuler") then - pcall(function() obj.call("clearRangeRulersOriginal", obj) end) - end - end - macDeferRefresh() -end - -local function macFindNodeById(tree, id) - for _, node in ipairs(tree) do - if node.attributes and node.attributes.id == id then return node, tree end - if node.children then - local found, parent = macFindNodeById(node.children, id) - if found then return found, parent end - end - end - return nil, nil -end - --- Tracks the panel's desired active state across refreshes. Persists so a --- click on the close X before the initial refresh is honored. Hidden at first --- load (default = false); the floating "Mac Patch" menu button reveals it. -macModePanelActive = (macModePanelActive == nil) and false or macModePanelActive - -function macModeToggleVisibility() - macModePanelActive = not macModePanelActive - -- Defer the XML rebuild: TTS occasionally throws a UTF-8 byte-buffer - -- encoding error if UI.setXmlTable runs synchronously inside a UI - -- click handler. Deferring one frame + pcall has been reliable. - Wait.frames(function() - pcall(function() - local tree = UI.getXmlTable() or {} - local panel = macFindNodeById(tree, "macModePanel") - if panel then - panel.attributes.active = macModePanelActive and "true" or "false" - UI.setXmlTable(tree) - else - macRefreshModeUI() - end - end) - end, 1) -end - -function macRefreshModeUI() - local seated = Player.getPlayers() - local rows = {{ - tag = "Panel", - attributes = { - color = "transparent", - preferredHeight = "24", - }, - children = { - { - tag = "Text", - attributes = { - text = "Mac TTS U6 Patch", - fontSize = "16", - color = "white", - alignment = "MiddleLeft", - rectAlignment = "MiddleLeft", - }, - }, - { - tag = "Button", - attributes = { - id = "macModePanelClose", - text = "X", - onClick = "macModeToggleVisibility", - color = "#3a1e1e", - textColor = "white", - fontSize = "12", - width = "24", - height = "20", - rectAlignment = "MiddleRight", - }, - }, - }, - }, { - tag = "Text", - attributes = { - text = "Cohesion & Range: pick your renderer", - fontSize = "12", - color = "#bbbbbb", - alignment = "MiddleCenter", - }, - }} - if #seated == 0 then - table.insert(rows, { - tag = "Text", - attributes = { - text = "(no seated players)", - fontSize = "12", - color = "#888888", - alignment = "MiddleCenter", - }, - }) - end - for _, p in ipairs(seated) do - local mode = playerOverlayMode[p.color] or "windows" - local label = p.color .. " - " .. - (mode == "mac" and "MAC FALLBACK" or "WINDOWS ORIGINAL") - local bg = (mode == "mac") and "#1e7a3a" or "#7a3a1e" - table.insert(rows, { - tag = "Button", - attributes = { - id = "macmode_" .. p.color, - text = label, - onClick = "macModeClick", - color = bg, - fontSize = "13", - textColor = "white", - preferredHeight = "28", - }, - }) - end - -- Deployment is table-wide (everyone sees the same zones); use a single - -- global toggle rather than per-seat. - table.insert(rows, { - tag = "Text", - attributes = { - text = "Deployment (table-wide)", - fontSize = "12", - color = "#bbbbbb", - alignment = "MiddleCenter", - }, - }) - table.insert(rows, { - tag = "Button", - attributes = { - id = "macmode_deployment", - text = (deploymentMode == "mac") and "DEPLOYMENT: MAC FALLBACK" - or "DEPLOYMENT: WINDOWS ORIGINAL", - onClick = "macDeploymentClick", - color = (deploymentMode == "mac") and "#1e3a7a" or "#7a3a1e", - fontSize = "13", - textColor = "white", - preferredHeight = "28", - }, - }) - -- Build my panel (will be merged into the existing UI tree below so we - -- don't clobber legionFloatingMenu / Welcome / Chess Clocks etc.). - local panel = { - tag = "Panel", - attributes = { - id = "macModePanel", - active = macModePanelActive and "true" or "false", - rectAlignment = "MiddleRight", - offsetXY = "-10 80", - width = "260", - height = "320", - color = "rgba(0.06,0.06,0.06,0.9)", - padding = "8 8 8 8", - outlineSize = "1 1", - outline = "#303030", - }, - children = {{ - tag = "VerticalLayout", - attributes = { - spacing = "4", - childForceExpandHeight = "false", - childForceExpandWidth = "true", - }, - children = rows, - }}, - } - - local tree = UI.getXmlTable() or {} - -- Remove any stale macModePanel before reinserting the fresh one. - for i = #tree, 1, -1 do - if tree[i].attributes and tree[i].attributes.id == "macModePanel" then - table.remove(tree, i) - end - end - table.insert(tree, panel) - - -- Inject a "Mac Patch" button into the bottom-right legionFloatingMenu - -- (replaces the first interactable=false placeholder button). - local menu = macFindNodeById(tree, "legionFloatingMenu") - if menu and menu.children then - local hasMine = false - for _, c in ipairs(menu.children) do - if c.attributes and c.attributes.id == "macModeMenuButton" then - hasMine = true; break - end - end - if not hasMine then - for i, c in ipairs(menu.children) do - if c.attributes and c.attributes.interactable == "false" then - menu.children[i] = { - tag = "Button", - attributes = { - id = "macModeMenuButton", - fontSize = "10", - onClick = "macModeToggleVisibility", - tooltip = "Toggle Mac TTS U6 Patch panel", - }, - value = "Mac Patch", - } - break - end - end - end - end - - UI.setXmlTable(tree) -end - -function macDeploymentClick(_, _, _) - gToggleDeploymentMode() -end - --- Defer one frame: TTS throws a UTF-8 byte-buffer encoding error if we --- rebuild the UI XML synchronously inside a click handler / seat change. --- pcall guards against any residual race. Promoted to a global function --- so macModeClick / macModeToggleVisibility / gToggleDeploymentMode (all --- defined before this point) can call it via name lookup at call time. -function macDeferRefresh() - Wait.frames(function() pcall(macRefreshModeUI) end, 1) -end -function onPlayerChangeColor(_) macDeferRefresh() end -function onPlayerConnect(_) macDeferRefresh() end -function onPlayerDisconnect(_) macDeferRefresh() end - --- Initial UI build, deferred + pcalled like the rest. -Wait.time(function() pcall(macRefreshModeUI) end, 2) - --- Override hotkey init functions to capture playerColor and route through --- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys --- here SHADOWS the originals - when the original onLoad runs init*(), our --- versions register the hotkey instead. - -function initCohesionHotkeys() - addHotkey("Show Cohesion On Hovered Model", - function(playerColor, hoverObject, _) - if not hoverObject or not hoverObject.interactable then return end - gCohesionTrigger({ - figGUID = hoverObject.getGUID(), - playerColor = playerColor, - }) - end) -end - -function initRangebandHotkeys() - addHotkey("Show Range On Hovered Model", - function(playerColor, hoverObject, _) - if not hoverObject or not hoverObject.interactable then return end - gRangeTrigger({ - figGUID = hoverObject.getGUID(), - playerColor = playerColor, - forceFigMode = true, -- hotkey always shows 6-band fig overlay - }) - end) -end - --- The vanilla showRangeOnHoveredModel calls spawnRangeRuler(fig) which spawns --- the Range AssetBundle directly. On Mac, the "Oblong Range" shader used by --- long/snail bundles isn't supported (magenta). Override the global function --- to route through gToggleRange so our stadium overlay renders instead. --- Vanilla Range bundle still works for round bases via gWindowsRangeToggle --- when the player explicitly chose Windows mode. -function showRangeOnHoveredModel(hoverObject) - if not hoverObject or not hoverObject.interactable then return end - gToggleRange({figGUID = hoverObject.getGUID(), forceFigMode = true}) -end - --- Note: we deliberately do NOT override the global spawnRangeRuler / --- clearRangeRulers / showRangeOnHoveredModel chain at the Lua scope where --- the vanilla mod defines it. Overriding spawnRangeRuler from a token --- script reproducibly SIGSEGV'd TTS on Mac in May 2026 (Mono GC stack --- corruption). Mac routing happens via the g* functions called from our --- own hotkey/button wrappers; the originals stay reachable for --- Windows-mode players via spawnRangeRulerOriginalGlobal aliases above. - --- END MAC COHESION FALLBACK -""" - - -# ------------------------------------------------------------ -# 2) Wrapper appended AFTER the original Cohesion include (per-seat router) -# ------------------------------------------------------------ -# Strategy: keep the ORIGINAL Cohesion/RangeRulers expansion intact, then -# append wrappers that alias originals + route through the Global router. -# This preserves the original Custom_AssetBundle Projector spawn for -# Windows-mode players (called back as spawnCohesionRulerOriginal). - -COHESION_WRAPPER = r""" --- MAC PATCH per-seat router (Cohesion) --- Aliases the original spawn so Windows-mode players still get their bundle. -spawnCohesionRulerOriginal = spawnCohesionRuler -clearCohesionRulerOriginal = clearCohesionRuler - -function spawnCohesionRuler(cohesionSourceObject) - if not cohesionSourceObject then return end - Global.call("gCohesionTrigger", { - figGUID = cohesionSourceObject.getGUID(), - playerColor = nil, - }) -end - -function clearCohesionRuler() - if self and self.getGUID and self.getGUID() ~= "-1" then - Global.call("gClearCohesion", { figGUID = self.getGUID() }) - end - if cohesionRuler ~= nil then - pcall(clearCohesionRulerOriginal) - end -end - --- Vanilla Order Token's clearTemplates() calls clearCohesionRulers (plural), --- but the save was built from an older mod version that ships only the --- singular clearCohesionRuler. Provide the plural as a delegating stub so --- standby/clearTemplates/etc. don't nil-error on click. -function clearCohesionRulers() - if selectedUnitObj then - pcall(function() selectedUnitObj.setVar("moveState", false) end) - pcall(function() selectedUnitObj.call("clearCohesionRuler") end) - end -end - --- Same root cause as clearCohesionRulers: newer Order Token code paths in --- the live save (e.g. moveUnit -> stopAttack) call helpers the older bundled --- script doesn't ship. Stub them so MOVE/AIM/etc don't nil-error. The bodies --- mirror the current vanilla source but pcall every dependency so we don't --- chain-error if anything else is also missing. -function clearAttackLine() - if attackLine then - for k, v in pairs(attackLine) do - pcall(destroyObject, v) - end - attackLine = nil - end -end - -function exitTargetingMode() - enemyHighlighted = false - attackModeOn = false - pcall(clearRangeRulers) - pcall(unhighlightEnemies) - pcall(clearAttackLine) -end - -function exitAttackMode() - enemyHighlighted = false - attackModeOn = false - pcall(clearRangeRulers) - pcall(unhighlightEnemies) -end --- END MAC PATCH per-seat router (Cohesion) -""" - - -COHESION_BLOCK_RE = re.compile( - r"----#include !/Cohesion\r?\n.*?----#include !/Cohesion\r?\n", - re.DOTALL -) - -RANGE_BLOCK_RE = re.compile( - r"----#include !/RangeRulers\r?\n.*?----#include !/RangeRulers\r?\n", - re.DOTALL -) - -# Maximum Move spawn block in Order_Token (a57c41), inline (not an include). -# Captures from `local maxMoveBundles = getMovementLinks()` up to and -# including the two closing ends of the original `if isDeploy / if ... ~= nil` -# scaffold. The replacement is self-contained (opens 3 ifs, closes all 3), -# so re-applying yields the same canonical block - idempotent. (Earlier -# versions only matched up to setName("Maximum Move") and re-matched their -# own output, accumulating orphan else/end copies on each re-run.) -MAXMOVE_SPAWN_RE = re.compile( - r"local maxMoveBundles = getMovementLinks\(\).*?" - r"\r?\n end\r?\n end\r?\n", - re.DOTALL -) - -MAXMOVE_SPAWN_REPLACEMENT = r"""local maxMoveBundles = getMovementLinks() - local baseSizeMoveBundles = maxMoveBundles[unitData.baseSize] - local maxMoveTemplateBundleToSpawn = baseSizeMoveBundles and baseSizeMoveBundles[unitData.selectedSpeed] - - -- MAC PATCH: permissive condition (covers nil + false) so the overlay - -- also fires when changeSpeed2/3 calls moveUnit() with no isDeploy arg. - if isDeploy ~= true then - if maxMoveTemplateBundleToSpawn ~= nil then - local _macMode = Global.call("gGetMode", {color = macActivePlayerForMove}) - if _macMode == "windows" then - -- WINDOWS ORIGINAL: spawn Custom_AssetBundle Projector - maxMoveTemplate = spawnObject({ - type = "Custom_AssetBundle", - position = {basePos.x, basePos.y + 20, basePos.z}, - rotation = {0, basePos.y, 0}, - scale = {0,0,0} - }) - maxMoveTemplate.setCustomObject({ - type = 0, - assetbundle = maxMoveTemplateBundleToSpawn - }) - maxMoveTemplate.setLock(true) - maxMoveTemplate.use_gravity = false - maxMoveTemplate.setName("Maximum Move") - else - -- MAC FALLBACK: route through the Global Overlays manager - Global.call("gSpawnMaxMove", { - figGUID = selectedUnitObj.getGUID(), - baseSize = unitData.baseSize, - speed = unitData.selectedSpeed, - }) - maxMoveTemplate = nil - end - end - end -""" - -# clearMovementTemplates function: needs to also clear our managed maxmove -# overlay. We replace the whole function body. -# Match up to the first un-indented `end` (= the function's closing end), so -# the regex is idempotent - it matches both the vanilla body and any -# previously patched body shape (which has extra lines past the inner `end`). -CLEAR_MOVEMENT_RE = re.compile( - r"function clearMovementTemplates\(\).*?\r?\nend\b", - re.DOTALL -) - -CLEAR_MOVEMENT_REPLACEMENT = r"""function clearMovementTemplates() - if templateA ~= nil then - destroyObject(templateA) - end - if templateB ~= nil then - destroyObject(templateB) - end - -- MAC PATCH per-seat: destroy bundle Object if present (Windows-mode - -- spawn) AND clear the manager entry (Mac fallback). Either may be set. - if maxMoveTemplate ~= nil then - pcall(destroyObject, maxMoveTemplate) - end - -- Clear only THIS token's MaxMove (keyed by the attached unit), not - -- everyone's - gClearAllMaxMove was killing other tokens' rings when - -- two players had MOVE open at the same time. - if selectedUnitObj then - Global.call("gClearMaxMove", { figGUID = selectedUnitObj.getGUID() }) - end - maxMoveTemplate = nil -end""" - -# Deployment Boundary: replace the Custom_AssetBundle Projector spawn inside -# spawnBoundaryCell (SETUP_CONTROLLER 1cb552) with a Global.call to the manager. -# Idempotent: matches both the vanilla form AND a previously-injected patched -# form (between the MAC PATCH dual-path marker and the closing ` end`). The -# patched alternative is listed first so re-runs prefer it. -DEPLOYMENT_SPAWN_RE = re.compile( - r"(?:" - r"-- MAC PATCH dual-path:.*?\r?\n end" - r"|" - r"local projector = spawnObject\(\{\r?\n" - r" type = \"Custom_AssetBundle\",.*?" - r"projector\.setCustomObject\(\{\r?\n" - r" assetbundle = asset,\r?\n" - r" \}\)" - r")", - re.DOTALL -) - -DEPLOYMENT_SPAWN_REPLACEMENT = r"""-- MAC PATCH dual-path: branch on the table-wide deploymentMode toggle. - local _macDeployMode = Global.call("gGetDeploymentMode") - local projector = nil - if _macDeployMode == "windows" then - projector = spawnObject({ - type = "Custom_AssetBundle", - position = pos, - scale = {0, 0, 0}, - rotation = {0, deployRotations[cell], 0} - }) - projector.setName("Deployment Boundary") - projector.setLock(true) - projector.setCustomObject({ - assetbundle = asset, - }) - else - Global.call("gSpawnDeployment", { cell = cell, pos = pos }) - end""" - -# clearDeploymentBoundary: also clear manager entries. -CLEAR_DEPLOYMENT_RE = re.compile( - r"function clearDeploymentBoundary\(\)\r?\n" - r" local battlefieldObjs = battlefieldZone\.getObjects\(\).*?\r?\n" - r" end\r?\nend", - re.DOTALL -) - -CLEAR_DEPLOYMENT_REPLACEMENT = r"""function clearDeploymentBoundary() - -- MAC PATCH: clear Global Overlays manager entries first. - Global.call("gClearAllDeployment", {}) - local battlefieldObjs = battlefieldZone.getObjects() - for _, obj in pairs(battlefieldObjs) do - if obj.getName() == "Deployment Boundary" then - destroyObject(obj) - end - end -end""" - -# Silhouette URL swap to our Unity-6-compiled bundle. -# -# The upstream BucketheadBits_Silhouette bundle uses Allen White's custom -# Silhouette shader compiled under Unity 2019.4. TTS U6 on Mac fails to -# load that shader at runtime (console: "Shader didn't load correctly for -# AssetBundle material BucketheadBits_Silhouette. Assigning Standard -# shader.") and falls back to Standard, which renders the cylinder opaque. -# -# Our fork-hosted bundle uses the SAME prefab + material + shader source -# (Assets/Sihl/), only the compile target differs (Unity 6 instead of -# Unity 2019.4). Path A from Dicewrench's analysis. -# -# We swap ALL silhouette URLs (default cylinder, snail, long) to the same -# fork URL. Snail/long visual fidelity is acceptable for now (cylinder -# placeholder); per-base meshes are a separate follow-up. -SILHOUETTE_BUNDLE_URL = ( - "https://raw.githubusercontent.com/ironsquadronfr-hub/tts/" - "mac-projector-fallback/mod/data/mac-fallback-assets/" - "silhouette_mac_fallback.unity3d" -) -SILHOUETTE_URL_RE = re.compile( - r'(silhouetteData\s*=\s*)"https?://[^"]*/ugc/[^"]+"' -) -SILHOUETTE_URL_REPLACEMENT = r'\1"' + SILHOUETTE_BUNDLE_URL + '"' - - -# Cleanup of debug prints and corrupted signatures from earlier sessions. -# Safe no-op on a clean save. Matches both the legacy `print("[SIL DEBUG] ...)` -# form AND the chat-visible `printToAll("[SIL] ...", {...})` form. -SIL_DEBUG_STRIP_RE = re.compile( - # Match leading horizontal whitespace only (NOT \s* which would also eat - # the newline before, mashing the function signature against the next - # statement - that was a real bug we hit earlier this session). - r'[ \t]*(?:print\("\[SIL DEBUG\][^\n]*\)|' - r'printToAll\("\[SIL\][^"]*",\s*\{[^}]*\}\))\r?\n' -) -# Repair function signatures that were mashed by the previous \s*-glob bug. -# Idempotent: target the specific mashed patterns; matches are zero on a -# clean save. -SIL_REPAIR_TOGGLE_RE = re.compile( - r'(function toggleSilhouettes\(\))( if silhouetteState then)' -) -SIL_REPAIR_SHOW_RE = re.compile( - r'(function showSilhouette\(\))( for k, guid in pairs\(miniGUIDs)' -) -SIL_REPAIR_SPAWN_RE = re.compile( - r'(function spawnSilhouette\(obj, pos, rot\))( local globals)' -) - - -# Silhouette state-desync fix (upstream bug). -# -# Upstream clearSilhouette assumes removeAttachments()[1] always returns an -# object, but the state variable `silhouetteState` is persisted across saves -# while the physical attachments are not - so a save made with silhouettes -# visible reloads with state=true but no attachments. First SIL click then -# calls clearSilhouette which dereferences nil and crashes. -# -# Two-part patch: -# (1) clearSilhouette: guard the nil case (skip the destruct if no -# attachment), so re-clicks don't crash. -# (2) onload: force silhouetteState=false on every load, since the load -# cannot restore the physical silhouettes anyway. -# -# Also fixes the "two silhouettes stacked" symptom: that happens when -# showSilhouette runs while state is desynced, spawning a second set on top -# of one whose handles have been forgotten. -CLEAR_SILHOUETTE_RE = re.compile( - r"function clearSilhouette\(\).*?silhouetteState = false\r?\nend", - re.DOTALL -) -CLEAR_SILHOUETTE_REPLACEMENT = r"""function clearSilhouette() - -- MAC PATCH: guard nil silToDestroy. silhouetteState can be persisted as - -- true across saves while the physical attachments are not, so a load - -- with state=true + no attachments would otherwise crash here. - for k, guid in pairs(miniGUIDs or {}) do - local obj = getObjectFromGUID(guid) - if obj then - local silToDestroy = obj.removeAttachments()[1] - if silToDestroy then silToDestroy.destruct() end - end - end - silhouetteState = false -end""" - -# Match the existing `function onload()` body (one-line empty or actual -# code). Idempotent: re-runs detect the marker `MAC PATCH: silhouette reset` -# and skip. -SIL_ONLOAD_RESET_LINE = ( - ' -- MAC PATCH: silhouette reset on load (attachments lost across saves)\n' - ' silhouetteState = false\n' -) -ONLOAD_OPEN_RE = re.compile(r"function onload\(\)\r?\n") - - -# SIL button rename (root cause: TTS engine intercepts the click_function -# name "toggleSilhouettes" silently in Mac mode; LCK/R buttons unaffected -# because their click_function names differ. Confirmed empirically 17 mai -# 2026: renaming to "macToggleSil" + forwarder unblocks Mac mode entirely). -# -# We swap the click_function name on the SIL button definition AND inject a -# forwarder function that simply calls toggleSilhouettes(). The original -# toggleSilhouettes is untouched, so Windows mode behavior is identical. -# Idempotent: re-runs detect both markers and skip. -SIL_BUTTON_CLICKFN_OLD = 'click_function = "toggleSilhouettes"' -SIL_BUTTON_CLICKFN_NEW = 'click_function = "macToggleSil"' -SIL_FORWARDER = ''' --- MAC PATCH: rebuilt SIL button click handler. The original click_function --- name "toggleSilhouettes" is silently intercepted by TTS in Mac mode (the --- SIL button plays its click sound/animation but the function is never --- called). Renaming the click_function on the button definition + adding --- this forwarder restores the routing. -function macToggleSil() - toggleSilhouettes() -end --- END MAC PATCH SIL forwarder -''' -SIL_FORWARDER_MARKER = 'function macToggleSil()' - - -RANGE_WRAPPER = r""" --- MAC PATCH per-seat router (Range) - ALIAS ONLY. --- --- HISTORY (do not re-introduce): we previously overrode spawnRangeRuler / --- clearRangeRulers / clearRangeRuler on the ~20 token scripts. That --- reproducibly SIGSEGV'd TTS Mac (Mono GC_clear_stack_inner) when a fig --- hotkey spawned the vanilla Range bundle. Overrides REMOVED - per-seat --- Range routing for fig hotkey lives in Global (gRangeTrigger via our --- initRangebandHotkeys shadow). Tokens' R buttons keep vanilla behavior --- (magenta on Mac, native on Windows) until a per-token Mac fallback --- ships - see TOKEN_BUTTON_WRAPPER for the per-token override path. --- --- The single alias below is REQUIRED: Global macModeClick calls --- obj.call("clearRangeRulersOriginal", obj) on every object holding a --- rangeRuler state, to wipe the vanilla ruler when toggling modes. -clearRangeRulersOriginal = clearRangeRulers --- END MAC PATCH per-seat router (Range) -""" - -# Order_Token-only overrides: intercept the UI button click handlers -# (toggleCohesionRuler, targetingMode, changeSpeedN, moveX) to capture -# playerColor passed by TTS for per-seat routing. -ORDER_TOKEN_BUTTON_OVERRIDES = r""" --- MAC PATCH per-seat router (Order_Token button click overrides) -function toggleCohesionRuler(_, playerColor) - if not rulerOn then - Global.call("gCohesionTrigger", { - figGUID = selectedUnitObj.getGUID(), - playerColor = playerColor, - }) - rulerOn = true - else - selectedUnitObj.call("clearCohesionRuler") - rulerOn = false - end -end - -function targetingMode(_, playerColor) - if not enemyHighlighted then - exitAttackMode() - highlightEnemies() - Global.call("gRangeTrigger", { - figGUID = selectedUnitObj.getGUID(), - playerColor = playerColor, - }) - enemyHighlighted = true - resetRangeButtons() - else - exitTargetingMode() - end -end - --- Capture playerColor for Maximum Move per-seat routing. moveUnit() doesn't --- receive the click color, so each entry-point button stashes it in a --- script-global var that the spawn block reads. Defaults to nil -> Mac mode. - --- initMove / initDeploy: redefine the body INLINE rather than wrap, because --- wrapping (capturing the original via a local + calling it) reproducibly --- broke every Order Token button click in testing (root cause unknown; --- suspected Lua chunk-level upvalue interaction with TTS engine click --- dispatch). The inline body mirrors the vanilla logic + the playerColor --- capture so the MAXMOVE_SPAWN block can route to Mac/Windows per seat. -function initMove(obj, playerColor) - if not selectedUnitObj then return end - macActivePlayerForMove = playerColor - initPos = selectedUnitObj.getPosition() - initRot = selectedUnitObj.getRotation() - selectedUnitObj.call("setStartPos") - moveUnit(false) -end -function initDeploy(obj, playerColor) - if not selectedUnitObj then return end - macActivePlayerForMove = playerColor - initPos = selectedUnitObj.getPosition() - initRot = selectedUnitObj.getRotation() - selectedUnitObj.call("setStartPos") - moveUnit(true) -end - --- INLINE the vanilla bodies (mirror Order_Token.a57c41.lua) rather than --- wrap. The List Builder copies this block to Command Token Custom_Models --- which DO NOT carry the vanilla Order_Token function bodies - so a --- wrap-then-call pattern hits `_macOrigChangeSpeed1 = nil` and crashes the --- moment a Speed/Move button is pressed. Inlining keeps the behavior --- identical on both Order Tokens (vanilla body re-defined) and Command --- Tokens (helper fns setTemplateVariables/clearTemplates/moveUnit exist --- on both because they're part of the include set the List Builder emits). -function changeSpeed1(_, playerColor) - macActivePlayerForMove = playerColor - unitData.selectedSpeed = 1 - setTemplateVariables() - clearTemplates() - moveUnit() -end -function changeSpeed2(_, playerColor) - macActivePlayerForMove = playerColor - unitData.selectedSpeed = 2 - setTemplateVariables() - clearTemplates() - moveUnit() -end -function changeSpeed3(_, playerColor) - macActivePlayerForMove = playerColor - unitData.selectedSpeed = 3 - setTemplateVariables() - clearTemplates() - moveUnit() -end -function moveForward(_, playerColor) - macActivePlayerForMove = playerColor - self.editButton({ - index = 11, click_function = "moveBackwards", - label = "B", tooltip = "Move Backwards" - }) - moveDirection = "forward" - moveUnit() -end -function moveBackwards(_, playerColor) - macActivePlayerForMove = playerColor - self.editButton({ - index = 11, click_function = "moveForward", - label = "F", tooltip = "Move Forward" - }) - moveDirection = "backwards" - moveUnit() -end -function moveLeft(_, playerColor) - macActivePlayerForMove = playerColor - moveDirection = "left" - moveUnit() -end -function moveRight(_, playerColor) - macActivePlayerForMove = playerColor - moveDirection = "right" - moveUnit() -end --- END MAC PATCH per-seat router (Order_Token button click overrides) -""" - -# Wrapper appended to objects that have `function toggleRangeRuler()` - -# the R button on tokens (POI, Smoke, Bomb Cart, Objective). Per-seat -# routing: Mac players get our vector bands (TOKEN-spec via rangeKey), -# Windows players get the vanilla token bundle (spawnTokenRangeRuler). -# The vanilla function is aliased BEFORE override so Windows-mode clicks -# can fall through to it. -TOKEN_BUTTON_WRAPPER = r""" --- MAC PATCH per-seat router (token R button) --- Aliases the vanilla toggleRangeRuler before overriding so Windows-mode --- players keep spawning the token-specific Range bundle (POI / Smoke / --- bomb_cart / objective). Mac-mode players get our vector overlay via --- gRangeTrigger which dispatches to macBuilders.range with the token's --- rangeKey var. -local _macToggleRangeRulerOriginal = toggleRangeRuler -function toggleRangeRuler(_, playerColor) - local mode = Global.call("gGetMode", { color = playerColor }) - if mode == "windows" then - if _macToggleRangeRulerOriginal then - return _macToggleRangeRulerOriginal() - end - return - end - if rangeOn then - Global.call("gClearRange", { figGUID = self.getGUID() }) - rangeOn = false - else - Global.call("gRangeTrigger", { - figGUID = self.getGUID(), - playerColor = playerColor, - }) - rangeOn = true - end -end --- END MAC PATCH per-seat router (token R button) -""" - -TOKEN_BUTTON_WRAPPER_RE = re.compile( - r"\r?\n-- MAC PATCH per-seat router \(token R button\).*?" - r"-- END MAC PATCH per-seat router \(token R button\)\r?\n", - re.DOTALL -) - -# List Builder _loadArmyFromJson nil-guard. Stock SWL never nil-checks the -# result of JSON.decode(text) inside importFromText, so any blur of the -# import InputField with empty/invalid text crashes _loadArmyFromJson at -# `data.armyFaction`. The guard is upstream-able. Idempotent via the -# negative lookahead on the marker comment. -LIST_BUILDER_GUARD_RE = re.compile( - r"(function _loadArmyFromJson\(data\)\r?\n)(?! -- MAC PATCH: guard )" -) -LIST_BUILDER_GUARD_REPLACEMENT = ( - r"\1" - " -- MAC PATCH: guard nil/invalid JSON from onEndEdit blur (upstream bug)\r\n" - " if not data then return end\r\n" -) - -# Idempotent marker - used to detect and remove an existing Mac patch in the -# Global LuaScript before applying a fresh one. -GLOBAL_PATCH_MARKER_RE = re.compile( - r"\r?\n-- ={5,}\r?\n-- MAC COHESION FALLBACK.*?-- END MAC COHESION FALLBACK\r?\n", - re.DOTALL -) - -# Idempotent markers for the per-seat wrappers added to object scripts. -# Strip these before re-applying to avoid double-wrapping. -COHESION_WRAPPER_RE = re.compile( - r"\r?\n-- MAC PATCH per-seat router \(Cohesion\).*?" - r"-- END MAC PATCH per-seat router \(Cohesion\)\r?\n", - re.DOTALL -) -RANGE_WRAPPER_RE = re.compile( - r"\r?\n-- MAC PATCH per-seat router \(Range\).*?" - r"-- END MAC PATCH per-seat router \(Range\)\r?\n", - re.DOTALL -) -ORDER_TOKEN_OVERRIDES_RE = re.compile( - r"\r?\n-- MAC PATCH per-seat router \(Order_Token button click overrides\).*?" - r"-- END MAC PATCH per-seat router \(Order_Token button click overrides\)\r?\n", - re.DOTALL -) - - -TARGET_GUIDS = {"99f1c8", "a57c41"} # Objects with the Cohesion block to replace - - -def collect_patched_scripts(data: dict) -> list: - """Return script_states for the TTS External Editor API (Save & Play). - - Includes the Global script and any object script we patched (matching - TARGET_GUIDS). Format: list of dicts with name, guid, script. - """ - states = [ - {"name": "Global", "guid": "-1", "script": data.get("LuaScript", "")} - ] - - def walk(o): - if isinstance(o, dict): - guid = o.get("GUID", "").lower() - if guid in TARGET_GUIDS and "LuaScript" in o: - states.append({ - "name": o.get("Name", guid), - "guid": guid, - "script": o["LuaScript"], - }) - for v in o.values(): - walk(v) - elif isinstance(o, list): - for x in o: - walk(x) - - walk(data) - return states - - -def send_to_tts(script_states: list) -> bool: - """Send a Save & Play (messageID 1) to TTS via the External Editor API. - - Returns True on success, False if TTS isn't listening (likely the API - is disabled in TTS Configuration). - """ - msg = {"messageID": 1, "scriptStates": script_states} - payload = json.dumps(msg).encode("utf-8") - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.settimeout(3) - s.connect(("127.0.0.1", 39999)) - s.sendall(payload) - s.close() - return True - except (ConnectionRefusedError, socket.timeout, OSError) as e: - print(f" WARN: could not connect to TTS on port 39999 ({e}).") - print(f" -> Enable Configuration -> Game Tweaks -> 'External Editor API' in TTS.") - return False - - -def patch_object_scripts(data: dict) -> tuple: - """Walk the JSON tree, replace Cohesion and Range blocks in object scripts. - - Cohesion block: only on TARGET_GUIDS (Unit_Leader, Order_Token). - Range block: on every object containing the !/RangeRulers include - (Order_Token, Tokens, POI, bomb_cart, etc. - ~20 objects). - - Returns (n_cohesion_patched, n_range_patched, n_dep_patched). - """ - n_coh, n_rng, n_dep = 0, 0, 0 - - def walk(o): - nonlocal n_coh, n_rng, n_dep - if isinstance(o, dict): - guid = o.get("GUID", "").lower() - if "LuaScript" in o: - ls = o["LuaScript"] - changed = False - - # Strip any previous per-seat wrappers so re-runs are idempotent. - for rx in (COHESION_WRAPPER_RE, RANGE_WRAPPER_RE, - ORDER_TOKEN_OVERRIDES_RE, TOKEN_BUTTON_WRAPPER_RE): - new_ls, n = rx.subn("", ls) - if n > 0: - ls = new_ls - changed = True - - # Cleanup: strip stale debug prints + repair any signatures - # mashed by an earlier (overzealous) strip pass. Both are - # no-ops on a clean save. - new_ls, n = SIL_DEBUG_STRIP_RE.subn("", ls) - if n > 0: - ls = new_ls - changed = True - for rx in (SIL_REPAIR_TOGGLE_RE, SIL_REPAIR_SHOW_RE, SIL_REPAIR_SPAWN_RE): - new_ls, n = rx.subn(r'\1\n\2', ls) - if n > 0: - ls = new_ls - changed = True - - # (Diagnostic SIL prints removed 17 mai 2026 once mute Mac - # mode confirmed. SIL_DEBUG_STRIP_RE above stays as a cleanup - # pass for older saves that still carry the prints.) - - # Silhouette URL swap to our Unity-6-compiled bundle. - # Applies to any object that defines spawnSilhouette - # (Unit_Leader 99f1c8, Bomb Cart b497e1, POI Token 761483). - # Idempotent: the regex only matches Steam UGC URLs (/ugc/), - # not our fork URL, so re-runs are no-ops. - new_ls, n = SILHOUETTE_URL_RE.subn(SILHOUETTE_URL_REPLACEMENT, ls) - if n > 0: - ls = new_ls - changed = True - - # SIL button rename + forwarder. Bypasses the TTS engine - # bug where the click_function name "toggleSilhouettes" is - # silently muted in Mac mode. Idempotent: replace() is no-op - # once swap is done, marker check prevents double-injection. - if SIL_BUTTON_CLICKFN_OLD in ls: - ls = ls.replace(SIL_BUTTON_CLICKFN_OLD, SIL_BUTTON_CLICKFN_NEW) - changed = True - if SIL_BUTTON_CLICKFN_NEW in ls and SIL_FORWARDER_MARKER not in ls: - ls = ls.rstrip() + '\n' + SIL_FORWARDER - changed = True - - # Cohesion: KEEP the original include block, append per-seat - # wrapper after it. Only Unit_Leader / Order_Token have it. - if guid in TARGET_GUIDS: - def coh_repl(m): - return m.group(0) + COHESION_WRAPPER - new_ls, n = COHESION_BLOCK_RE.subn(coh_repl, ls, count=1) - if n > 0: - ls = new_ls - n_coh += 1 - changed = True - - # Silhouette desync fix (any object with clearSilhouette). - # Guard the nil-attachment case and reset silhouetteState in - # onload. Idempotent: the regex matches both the vanilla - # body and our previously-patched body (same shape). - if "function clearSilhouette()" in ls: - new_ls, n = CLEAR_SILHOUETTE_RE.subn(CLEAR_SILHOUETTE_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - changed = True - # Inject the state reset at the top of onload. - if "MAC PATCH: silhouette reset on load" not in ls: - new_ls, n = ONLOAD_OPEN_RE.subn( - "function onload()\n" + SIL_ONLOAD_RESET_LINE, - ls, count=1, - ) - if n > 0: - ls = new_ls - changed = True - - # Range: KEEP block, append wrapper. ~21 objects. - def rng_repl(m): - return m.group(0) + RANGE_WRAPPER - new_ls, n = RANGE_BLOCK_RE.subn(rng_repl, ls, count=1) - if n > 0: - ls = new_ls - n_rng += 1 - changed = True - - # List Builder import guard. Applies to BLUE/RED LIST - # BUILDER (any object that defines _loadArmyFromJson). - # Fixes a pre-existing upstream crash where blurring the - # import InputField with empty/invalid text fires - # onEndEdit -> importFromText -> JSON.decode("") -> nil -> - # data.armyFaction crash. Idempotent. - if "function _loadArmyFromJson(data)" in ls: - new_ls, n = LIST_BUILDER_GUARD_RE.subn( - LIST_BUILDER_GUARD_REPLACEMENT, ls, count=1 - ) - if n > 0: - ls = new_ls - changed = True - - # Maximum Move spawn (only in Order_Token a57c41) - Mac only - if guid == "a57c41": - new_ls, n = MAXMOVE_SPAWN_RE.subn(MAXMOVE_SPAWN_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - changed = True - new_ls, n = CLEAR_MOVEMENT_RE.subn(CLEAR_MOVEMENT_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - changed = True - # Button click overrides (per-seat capture of playerColor) - ls = ls + ORDER_TOKEN_BUTTON_OVERRIDES - changed = True - - # Token R button: append toggleRangeRuler override on any - # object that defines that function (POI, Smoke, BombCart, - # Objective, etc.). Routes the click to the Mac fallback - # Range overlay sized via TOKEN_BASE_RADIUS. (Already-applied - # case is handled by the strip loop above.) - if "function toggleRangeRuler()" in ls: - ls = ls + TOKEN_BUTTON_WRAPPER - changed = True - - # Deployment Boundary (only in SETUP_CONTROLLER 1cb552) - Mac only - if guid == "1cb552": - new_ls, n = DEPLOYMENT_SPAWN_RE.subn(DEPLOYMENT_SPAWN_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - n_dep += 1 - changed = True - new_ls, n = CLEAR_DEPLOYMENT_RE.subn(CLEAR_DEPLOYMENT_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - changed = True - - if changed: - o["LuaScript"] = ls - - for v in o.values(): - walk(v) - elif isinstance(o, list): - for x in o: - walk(x) - - walk(data) - return n_coh, n_rng, n_dep - - -def main(): - args = [a for a in sys.argv[1:] if not a.startswith("--")] - flags = [a for a in sys.argv[1:] if a.startswith("--")] - if len(args) != 2: - print(f"Usage: {sys.argv[0]} [--reload]") - sys.exit(1) - src = Path(args[0]) - dst = Path(args[1]) - reload_tts = "--reload" in flags - - with src.open("r") as f: - data = json.load(f) - - # Rename so the patched save is recognizable in TTS load list. - data["SaveName"] = "SWL BETA - MAC PATCH" - - # 1. Append manager + handlers to Global LuaScript (idempotent: remove - # any previous Mac patch block first, then append fresh). - original_global_len = len(data.get("LuaScript", "")) - existing = data.get("LuaScript", "") - cleaned, n_removed = GLOBAL_PATCH_MARKER_RE.subn("", existing) - data["LuaScript"] = cleaned + GLOBAL_PATCH_LUA - new_global_len = len(data["LuaScript"]) - note = " (replaced existing patch)" if n_removed else "" - print(f"Global LuaScript: {original_global_len} -> {new_global_len} bytes{note}") - print() - - # 2. Replace Cohesion + Range + Deployment blocks in object scripts. - print("Object script patches:") - n_coh, n_rng, n_dep = patch_object_scripts(data) - print(f" Cohesion blocks replaced: {n_coh}") - print(f" Range blocks replaced: {n_rng}") - print(f" Deployment blocks replaced: {n_dep}") - print() - - dst.parent.mkdir(parents=True, exist_ok=True) - with dst.open("w") as f: - json.dump(data, f, indent=2, ensure_ascii=False) - - print(f"Written: {dst}") - print(f" Save Name: {data.get('SaveName', '?').strip()}") - print(f" Version: {data.get('VersionNumber', '?')}") - - # Mirror to TTS Saves folder so it appears in Games -> Save & Load. - # Picks the next free TS_Save_N.json slot (TTS scans these on load). - saves_dir = Path.home() / "Library" / "Tabletop Simulator" / "Saves" - if saves_dir.exists(): - existing = sorted( - int(p.stem.split("_")[-1]) - for p in saves_dir.glob("TS_Save_*.json") - if p.stem.split("_")[-1].isdigit() - ) - # Reuse the highest existing slot if its content matches the prefix - # "SWL BETA - MAC PATCH"; else pick next free number. - target = None - for n in reversed(existing): - p = saves_dir / f"TS_Save_{n}.json" - try: - with p.open("r") as f: - if '"SWL BETA - MAC PATCH"' in f.read(512): - target = p - break - except OSError: - continue - if target is None: - next_n = (existing[-1] if existing else 0) + 1 - target = saves_dir / f"TS_Save_{next_n}.json" - with target.open("w") as f: - json.dump(data, f, indent=2, ensure_ascii=False) - print(f" Mirrored to: {target}") - - if reload_tts: - print() - print("Hot-reloading into TTS via External Editor API...") - states = collect_patched_scripts(data) - print(f" Sending {len(states)} script(s): " - + ", ".join(f"{s['name']}({s['guid']})" for s in states)) - if send_to_tts(states): - print(" OK Sent. TTS should restart Lua with the new code " - "(table/objects state preserved).") - - -if __name__ == "__main__": - main() diff --git a/mac-patcher/requirements.txt b/mac-patcher/requirements.txt deleted file mode 100644 index fd6ee21e..00000000 --- a/mac-patcher/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -UnityPy>=1.10 -Pillow>=10.0 -requests>=2.31 diff --git a/mac-patcher/retro-cohesion.md b/mac-patcher/retro-cohesion.md deleted file mode 100644 index a67ea58c..00000000 --- a/mac-patcher/retro-cohesion.md +++ /dev/null @@ -1,170 +0,0 @@ -# Rétro-ingé Cohesion Ruler - -Rédigé en mode prod, base pour le refactor event-driven vector lines (15 mai 2026, post-finding session 14-15 mai). - -## 1. Définition fonctionnelle (rappel règles SWL) - -Le **Cohesion Ruler** matérialise la **distance de cohésion d'unité** : tous les minis d'une même unité doivent rester à une distance maximale du leader d'unité (sinon malus / dispersion). Le ruler aide à vérifier visuellement cette contrainte pendant le tour. - -Distance de cohésion : **0.5 inch** (1,27 cm) entre les bords des socles, règle officielle SWL. - -## 2. Visuel attendu - -3 tailles, indexées par `baseSize` du leader : - -| Clé | Base size physique | Bundle | -|---|---|---| -| `small` | 27 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305632/...` | -| `medium` | 50 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305877/...` | -| `large` | 70 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305957/...` | - -Rendu côté Windows (TTS Unity 2019.4) : cercle semi-transparent vert/cyan projeté au sol via Unity Projector, centré sur la fig. **À extraire empiriquement** via UnityPy + référence Workshop pour la couleur/alpha exacts (en TODO ci-dessous). - -Comportement Projector : drape sur le relief de la table (rochers, élévations) grâce au Projector legacy. - -## 3. Déclencheurs (qui peut spawn/clear le ruler ?) - -### Spawn - -1. **Hotkey global "Show Cohesion On Hovered Model"** — `StarWarsLegion.lua:611-619` - - Bind via `addHotkey()` côté Global - - Appelle `showCohesionOnHoveredModel(hoverObject)` qui : - - Si déjà selected → clear + reset `selectedUnitObj` - - Sinon → `clearCohesionRuler() + spawnCohesionRuler(hoverObject)` - - **État global** `selectedUnitObj` tracké côté Global (variable du script `StarWarsLegion.lua` via `require('!/Cohesion')`) - -2. **Bouton "COHESION" sur Order Token** — `Order_Token.a57c41.lua:347-352, 369-378` - - Bouton créé pendant l'activation d'une unité - - `toggleCohesionRuler()` toggle via flag `rulerOn` local au Order Token - - Appelle `selectedUnitObj.call("spawnCohesionRuler", selectedUnitObj)` → s'exécute dans le script de la **fig sélectionnée** (Unit_Leader.99f1c8) - -3. **Re-spawn automatique après mouvement** — `Unit_Leader.99f1c8.lua:278-286` - - `dropCoroutine()` attend que `getVelocity().y == 0` (fig au sol) - - Si `moveState == true` (= en cours de mesure de mouvement) → `spawnCohesionRuler(self)` - -### Clear - -1. **Hotkey re-press** — toggle via `showCohesionOnHoveredModel` (idem spawn, second appel) - -2. **Bouton COHESION re-press** — toggle via `toggleCohesionRuler` (idem) - -3. **Bouton invisible "unitID" sur Unit_Leader** — `Unit_Leader.99f1c8.lua:45` - - Bouton au centre de la fig avec label = numéro d'unité, couleur alpha 0.01 (quasi-invisible) - - `click_function = "clearCohesionRuler"` - - C'est l'overlay cliquable du numéro affiché sur chaque fig - -4. **onPickedUp sur Unit_Leader** — `Unit_Leader.99f1c8.lua:274-276` - - **Event-driven déjà existant** : pickup la fig → clear automatique - -5. **`standbyTokens()` global** — `StarWarsLegion.lua:562-571` - - Boucle sur `getAllObjects()` et destroy tout objet nommé `"Cohesion Ruler"` (et "Range Ruler", "Movement Template", "Deployment Boundary") - - Déclenché par... à tracer (probablement reset de partie ou hotkey debug) - -6. **`removeLockedRulers()` GAME_CONTROLLER** — `GAME_CONTROLLER.623b03.lua:148-155` - - Boucle sur `getAllObjects()` et destroy "Cohesion Ruler" / "Range Ruler" - - Appelé par la fonction de reload (`reloadObj` voisine) - -7. **`clearCohesionRulers()` (pluriel) Order Token** — `Order_Token.a57c41.lua:788-793` - - Appelé par `clearTemplates()` lors du reset après mouvement - - `selectedUnitObj.setVar("moveState", false)` + `selectedUnitObj.call("clearCohesionRuler")` - -## 4. Lifecycle complet - -``` -[Spawn] - showCohesionOnHoveredModel(fig) [hotkey hover] - toggleCohesionRuler() [via Order Token bouton] - dropCoroutine() [auto si moveState] - ↓ - spawnCohesionRuler(fig): - 1. unitData = fig.getTable("unitData") - 2. bundleURL = getCohesionLinks()[unitData.baseSize] - 3. spawnObject(Custom_AssetBundle, pos = fig.pos + Y+20, scale = 0, rot = (0, fig.rot.y, 0)) - 4. setCustomObject({type=0, assetbundle=bundleURL}) - 5. setLock(true), use_gravity=false, setName("Cohesion Ruler") - ↓ -[Ruler affiché — STATIQUE, ne suit pas la fig] - Note: pas de onFixedUpdate. Si fig bouge sans pickup (impossible normalement - car les figs sont lockables), le ruler reste à l'ancienne position. - -[Clear] - showCohesionOnHoveredModel(same fig) [hotkey re-press toggle] - toggleCohesionRuler() [re-press toggle] - onPickedUp(fig) [pickup auto] - clearCohesionRuler() [click invisible "unitID" button] - standbyTokens() [global reset] - removeLockedRulers() [reload] - clearCohesionRulers() [via Order Token clearTemplates] - ↓ - clearCohesionRuler(): - 1. destroyObject(cohesionRuler) - 2. cohesionRuler = nil - -[Re-spawn auto post-drop] - onDropped → checkVelocity → si moveState → startLuaCoroutine(dropCoroutine) - ↓ - dropCoroutine: - while velocity.y ~= 0: yield - if moveState == true: spawnCohesionRuler(self) -``` - -## 5. État scopé : ATTENTION pattern hétérogène - -`cohesionRuler` est une variable Lua **scopée au script Lua qui fait le require**. Chaque objet qui require `!/Cohesion` a SA propre `cohesionRuler`. Trois scopes coexistent : - -| Site require | Scope `cohesionRuler` | -|---|---| -| `StarWarsLegion.lua:8` (Global) | Script Global | -| `Unit_Leader.99f1c8.lua:1` (chaque fig leader) | Par-fig | -| `Order_Token.a57c41.lua:4` (chaque Order Token) | Par-Order-Token | - -**Conséquence pratique** : -- Hotkey hover → spawn dans le scope Global → `cohesionRuler` du Global mis à jour, **pas celui de la fig** -- Bouton COHESION → `selectedUnitObj.call("spawnCohesionRuler", selectedUnitObj)` → exécute dans le scope de la fig → `cohesionRuler` de la fig mis à jour -- `onPickedUp(fig)` → clear dans le scope de la fig → ne clear PAS un ruler spawn via hotkey hover (mais le bouton invisible "unitID" est sur la fig, donc dans le scope fig...) - -**Bug latent connu (non reproduit)** : si tu spawn via hotkey hover, puis tu pickup une autre fig, le ruler du Global subsiste. À garder en tête pour le refactor (à corriger ou pas selon scope). - -## 6. Filtres de cleanup global - -Deux fonctions scannent `getAllObjects()` et destroy par nom : - -| Fonction | Fichier | Cible | -|---|---|---| -| `standbyTokens()` | StarWarsLegion.lua:562 | "Cohesion Ruler" + "Range Ruler" + "Movement Template" + "Deployment Boundary" | -| `removeLockedRulers()` | GAME_CONTROLLER.623b03.lua:148 | "Cohesion Ruler" + "Range Ruler" | - -**Impact refactor** : si on remplace l'Object Custom_AssetBundle par des vector lines (qui ne sont pas des Objects), ces deux filtres deviennent **caducs**. Il faudra leur substituer un clear de la table d'état globale + `Global.setVectorLines({})`. - -## 7. Paramètres visuels exacts — TODO - -À extraire empiriquement via UnityPy sur les bundles small/medium/large : - -- [ ] Bundle `halfcohesion_27mm.unity3d` (ou équivalent small) : material color, alpha, orthographicSize du Projector, animation éventuelle -- [ ] Bundle medium (50mm) : idem -- [ ] Bundle large (70mm) : idem -- [ ] Screenshot référence côté Workshop (rendu attendu côté Windows TTS 2019.4 / U6 non-magenta) - -Scripts UnityPy disponibles dans `Mod TTS SWL/` (`inspect_cohesion.py`, `inspect_externals.py`). À adapter pour extraire les params materials. - -## 8. Implications pour le refactor - -### Bonnes nouvelles -- API publique petite et conservable : `showCohesionOnHoveredModel`, `spawnCohesionRuler`, `clearCohesionRuler` -- 80% du pattern event-driven déjà en place (`onPickedUp`, `dropCoroutine`) -- Lifecycle bien défini, déclencheurs énumérés exhaustivement - -### Décisions à prendre dans le design doc -1. **Unifier les scopes** : faire que `cohesionRuler` soit une table globale indexée par GUID (Variante A) plutôt que par-script. Résout le bug latent (§5) et permet le pattern multi-figs simultanées. -2. **Remplacer les filtres de cleanup** (§6) par des appels à la fonction de clear globale. -3. **Reproduire le drape sur relief** via `Physics.cast()` par segment, comme le POC validé du 14-15 mai. -4. **Compatibilité Order Token bouton** : conserver la sémantique toggle `rulerOn` ou simplifier. - -(Note : esthétique du rendu vector lines hors specs — on traitera une fois le dossier refactor clos.) - -## 9. Références - -- Code : `swlegion-tts/mod/src/includes/Cohesion.ttslua`, `data/CohesionLinks.ttslua`, `StarWarsLegion.lua`, `StarWarsLegion/Unit_Leader.99f1c8.lua`, `StarWarsLegion/Order_Token.a57c41.lua`, `StarWarsLegion/GAME_CONTROLLER.623b03.lua` -- Bundles : cache TTS local, sources Unity dans `Mod TTS SWL/UnityProject-U6/Assets/` -- Session 14-15 mai validation empirique : `~/.claude/projects/-Users-martinpourrat-MARTIN-Star-Wars-Legion/memory/mod-tts-swl/session-14-mai.md` -- Plan B identifié : `~/.claude/projects/-Users-martinpourrat-MARTIN-Star-Wars-Legion/memory/mod-tts-swl/paths-forward.md` diff --git a/mac-patcher/retro-deployment.md b/mac-patcher/retro-deployment.md deleted file mode 100644 index 20d1741e..00000000 --- a/mac-patcher/retro-deployment.md +++ /dev/null @@ -1,76 +0,0 @@ -# Rétro-ingé Deployment Boundary - -## Visuel attendu - -Zones de déploiement rouge/bleu peintes au sol au début de la partie selon le scenario. Pattern matriciel avec 14 codes de cellule : - -| Code | Sens | -|---|---| -| `r` / `b` | Base red / blue | -| `rh` / `bh` | Home (zone arrière) | -| `rs` / `bs` | Side (zone latérale) | -| `rss` / `bss` | Side stretched | -| `rl` / `bl` | Long (zone allongée, spawn 2 cellules : sX + ccX) | -| `rc` / `bc` | Corner | -| `rcc` / `bcc` | Corner-corner (autre orientation) | - -URLs bundles dans `deployLinks` (`SETUP_CONTROLLER.1cb552.lua:188-202`). - -Plusieurs codes partagent la même URL (e.g. `rh = rs = rss` → même bundle, rotation différente). Distinct rotations : `r=0, rh=0, rs=90, rss=90, ...` (`deployRotations` ligne 204). - -Rendu côté Windows : grand rectangle/zone projeté au sol via Projector. Couleurs rouge/bleu. **À extraire via UnityPy** (task #13). - -## Spawn - -**1 seul déclencheur** : `spawnDeploymentBoundary(matrix)` — `SETUP_CONTROLLER.1cb552.lua:267-314` - -- Reçoit une matrice 12×N (codes par cellule) inversée puis lue ligne par ligne -- Pour chaque cellule non-vide : `spawnBoundaryCell(cell, x, z)` qui spawn 1 Custom_AssetBundle au pos calculé `{xStart + 6*(x-1), yValue, zStart - 6*(z-1)}` + `deployOffset[cell]` -- Cas spécial `bl`/`rl` : spawn 2 cellules (sX + ccX) pour matérialiser le L - -Appelé par `SETUP_CONTROLLER.1cb552.lua:369` (probablement après sélection de la carte Deployment via UI menu). - -## Clear - -**1 seul déclencheur** : `clearDeploymentBoundary()` — ligne 316-323 -- Boucle sur `battlefieldZone.getObjects()` et `destroyObject` chaque objet nommé `"Deployment Boundary"` - -Aussi via : -- **`standbyTokens()`** — `StarWarsLegion.lua:567` — destroy global par nom - -(Pas dans `removeLockedRulers` de GAME_CONTROLLER : Deployment n'est pas dans la liste.) - -## Lifecycle - -``` -[Setup partie] - Menu UI "Mount Deployment" → checkDeployment() → spawnDeploymentBoundary(matrix) - ↓ - Loop sur matrix → pour chaque cell : spawnBoundaryCell - ↓ - Custom_AssetBundle spawn statique (scale 0, locked, no gravity) - -[Clear] - Menu UI "Remove Overlay" → clearDeploymentBoundary() - ↓ - Boucle battlefieldZone → destroyObject par nom - -[Aussi : standbyTokens() global au reset] -``` - -**Pattern : 100% statique.** Pas de suivi, pas d'event. Spawn au setup, clear quand on remove. Plus simple que Cohesion et Range. - -## Différences clés vs Cohesion/Range - -1. **Pas de fig source** — c'est un overlay de zone, pas un overlay attaché à un objet mobile. Pas besoin de drape sur relief si la table est plate (à confirmer : sur certaines tables custom avec relief, est-ce que la zone de déploiement doit draper ? probablement oui pour cohérence visuelle). -2. **Multi-cellules** par scenario — peut-être 10-20 Custom_AssetBundle spawn simultanément (vs 1 pour Cohesion/Range). -3. **Pas de variable d'état scopée** — pas de `deploymentRuler = nil` ; le code itère sur `battlefieldZone.getObjects()` filtré par nom pour le cleanup. - -## Implications refactor - -- API publique à conserver : `spawnDeploymentBoundary(matrix)`, `clearDeploymentBoundary()` -- 14 bundle URLs (mais visuellement ~6 zones distinctes vu les doublons URL) → 6 sets de params visuels à extraire (task #13) -- Pas d'event-driven nécessaire — statique simple -- En vector lines : chaque cellule = un polygone fermé `setVectorLines({{points={p1,p2,p3,p4,p1}, ...}, ...})`. Pas de Physics.cast si table plate, sinon raycast par segment du contour -- Filtre cleanup `standbyTokens` à adapter pour clear la table d'état globale (ou laisser un `Global.setVectorLines({})` de la collection deployment) -- **Décision design** : Deployment partage-t-il la même collection `Global.setVectorLines()` que Cohesion/Range, ou faut-il un canal séparé ? `Global.setVectorLines` est singleton donc tout doit cohabiter dans la même collection. Implication : la table d'état globale doit indexer par type (`cohesion` / `range` / `deployment`) pour gérer les clears partiels. diff --git a/mac-patcher/retro-movement.md b/mac-patcher/retro-movement.md deleted file mode 100644 index 579aaca3..00000000 --- a/mac-patcher/retro-movement.md +++ /dev/null @@ -1,122 +0,0 @@ -# Rétro-ingé Movement Template + Maximum Move - -**Correction** : Movement implique **2 objets visuels** spawn ensemble lors de l'activation de mouvement. Un seul des deux est cassé magenta. - -## Composant 1 : Movement Template A/B (templates 3D courbes) — NON AFFECTÉ - -Code : `Order_Token.a57c41.lua:480-529` - -```lua -templateA = spawnObject({ type = "Custom_AssetBundle", scale = {1,1,1} ... }) -templateA.setCustomObject({ assetbundle = ..., material = 1 }) -templateA.setColorTint(...) -templateA.setName("Movement Template (A)") -``` - -- **scale = {1,1,1}** → mesh visible (pas un Projector caché) -- **`material = 1`** + **`setColorTint()`** → material standard avec colorTint TTS exposé -- Bundles `longBundle` / `shortBundle` / `sharedBundle` par speed (templateInfo) -- **Pas affecté par bug magenta** : c'est un mesh classique, pas un Projector receiver - -## Composant 2 : Maximum Move (cercle de portée) — CASSÉ MAGENTA ✅ - -Code : `Order_Token.a57c41.lua:552-574` - -```lua -maxMoveTemplate = spawnObject({ - type = "Custom_AssetBundle", - position = {basePos.x, basePos.y + 20, basePos.z}, - scale = {0,0,0} -- ← scale 0, pattern Projector -}) -maxMoveTemplate.setCustomObject({ - type = 0, - assetbundle = maxMoveTemplateBundleToSpawn -}) -maxMoveTemplate.setLock(true) -maxMoveTemplate.use_gravity = false -maxMoveTemplate.setName("Maximum Move") -- ← nom différent de "Movement Template" -``` - -- **scale = {0,0,0}** → Projector legacy (même pattern que Cohesion/Range/Deployment) -- **Pas de setColorTint** sur ce spawn -- **CASSÉ MAGENTA** sur Mac confirmé par Martin - -### Bundles (`includes/data/MovementLinks.ttslua`) - -`getMovementLinks()` retourne table indexée `[baseSize][selectedSpeed]` : - -| baseSize | Taille | Speeds | -|---|---|---| -| `small` | 27mm | 3 bundles (speed 1/2/3) | -| `medium` | 50mm | 3 | -| `large` | 70mm | 3 | -| `huge` | 100mm | 3 | -| `laat` | 120mm | 3 | -| `epic` | 150mm | 3 | -| `long` | 100×175mm oblong | 3 | -| `snail` | 100×200mm oblong | 3 | - -**Total : 24 bundles uniques** pour le cercle de portée max. - -Rendu visuel attendu côté Windows : cercle/anneau projeté au sol matérialisant la portée maximale de mouvement à cette vitesse. **Couleur/alpha à extraire via UnityPy** (task #13). - -## Déclencheurs - -### Spawn -**1 seul déclencheur** : flow d'activation mouvement via Order Token -- Bouton speed (1/2/3) cliqué sur Order Token → set `unitData.selectedSpeed` -- Une fonction de mouvement (autour de `Order_Token.a57c41.lua:480+`) spawn `templateA`, `templateB`, ET `maxMoveTemplate` ensemble -- Conditional : `if isDeploy == false` → pas de Maximum Move pendant la phase de déploiement (ligne 556) - -### Clear -**Fonction `clearMovementTemplates()`** — `Order_Token.a57c41.lua:776-786` -```lua -function clearMovementTemplates() - if templateA ~= nil then destroyObject(templateA) end - if templateB ~= nil then destroyObject(templateB) end - if maxMoveTemplate ~= nil then destroyObject(maxMoveTemplate) end -end -``` - -Appelé par : -- `clearTemplates()` (ligne 770-774) → appelé après le drop final du mouvement -- Aucun cleanup global (Maximum Move PAS dans `standbyTokens()` ni `removeLockedRulers()`) - -## Lifecycle - -``` -[Spawn] - Order Token bouton speed (1/2/3) → spawn flow - ↓ - templateA, templateB (mesh visible) + maxMoveTemplate (Projector) - ↓ - L'utilisateur déplace templateA jusqu'à la position cible - → contrôle visuel "ma fig peut-elle aller là" avec le cercle Maximum Move - -[Clear] - Drop final (fig placée) → clearTemplates() → destruct 3 objets ensemble -``` - -**Pattern : statique au spawn, pas de suivi de fig**. Le cercle Maximum Move est positionné une fois à `basePos + Y20` et reste là pendant que tu manipules les templates de mouvement. - -## État scopé - -`maxMoveTemplate` est une variable par Order Token (`require('!/Cohesion')` ligne 4 mais maxMoveTemplate vient du flow Order_Token directement, pas d'include). Pas de partage Global / Unit_Leader. - -## Implications refactor - -- API publique à conserver : le spawn et `clearMovementTemplates()` (mais cette dernière mélange templates A/B + maxMoveTemplate) -- 24 bundles cercle de portée → 8 sets de params visuels distincts par baseSize (les 3 speeds partagent probablement le même material avec rayon différent ; à extraire task #13) -- Pattern statique simple : spawn une fois, clear ensemble avec templates A/B -- En vector lines : 1 cercle par maxMoveTemplate, dessiné via setVectorLines + Physics.cast par segment pour drape relief -- Cleanup propre : `clearMovementTemplates` doit clear la table d'état globale spécifique au maxMove de cet Order Token -- **Templates A/B (mesh) restent inchangés** — pas dans le scope du refactor - -## Couleur attendue - -Le cercle Maximum Move devrait visuellement matcher la couleur du template de speed correspondant : -- Speed 1 : couleur 1 (vert ?) -- Speed 2 : couleur 2 (jaune ?) -- Speed 3 : couleur 3 (rouge ?) - -Note `templateInfo.moveTemplate[selectedSpeed].colorTint` — c'est le tint des templates A/B, mais le cercle Maximum Move utilise un bundle distinct sans colorTint (le bundle embarque sa propre couleur). À vérifier empiriquement / via UnityPy quelle est la couleur baked-in de chaque bundle Maximum Move par speed. diff --git a/mac-patcher/retro-range.md b/mac-patcher/retro-range.md deleted file mode 100644 index a4569069..00000000 --- a/mac-patcher/retro-range.md +++ /dev/null @@ -1,86 +0,0 @@ -# Rétro-ingé Range Ruler - -## Visuel attendu - -12 tailles dans `getRangeRulerLinks()` (`mod/src/includes/data/RangeRulerLinks.ttslua`) : - -| Clé | Cible | Range | -|---|---|---| -| `small` | base 27mm | range mini | -| `medium` | base 50mm | range mini | -| `large` | base 70mm | range mini | -| `huge` | base 100mm | range véhicule | -| `laat` | base 120mm | range LAAT | -| `epic` | base 150mm | range épique | -| `long` | base 100mm oblong | range véhicule long | -| `snail` | base 100x200mm | range escargot | -| `bombCart` | bomb cart | range spécifique | -| `smokeToken` | jeton fumée 18.8mm | range 1 | -| `token` | jeton charge/objectif/condition 25.1mm | range 1 | -| `tokenRangeTwo` | jeton graffiti 25.1mm | range 2 | -| `poi` | POI token 50.8mm | range 0.5 (3") | - -Rendu côté Windows : **4 cercles concentriques** projetés au sol via Unity Projector legacy (1 bundle = N Projectors embarqués pour les bandes de portée 1/2/3/4). Couleurs/alphas/rayons par bande **à extraire via UnityPy** (task #13). - -**Configurations par overlay** : -- Rulers de fig (small/med/large/huge/laat/epic/long/snail) : **4 cercles** (range 1/2/3/4) -- `smokeToken` : 1 cercle (range 1, effet smoke 1 pouce) -- `token` (charge/obj/cond) : 1 cercle (range 1) -- `tokenRangeTwo` (graffiti) : 2 cercles (range 1 + 2) -- `poi` : 1 cercle (range 0.5 = 3") -- `bombCart` : à confirmer empiriquement - -## Déclencheurs - -### Spawn -1. **Hotkey "Show Range On Hovered Model"** — `StarWarsLegion.lua:601-608` → `showRangeOnHoveredModel(hoverObject)` -2. **Bouton "RANGE" sur Order Token** — `Order_Token.a57c41.lua:354-365` → `targetingMode()` ligne 922 → `spawnRangeRuler(selectedUnitObj)` -3. **Bouton "RANGE" via attackMode** — `Order_Token.a57c41.lua:934` → `spawnRangeRuler(selectedUnitObj)` -4. **Bouton "R" sur POI Token** — `POI_Tokens/POI_Token.761483.lua:23,41-46` → `toggleRangeRuler()` → `spawnTokenRangeRuler()` → `spawnRangeRuler(self, tokenRulerBundle)` avec override -5. **Bouton "R" sur autres tokens** — `includes/TokenWithRangeRuler.ttslua:37-43,52-56` (smokeToken, token, tokenRangeTwo via `rangeKey` scopé par-token) - -### Clear -1. **Hotkey re-press** — toggle via `showRangeOnHoveredModel` -2. **`clearRangeRulers()` (pluriel)** — Order Token : appelé par `clearTemplates`, `exitTargetingMode`, `exitAttackMode`, `attackMenu` -3. **`clearRangeRuler()` (singulier)** — POI/Token : appelé par `toggleRangeRuler` et `onDestroy` -4. **`standbyTokens()` global** — `StarWarsLegion.lua:567` — destroy tout objet nommé `"Range Ruler"` -5. **`removeLockedRulers()`** — `GAME_CONTROLLER.623b03.lua:151` — destroy par nom - -## Différence clé vs Cohesion : suivi temps réel - -`RangeRulers.ttslua:78-79` : - -```lua -luaScript = "targetGUID = '"..rangeSourceObject.getGUID().."'\n" - .. "function onFixedUpdate()\n" - .. " if targetGUID ~= nil then\n" - .. " targetObj = getObjectFromGUID(targetGUID)\n" - .. " local targetPosition = targetObj.getPosition()\n" - .. " self.setPosition({targetPosition.x, targetPosition.y + 20, targetPosition.z})\n" - .. " self.setRotation({0,targetObj.getRotation().y,0})\n" - .. " end\n" - .. "end" -rangeRuler.setLuaScript(luaScript) -``` - -→ Le ruler **se repositionne 60 fois/sec** côté objet ruler (pas côté Lua de la fig). Différent de Cohesion qui est statique. - -**Implication refactor** : pour Range, le suivi temps réel est utile gameplay (tu mesures pendant que tu déplaces la fig pour évaluer si tu vas être en range). On ne peut pas simplement passer en pur event-driven sans perte UX. Le drape sur relief recalculé doit suivre — soit redraw à chaque tick visible (avec mémoization de position pour éviter le leak), soit accepter une dégradation UX (redraw au drop seulement). - -## État scopé (même problème que Cohesion §5) - -Variable `rangeRuler` scopée par script-objet (Global, chaque fig leader, chaque POI Token, chaque token générique). Pattern identique à Cohesion : 3+ scopes peuvent désaccorder. - -`selectedUnitObj` global aussi tracké côté Range (`showRangeOnHoveredModel` ligne 7-17 de RangeRulers.ttslua). - -## Override bundle (paramètre `projectorBundleOverride`) - -`spawnRangeRuler(rangeSourceObject, projectorBundleOverride)` — le 2e paramètre permet de fournir une URL bundle au lieu de la lookuper via `unitData.baseSize`. Utilisé par POI Token et TokenWithRangeRuler qui passent `rangeRulerTable[rangeKey]` directement. À conserver dans le refactor. - -## Implications refactor - -- API publique à conserver : `showRangeOnHoveredModel`, `spawnRangeRuler` (avec override), `clearRangeRulers` (Order Token), `clearRangeRuler` (POI/Token) -- 12 bundle URLs → 12 sets de params visuels à extraire (chacun avec **1 à 4 cercles concentriques** selon le type) et reproduire en vector lines (task #13) -- Pattern suivi temps réel → décision design (redraw par tick avec mémoization vs redraw event-driven uniquement) -- Filtres cleanup global (`standbyTokens`, `removeLockedRulers`) à adapter pour la table d'état -- Override `projectorBundleOverride` → s'inscrit naturellement dans une table d'état `{fig=obj, rangeKey="poi"}` ou similaire diff --git a/mac-patcher/scan_bundles.py b/mac-patcher/scan_bundles.py deleted file mode 100644 index eb5edfd2..00000000 --- a/mac-patcher/scan_bundles.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python3 -"""Scan TTS AssetBundles for material -> shader references.""" -import UnityPy -import os -import sys -from collections import defaultdict, Counter - -BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") -STOCK_PREFIXES = ( - "Standard", - "Hidden/", - "Mobile/", - "Particles/", - "UI/", - "Sprites/", - "Skybox/", - "Legacy Shaders/", - "Universal Render Pipeline/", - "URP/", - "Unlit/", - "GUI/", - "Lightmap", - "VertexLit", - "Reflective/", - "Self-Illumin/", - "Transparent/", - "FX/", - "Nature/", - "TextMeshPro/", - "TextMesh Pro/", -) - -def is_stock(name: str) -> bool: - return any(name.startswith(p) for p in STOCK_PREFIXES) - -shader_to_bundles = defaultdict(set) -shader_material_count = Counter() -bundle_to_shaders = defaultdict(set) -embedded_shaders_per_bundle = defaultdict(set) -errors = [] -bundle_summary = [] - -bundles = sorted(f for f in os.listdir(BUNDLE_DIR) if f.endswith(".unity3d")) -print(f"Scanning {len(bundles)} bundles in {BUNDLE_DIR}\n", file=sys.stderr) - -for fname in bundles: - path = os.path.join(BUNDLE_DIR, fname) - short_id = fname.replace("httpssteamusercontentaakamaihdnetugc", "")[:20] - fsize = os.path.getsize(path) - n_mat = 0 - n_shader = 0 - n_tex = 0 - try: - env = UnityPy.load(path) - for obj in env.objects: - t = obj.type.name - if t == "Material": - n_mat += 1 - try: - data = obj.read() - shader_name = None - try: - sref = data.m_Shader - # Try multiple ways to resolve PPtr - try: - shader_obj = sref.deref() - except Exception: - shader_obj = None - if shader_obj is None: - try: - shader_obj_read = sref.read() - if shader_obj_read is not None: - shader_name = ( - getattr(getattr(shader_obj_read, "m_ParsedForm", None), "m_Name", None) - or getattr(shader_obj_read, "m_Name", None) - ) - except Exception: - pass - else: - sd = shader_obj.read() - shader_name = ( - getattr(getattr(sd, "m_ParsedForm", None), "m_Name", None) - or getattr(sd, "m_Name", None) - ) - except Exception as e: - errors.append(f"{short_id} mat shader resolve: {e!r}") - if not shader_name: - # PPtr fields - try: - file_id = getattr(data.m_Shader, "file_id", "?") - path_id = getattr(data.m_Shader, "path_id", "?") - shader_name = f"" - except Exception: - shader_name = "" - # Material name - mat_name = getattr(data, "m_Name", "") or "" - shader_to_bundles[shader_name].add(short_id) - shader_material_count[shader_name] += 1 - bundle_to_shaders[short_id].add((shader_name, mat_name)) - except Exception as e: - errors.append(f"{short_id} mat read err: {e!r}") - elif t == "Shader": - n_shader += 1 - try: - data = obj.read() - sname = ( - getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) - or getattr(data, "m_Name", None) - ) - if sname: - embedded_shaders_per_bundle[short_id].add(sname) - except Exception as e: - errors.append(f"{short_id} shader read: {e!r}") - elif t in ("Texture2D", "Texture"): - n_tex += 1 - except Exception as e: - errors.append(f"{short_id}: load failed: {e!r}") - - bundle_summary.append((short_id, fsize, n_mat, n_shader, n_tex)) - -# === REPORT === -print("=" * 90) -print("BUNDLE SUMMARY (id_prefix, size_bytes, n_materials, n_shaders_embedded, n_textures)") -print("=" * 90) -for short_id, fsize, n_mat, n_shader, n_tex in bundle_summary: - print(f" {short_id} {fsize:>10} mats={n_mat:>3} shaders={n_shader:>2} tex={n_tex:>3}") - -print() -print("=" * 90) -print("SHADER FREQUENCY ACROSS ALL MATERIALS (sorted by usage)") -print("=" * 90) -for sname, count in shader_material_count.most_common(): - flag = " [CUSTOM?]" if not is_stock(sname) and not sname.startswith("4} mats / {n_bundles:>2} bundles {sname}{flag}") - -print() -print("=" * 90) -print("EMBEDDED SHADERS (shaders bundled INSIDE a .unity3d, sorted)") -print("=" * 90) -all_embedded = set() -for shaders in embedded_shaders_per_bundle.values(): - all_embedded.update(shaders) -print(f"\nDistinct embedded shaders: {len(all_embedded)}") -for s in sorted(all_embedded): - bundles_with = [b for b, ss in embedded_shaders_per_bundle.items() if s in ss] - print(f" - {s} ({len(bundles_with)} bundle(s): {','.join(bundles_with[:3])}{'...' if len(bundles_with) > 3 else ''})") - -print() -print("=" * 90) -print("BUNDLES REFERENCING NON-STOCK SHADERS (with material names)") -print("=" * 90) -for short_id, items in sorted(bundle_to_shaders.items()): - custom = [(s, m) for (s, m) in items if not is_stock(s) and not s.startswith("A2>%liFU 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 diff --git a/mod/data/mac-fallback-assets/deployment_red.png b/mod/data/mac-fallback-assets/deployment_red.png deleted file mode 100644 index a8e05c5e28d935439f57cf4d2f709049cdbfa337..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 856 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJD{c~2L|kcv5PuNX2iDDWKE z&}jGPHlw7BugI5IyNuHtmi)D5_;H*;;mjx+4S^9C0tvh1F7Idd-q+F|3d}JKp00i_ I>zopr0J6>oo&W#< diff --git a/mod/data/mac-fallback-assets/max_move_cyan.png b/mod/data/mac-fallback-assets/max_move_cyan.png deleted file mode 100644 index dc017745ade7c63aa858f7608a3ced343b7a245f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4124 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

Bd<4L18 zPhy1_KviTWqb66G4Wh}(ygXz&9|O)f3uwdFCa!4?KlGMbAr;%khN$<(Y752wfi=7~ zWG4G*^5@_P?P@?2+hFLeI(3EmX(Km6l@0{RXC?DJIj|^xzb^^HF3sM z9~PS`6uUoN8GIH{0(P50y^o{>`Mwj7&yB=yv(@-6K(qgn8eWbySkKes&)x$Z9RE3* za02qV^R>uF*@S3(l2A;Yjo6Tj5aKKL4 zu=%7aeAiN__!24|{fT8`0nNkY9a?!V z2v_uGfr~Z0(yKMiEmatc!CA%v8h^#3B;(YCdOu0pdwj|uQwv`&QSUVCoau))5ypqA zv!Qu!vUoYgeZX8+>#Pj9vozsWiEZWlGfHL1JVgkn?@1cm$X4=nb3n3m3_fi@+k@bR zI+`%|Q{6+MP!xim!pt~U7Y=Q%(Yk-1WiTd4I^Gn3Bdu+VLTH}4D-^r-vkRISV5>kH z_so){ZNVQx)jgnCB}StZF&z|CWw3a>bf&5kaM)@1jK#ho#=CL?JWzdsqc%RefUiho z&(!|!c?lYt*PzcFW#g{)7KJFotcVp!ipMD&aeI^AG+})$1IEA4q{DHC!~98D?POGG ziYr^^Nql-nabO~oy#5I`Mb_#;?vq;tp5A1rqFQs-Xh0gm=h5H=FXm`Kh8GlDr#Yb$ zI)?Tu0v8aVDbMqfY_-$m`gwx=l6{5EabF9YCmj`u(w9C9 zQZ#cX>%Uh@9X5^i#>>R5QkyYzu{v%{TK_?U5GF2utp#f)5!r0$iXzi=g}_5*K@Fi zOA0w^<2+1nMESu5Oob^6aVXb9`9zevpj;W{T$H1^h)b1$D|H0EVLCs634DvniQJ$~8psD0W)Fkby;G_juxYBqbDUNaWx{ zaP)=HJLF^!rWA0qJxI816Nu#l~@=dv@RHBjpU2((sL j_or@ySp6?@>S;C418-OBbs(nX|BS)Y)z4*}Q$iB}(dK-T diff --git a/mod/data/mac-fallback-assets/range_fig_leader_huge.png b/mod/data/mac-fallback-assets/range_fig_leader_huge.png deleted file mode 100644 index 6ad39ebcba76a691f394fefe08f950cb75aa9228..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18811 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 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_laat.png b/mod/data/mac-fallback-assets/range_fig_leader_laat.png deleted file mode 100644 index 3a2ac1d221b1d13e9c357efadb6afdb7fda6c4dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19058 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 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_large.png b/mod/data/mac-fallback-assets/range_fig_leader_large.png deleted file mode 100644 index c1baafb4cda99568d4f54cd6317be0e17ec5320f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18753 zcmaicd0dR^`~USkvo!6i7SXEBk`|??=8+}RGFgibC6z;nEJd29O=KyAQp{vOI7A0! zsi{zDT6{PPVIq~oVHBE}n%^~?&-e5BzFyzg>(?LW-0peq>t3GwxvuNJuJ`-i$qVw+ zR2!}a0GhLAPMZV3#3B z@U-sJ?xb;{iMpS<+w%H%#K zWPJJb_RL4Y{-l&|1O0zqA|UZ&p}K+#!e0|}e0Ku8b^wS_DfTgoCLrzv0A0!H-{_zU z6akbq>Cyij zEW2UZPX8NG!DB;_9|7@XvAM)28bsl0K+noZpwWmkqm%0TOdtwByb2 zp>_CBI+IUtG$7zR&R~p^VTGSmMdCUgT6SW2N{@;J`{JP(`#`Bm1d8Du#PTeB zCL|k#!&$)F-l8oUdmn@(3v!Q1v_)YkGe!+8##Ncq^RYYw%fpJz=@oedWr$@K*7soj z1}sm&avYZDVR;mm-(z_KmJP7H63YfyW@7m-146V?4I&({yavmov8;;a4_JO;pxhCB zkR7oCk6k2qWJ!wR zC~0Lc2MlcF4Dc(k7TTHuZ#TmfjO*RuQ8Ca~xHu8mb7!|M*i$kUh;dy@rMQEIvx+m6 zq^*X!Sp=x64gtl?hPu1fkh>elG%eCGH9QU|J*`l%aCrtg0!^5yY<=AI5|xatom3nk zci(E*QO^dZvhT2hC=2XcXE#G1&fal{JCO_^l_Os%o~VN(=ZLB}upz<}9$z6~m#P9N zqb{@11~!a+g=%UN&bzym0Cf)ju(`|91N-yXm#zg8?`IINWeV|nxXcol1ihlE1lYx& zC7?ZKN!oxH3HqLO0AKrNf*5f9( zQnr0t^JO-u%_T*~YG6D~rg9}qHa|~AWatlxMI@Mwk}=W-Jq=hQ_31F;4A3eZ1y2w! z*tH+F@JNxf8hFfWhyY%4eTo47NaXW2#5_T?w^>dk`O={3KMV2*!xorkd zgEI4nwiyJNsw#92lr$IQkvfO<-~%=qnZ~_i(W76m3|{e=qK#@$hK)8dQ(We9M2q~P zEP#N897ViYq(Cc=jCZtwTR51BOn!c_L?si7rt88|dyow4Fz?clb@*h0_ym&v-fWv^8Gy6PY=%LNBdqJp5^nf$TY)~alXmjC1ufdkPiP1y*+78<%*1Z3ip|gZ zno#ro;mRDy-7g8Ap2p=Jw4%=$5X(=1#mEl9N+w^3uY8yTfd+^gUCo`3YJ2m27#%|9 zW|H)T4E|^xIZ<(%BUG~i6ML9BMP}z(Y#w164Nn}19XQ{_YHk>QO1P$V0jkt;HSpCR zs-G!UF`lYRE%1lqb3g|t?4HIIjmQRD&|<2+Q0*Q;7pg=siW9& z(NJJd?(iij<7%!6YK_Y%)>oBcqF9}wok3ES9f7(O+aDIpg_0s^@bWY+<(Lh}=aMD+ zN%~X<|G1uo;k3(j`+V`AyJHx1arjA(!eR~D^$9zI zRS3I}qv7umxZ(g7du%lq9(WXb94IgJ zdjb+mN8h5VcoxOJMtJNBguQbhK401#hm$jLAU)FcXlpN0WLeEEMP;@8jmB$@u627$ z2+H#gC2bBs*?)RG%Wq6>GINJE@!iZY4L_y6qYL8565Kt3 z8T=Sr$)1_IqOtz);S&cot184ex0ZvhxnbhJ+oKqCVfZxT#uf%FZDU8U3n4s;pnhp=XD{4^?!$W9;}G7e)VRSRu|5_!(0Wp&9Fx zv!C(pTgf*Tlp9Y1i>9Ra`yfKw6as%8^zfCb(4j?*BW$!kpM({4Sx2okj6vss>yZ^8z*#&eoeWoApjzZARFO zvks5bU_nckAK7;Q1h_o`d)%1nXk50e`&xR?E93wvv@lIFz%` z$@bSRD&T`=W^v*OAsVEX9oWyU7%vg1P zL~gFM*`}J?sXtCEnEopa*b`+lRC9Nc7A75n>rDQR-&U2GPoD+O4rJLh)!f>wKgWYd zkSyzJhTM(2_zhe*>YWF5cMy3ZazZ9_^+jUJW3U!p%0sWqk2 z_AZEvnUZlfRI(b){8_CLSvm0(x~V^^<0=@Sr#sZ)kt$gH5zW>$5cXRhcrhTJdxy%bYS+(P!*}$P#QSgG_)hWJ!fvV3W#VJDGP!fHEc@f z!%)AieD2W}?S2Qw*&tPVoYIRYIEveUX_I~d0ISK4(5;xba|W}8s% z{&YngQ8b%~cj-`82Qc|((d5`-bsltwK+#Ou$5}Ev^(ynf<}hP(;Z-`pN7@SD7cy)}`k8)5r24 zA&CiW{h^&`;4XfOW$wKH8k~-4()BpE=`uULVsj6F6Ka${of1cgPe4A70ju#mVmw`! zGQet-fbK0DZ5dkc8GVTmk~jkHl@LxlZz{H-aj=y!VndiENA!e7i z%24T3{h_TWV~R}0Dq+tumTQ>#`o|t-=#-0jn#(&b11<9+opvlZrxNHIi!$*@QgrtO zy5R?gG5t>?P|Q9a6~gO)}4@eza--6Yrna(z#7fq zcYD%!7QqvIB-?7OVX~^?Dw+uNe&2ULt)Cfl^IK=>HXJg49p#kWF-+o&-Hpm2MMh>s z+mxI04ojOkKfRL~Jn~Uj2)){iq_$Rbtx@UsmEChmE6|REI9%O8Ez&I7p5<|PHJC-~ z3mq3j{}X7#6M`d-wyFE6dbsuV=iRg6*M{f(y)Mb*g*UUwq>S;bldh()dBwh!dPAQ6 zGL;=QZUraAX&dX;UUz^MwJgC2jU(`>%snBmAa`%FszmeYi7nwhp4IWHx#@nSNA+uP z8}Jo1#6h^FywzHskRjF1&06K|m#@2Of43tZsKChBidf&vN`=-ZP)04DmlJc?JA+Tl z!yBH|TnIf7-hYgwd&66v)QC8|tW}e?=9|0~9fpcxn{#(Fi+WtumaJ7ii}Nji6I%oSf!d$1svEtxQ}hcj zJ(0H=>umRv6W|VK{bb!{Xj)>4{(OhuW)hMQ+&`8{7!uc5@^f#nbSAXM%+4Wsv%YJ>INr!=ZYwT(`|Asm;jS^`%nQ3Kzp}us%qd|{suulwkx6*e z` zDp}j8@z)%nt}*kV5qlgQU+4a*`hvgVI5paFwbr~C5os|x*5Hx!@{~1kA&^@*V_5ny zc;I>7sdWF80a7@1*W3bi2li;V5+4(}sqtgfCwXWEK(EV8B6m-z`i=`52;S}6zXhc~ zEhNJ1bqJJdu-_7D7?{0ocCt;yZV6H(`?jXv!ZW(@%#D+CbhBY z#xe1=@@-@D$ILba%8YFVH{!d9^9RSk=xY;xSuyjwGlyu|Q+mnVb1z~_!ZEQC+?ZdE+s}u2t73(t)3xEbr@CcMPUiZU-$=S>!tvY2=yd4EE*l@S#L9jy z%v%|aPiaAN;}ec&^4R?o8w{ZHr0U-_Gy@KMT7P%?e&y-JVAi}}qi(Dt-rtR9=*zc0 z;D6*B4)*&-{xiFsKaac4XN>^E@h1hc2ckXq8b^E$*q{G^Ub?GC|L=3o@p{4VTy!t8O=A47O7{!0@VCt(hJ zwva#ETf#j&hmOeuZ+qz75_E%wCx^MJ3U7`g&+dJ#tY8ewUTjNgUjvWDPycC#Lz%l@ zBuMwA)AHbpgR)tfvKbyGP~GNPN?T>O9hmkaTWT9Nn|oi`tWS67o&M0Q;GRo?Ce(U= z`dc1&dfPoCaVO%jz|JPM(&!m6Dpntirx2nIABI|OC2B*o8T73=j$NhO2ZhRNykV3}} zL3~C^%pA7PZatwRiwbI+4K6CQ=B-pVsl`S+y?AFKBY%x#oUigv_ISs$RMME#Z&iFy*M^(wF3T>g; zOBPkAN%#D%I?q<_dhq7)4PArH%bVgu-$hAAnBD*j?ahkRK&H8@x|?XLyU>V1HnXm@ zc{!6+H0*jr8-2_JBx5htiUZ%07QIiI^I{?*JgV5&K7~a(#ZJ1==e**W=3>ZAx4mMR zPEIX9uljwU-6_BHwqfZ+8+H2ZYE`thPErqnHT~I#Exu~04(nT}$FH;qZT-*)FTogg z{Jqv?>~38(5JiZhmi*;$ zS}~|Rx8&yPkngyBMVNdN%JoAO-T#T=6zs%mlEi5bKBn`-~>?;tmT_?R&qwQC7=5-5W^{a@L z(y$mtDe?LKZA&$H^>C~&6nyP%NCJlhysWGd#g)J{CVl*(}9 zta)hP)g942x){C2 zq%+kze6nm;fz|j@uVOWLr+@<{l2pI4?T{zF>kf&2QTc~YA5%D&F|5lDMO>TN7|?8E z?l{CJ>S{~0#5XR=V?VtEi*7dkMbS1PGwIi!Lj9rCg7BzCu2G%Q*K$6-t{-T#1|FSW z?wRfx&u%^PSUjAUPSTaXax5(wp7FM=BWBj_ZpOiXL-&2(TCs)J1@Wu1C^2J7Z(xxse7`o-|SEJae zx00T)Vfd6f5v@6`YdEW;U?2%YvKbRTHX2_@)d)Yo$axnPbjAQzme6wHF$&6Jh>WI2 zcQ{h2mX~wywr=`_+oHdMY$jm9ku z_Hv<1Ygw;&bgs4EGl0;>2#pPrMJgplq|kB?8 zmuRIswd`AEF1q5i8l$`qPa4Glds2et*oODyWd}HN`L6?6j6a;x$J47KBV+QtmZzs+ zvLn_-N+x$4;pA3=RL&0=Ug0&Nd%DR6;zh#hvGrpQWbM{z-5rkd@NYN|D5v3l$J;x| zvL?4Hqg*v7*rWcfU4B)?(4~!R?_Q+ZP^Y$TPg5&SRwpoN3VRX#cerbV^D}BJ<~hPN zuE6*5O=Q3c44C@|YYp#Rrcna5<=YP}yH`IYrYCX*AsOIfKRXIaSA)Ts`i3d4OLk9Q z2ssJk#@)SeApJd0KtjYQ#u$;u#oxeL9k*lr?isgq(npIDFa92#pA@m}#WjMLKg`Ty zA2BS#tTohQk48u#`Onxzz6(0C<73q#d|IFCiG6rN<%GQ!Y?h2-&!PLux~d(P@64ak zxyBSqcCc?jy1i&1eCtD3M%5%`{ti}91TXjCQQ3r-tTG0xWbdl@EiuKi<=IEf9KlPW z{YR9P8QOf+cty|a^nquXJPLSyv?x{ILaVj&k$76X4~ueMuo3fZy32QMF&nKkTfdh* zWa{Lsa%~aUs;n&j=GiFQv*!2A(_kYG`k#~$nqtT~!Lc{Y41UvMUR zMt0!6vu~6xfXop(SZ}ielYmN*?g&6n(!v%;@?nP$q@A^E+-Cg9mm9}n&>^5?et5{0@ym> zNN-3sqQWawEc*Ka6F!NS^3trE|5`OLPw$HI40YHi`FM^;el)`~qsU7`JWo zpn10Lgs8snsq4ja*40~-I{^KnUH6q?m5Wj3_z&wERgOHH4>FP}3y#ucarT!F3{Fvv z8Eq_hxEf$vx&EESX;I@+1mUlqYEL)~Gw+v91cqw%)qz1$!)90KDWFp?oU?hQr8Ayc zvqWAoAU4U}{zT1i7#vfm2o0u`jIZ1Z*-wtNqOifTzK4Z3MYp6rqA-|n5@y!9PIL|n znO!YvEfm!*exZNcQ-6+A)ZI}BIzLB#Q3v{MhS#-Q+@YJYjUZa?4zy4E+qbi1v12|S zJXWMhSr@9oQs7(<3rrU>Cw=WbmYOSGK>mv+3U6}ZK7Gn;KLkOQfpse zFDBx6yCnDQU(5!B6PQ^En>AZ>^r#QheMaB0ziPsCj}@q!bzcI=73AIv81e_0-Uc)8 zxVnQ8M^SWltEOb6$*E96G9KW!u(uZiEndgHNVw|hN_T#Ev)L4+(0shgH_iBb;>RkN znDL3bNjTeZwCD!YX+fXag@z}D!D#>sqpDr@w~UCHGl<-b@vS>*)fUBnkUSa{ue+yz zd0j8HOaSC3Wh4AF-AJXKRlZZc(~fX1~{lkp?G$W?VQ{wUasK zigZcl!~DAvV-`go`;B<2ced`{R>KwkG4)djc(g3KRBKB1gc` zMuN*Q7$PCOtDscW{@ZU;;*YC+d^5agTx3DL)vFK^AQZeqs*km=J9V8?lIu-^yl%JSZ=24>gn3Clk14&yDrG#lO16pMk@ zpqVj$gf7o-^SO|Msdpw<{xV82@Up_>i$5sG0Ywc*n>{QQJ>12fP9I98FaI1*(m@75 zEm`11DAq`afJ!4%AupHK45K8{hH6&|2;0f*Sr8g}3X#?#@>lX@$!RJYIBd zH3bmcw6#CBw0Nf0b`Pf=fNz=}Hi@ZNthhOhK+CKPl2ykP+I4?LqYGRaJ8npyo7IpU z(;IJ?85NWM9p@sptV<4*+!i;#@fM5(TimLmIP$}`lo{Z3l(evTc|`SOw^o&><;$=LmuqV_+p}7}+!)llvF+ZKbu%)TZBKuP6D{$(E-@*1$%6OC0VK{xKLn^1ecJHt z=!S0>2&d~HlYM^JpD;8AgmlmpSJO&UD0`dm;yM9$x}V%HyV1VS!0Gk7-pPOU7Btj_ zu|^4TMvHGS8`R_%L1N2-aLKc%$aJmER;>HUljKd4lD|r(#CR%`nea>c^7$#7A$}ie zk<0fPy!;K{K4@6KzJudf)_2O^dH&KVn^Zh2|EjF{OHg&8D2$57?b2f~V)Cb(O!^Xj zIc;ZpkNTxvK6bzI;eEk$e{ql>%iXeoWyqq7ES|Z4< z?8_De{#fKs&1bj^DiKjbcPu~ZflK&#-lrg^XR_yv7DSNjsK0JsyX>yp4;$1&OLHeqWhsIIT6RsYOzyf3lq=3|O<$aIr=(O(G!p;u z!}bGZzMvwhVJk_`z%R*bC|OS#kf3R0x00aot%+;V+eeJcyX4=DJ72k~i!^_>lswA` z=5DL}HXVTv3vQLyZe`N!xPN9y!_dIz+P@CtKb`i7_0zHW)1|gCR}0sCJUCkL3v9=9 z^wJbr?RY%f0HhaulBlzkO(zJI|E1YDkgxN6YPW9RSM)38^gkoUXEgEc z{BR-wJ`PNNm0Q^C1V;a<`zcCj%Wv9q;?`zNy5M&BUyc5|FZ`v2{|_>Hphk<+r~J|GY$_?)^Wy{5ZH7O`O>)R}B$nk^ip_bQ<^zD#8C#cY9mI|2=2P z_@5^#R2C62UNR+!>F-MG4+U#(++KJtc@tqAMCgbv1;6|~MHM@ESm)*ZpEdb1KkV-q zvyZwkT=rGz94)Nm;4=}9_@Re)H{7u>5`@7T+~F^a6ycEYN9IjtFr;r5YS1 zGRpkJ=iWp8OxW`uvDXLYIM-Fh4x=Y4cg(Ukh0V8&UKS2vuC@h+U3^ zt2HXdwuRuu9h85+rr^MY+tv;%?mc|F`Co5K*&?2xuR;^`)rq;1-Ax}!*n~!IScSK{ zar1(EkN<8uDJfra{pFPF&7tm1XL;gyglbvj|8$h0?f7EYbEnzNg7w7K3|BI0!>y?f zJC0p?-F)kHtUB%BBOa(FsZ#1&jpFwdo2wf&hQKLb6T>deq4!z`f=LJvMCS)2`1E*58)}V9KZUcELkekAnNNUIOEA7!oB#b57!#< zQFDq$e27SCeExTXJp4u96Cy&71??RT%gJ7Gv{{;XEBVnbMeFc@xU*xdTCJGfzpYJ* zyKlz#5h;9G<(UN;DG-X=R3x8#M6qblYCrSUxoEnja?ec#1a#@r!sJ!9?U7$xjH*s| zFG{}p-!8y3mgrrjE+_d_flI`!_$K3Hu9cfC#+42a|LXp7D-%OAi3Y+C)@1ciRQsTc zWEA4c`qjEB@q618LhjaldR~o4<&Ws#n!i&}%WxAkonm^}?cgrb;tcd*T(?MRyN0Ps zL1a3lMnzP3yBaQa)X$qQE@~e*Ia!ceROD${@L<`#$YrS9OSZ$bg)hFtw9z1UpdiSS z^I%WE7uc06T`U*~t7k^I$7=FWy={{2HVKgIwf`mYQcZk}YnvaTQ#Aeoy{EmPKG5m- zq>rE4i=0b#zSqN}C~eFfb*}dX+k%6ya~;S3-#%emeQ8W;*nOtr+G$G#91E5X2ZI~)3#>kW2?#&<{l7EpYY;JrMm-oU#S4Phg zkXea5lFQj57HJauX$d3IZ^q6yOHvf^FJ=>TebxJ=yOyfBVbaT>Ll<>o>;t`$e?7?( z-dpiv?lzAx)vgIuuTcj+vMO=T?$=Yg-upl8)*R8<6jUj(y|pf{KbYIZ@tEdbkMK)I z=&W>MhQ~i=nD}LQ$RNO`tmfeXs&>JPG2Wi0wAHoFO?(#6-{c2j4MKKSR9k~;(qmiO z^S{n2d-$+W_`eTOCsDsTOTTofzew!ZMwh}0rM?cXUb-};A0}Ka2kCS=UNG$HwDy6H z%$uUyvTc0@t`?f5!*(~mbH_}-SW`r$MXpY%gsJsbNqAzcXeVBcn{wH(GT3q5-zvj# z{UR3pDTFHLiIrUYssPq5b$H2uyw^cG*YOA`+qN!el@G7wSkW4Wg>&QkGy$&id6VYFl3vG;_sU9iXMa8CLYe4~(XMPyO@~&j)YX zG!lc`Vr@IjxA)5VnlDETYNsTBd;9(J_)I^0SoXccyz)Yd>ibh53HSv+S^)x!{&~LQ{3-;^c#^ld2B9N&mmVgoea7Ioi`qV0Y77BFX2hw0Q*=r2sA{f8_Xj?DTOPlj zYK#KWXQB6b3ms`{{RPETDJglA#89pxD%t?%#v2L!<||$eU@Ty~+(hJiq3h1+CqZIL zRY$ODZ1#)~#lHoB@w-cNyZ> zCL>W_GM`wT_WKcrDVTq~v;9{Rv!I@CuDV6RcuqN-svqIqx{U5qhaBQg)OJ-LX)MH){>| z$Q&EenoElMLF7DKYNc@!`pO?4iujs(wH(7n_=nz|z4$M!qPn9FbW(U!#{`#YtJNS> zJUZNxJ&*nrzPQ1uk1AFQ0ONVY_Vs}*<0IEGU>2Kcr?Mx}nZ1CnEkAT<+2i^to6h3k zm1EKv2VQKQ1GeK;8WWvAgcBpWhcC)Du~R9qytvO3dlijkhth-LlZn$IKv@j$O~X*# zv+|fjQP)xtC>^2rvZb0_@|6lSufby z+mdheM$1UF_2M3zg6Q5{-7N)kAW;K>&*_dLql-J?OxH?NXj~!dZ7DK}k+9;&jTLJg z$ck&hgA$oQ8^%ak|A}0}0%u^1EdNS88UxANRQQ5z>mQsniq&CHTGu$HQM`XwfX=fE z%9v+_8^gIJb=64GdNstwo_Il58J`uqUGyqd>vXz`IAd&@Vqw8>;q6;xnqjSe$K4g9 ztMsS|?*fhUQvJgpE^<0SnW@qDCh&FgQccc^u4?%1C$apz)R|(KlyABkXC!*__?)T&+k`p|ir_3H~$mx%v~PX~=rzOKRN zvQOTE`~#~I>OXv-OQTa{625AY>naM*DHX>p#SK;MMO7p6>)lSfxz1P;v-OY>4j%G7 zgr@&W-50(P{NF^GVm8r1JbP-h!uWBjZuo4c#V2Da9 zM&5U-(Z7N(3dE<%yGJs7;&-lMtj)I+t}(-pE7MbyRGU3sTJVlA${#7bc8Z0lA7w@( zWP3_aqZ|0U-0YU#a#F!p`f9>JxDBdxpYP$3G4wYBXg@Ve+??7pe*s;rE!e!qX-sEq zjNNoX;tU>H=|>AQ$bYA3>=cgUp>E0vj%L|p&KFtuRP~sR2rs9sho`vf8VcEhXm=-5 zYWzzt)+Z&=Vqqow!}soo=6FD3$fY?;@i=-#){l|d_qMXqebS}rHx)@gDcg9t$S!2<#*4Ff6Kh4caiTaK}bgifJ9UvhRZ01k5y!M~L@a%uwk&S=b5!H@PsV zrE0M0;e^ML5Hazkz1BLJ*Lu3NQp?rztvYSLN*CHK6GfAJ`0$&T%KdUo)AY7@JnjfW z+`O+O+$X_HRIoHFtjL8$o!rE{;iMzg*=!x@BXBkAypZK~{LQm?79_1OTjgvlbs|{z z`pTCx<_FS!rr~5mE}6IPD+&5}fw6fq$kw_;cFlLJ(n{#!bhx zCuU#Ey;K$}_-OtmW7w?i`YR$#1;f}0ry0NgoI7Tuz1@lYYInG0Cv#1eF3tJTD%y@)!ZII-%K`w9z$Kf0@NQFb)pQW$Wx~XA(HCy0t&@-7{yIv=p`k`{IZZ85GpK z69a6@+`71*;90?F=1P6ptRR^R0(0Zl_=Y7uHt;rSp3tmVV3ncxhDgJ)|EzM6GNVt} z&$7%-7X?qL=5MR!FKMoStXPVH_U%_xsaa-5J7!7FcyyQ>kNf+_Xg!~hJyTSv$^NP3 z8QYHN4sDI^=U(d0*!-i(=@R_qFnMl{#5hvWmBEiV^vD#(#}S>&)e*P_;X-5Cw3e*R z844eKG4DLz;)`*I(CVeEo*G+tuTk;meZtb*UH=E>%x3Use-5xEgcLE)-zSUBwj-1* z1UkPI$v!9$DKs`p9VAoIJg7+sJlpnt9`)7mw5yTGr~L`g4MqF5Xm-Giqoq2b#q$W>se^B4 zT0hOHQlT#vr4HtAL|J*Rvxh-vS9Mzs!J*d_Z5zyAxASB;!TarKs+NW?Z0HcDCm;I> zC@KGS=2jOSf_Jp^qFw2@2YF-R+|>!s%XnXxJCVZCvEjinPvytdyY6eBUkajU#M>;s z{K~geAgW>VEspFrh565p1Wh=7+aw?An!+9zhPhXEA5XdxM^Gt_MYm0gpzglO<3f`| zb^b;$f3ay#HLxi6Cdayu z9W(9n<+JF|@@RO07$`{0kSO7~!~ST8o~K`_iEIz}x_%R--5l5#@r)GqjlXTRXKbr+ zC$!x!sB(*GL;>BR$1J$|59VuY(XjBSdnfm=T~JALe0&B!5B}9E@~F_J7b5}|E~yT+ ztf@GXxj_3_ufPj#%xE~i0BsPKiI+zZ**x*?Co$Z zk(hxaeyk%;%8)KCys5bU1i+&X)Z%?wmncD_ai|v%$G96gN)e7#0^RmOrSkiZKXf+E zJDg7IYMlND19{0kX?3BtsbU*%h$>MPohANs2a$0a_XI~F-RTFLdhIC$Fwa0z*$8Wy z>wf7{!A-@pBql6D(B*!@FoN3}G{fat72@zmM5qhh7lRo4l(<(3Obp;l`S;2kQet7F z6cH1`gycYJtYJr`PY0iiC{dP{MvQZhGLK$27NhE7wuli&Jc`vvngIiTQ5_PGlJ`qK zD4g;2wo95BE(f@#oDDb#M8#iEVoHcbXUs;7631AuC&m9BbvCVUDM|a`gJusCFE0^j(p7Q9=SY2eK8`qqGjze~ zhXfBbS~U;0*%P6&bOCuGrTe8XcS*U2DoN0m9wtKTmuf&0x;jUs+BugIB0P>>k_Xd} zUKgV)--+Pl+UJT-2B43F2qASct-u&rG-@4{<{f-3Ts8|c6$`Z-Hst8ZcTSUKt&797 zvbxaR26P=5^wx|NE4`tu>-+g;ED;W3(4N@guM4+qWcmlCFAqq$h`f&=a~L`YYZ-Ji zlW(Pjz?*^{F2n&$lo=2$Ig;QF)%>L&hMMuDw@*)KX9HpO454GOU~Gn>B156XIotMm zKv<#|{G}_e#~}>FL6O*yT8ea}5GJ)Gjr#;4<+!nN2;>jg_VQC2v@9d#3XV*FsQDCx zPE&0;tVfFt2p#M$rkcODS}}ka?jrGKl4rBRo)Iy!SfG}{N9r7%i7R}XDb$MwvEApn zJEiFKDADC}NoDQu>9E^QHg^wV;4Amy%q(*lygh!*2#sRFxePv1@+i0lJd$@5iC+5O zehWeXJ~nrNG#&FVc>o-RvrG#(cQ%tP z-W&(!*_o0vt{vvfE)zPt$H5YNf-<4j_;NXl>fcATf+I-}?jfnlqcorS+7LJ8kzBPE0kwr=!He<}@Os zpgMhlDkPA?IYQe=q|^;rnw!F`HWE1w$j^1oO&4l+>kgIQL2Q!&>_kcga_d$(upjGu zjvUt^n4O^*VF2|%tDPNG%&a@7A}8oDN0EfXwqLI^hO}5p|Gh|$)1R9o(RRbMcAX)# z+7rTx6{k|2v=O}6j&LzYHTq;8grC4v(+uovx~#eT*`MR-sN}Y?ds8$~H=ULkCXKRV zQOkTbgufQC!7TES#L#opctj{oM;Zx4;3;ioDUn4wYcoiA^eQfP{6MigM%HB6m6#?QP#qH4xWn+4 zSL-sW2w1;De&S6QjHF|PL8+u*S z7{XS^q?}2kb!Yk#1JE4EO~@k{uxy=sAE#Vso&yuQ)`3~aK7>2@NxMU;`N0Tmn5toh zv0GPT4}YR1|8b6=2?dhyrX@3S8Q+jzYsQM;X1H|s6(Be3vMLg{2#A9Sa4aJ}!csxi?B z`oV%5+qM2HQqdyOBJV=kE;@401JY)MTbdnbN&>P0tn}obh0^8_GXfcQK1KC3i<;16 z+Y~d@rRxsH)wU3eTwjrY8nQDKUj7I$MFJy_3Gud7g83PIJR~^fDfx;7h|w$AFKxE3 zP?G+NLhnYfMPr+c7q~2S-LDDg+>88i`o>eFv2GoL2L=#uf*Ij99l(z>G|fWpnoiQc z{WK5}>Qw@Jq)~)m9$$~PN6Fz4a##Ke*t`@7T%?pgQ$OS|<;QrdD8K zSS1KRP7A_9x9iaXD0Y?V&~l`2rjj%wGMAs!z*s^{@PcY?2(m?I=~6RLtS3`R5!yL} zphA@lS0?PXlpQFL22Vq=bIIHSE07ph3Em^06G^DYlr&iao^gieS;+MZNZJ6&x5XNG zC}{~s2lW9mW~ZuBHrXJ=jk*#EsyxWe)}yoY2+GKpJ6~0?ycij?HZUKZfOAa#Xk@t} zW7gjvM$cu!e3WV@Kd^_S9~lty&S=0D-EYXxwHw2Nw+=*)uL(@E`TTIFG#Kv*@aG|u z7(E7G=^4`w0aADKikU*|Xoz<&7F34ZV zOI8aqniccTfKby0Z%)vmC;hM=XBaH_+n$V=91We;pKE;5xJT__#Vl34Xn|qjSZux* z8D+T6i;*tA+ZP@ysn*pC*ryoz*F$eH#hnetL6(rCFN^f55+LtdYGi{f zZ5{eMGbJAx7%WJht_s~e4lUA-)f?(SNBdSWlkP)S8YRhc%it^X$qR8L<4)n%_`L)# zSonz9OSTQ%Db}%YNi|=F1UnbZv084-=`w2#shmwwo<>$bhOrH_#~;Rd|F~hxt9( zrWUSByaxdqu@!0PafwFZ_X>OU<&j>d;Iwlp9+!<143w<>Aas90;!|agIRPa1=l?_Q zH$c{Z)^HF-ca8GMLPv!_4UD%Z#JeMIlCIH$o+dA*^3s4^2(pDqy7I`8_(T3w9q6fU zXRN|wH-(=uChmrG(((R*I~^RD7gdq!fiW*0j)WE?A+i>4FL=|!gexw!RDe5J5SX)~ z*vQL|LH1&NJ_!#B7Fzh2;-v_v>n!xD%zH7gQSY&wpzXyrxx4UE1TkvLk~E37rvnH9 z)lWRT%6xqvUdSMEV*mTD1)(vD70pcJDlcR>jvt=L8VP!}2uIGFY6>3rM-a$EMX&~r zb}?s&hd9#V==j57&QMFb9|(1`CWG|Q0DQx~197hO3nsq@Q&x=*BnXYF%#{XeLM#Wt zc}3q_P=iU<;`D^JNYyI_c!82A%`~p`2nl8A)q0x0^Vcr6qX_R?RNtZS)xV{oh?=QiVy*96eTKW@|Cw^IAvG?bm_@c{V+3Cj8k$t zB+*7PHo|4_p(teVPr!!`;zM6iQW#2dz=z@sCE8*PZ3Lad(c*a(uL}W@9(Hj+u-_EKgI`V|kvkjP+so z%pZ9K9wdLHrk$qd@Q4q0UHMfB<8hf&qL^#KkBfO!xHUzrvMfy zEfe}{s3YKM2pgn$t-uDHa_XH zxP?v2rlKXtl4vV$WQkCTMpTb4SLBWj(y{S7&ft$LT+B{#zyte=$2%w@ETTb_hz}X# zL$mOqTGhcHm%#)HHJ~iGMt+b7 zSHm(o)lh}y04$ebx!#^h&t#_jxVB~|)(^va<#jfhSZ{*$!?8Ra>-}(aUvN4{cpSLS zkt;bjaAXdLT1#@3*X7{sTQXX-TX4_PyYk4~!V+!KVyt!#;ZSiT_s69>9@8w*g2@5X zc1YGgg+ROaNIJ&`zZ#J7yG7a}4Qvo}$^du7cYHgTCFNuj=kz}l%KLuMoDo;_zM{Rw z@}z9yxXx$g^$d7#(0yWtelPLk-XPQa#6{g+LV2GM@I!PS*+>|eU`Nn}-W}_9B*&xt k{ApqFMLcZ?t{LQR?aAhg&otoX#9NW32Ti-^8`}_CT?;o$*J@<2O&;4B2bzj%}eI@U3 zbyk|MIUgaUv}Lp7E`$geLNaTJvQTrj;XG7qRo~*cVNZDOm$yxqm(^Xl)xZ45WR{jo zR*ej9uiQFDF8-CeQd>WfBXh$gov?J&Ys=0bJhFv?#ELi3QN4TZ%1_VIbDN1LXCGxd zKdVGY=XAOF>zw|zn@x9_EzwGd)A{b{43mIb22NYl5|Fx^n84ZSV{cH4N8v7F0;^;# zkA+yHTpC)P6gg_5g$mXSNpuZ7TTfjP?T8V{QI}qDlS#m%gSrwNaohWh3)c`*PG+JT zs?*0A%Mr(gPhstrKP{@EBUV`w6D`o2@U|o1P%2e|Q{OmT8=@$Ky0(c4CCOch5sC!l z4rM`lTcQZ+%blT|(3L2L`tU7c0(J5lt`@Q0acOi1ABwRe8g>#B)=?*8oT0(bsu*&T zk7lfjL#$KKLfMC6b=Divqxlr7Ni`|Ag^H+#LQ;BCmt3KVB6_|-Opq}Y#TY5#5nlyE zIQeL*4Ls@&kCJ04w_Z>Y!(K=-zR)N4*o26(9K?j&%_D@U7Gu<>#i#Vi6jJ)^9T3Ym znaPSHMzel_e(GTg1o>!IskBVSkPVbW)n(8TLkZ4;EJmRkGN?#Tg8Pv?hf3A)$Oy{X zP;U)wdJH7EVklQYo2cz#!V~HY?q?GjB-$({e1U$n74fWAF0Jj#u-tGX84W}A9bz=v zZ%Rau$rxRskiv>o#IZhcX*+U;d#bV*NyQ?I6?TaIFaUs5zmWf0LGsw8H@ zk%j_{Wss>VM&BAsVP!+_N4T`~v%_*!6$eCB5R!g{2_!X>QIe~ea4k6+nZjfWRWSNy z7^xXXI>e=s6Z+%|bmI^eFC@7b&ftbQWRwN(=_QP`-c$zplQGUm7zvg-q=In{L-C0p z5#_?xC#udNL6!~*UM3`IZJHo(P9-3YGM8qYJ1obVgYGXt`5csmP=@-Ft56=$z@x~h zvd=W7`-Wm%pVnvvM>|Xsv)zb}7}_@x>jAWb6|k(}nctwC1m$ulJ9BBlnNYTc@;)eE zhVm0AcSgOT2@(Wyte&2xn9dO~f$gyz(d}Guh{vVD@(XYdXn4EW!MBitW=v#Q`L^Sv z?ea`C+^C68-5V#hl=l-5+iD&a?QA(go?bs zpcCEGiLpK9IM&l#JZtZW*ukWRg4C;X@IRN1lbp&o5Rh}oeDqZnONT8$tv*B)VLeGo zhT-~LI<(U>^O&2TpI?l!T2`Rvb*;2zdSV=lShR(@*al0FUt)yFmv7M(q={hUt}6!y>h_=c+}Of0;z8k*D`7- zRyApvRckNIg-i!cZbS1;5i5jCQ(PvFX~xk~s&T<-ICM@&;*Hu#r{!ltH5SqjB%qQs zQDR;^?Uli3sEHQqwIX8J2uU_X=B*8VQw(Edu8uese2N8mE%o zL88dfUQc~KJxUgpGP++^FODyoZt^wk-yLb`=XKBz8gL3lhUV>V zP9SD$sI0S!xaV8N8CL{1HQPyh{D^dOS#)rjWXl%O?la$nZ^3*5x~Gi&swcippCG)| z!0(8`q3k5)E@jCh6W;DVnuc5tjs;>cOSGBrL|KOo8iS?xvbu;&J zoN?@K)Z8b7oRu-HO`=c>=t@};XLS{aoXMEhMp3A=a}B7;z@h6AgY5ozT7ZfK<<{9H zntALt%gJLV9J|0bQe&WIkPZPMc-BrXt(M4al`0c1nPsMqjAKOs#C~2oh^Y%2_~3N} zH2JleRw>U+1!0xc`t8JzdlrW987Z;Q6cz zxu{QD$n&aU?}Ul>_!BwHWl;~mi)Ja$+@vf)!R$!=>+ZFoAX|%%wKHTCG>|)Q1=^#y&WQIf zyls3o&r_?bmM?=+`?Z_2J48`@1x%Egg5T$njyMsvvYa2EA+ildWLm(qgV#*$e|T@O zp_LiB27kmG#PhiIJdTq-<@{?r=7Q=O=*rup* zj~6vK^mSYvZL^m2#^qrK@w9EAu5MM^<^6VlBZ*&yqxfIBw;8ea_Pp`fYW8ZViy30i zHS+dG=#uY4;!`xsh={enXZG6K21$x(>43InG~)Q8 zo&gclE=;1JBI%K;zJLyyp6e?3g1I zr+Iw=PrSI_p>cK4Fev3`a;$w(a`fo1|5D7!7=C0X^!!yzI8x>^orhAzTC;AgiRpno z_pcRGp3TjvG_o=Ax>McW)E7r= zpKvhps*PSqz)M9$ry}|qL7Sb2X62b#4t>1oZcRl_?gP*O@BN2pI+cZl zoK-#!t?i!UIf)JR#1UIWweGrA1`as&CdA5wUdUK7k-1+*Qj*p-)s@bTUAN$)vh5$ zsmcITM-lm`V%74@e0ioQr^^sui$po!s`>C;${30_jARU)KCDhZ`AiP^l#NreyqNK{ zJo#u-Gh%@m0YR_JBylPdA}ER3J^t!+OIf6jxOx)8w>q-;_(3uqG!NAAv!#Z|?!3O6 zm6>cD=!Ws7QfmZbjLb|Kh;MHd)cVTe_V}T;t8XzYE|XeQW!dj$9!q%$BWV&??_j26 zYy%N{7f<_v7_H8uvQ{dhQ)VcOXNDPP3k+?lNey3@;`a30AO=`GxURp)woGJX_a#m% z;-5E@3k)l<&}6JJp6OP@E^?%CeTnoRWs#i*YSxn&sz~fW@+*te6E~o4bu@7h87Brv z`W+9Qk^i@HAiEM*mTj9o;cZc!r?SX z`2f_GhT~Ld%_hdlGj&zx+7g}dNv(P)*vJMmEc@J>A~4jgCPn#>>5j7KDRht~qFoS- zfoeHFEEfba9~Qh{5w+?_+;qi}$`VK2nV8=|S#VLoVghR*Vvr)MD%B4$52Dth3!()` zy{Q$GhmF(`Cxf=Su8t?i;OLYvEKOfRGJqbi=4CT6P;Hzc8mmKnR$!k|YuFx+wBU7V z1mlbW%s3$rBXs{g<{&bQ8N9DO$QdX9zFib5(@r`bKm-+r zehw8gDK=Peq99;hJ4tXM8SN&)der?aXbyw3+s!ggGO~u@tV~S z&--Fa!pr{pU{JkZtqA#oAl~naUVRW@>%%1daB`l;(>US9cSiiq%TcT4tw+GE1 z@%Ek{yx=D4s+DKuj7Zi73{e7TQ zrVnfubuJTkZxj)lhxRtBbwryVe&F|Rt+*=gn5WpIX`?E2@I?0t)KQGfLj`&^m_pXF zzx2c&Al$hhG+Xv|#QuHgVT0{B#%5GHDFGIJ>$EsRdX&j{*3YF3s z#QHoU?)C1h(yo*kdG`}{#O+2#FbnNUth0OircysorG`?lp!4{Zl1;xV2$^wS*j~?9 z9yPkk1=^iqS4w^?a%%D#a+}+kpJJWnu8w~-=!U-+Ps43K5hl57ydu^Pq!uB zh}8>-CCUspJYYu_Ee&iBI}kd;zUTR*qOOKiV3`J&f&pxg-w-x0?eGPJu~KJ0^&<+wZ}k`JJ)-j^RbtJa+Fnl@b8p9ZF!#guN*mc|=i;x~UM z_82`xNi}xz$7b7#l-|_QDzZx^{V&X;uN&R|)Z^;KEq{o{EV^&k*FCUIS%nIZrG0AA zG=D@ouUWcXd~)Wujpk}UAjhHo^CC_k`t$5Be0Gn^p4od@c&frw(X*l-8Vj7nC$r!0 z`QY<9(`g~5E1Wr#B_?zqs2=8k<0vs%=4g2Htg~nH%lAoQi$Qxcb6#-E659zF_54 zd32HA_{@%Soox-Y^f)JF^jAeEuI%!8?1k#pPcB;{!H-g)F!f}A(P3zHU%#oIW?iv) z(nnc_ZaI4Tsrwt1EsdhP>u<+wHHt>VFYL(t*&QV-wn3}xbCcff1p3L*GpF4LRGcbA zcdf2Lt1#53H|3c0#i8_}(PHfVLEYDJP76>}%hOW1Krh|!lPi9Geej0D;#zH^pgu%% zo0*bWeC$;?GdIn1D?5NU6n$GON7gO#0QPvH0Ja3~Sdw(=aN4Q!vhzwr`wt_eb-TH> z$_vXKnE`R8bGVOP%|P#r(JkI6)75QW81_YMc3yDTP4qJVdGF-7$-)8?Lv*SrMhRtI z<9>UP%lxH={iCDAT5;YX&<7Sm_RHO0-!g457aeh+s^pU4q_4O~iLks`p7nY}ViqSF zI$l#{(eGxxk=8j@PiJied1!|Ej(i;Gy}(2Xs$%o#ld?FZ7{t5Db;e#c&?>tAzS~kv zJDfb1V{1Uig<*kZv%KtG%zm~P7R6 z)X9j(+vs8lzFi5h&8Ydkw5X)~7lx40} zT4rpBSU=ieVRNCSpATdR47J*M7Zh>CdQ>lD~r;aiVQU{|}>q9iv_=+6@h%-;pkgN~zr z(X;bFN<=_+bw07R$$INUG+A-hxE(2PG248V5y_irxgZsZ){ElhOCx$iji-XBC*afM z)fUqP=^`APz)%kSY-_*lP{%?cDVNuZt4&@S-ZT_TC{HAGCNUFCvWsd9W3F+PYzISY z7^5Ffxcj~wRKQHN*c_+ZDk<|4lub+W*2*3PQQ)4q7HjHXAy|xcPs`b>N))~2R42!+ zeaKT`R43_})i7qZdrWE64Ho7eIwa5uu1!c~_SuHXcGa!X*J>C_$?F-2Y2%5V^RonJ zTFqC$_XZ)I_kmnyZcWp&G z&O~Iiaa%{0;|_#2ZXKO#dvw^Lo+iw;8XOwr`S9`lacjSMfh=)R z-3nz)*>Ks8^H=RtlI5S*1zb5(G_T;mn}if5-|TR_?1=8h{q|LDvF6r+$;?Cbzp@h@ z39tIfnh6~;ZB3QisO_mvf-CaAK|x=8^cNNs_xe{M-Pv(A+rLUC>|b9Y;oDaz!x*CE zkR?6(;%gS{kkOJ&EbSUDoJrBhjKhX}r3Cw^ch{B$PKfMf4!<=9Z)Afb`wvGn@xF|= zsAK*PwkSYZweCqzb42YZrEfiFQHNrL#~&F6lIvP=XgSY^65eQzE7(4ski_&c+tS=5 z!@leOp3qrtA&7n8u)kx(R#$Rux#dy&&?k$-xhG;57wHt-_$Z|GiwKXhsT+@}jdL`M zHPUk)L>{3SJQ|x2$<>>c4joVva@2~Kx^JUz8C&mWU(S1PY-ky1)Dz_2rvr zRq5(WYdQ%+=cKJy^xS9Dm^>zHYdp>PV&Htb2A}=M$js2e&MNDa<9)>9)ptDhy+63x z?Uv}|drJMW(4gi=#|h4%d52px2|wR2fhU`d>SN3*2GOtjl89Q9Yf&!muihid`3HYF z+_+bd5_oDOw@ME)PQ4nkTdkMlQoLccM<{*N;NiD>s}3$Dh>GvjCGuY9XL4!F{PDf% zbr$60z+E=rx$LYg!_K6N8~jYOQm!5~Q|qE1uL6yn`|7^CiExnCY2{`_OOjCf>$_=g@RLY1m7#?bqzOKG` zDW0rHo=b49hYv~JxXBS4)E=Qn6`QX3t|kbRFn_JF7+eqP4@FqIo?Ja#6!xxS{r?uth zt&&=?F@yG6ZLh*4SA%m6?K6ZMjrQUuLsze^)ikuf(-tR^Q*ZQH#L(*8!}8|RWEi^p z+N`Te)$Sa;UzAMs7TbI!K_3n3?LFXKIObxwMs<`sVuSDn5WgJ*K==17URg^vsSfgy^=1txw~gdUE}DSyqk4CdG+%W0Vw88r&B)hRs>;e%RLA zL}!_G&$%^>GF|wzD^i2@muVj8f$%M&(ECB9*l<;qH+av)ls+@&r@O>Ti3!|x?B&L8 zV*P>97952RSChl%H6ij5<YjIEaiwD|a?OM~YS1d2g z(MZ;#cISkZcuiN0IZY{W1yKddpOPzBbV1{4FA23ob+x?@lVpU7Wy4AcT)jfm#Z_ys z1b<0vx$hjrr^tr7D9tBdsFJig`N}HWaeLOfqEd#UkbP;uq9kT%{yVOAXZV4>cj%A97i2*SFwriRleT<2 zJbh!J=G#`9VxQGsd|242dw(=(zFT{`!Z|+R&b--VW`TXm<9tW0eGy|5@Au;tg4gBi zIVH~0u%jB)v&6nN+b>E(7g`Pqt*)+Xea^Q(xp^vtM{LPaa6QXBFeyhrq(3uyzOVe`Zpt;3S;tLzP)3*1ck*c(ybdm3q~T&vZ*_G_%f;`@q7;ip2!)z=SKM}s zH%Xb=e|l7hzF{n&3L$r=aOIDuMOBS=324(OYzi&BskDqU()CJToLnDyZ_U)jlA>zH zzA~Fhc2dC4tPV%Doe@Xz=MP`9g-~KjUQP~oQF}yM$NPNzLf9%oLrb4y;aQ_6KlQKc z@F~d`$$ctu*qxT8yJW2URL?%v8sS)z#7AJH)Rvm0yK2m}p6VQ8{Z}y2KgWm=Fi)-y7rP;%JoY*JEQbSMP5-0-@^A zWNp#&031s-nMpOYS!Ig42*dUL|` zx4T01A{f?J9@Dy3HT6K9I1+#mCx%P2?b9%2w_WOX*niwADR1v-*`S>CzotzpZn zN>ZMtv3onC9x@gMTDhxZl4c4yb>NaPIU~*SvwL*@0nxcSxMRwUKmPJXEcgD62~I`j zc7w2N-e%{Q;5)>1-JDJR_3g({k@aj5t|Uby$GY>lVOTMG5S1j05?4j-bfM;7J9lT? z^;crv&2iqd@qjgisUqW|4}GhJB#wob9#=T7z8=$l96gD;;M3sj^71t^tx`qqj$*li zeJ1_$8VmhbeMaq(EW0+4vK<_l(=Lr-PG>xoZ0J_$umHeNf=$cLQ#}9aIyJMeKi}Qji1_- zsVK8P*+E>Z@NAKq=9tl0?&J01Ae*R7-h@CK(MB zXDhMMRZM>z>`*N;YKhUhUDgu!`bhM;q&_Z<%Op!A`efI?mO8dO|2?(wFW#E;D;>!Y zyblStP+|St^M*wQ^?Q2H+mC)WWAZ2m5Dybp7Diu(C*Iu>X~;4b3#E#+4tD9&ALV6D z?(g&vh{9?%E9S2C8bx!FQEfoSDZv3TQwm8LS&Uyd^d{Z<>{72QU-$Pf9}VFgCTXV@Z*cj$edqZ3axTjx zxX0s8jJ7!5dV1jZ5oQN#KV*UsCZevo(j%#ZcCz$ia3TG#Zm`DaCt^DYEW97IvIr3F zCv-W@HtS1p>&L(Jwx#fF>HGNXj%d;3RQFmOVr96`wr7jv5MuGan>~h-n26~7Is9hc z;*t=HG3&`=V*~?b3|6-z_kY{r9)F#BcFmA|nsw!$MKfQ}Y%ixH8e*tZOVQ|}J%>SR)`QPt`kuSXt z|2cl=b8bsy>eHTC)|P+2_HXUn0>7&h_+Yja%?$(6>-gZ4kN9EgXOT)4JuO5gf<=$N4H)&`_q)5;>ox3p0F1RQ!EhL++59_~pBQh1~SIZ~HAA z(_dbflO{!>fw2##ep>udPa4i87y#nxRir^z$S;$>PiX%>(Jjj`6H+*o%rS5Sz=u+M zD1REBGXLRq#Rt!KHfSO2p{Pd4xK$UR$-+Q)eYzLdwD+AE+LMZXUy=?z^x3@Z>q*xC zD~0kqBfo_J=~}7s4odb-g6s#{5Wb+xg&mLJM#}u>i^L6wcy_g}$$PhC5v>-D04i?y zy3hO4v?&?1J&8$O(3_x0Hy{0lPUh2+Xlr^~qUDiYs?wW}zs*Em`xLNv^R~}iDg+56 zGK%YVna{Yl{v(WO$nGS$?!bY|%&E(?GHJ&#?zT(hrLI&u6$IY1m18J0uh$yA9l$<* z{d^$wzC-8vSUg z{q>TV2TP-LBn08q+J4$IYO=?AM<2`Ro6Y?Fve=(iA+TLnKHe)^uJ(EOYvst-ZkcZq z-s^g)X_W&KofXOX9P>22;c9b^Z*teQs?wF+=+aZmV!{6R{JBE+4}lGV^#_;sgul414t|{F3q!F!3Q4_ zW%{sH`A>6OynA+Q1q;%(LH}S?5*rlSLKUj?YkDlf+ z%c~dHaklc`E3`lUj|Ef?!j-dAX5>_~KME#FO(`qC1!2p3@1;EbUt6kPWmRYLoBtSH zluJh&&Q+WFa=c65SclIkvf|t;>k9mQ0EnlZ`oo}i!?Elj+h}62+A1+V(zpB_BA2LhNj3o8^>IE_DlJtN^ zhOyA$KW05H%UP!V{FE(H9_Y1{5c;j7v)Sr}3S)nZdqP=#qvlWYFd17L)kI&vVDe7K z6YmCsiGgKS>$ZW#@SNxBqQp=7{mG-i;JdhsAXm6L7jM-^YD$fq9dS#u*K<{V2w&UT zuT5A;m3Zs(u2joAs&+?^YWs^C8NuS&dQ3ekMz$?A{C}@Kuln8n?R3u`c@pqBw_8bOl|9PgCaoiS8t?D*XCRM)T$J zI(~1aq+iCwC-KTiHgj4KPA~WIdgN@;fo<^?87XlPqaaRma5SpiOGTM zo#C^ry2!isQ{)1wkbN%TXP=M}MFUlDPzVcd+?zb>boD)mkjEoE6UinU>f4|2W`f7i z=`lTHXJ;s7@+l@(~YZ`=rAA-&b0>Ph}Y| zGlG3rjpgO{tdfdE#Xj}Dc(oID-B10L#xkGv=+%^U#AXb@a&YMv3kHg^L&$-1&eKLqjbeHUn_@H zr(_B){3RT#z^t?$;kp_}kFJgGMU{ustvVi77CG9k!WUeq#{8Q7z+tK?xU zpbFbEoG6`1+`VG^iaI-VXfxms4PNbF9cpeFBW*3Y5nIY-9?P$YSvn#XBqcT1jiu;& z&U%}DX*ZUf{zyT0l3sL)qtT(k!wwm+Hw3F2SCW=eA3EM>%jJ#5uJ#;%S#l*-$YtVE zdpWLZaYuA)hXkPkt2$40IfcHTl19j0sD1`a$?3@^lsakZh#lpBzU4CFuqtd$rwd#z zl9*j1M3dO-!vi7@(~E(&JDzu_!39KcEJQRuvUr1rt4xvMgmtVyP6XJ}6oI0vOX?jQ zb}!}hRkjj%oOiqIy)FxWGeD6H9lERc=*?|(ftNipLx`Fjz4uajR~VZMFs}dh&B_D` z>X`Bx$awc<_@o02`q`;oSow&!p>(KnYDBzvc84Zt+tk5Z@IjMyToK%b8;QKQyDDA_ zGxR-3G1nqb9rW#_Ev=LU+X_j+DLB-fbet?f&ZpmC`2h>md8JwiF=T#GZhAL4c~jE2 zEVWGEPfngn>KwTw1&9KOS~+D4OXT?nZd3)EXPvK_*%;=db{U0KY`W?9%fieXw*oec zBS$Hq^ixd=E53+p8(qp(%gSWPX@Q}8I~;@82oGHHmHPKw{_*eK@D`2@(~JWC%*(Aa z;6xSI-S$wZp1$)7sJ(9{{~7CI)vL)TkA>x;!4M_a7R}`rN;@+}a$PU@s(n$#Pto{5 zZz#AT8&BfYuA!ljmhHUPa)ag%^Dw^F8?e5vv@Lj$%*<+*d4gO=gs@*|vDiZK5c1xwMSRHak2r5tsT$1DhPPzAdo~XvWbXj1Q*18QecvVNhGhKWJ z{l)KX2VeNiF=Oks&4vG$N1NEX{l0O^fdakGXZMOcr~s5T&TukrOAgege-WLWkov;E z8gE(K>hphl(TPT9V_tX;0C50o$rgX`E_}zTGud$=a9>2n@(B0YDZt0;mTs0^8M2)) zyco-$O>2@dCT09>V00yBzGu_k5{tcnVN-fqA~4}5d_r)LMcD|9SNZ@Mi!3LedQ z5FXI$T*FE~e`JWww3fY;vL#Yat(|UA?D30x%6p$XJTuTld3Ed3V*(e^4~_YYx6wSK zr=#nuG8?{M(@JVcy%PIb{ose}fGWiL$&qREEmleCeA$>*2jYO+6Sd^tb6MSx?LRlX zCVW-FqUsKkRZ6;Kb%R&O3W~mDYh%y;rqr9n9DPx^B&*TocvCpBKWKIbFw`{(VsbLSgNGf-Qa7>;sLeyoC}@-d&}xsnyXOWQHy8d>9Q2Y@$RSFXX&b$hb@wF)@848^Xiu4P9{U{tu!FEfmsb0a3U_ZJM*K zSRVb7W9cYXWpDA>ZyzQKD>5bH$4q%Bip2L-PaY-CFA+cD9nOK5cMz zNBUdZaP832t@k#ijjh=HIU$+Z=Wa05qfadOC%E^{D|R|~`onDZ-I&>by|>!$?~VMH zS9SmatzZb(&%O!~!2H;e8CsbX$lo}g?#|MTx)Q<3L8p|lBL5nqQn-cs*RRuO)QTFn zP_PTBPV^rP=8O!e)WvKMwR>u;=wFtsku+BSYC^L-tyY#o6i(;fi#4Z5cknMIV~12# zZoOz>|G=I1KXFj&EUS&XHxhgV9`RR-s=K#}9YVuU7dJ?+d0@O@)97^Do62pBf4wO| z*Y`Ii`>!`;Ity=#B`ZU-A!3>k+!HJmJjcvzFiw@x{H{9JW&cES1wzURpPEh=Y79?i z{<#lk^TZ7~Hi zG=e;~{@VcIofyQq6GC?WUh5NcZG=(dYj3DHxPO15xyAJ)X5Y#b*#uQf!4=*-xnPpq zpY_a%0o5pPQ+eBEVVoi4&V9W;?WRmi4%iviBU-`Bg$~Z|rnH)$F4;VHhRGK3t8AmD z53GKM(qD>>(O0U><~oWxeL89IOr?Ye*GZCxOR#2T;U4~6b8^ogF}rLqfG`e8AxDPD zDlY>-;v(y80mSo}+HPCI=Gaf!bNE}Q+fC7}N9D$dvDZ;UhMe6OpwfRJO)#_^?ns^? zfZ<+NNMSAebLlK|aG1F!4EtO<>!vP?Yo?0iYl;D2Iu{zT_4s?~vKi(Ln0&1-F4sw* zdQKn*lnq+hEE@v+If{tbNM>Ddq~YYT9N4I@myUefD2ACK{Bjsgs)&Jk5fk=(!T=vz zLqNNXiGAADZupw2HI))6_Tg`){}B77=q2-nHAH~Q%@l{Xk7S$^+}$P$z4Ge&le&^X zGelh-$I_OeZIQ=bbD3-0ww#<}cTW%P0Zc|Ho^?NW{;e$|yIF#}fOfkvwa8#?7=}}S zktsWqZOhKX0>ZFRcmX_we!OL&P8OA9Nq-ue*u(g?Lw&)nh_j*8HgE@Z*xRa~q;=<2 zD`r-SS$EHkRfCFt2Pn+0zj+^TKQG40j)D&(e9)&Wu1@LS0G#EEvC9T#J1$5uIq&m{ zrFM$~n~n+H_8SzQb{|1)d6C|fj&jGHrjkFlA1csI(MFGtk>W-9^DX=uMTToL+V@Lg zK=n`WE)LwMyJ88p$k5!Y{5xD|@o28jK{|joAiv6y7b%D#)+0y0z z1|*$Y*^~a;0K4a3FeS~YhtWme$-hU5f6DKnJ4xY8{rR#Lwu_E7Oo7C)&B>L7Pe)ER zG5+-Vn0S=Qp4){O=IyC6^*)?|SUp)mYiRmU@wtdEgm)PE);h z9P=EAM5uhz1i-iN))r&VKp?X)8b#qPPjSN=#Z?U5J&9XZKXQMM6C_9CAGvSqxF&oi zxG7oQ#Ks2Hm^;Y?k+x4=@x?9;iD+rziU8GW=K(JJc55r9WZ?9mJgY3ePKRz+{Kd%oj(f_LN>}mA zgp~E$+?U~B_2;yzbR-=Y22Cm?jehqd%hrD-qHPh1z+-IZ?E<560^6kWc*QF4y`qzLDZ|`^Wy6nmWhF#7V6XvJ1hhlkp$5%rf z&PRFfh>rNaH>aP27uNYff;a29BGl=f8T8^{tkQvjH_SBeB=DURgoS8)v)oF_ht`d zs-km!(AK_?tqdTsu({5%@u~}!`@Diw&N15Om*fR5*K>dQ*Kc(XBdUZnxerHy(_s9w z@Dn^@TFBlRPkZvA3v*4yI{gu6a{@x0$*N4HYN-b-yTxsuu5I2;Oq(e9Dwa-Bp|j83MJkVyupG!S$$bb!e>JmgYS zCD%{O_r*%$oT0J-_?C{e098pzo)n}#H&%;SK*M1HeA|qO*^kUfLE6ozfJ8t5+y*Al zOg40^BTgIWqABu~kv|}`gV(k5TxuxwCF4wdB?gp5T_AvNQl4|9N@hzwOQ)a$cyAyy z)sRuw)(O=!0)T>3c%@ABW+AgsV1-Jq3>yM^)3@qt#Q>wx-H+ua;%suuS%wS_bQqOS$!ls_5G5u+kz!ozlvchF(9`acJ$P5pt2*$wKL{i!+!@k|kzoyEt zpOz^i)u4j4Km;=*vc`$g_f+Rj%VX2PrpTDqMl0)*HlTBcqT3rqwVq)TLg{QT9Ff0( zL>Csz{yCm`G;Ge7j8y|qz#q|nki`kI`fjf5@4YHWa}diN1O@{%)&Ry`Ec+8QuA!;% z#16*K2&_+0&giXt^kcF_N_;37k@y0`5=ajQqK}(IaGTZ^QnGLeczQsj@b_W2#?xwO z20}`%w2nchFNmc`YfPcH;AaH%ZqsFNq?~kGlp{#&8W9^Zrq}_pDEO*33Lwu0Ulj~x zdtqH%riv`GH3Md&B0+t_NUdoNJD`SUFJvd-5L+G3S)YJ2(wX#L+Rihqp~*qd8-eIC z>6{lsKe~}JsVqrP%fk+iNX9P$2h)$bBcm;`G@iD$`){6u*bq2o+h9OjhZrqKdo4H7 z_(g$~z2f(++BoqVM@yG7NS0Du#)Zwag?%=Nx<6i1aYRp`qt;KF?w)pQ9;I~xYgXG! zPNqitD|yZnHo;KHh6Q)3Bf3|+tn5bZPbv~mJ@Gg=34r!?9fEpfhHt z$;ZxLwfHx8hf7;HIH>fUa%(+#AZY;W88AVpPYuL&*j!-A_<P}^u1GzydMknOp?`e$Wq-t@mnMa?;^$aPM3`O|BgPa;9m7=+=X9cgJqu={`) z93(+)DUq}ZeTjlusjqwr`ZtX!bk#X52Cp>!9m*2W;?=`sw$yHz&1%jqPUzO*Ibi}hIaEm8d2D;lK`kK+VMc0(z za>DUaQ%31=kru&ezSwqa2~SO%fLP&gT~lBWkdaqphZof)%ISCrj#wRE4~s{zXx%KX%lQ6d1Yx6!sUUz-ZI0^M#iL z9LY^(w3tG1fUCv|e@YoPjukTgp@mG4Wpa-OM|xJcO>+Kv&D4tLLCBmi6;~1yNID0#g zWpPsCC*r~|E+SPR%Ps_(t(go;0!;!8*ZD#>D`4|Nq5|X0VM9W{+cG{o9oW2}mHIpj z*+x>{EVBm2T@nY`7eHgYY?|18 zMqs!^=(blD?eQg}J^G^Cn?xBGfW}NfE1}ch$#Z*_i5s_ycgIQ@&N8T*82!7-+(ARh zidw{a0h)T)5g5_Tm1OLa52DM-;^+%w*$aTjmc)Evf(lgQ0FEEab_LpW5_795Dp&$4 z^iUx?D4zBgw1A3Lafq?7+U=(r_E{icYv9|mH-JL!yJ^<$=ErBNDIyLPm2Vfs32xd8 z-ImBA!I=$+D(YA0ca!6@e*^;h>Nqs)AgJ9}!(IcdaUjS4B8%IR)|dDO$b3o6*QPS8 z21NpuQc6loj$W^V-SH&=-4Cgd_qSpDFc)o`%CQk&ZNJ z{sChGDj4Xaa|q64iE*kZk)RHD))76iw~>$}2gd-pJ5SV_JhL8VE0jmzP?68~YzKxQ zXi(pJNDu(NCc}{3kj&{f5r18Td=ePx>g`H ztG1OU0|qkMkvE)J0ZIn}hn()$vEG;sX&mZ7K!d?a3W1~yjGkpU*8HNwR5d^eXTi5l z2d2Bvy>U`ZMFbvYyI0b|kkOF`amKm@XI z(PJ<&7!jP+e2P1l3`jkdIgTEb;WCf-P``^$>5|qDLi&QeP?pxW>dnO5flMGJDNc}^ zq}Fb67i_sP5dV;IK4>t=VG;}it%MC_NMhm*LWW`2PSTPlF#)N?Y5)c$Br(YaTNvCI zb5hQI@Oqdmj8*|TOwPkxmnKIuWFbXL0FY4R3MtYQDc&GxcU%~j(*>mCX5ii&G!Vrc zf{ZFs-IARYO@-Vozk&#)A1tIu6R_Nd=H%pIxqHxjE6hg9Z93hSY42=$?m7v~F?#-YOkeIbX936wWLZk&h8Vl)7;5LI=^dt)fVr2^#1 z^&k->aXTq^fi&rkH)=cIB_~a~Bjf!|+~NiyB8!s>R_Mna9OJ9fpV9p!ExA-snW@>yddO4SwNAg2m)(vU^_l23oj3`CH{ar9y-z?&kqrF$dl zBo2%!q%KP*_)rQg0Y11S0hiA1lIzolvO@xnEG+{FoMRx1H%@DVya`Yy1AAJll*HK& ze#KgKBIndb3hX`;x%yGj0pe+CG6q~$E1k6<-X56&z^cY{Ayw}Ji4OP|)^WvoN%Z%G z^kk7-fuaL0mHHKT3(&7^vDIjb7=?a^`7*3_S4!yt7wyc@~viaew6m0}7yt7L`kOqc91i(z@m(TVZATXf=j4Z6YJbxYl zPSIFLUKQXAd^Icw>hu{c^aTDmEdQ<3x6cpDwpgy!9jFhS0(^fg`*FVhvd96%8?d~{ zru$BpJ{5{)6Cj>i>*E!GAK3U|uZ|)@YY%{xJ;08va_x-{bZDNBjch5ryXQF|Gs8)Y zXN}~Z@B=bOAAtF-F=H`^rqhAH#E&;{o(WHz zN1EkWpLiC_*gnqnl)23OA&~tz=SAEBYU>3y6kunUeT~|3k0=oDPzN$KBZd5&N5EQa zq@_DdG@N0>jv@-keA9U18QDON;$mhpN8z(Q6|RQ>v|0>MJ5I5oauE)(N2Z;dZ;8JS zQreD`b|NJqHe!x}lRbs((FguYKOR5)A&9K#(0E9uEjF1Ajr%CD&bM1lW)=m(Cn;dR zP_8C$o(;iAu{=+%CbP%#K`hVeRudRtn^X$S4Z?cIY~Z@lA@>;4nTTbinVa3H&7b-Z zxEfealWFr4v3>>}ILEQrvLclx^0YTQqQu)d5 zz$LJER>LjIXY4IDJq!E*>}_iToIG4bWYnS50vy{#YXOLKUjheb%k;-d{W_(MZ;HJ^ z%?cV*H^K5p3~;m^p~9+yT>>W)D9@sDpj+Y&MN<60#SgCvGi5lQ%wKB;kj^a(GNBpB zBTOYge&G?BsXBP1&e8;#d%v!vO9u+-d>K$)GKV72GQo?3Q;{1c=u0A~z(^1faA2(o z+{cwrS=fJV;bnm-9oTQ;C{S>xh`tuLS1X=N#3;tz4_>HCNgDp?%X~O4p9@}ichs0l z{y9!{b|3N)7UYD0I6XrsJ17up%_ii^t1}s~EpGPYky{b^FOOk7UY54h7QSG#VmHMPoV>yt2G-&kbCqpmF0)TK~d_&a-1IC zX>nVI&?Bc@JCN`=p$qyTw`(`=)K#?m>;cIInrJ2+eq)WfWQw&PIM4)UvtgA3tedaM zypoYpFvkeqIuPA+3I6OCfDXc;%N|R)|h|Q zQ&^bq0j~fZ6%${<8H<>C%KSqxL+ld>zb=8`V!8GpYc#mT6&VsGxR65rogu7T)SY=d zkBB>=Oa7`4c{U?=@-tFoR#6ako+h%S!#n1f-1C%qbD$A~FQx_WqmYX;go^!*+NS5& zq|lzY=tuC4YI(0%qhiO05KIY9r;xfC!W+87sTB^??gcFVPwMcu-q`!OaUZTV!Gbp$;&2p8SHwDf7AjBfecWTn&NTL-HWCG+ypWOLB4^M4krj zxb~8V87ZQPQSi)xsKa56Yk5Dg#`xp2Vd4@>ZYqV;&k#=39Z4h(9e5ESDg6lLw7M2E7UUgits~DP%eJ!OMsYDR_@AnWYaI)+0v>8?{~bx{{yr z!2b+z^=nEbG$HA!Pefi|s`y_7QyFQapcc*2;YTCcU}T8$pa3Lmj8k7sDL+mj-(?7| z>WmO;f@!&jb;+ap;Ak^)WS>l%bHtL2&4bd@ATVk#aYVAFNcJ>MK&QieB+FG$JI*q> z8a5EK1mb42mvmzjV{9U#$rh-ioE_0ClxYVemF_$hYZb505S~UhwlkQDHK&^Ysb;Gc& z*y!l-Lmn{qd;{@EgLWqSdpD~P;c*qygKRdVHvQdJatO zyd$;Ws|N*AE8;-MR?=JRgb)nBsB7$k(U)o)sKa#3f`{P~lDAT$qz&DpE6ng-oTX#R!!j z+pyu#&*OIn!&KQFi4H>@>S_ufhs4j?U9zkixVE!s(BWO}tqWmms3H$1vm87=-03l% zIX|1Dtkmzk^T>+LhTwF!lXL2Iwhc{!*+Y$2^DPMYfJ5cT^N9~vh~QkgbTyuqr=6u; zI`Ol;)yXxlQNP+_E%G2p%Dc{=0Y2k)kJXCPltZPC3Qb`6r05dOi(8+ zlkHm`rfS12SH1GgTOsKb5ts*Dk8h+xZ=q)Mte!e)39+CdGuO$q2gKgD$CJN}B#CdK z+}w)emhUMfJ}d$2&K};NM;$?WOSEDwGCR(}+=rQmcA3gRY$f5uTI6(`g}HT^2X~pa zf>&vB+p-ah#-9u8QH%+hY#-QT~&x_a#Moe%j? zkMI;&X~pdvi1~x8{+O+n#{n4 zpR7*OzSl=MSqL@XLFu{g)v>p!@U!k%yH{Gup|;F5(9k}U;5wT?P1QH4O)XDi^{MH! zKADsUVQV$Glj(dxEzcaK&>>OVi8XLPgci3?mupMs2dk=;aR=Uj(1AwaVl=|{oS#2PI< z%O*eOLGoEz)+~aPC71510^l7>i|W<8QF4(b`aI1oF6Smnnkf+_7K(`$JrnWG}M zXuJ(7dc^b36M#daJn`d&DHqsNFE(ta@Y(KQfK&dlmUomjT3+HxKFq^=NH5<@5$Lq5 zs>iWLT@JaD*;pMyDUw>DP9zVq@#9I_rw|kSJn#-t1;lox&e$*f?#yj@#8Tu^N1ez7 z2Uuh~Vl{H)q)dCOJ*5s;Hpxbuqz`twV>)B?f!&#kJR%Z*rlYztQ~a40S&kezEYlwH zxs1Z&^GS$9)?>GIuzGrXNi0oR7o@{?4}@4kasd@AdD{tjkbxiX={dGaL*t-DcWcjU1{t%pGhK2CJRFi;nL3I{d^y@-_B90|j#xz4H}z6Hl2d<{I&rVs#!Q zpsvNx8{3D{x$N@}2uU)9`xS)CzMD#KmeSu!LEYs*$qHaK<~xy}^+AvMy&Ads9IW=o zYF&Y24v3+a7s4D9+nZ@}b#V%Y?x^JrR-1npAp-Z=sKgRGuhLx(Q3Tv}RrNEPP!^jF z%a%|E&e9C;Sb?0|UUHNs6pYJZlplXU$hIN{+ymt0KhT6+zt46!lE}E>`V~UX-LCw`87o9H>qaom z<%F(i{7U#Xqy?3}iWv8H^X|LTa#|#t<4$OjYL(yG(y1AR>>B>|>qJ4lE#Le)S~>8^ zdSsA0;)a%r+;cXXJj8G?B^LE#1z!9Eh%@H{W-M zGcA)rcEfV&yqJle%?dUfkpcD=?}07G5Hk&aLH%--gXHsz1<_e(U9wOFl4Jo@HhH^pV*kFJ-f@y??$Y&+ww?L(z;_^a8 z7qi`5;N7(5kc-B1JL#&g?;k479>&*nh3s8O7Boe@1g{x-FK8kY6Bxg4{|DW%B(zC5 z`5UL^xSBk-ZQ6~7dR%6WF4w1N1NkkPB~x2*`o`2-pP~wAb@f)nr*|Rbi)0HKFBWhI znrucY^5tkXZ}iwzIBw&S{;^Xa@*1D`)zyR2(Y2ysb#%ZCIaA#ByWh5|f{LRNIwIQ~ zTKUmA#D-m8?MY48pf}cn*4V{I(~5tS99nt-;s&c7$H!fFHKaW4S|ME)U3X)3$L$M2 zm%>3Jt9@*+*Ox4A+KnWljtzLgxNB|DNbwD-MVq_9ZW7#)CaaMIJdNUM4b06`);=Hj zd#d(zCm?;;KSAr;QkbzW6cRV>n)pN#e=t>x{H@3+Z0+;X_va!e55&McDZOw$dn<`2`t?Zb>r{cuaR7eNF!!-P;{&D z>gXk!h@n$owg5wrR&$?+&HkxlWK7xpHfxfPw+Pz|CPe_fy6Gy zPY)#XY40^ib*BX}lQTvQcYw^f;cklL_Ryc!(vtTYz|~Gi<+7oLS@izWg}Yb(aDKI( zziWz2OjE8Nkd8`E3n%M;{b?JV>L}{XUg#VKQ3hW{FXxSch~Z)SzphD zmz;*5P96;e!S&xwpl7VBvqxx-pV*3+5RQ0!9_((W)b)C{8{o}ZWH&NsG4iBXq->VX znZjS|PV9VqHxL?*#Z$$vl&$*U%~bww^+|e8Uu0y%*H$M-ijQm}n$ZJPE7Y##IWb2z z3SX-e*%hqDbSG%|?&%qGm@mtjZNaAog6x-XR`6WeA3s>_(_XTeHrA?7(LYVQC^g`3 zmhLm7mJ?!!3?bJmwc5ah!nZV|%N&1kht^T)d?L8yrt+-yz~EUn*J34nae(+;i>}fI zRJ3@M!x_TGI>SjyvtUMUMlTh~LCo9=0{Pi$b@By%(IATYs^sGge+tlODT4!2}hCidb^k<`TKV#oN?yz*3lSG;sEoOHcMQ{{(Orjy?mQW zwog5_&1mZKfJV!SZR5miZflDshcoBi)FiwAVI^9QRAk&NjeIlu(6V7&);pWHLz=4~ z7x~UKBS(*u+}Y8us~u&fW%aR-*O?K(Wu)%g;AMvXo(Lsd&~c45`AP8ejzg3utg*z? zOo3~gTJVuxO5J-;SqW``iRO!3y7c+k_jTWj>x*9TZ)n1HWj~5tGb-%~lPA|UAr`FB z6Pc|9Pj9uC6!`>zqFM6Hk0lzj0qo>V$V4H8Q(@bU+}Y$~#qM;x{6Mm>o+2LRl|~5! z_R^Vme)4fnt};v3EJZrRMYxMQ~$wIYn;?90z~B(1D+H!vv-! zcBJlV%DdKJRa5T9t(oL3+PH!TUHj9EK3^-S@$@6WknIuRlA53>-AY!N?fCLTp82e^ z*O)y|yq-sJWf8T^(zjAZpD0|Zi1;#JkQ-ind)26J^Lv#$yx8E|@5AWqJb-$cPi8j2 zRc{I&hsix-P3=9rn=B<~ztnoB=eLh8N4olTxr2MrKiO{t&G+(ieZzc8K1DLf+xqb7 z^!n188tLRTdWWpyp;mONv840tB(N;?g}Bt4*||AVC;mF;{r$at6w+(zfobbTTKq|} zHF%o_Mx%7`#Qbe;6mqB_-&RdmOSZSvMlQbh!D`TsG z-eflS-T~=*I{8IdFvqGbk1$Rfc; zSf5SWa$lesDO>R@JCqiGd<865=13=UXfYy&>(<9iR3SIyc!+cWyM&}wQ4GJ zmsscKP()KS1wL9Y0@#iG=(31S1q=HQ6FW~V!~SYJa%+q(im$P`VZ}JyymiedTwJI} zE6-mO1f+WD!@|(fRFeIWKf7!b=a6G#J_U3eOQ#F?jEM;cCrL{w2Q@&dq<#{(FlNO4 zMqOTfX%kG1d}lZQ;Lfd#H6p#HTYpO&tLkXp4gkxhFsT!&z7kIzpj7$b{QF0Iu<^iY_AOAxEp)7BQ@1*R@s&4JfLnG} z1FpY=e1v!4l&AB!#IA&n%2kI;OlPbh{|R#=&UbD?hj!NaTZZY5;*_$J=L$A)4d{I8vYi0r%E-ND z`WVgGd&_WpIhb`BugEr1%N_^CxN{F=>TyLI9mMk1P3V8PgA8#jibm{Wz53Uwsde5d~x#JUJTC@oEANHq}8iKI52;Y zl+m*8Ofy>d;a!c_W~#{W(u~jRZxJBzH>xxEg5js@@fTQJv!-bBF}kQN7j9p}SU^^W zoq6JFQd8yQ-KXpr5(2YZXgfMM)_YdD8e|cdt435D@%(tW+hH`J;OWSS9AY z+3@92M~twD^o+S>Hww`b|0{Vsh-Zg~kA+juELR52RmAviYEPwFwel0Wl#|Bz9?eN&<+HGhKVD+VlxJgTX# zH@8WikomV{kxM>>>v_q;C3oiRSPSyLY*4(c>>rl{pAMIx9|=}6Ph1f?kKw^O@KSYr zKds&4WVAi2^8U$A(~;uJd((Wzx#@rUW7_4Hc=YCH)EC9lK>WjPDtC%hCg%3AKNrue zpQiBcJG{}GZIRw_9!OScnavt*hj;1~v+5pB9EKd(LI~mOEGPnp?AAT)yMac)z>rCx~vh(mRh1 z`fh0e5r$M@H#0f8!<*n+se@tNuPbzWMh2|Aw;mh}2x>^vmn?HRhHK+*s-3V>fq~4a zaoeflaL9U)zgbN5C>p7(8zT@kaBY+KGQCuACs z>e|}=%RM;&C+}iTXeD2%W*9qG^Cy@+1ak(*n z3v_%B{BUbfz;YXJdI9F%&5y-kHLRW1>r3+L?W<5YzbH+7ZnZ;a&)^#G2DWG;A#=vF zct|{Z>gyvwOLUUH1d{5t-=Juw=QZXE|95|e?ys<#S~1=fnLF2q*x!9?4l$QcgFta-VXlX_P{8bW3H9Du!yUp<70tQtbgcKKpw)3n!Th3nx2fcy|KIuvyBrDgynok(kWk>s@HHNi~ zwp~#w!>f(I1Xd2bR<=EJPJ0z25iYA{l5g?WHhOmVj`se*2L1|lbUsu>yJX!|ex@kT zD3kYy*Txd!@|I6cyp-jmu3Syjno7PX!!ne~r-UCjE@6B+L#w;D7Wf24__OTeyQ>3? z08S0Lk*Pxl3q_{em4EE%KG0tMq546rj~;)D>jI4Xa;JY(E)ALH^5f{>X}cJ$;9o$Y zm`))ChTCWCQtOzkEY6X)fZ+6?XBz`zJDfpGP#W@YFy53YUk@q{i0??&m7jDbXrvpf zxeDVcYFgyYUxV{1SSbEeDrLg?B|v*8jRBj= zGY-u??J@7%sCnE+ch3Q-nOwy#Kk@AA14R{~?Q*Hc9Vclg72IzcT67+Eg>{ugbV(N( zG;q193wGeCXXT;J7#1X`lld3SmD9df8>yspt-{@~-V+dsob6hZbMx0LG}gx)aq z?`da?NBc*=TzYny28;WdsItd6ul@K_*J1@P=#P?Ze@GX*H&}8l=+N4}GAi}b4f@~A z5lQMKNvtXqNp79->WaR9)BAyn-ho1jUhpV?iw1U9w88?e_G?l`}>* z&q%%x1sF}8u1Ij@pCAiQw&@6dO5a2mtMDT?l;-s1l6)tXea@QuX4)X~I$6PoOZ7dD zT_K~}e;BP`h!b&(yN&H9_06P`mvErCecZzQwh0h+7MPviF0`THN&Zu6eWxn9lk|@~ zfetm0AD&q^G&F%C9*^(_t=myXDodZQi-gRUY#Y~d^6iXY$0lD&`TBigKPC@8mR<@+ zjy$ulU52qdSX#ZdwXn?rMvw#IKV!U2<>B{Ya$h-=U3$}d{Y?z#`-<^o`>6U&uMN*uQBFYqCc3;~ zQhRIHb}jx|b&`KuIJ_G}(5NH?+~22yxcqcGk-g?~Ax)r0hcNl8>AcG+EOG3zns0qK zGy5d*afSaqMF2kDR_=A-~#l(YMK<>p|`Okd+v!H|3)3v4y#Zt-- z|1S!#^YcHww?3~%6awzWzbXq#AY_*RFNJ@{!@6~R6tRdqLInYrL;U~l)cvmM^0I2m zY5D)xkg@&MXywKomfC^|^2PsW0Fimy*r7oj8996NSOuC~;(!K6uE+nHL0rvc;Yc|i z>53%AEfjC|Qb)CXq}ANeW|Fx)*_LSbB{+4?L2YFzEF9oYoyQeiO829QJ9sHwFcpup zYWcTQRL+fO&Z^xic*FqsvpP`{xWoKeO4Bo+{jlHyz?*MUjJUvdk4q#CcPsdx2AA-@ znGEc5_zPkN4U(-IH?R$^Tu^(%VZ~PyXl3l_|dDm@lhF!Tpo#otkam zkL?$u$n6I{UL3wzI^06VdkVSf)tA})r49C67Sd0wN@(5JW&z2#6v=;<@-lSx-h?3u zv9HekGcHJ`yFItR?jp1 zQm&2zX%KJp`tTmU|9|2~{=uUS@${5AWQ*qJosUP|0X)j?Y`97q{g~OdFjH#yeN}=> zTE1>3)q2pp;5)nM305!KnmDv7aVVB>L6}6{J=LZEmz{7L|1;s%zuz?^M5d_0srso? zaBB=NJo)u1+*ov>WcLNBzKX6)OSp(p`RfwS!CI+lWvzrUh(CR@SAx;XKV}}Iyid9@ zT6%N$v8!7R2-fZuqZp`L;4<2yTvq+_@f&m@6x_w1?d`Td6C5rcYWfoM;)@$i>`52* zKMAUSJUWNqQ&&+xvQv(M%iWc)|3K$lN6RC{jJ6v6u$+%0Zl6Y4>T8W?*5BFrB}aKM z`H$%u^LJegFL5d3r9Gq#e^+A(na6h5JuTiNrODBxZe(F4Znnz_!LE`Loqu<>j~%Vm zvu^C(hHp_Rla=z&`kEH1_1E=ZG=N<8y0&M#a`5T#Ms2u&{&AC`cq0$-$;Zyy{_KDC zrR|$piO!A!BhBke@b}XqE^O!a^z{xgOEh7b{`X($r{_FF0aLnW@$6@0*q65b#YLWc zzutt~nxuGSh>g;6#y48$M5S59&;qzu=W=C8mk(LpyLUA7?MbEZAOlbOhSJEfU0?PK znkGn9+WF7>!WhiaoPW4J5N;lFs!&A}Kjafb4#a(WPlX1NDrC2t@0E`SfjOSU z$e)~Y&7Byu-C&Qfxu>!zWHQEtY{U=V^$2~QD^o2;#*wn*{kFl!x&G5%_wysJ&ab7& zM%$RQ+Z~~9h6}i-|D$>g?v@N9rfNp%I*QBj3c9^N9;BT;2Ns_k^Z)|SbCxgv^u*px zkoH~Kc_vvHeA>A8eQS{ee+q^wlTSo@-A-hHU_!-mFW326c!}0*eUPWpnd*W;v4rGr zSmxfZS5()q%Yzrg%56OtwuK600nPqu-)lP8o(^7h6)|xX-|X01F%dJ|3=t883fwdD zWa;MT7Qu)23~rdvz-gYntRA5_{N%K8e=m8YR1=y8p?{C_Ee+P?eO-LfMt z{yRFWr{k$r+<&Q*SsRmHGx(Ob!>FKm`g&< zR&@~hOgg!ZwWjFtuCmE7bd>}mTWPKhZ~kQPPseRuMqC@IGLW63p zc)bzAzK#LxHPbZHbYwg72DLKQ+)vx853Ho;46X7HE&I6-R=;-p(?d@d;X3axl(z&W z*Qb%St@Z703ZEG4yQzsrLN*!+iUI4^|7j%PtbJ(SK7GX?(m~kak(s>RpgSbCe4dDZhf9M`#< zRrmp5zIXB(lfG%H@$ut9SQHg(uiQ9xVCJl_w~OMnQi9MGRg!I_PSJ+CMg&} z>vsLd)u?+H#l#7?+q0VeWCv<`Q-2G48DZFB#iOGwxCd=P{(swos8T9+T^eoEx*UZ#uDlS6ns6aAtrC*QXl!=wp6v)Qg}_fjYQyclrAo~YCPJ!Tw@;$y^Ay6pL)T%i(t zdqi5$D*y6?B{c%!l{d!9b4AA8&R6?WL= zh_8WQX43-Fl&(4?j{}3uj`yXpe}}=UWyjW@ub;InYs=Et=&g%mq&tbXl^uo)=quwJ z_n$_`S9ts;$9hMqraI&hccZVVAu>8BAZtrx1Ge{F->^Uaf`-hzY@bbG1VS@F+=A}4 zvOS<-567>(IkKPer%SqXW8p?YXmoy9WO~Zp@o-xW1Xfg+#={%U#aENj(`B(ReDd*u zH|$!zU0Kq(!qE7`tet4GmPT{G`v5P7shShe?BgnRuQMqR`* zm7lWSW1YzqKGmq|r)v?msmX{|S`QW;=~Hv}Z8XS;{u;A^kWGPtS#Mt-NHwk#U;A}j z1+zot83k9D<bv*&@D>U8e|V3ft^u9NHP;y2KQA|oWd7+js<`Z?O!E4VX=66@p!vKE87- zGEVJKoMz1e!Y1LWCQSa@(2rp3km;07$Sn>n-lz}3x%Z2ujHEkW<$FHTdkbtt=bvcc zxeX{%>#miJVOs&Ut2Gj~@E4H!t8^L3?3uLuuo}Wd}bJ;|nH;uG^@# zps$hJ*~RU$oW&dHG#1mN_l`(~?&NCEW(J&CZ?G}0v0`5%R1L8!QVT^4w;8*VUwkQHh{8$5;F~5Q)=oj?BvHj{ zF$83|u~AX6pHFxyO0TAeiWn71KGN@@6 zS@&XoK3O!nt)PD1N+?~O4g$69CK28xv&h6^YR791#*5dPevGptGg;g?pOTqcah**O z26np%&MD2*GB(ZB@_^Vg`MHNURD0v>`kN8z(D#?u^EECq&UTv!vl3^m`1?(37+qOG znPb(2?9V@P`+DzPrcUGx6NaBIT@b7;P8n(X&?NTx*JO8T+}<{FYuF}upCx?N8!@VT zrCfp0%lDpj!^%lH03{d@%X=2p+|aeBrszFiLE$c*S?hkhZYP_~wtFi6@4(v0Ckf@r zA7`RC$?R1lompd$_!|8c2P3_EOkEC6i;;T@CdTaWj@}eF9{zf6Et*87En)E!J9bMW z8#ZUX^%Rt8h9V3;`l@X@?Fq|2QMS8*Rn#)A@XC5bM_t~ukH=y*)_sfX*hRUBf@a-E zPuVNwkM3X?I4ktXO!cCMSBqWy$D)XsZ409}2B5Bc!q9QKU#_KI?~evs1E?!^f6*_( z+ck`kZ45=tyZx=R3;*_bnenYDy!|ZYHVi*CB{Y6f(Ii)~jUBrtPtw_8j*wL&`yau7 zb5P}?wX6kpw?Q%P7xp45EAw?^d93 zGAj?xir6yaQziZxLeXO@xa`HqMU2xh)}Li}&+E3cXs`C!u1wgqsC-QM5QSjILRfs& zs4=rKXiY7zF`lY|dfh_m7eJ)HhrgM^+hY$OP$U1N%X+&qyai-aZ*$5KE0d>{tDf3S zOUc$9`=~eOx#Wf>`6EV(7sG&vCZg{Vub}WW5mjZcid`oTDiNHzC`v=5b^?AE%#wvs zc#rWTQ(3f-fCZ;$qLcH99~|U{5ha}&Lcy`T9JtkI$lrV&5LG0mXQx&(hY@UAt&L&a zU5l=NpZNS8`XcQUAm__}Tg+i9%J<9LG7){dembmfq0}uT8u#mh?eZV=nO|Ls$KQc@~QB6o(2(P{QY9KA0)O*KGq+YZylvhs&8T^MsX>k88H(& zzJ^(EIp%;VKeJxs*D%Rni?;4A+_dlfQX=@=QM|r$Vd>l0^pRqXcap_)n6@r(mzzBi zyvOag>26LsM<>C=Yi=j21Ye^C6a)l^Zi#N0QM~`%qj)-^P-|FZX;B1!sf`A?!D-=- z$Ct2!A@c2~TK38Z2{mD%S3#sTKA622BHJakp8^w6Z_D1KnHVLaaA(Afd)gN^HRzZV zT%2{};iU9&5aM}eM^v;;`rmZ^v*+in#xGrMR1R79TkcM(*x)4HlMwkHeY_7ubiRIr zHTOgxo&WCZd6UNzx3I&YwA1m;L8EKOy50<3NhbJClgn>S*+G&cxGO-&v&_ zL50U!MV;dcX~L0P&F4X6Hh#5_tB62*ZcmOx6#4vD=>GcyKZY?)1tH>VOAp4cxkMp% zPAv~xBcT2VX;feKIGora4X0gv5KfmF3oU^3rl|m`8+UI0_Z$bt`F0woo5D&@x61pa z^Ts)8kei*f6JtbFnOO{dpug2+{B=jTIM3~o6JsWnZ4HRqZ*(MZMT(l(q>YX^1#H%) z0pdwDCA%_|H;PSOj|xBBCK|uwuRf_UZO~8waRzZYg;&eE?t7MRLmMPYPVIhV1c}dv zb;#+6yQ)6}vF{E8X+{%XN3aIA`+lxPj^j69cjf#C^)+|m5?GfSm_Wo#O1I#zD*GH8 z^;79Tp*_2XRagOk6AogRgVR;$JrfWh zjQJmE+WXpYLLtU|c^u9_)H)05eC1s_wZh#!W6C=WvD#}>@f=1VsR+B2I=|zl6tg{M z-+6R~eN3f__uE%kyxKvNspn9hc80(qjJELEX}md1<(H~&N*fdSy(3Mk!&pRq3ES^k z>o^yA>E?2JaLGbAgvvwQE6rL*t0fGv=Ig0qZ$=2&U)f1nS+})m_l?iObOX(D zCkW6yi~3n7XT<2h$z!bB z&BxYI1yrZS+FFbN*Gh_4!?e7^k{!G2HVq$qK~yGL zj}fl86Ef{MtsM9y`r(7`5{V-tOce}v-CtOy?YU{D3F0Q4>5&H5Rj4ykI8TxJC|ONR zO&-rT4}|C?GoRk1vTpiVGT6S(Kl z;TdbJ$XAj1&5II8&LMV&P~r?QDQ=HU8k(U@X&n_F(1hkXTZp-$1Ce%$1lxA=>-`fz zZQV(-5JT?)Wvj%o@y&0&C@!krSLmu-EE01tmLXiwyj^LCBP}wcBle#TA*``d%aJ=N z2vxm8Z_H6;c@W|aX}n1+rFYdgVG)8|ZTP%EM7h!+MsFl^p`v%5!W_Y_apVJH`QtN$ zIFzQ!r)W1cfpkQB&SUZQvq3Pmro=u&dAoJgd=BA~sw=Xyfy0>Nb_Uz(jD6D`dw>}Y z>^ED%^7A^7+t-w=%McnqV2G_z2rQ;>4qHOy9J!rkd-e3~$_Gne!C6}Od=`IcHpr}M zN-kz7UuP&iRiNP=rX)N4wkr(l4)0r}$n=bb1<@L!={8_wPZ4R?l&~{|t)nG#h&YN3 ze@h@-4W{u|rSU?UN~SU)i3Ya)9uT_23pVHs*9Ryvhhm}Vt^ovs>tY(;Cyi&sQr?Om z6#CG``|<#-(Lf#e%o>Zd8R-vH@G|FM7MYnEcT$cTe}Nyb1rg?x5%XL@N1!x}v!@6q z)Rd@a2wzuz6PnS*lt5VRKwR`Uf_~QMQp=J400l1}G1@k8IS-a1qTRv(%JTxJfnao+=4cVPDgt9J7@$NqX_L#V=C(+6LB72+Ho zVkmPM!N>D!l{+z1fN1dxG*aq7%UU4Il(Lk`)*}yOGl6T5F_=H7$Nb2nw3JHR@XA=A zt<@lN@-X?#m2_i{#$h-WL*Nfi0duYj3=hq7EW}I-?-PPX_sPvsGTYmLxdS0HJO_U; zzr`KE=Cwkv+X(cRBWnCWFGuE`YJMLx1hh>cwS^)>)U@P`;lVzV$wono~kuK z0K!z~D5Q6`1HXp4LI>@78^tW|a14rkIZ?8q>} znxD~hoHtZ{We?R9JrJM7%lhUnQYY^wcQ@Dis?iO+`E(WX3h-!{zAy z`FhaXR1V@@(bTU$^WrhrL^i9=Olv2|juw)Cb{rbfhuI33tC>`D*y*NGo_{Ok&W{+; zSo_`3dv-ZBEE$8z0~U&YW_vYeFg6zXVj5^%Cl!-VeoyeK^^(Q z!F&~XYN1HTkca1JlBbiGC*}DPup#bzLWWn!5sJvge1%z*0KI}m?@TS`JbkA_GQ70J zD)~Tq(S(O|$2OvCxivBuNYkPgF($b%e&1mlCBhI&(X#{G=})_K$N(>OF;msSgC-2n z8QWD(G-hc}vof5DG2|H6Gj*)p=py*s9!;aFLFqfkX#F7(W{ysMw-o|D4yV!uZks@nm?)af}(MGKY|5&=n&yNgar*1YaQW!7h{9vK=- z1@X2to*OcSE2`Wl(}H^fD&%`w71k$9cez6)GGsTDG*DAAlp$2*N6nsWAc>Tzlt+t- z!mcWe5KM2O3EWXeBw8vG80{q+eO8TDLy-s50kx&|TyLy!9%5^; z38qbV`^xPw+YrI7MFD6vV*dJZn$VSlIdK$n38q`}byR9lkO5}kgAInHGn z4m1r1(i;n%NAQ^_KTudA$kc8oCW&A!<|!I?=4=p~rt!vMhN1{_F~3U)xX&5+#ZO_U zP6rDI%0)jTD9>pOvPt9pWe-dgcQ#1&B~AFN4kor>s2sCjLui;|B)?$UUUCu1V&3h< zC}=#|7!*~@JB-<K(#7m#2AH!(<1ib@Mjak5rinK_gzov>ncrx_iHjllxGrY+GWfgwc(HaP zOEAl|FAsOf6+Uo=BY2obs}H}6XygNXD%bp!xfwRWq1{mu75Tv%e6WGtUZRCf&M?U- zc|`f4M(ww_NVLmrY~a8J4w*ztDVU|WfGgbOKnU31WIx zCu9?obp}o$8CCXnKsH>QKcz8UqKR7$?Y$(q^Epx3g+ktNxe8bQtAm-v&_q$Y%b`kb^fV1J+0Em+^gsRsb7ISWeEt05?tOfr&iJ#Sv! zk3H@oYlV}{vGPOsL1Mv##E72MYnZsmMb1EG(}YW1oZhE%91oX+K$K=1)1E3mG?G<4vHWCJ1{vHY0{ z`tKSU@Lx|OP*t;aq6sgeo8usI4RStB*jV)h88nt9XFH;NNPew8|3AH=J0 z9%z-RZT@USidXGrL6$U0vlTa_S;f!qVtU(3^buv^LcuD zMM|mJa<)!Ecy@d6_sxsDxV0|u@(Q2V0n+)zvm4Xy=cpqc;A{Bt8WA}ln=l*r z-f28>IL3W`#O8hYCd%!z0F|?JAU6{YKx`yX!wz14JTW4zNHrYyxy~?w?=^ra3Mr2q zw6YN-BIZ8?@^eNCIRz;lK}u$J1JnZ-@OY)Vg3RZXxj+>ZsmGNm(c{{HajE5V(dxGz zpq8G(h#|^?rM6hc4os|Hito4FJ&ol>Mi7ZRaTm|U+yD#kx0K^+ELdKE3>w>X;8gAX zEkMGRQ=f{kjEvh9Uu;puz1I0zU?d3Zk>io^M zKB%8#m<-8W!6vQPRc4`j2SSFgw9pa4Vow)nMVOO1xUdChIOGH72uN@&+0sBlQBqsZ{fxx863VGYp{b^s-)rpq^ZtCl-@kr69{cdxYp(mcuKT+0>veoS@4X=l z1GP0wH2^?+?wpxn01Olvz-Hk;Lh}ik0D=8;XHH+dz2wvLhY7Z=*C&PKJ+g1mS?GP% z-d!@x)%D&@_?=VhpU&D4u+Zm_d4K1ha9nWarsk5Nq=hF(E0*3lu>EsyK{(@L|Hr-u zCwBFn1hU-Ue^lPJ$h|rEw!C`S6X4}=?>(g@v`oGgcxon)pA7-ZGeazMuL0Z$1)xf^ zM*hfWfCxaON0bgavZ04h0N*LV|BRCwTnIpUYJh*JqZ%~KL^(XeKf;Lx*>g~4*ZMzk zVnX-=0;sc$owCDgfxP4hP&B)3&$@gXh;dMXak}N(5H`4G(14tgwKII46Hu#gPrUSG zR$jXRa`A%7xsp8{hBQdUk;ro01&&PEu?S$Y-Ip0#8i7p1H7{VxDL)N@97cJTx|}jW zc?Zh7*rSvd_Ay0yFScu7dkM<-*`s`0Z1+XEMqSRg$M#B;3H4Dv6Wha4o{oLiqx=7TD*%Rfl2E&B^A{Imj5)e32P#qn$hlc*WLG``T}}Wj{bAxBcFH*g;61lT zt@5BdNW7UqP0!#_Q}+QmiX%;9kHTk1`uCmo<$j^RKBWcQOKBi6&*1S_VW%xP(hJTo z@jjFVsiy%ra$^b_&L{YR8Xz*Su_Tx61Cap}S~6O6DVs(RO=UvJF^MjP z!-&SCd>G{jluc3IjeRC!9~u+#PGb8KLxLZz0bFMsM6+?+poRqXr0^P~MF4c$C#q?nU{Dq3Vqgg6yz0 zRPLieWtK!22AybRJN~dZg9jhaupt^>zeQ{X#2>T2LF7C=7Al(Pm%%y6*BA~w9h^C-l@ME0{lif=hCq$SH5b`02&EhSQ2*y%lZ zYKj@O;NylJ71}WB2qF`lV>>Qn=XM5|>M02FO0lj;7dw%e=FnE_1&_}GS&R2AVb6lc zul2w&NTvn{-2?dxyg+2E<^`^YH^GG&1kA$esQLS#q1gsPNSsa_{eqOo0e`K|S`aXw zLEaG>=F2kz@I$^-I zc?O&#+Q3%Lea*d|Ki>J$s&OOw^#4zOYVYc3tynr#TzJ=#z-6PMzBZyYlT7hf|j9H6U>MJC|&{8}DR;cq7x z<5A1>E&+J!8dGCzL6Aqr%>iirJO{>|2ht!*C>bvl4)-mJy6rz&K5PdE90`dr6UN!f z=}!XW;~VJ`wjF%Lt|u5mNo>Zbry-Ma-Un?92|R)AMfVMfZJ%41$ zE%NCh$MwNt5m59D-eaqtr2T7QV<>|fF@*R%Qe7YQ;T(}Mc`F~{kJI3Vm3)Ryt^cwx z4ZcMX$d1yW+FCXvvesYEiOKbv1*!Ptr(_AUXS6wZoP|558Dy{_NF6#YpX$qgnQsLj zoCs8_LXM(yd9A-AQ^2%S9r6teqy3#e5((QI)2qxKqId+*V z+w(aqttik0k{pSi*#v)DEicDPS~pdLFb-7bJ7~Z^9C@ejDa%)(EXsHvObtODA=RyC z%ky#r;NT)!$O#(RpCxPy?AbFlpC~WYCmRhR-(Ftl6~KFZUW@#c4~x%&Xkw>jufgys zm-XO#jv=yU!W*_i>VL{ocFY}m7coNi(#WtZq3mdjZroWd@)=%q3Pf6+mi`9A=@t2e z)XomZID+GJnc68!)oM!}iH3y)e@89vg!(A;B%j!Np278C!Y}H|t$5NeInjFNtMl>b zhUv@S&FN7)9mZ-YutwDbKikOvJ|NMJI>i>**g>%)%$X%SuX)O{VWA1%AqcL8K;CX? zuv!K$>yYg0_9oKV0$zPI{V;SC@tQT355oV`#5t0 zcCinFvY$bs8U+{UiK7Ilqeh3w*)oTzHI^y`X5vUxX3)raY~@UQ8U2fh&7Eoo6;4ou zYZ;GS9A?2GC!z?~vbdI~hikbN!r&C>lM#k6-$rhLo2@!6)?qZ)Od9EwCA3Dx*cPD2 zpBx0UN77_Mn@{S>nW#3(3p_G6h@a#{)5%kjKWEo+)F_i1MD`tfIO7P$xE>lBfJ27F z%u@^jg9$U(%FDPO*CW;>J0D8kOat!)vKY@!OR~MhbEcs8?L;#ud1ZYkXtx_IE!U1PNZt>sCcIQ@J>;{sGzPT{B$+L=ftmZP`-D+#xi$bA|!niltmkm zfrj8~D^D7Om+dnoBIl}8einpeQfKKUwh|V<$>|DuyRCO9eo(0CT6~I4F6=(B2?Ta# zaBE&lLmaBE=iVcg89pZmWje4q@sYs3P8Zr(0{@q4RBIeawL43TaeCRE;)b}lJF0Qh zTq@_d?zd8d7cpjJaU4zB$U+;WD4vDJYNVt<9M#lapbZMiX-aFi32IL-sAywQEU4uz z!u7=Gc;rWjN84aS(=76lmNU2>+yv3(>Me!Z8Bw{r~*i|{wyoyCY&)xQBy~Nq(8l-iqWBUViwf%|-?|>HLjGh@pUeUJ$zmEF|lwUOSTi%TR zesh>R)N*xK-8g7_Fi0ye=%Y8(v-knEycM{&lwLIZ#^HJWp|T{N{7iV#dgzl-89u5{Y7S`y zdMi?dLIWB3J;-iDCQR;Hqf>tdueg|MlFeXu!{c1JP|xQ?Bc z-}Gd~Lup}b>Zj_l@ah`D$-1e$Whbv&N8?R!Vo2QVV08tv{3MMFou$9y`W8qW+;>68 zIff)Qh`eqH`_;d9FU(4F4`We<`4~N@mmll{lA9&u$bP9ARHF)09pKIyy1;6iGTBc4 zcX3OwLnBOGM3WwMRyq3n*~(k`3YG0y4}MO>_Ic{@ez`#5(>%c*l`3KaO!#9h)X3{` zFj3OWbIB4;R&$y5ze^WWwm>7Ham zV)5DU&8E1clWgUS4z%dDN_P^A-%KMU%e`RIt6rYU>Wp&sxRc}a;Z`}*wUj2I)ne-y zwt`b=O=@6k2u=J(4~pi?9H!M+_NOi!LqxqzfTCs=71gU4w3i<#Xwe;VqN9BwoylZF zFi=Qi23*t8%%IVK>%z`ER8I)d0X$MU^0H7GY?Q(Ka+b~2 zwBv8N0rwWce6D=ZPJSd^qI)Zhk(aJ7nqo(VqgysTOL*Eqo_=NM0}&-j0ONWVCBOyP z$_i<4x^QH>47)@yM?Jd7D&&oUCkob z#X-sIULkJPB{cPh`(WuJ+LtIz48th(T2=D|Y+j7bVYDwlGraHFK=~nQ^Y{!N=R_bq z{C*ddpU@^VJ4X9v$zm`BY6#M!G=j*qIO5~rXu4ACH*0wXdQwquP48($duG zm1}3v?r4$eGl;xz#OK|BZ=vuJZx+oD+K~$j^{6HJ7%dPt&;+YTqi}5zlu$Vl`Ic{|v-Y1|r>DT1U4Ykb4 zLFn!vGSXL{3bkNf=xaX%UT`5;){Pp4YuUQ_pu5Lk=``xO@~_ASpGHv6D~$CHEH;lF zaUAqhSw*mRn@oHB$H^31kW_p#gXN+>CRcv6lhYq}{nNB6y5S#xuV&#VP1u;&8SO&1 z=C3UK{Bix!pD#WYGDRsPiwZQoQj9f-9T=FT$R5Ctep5#G!=@>aCFIN0dg`xR_LQhS zGs4)fm$$+Mh~Y$5c|rT(jm!OME!RK&(!@OGthEzvy*UIoq>lWR%?~Y@HU^)r#e_FNCz@NnBi%NrBmLZHn98`d6fda3+3g*Oe%B|)v+#{E)frPJ|L>5WDbY%uWhU#T7G$w-it0t zQ{qv+i{5urDH-0Qnn4NS(YT@yNpQYLB zdq$X9C?&MXh!Qvcm^YfFj`Ko?V@o%?6%nq-D|W9CpZl6R@ucO0vGBN)_Q09t4rQ;z zokkwxi&F;|hkyNc&TQ5 zRaML+_-V%;z0jHsi@Ps>czAi9;X6%I!&z%2 z!|nt6)8Es$DI*!0YhqYj_#=_^ZT+`jJJ-D$ckB;*zgYx8tO%KSe@uaTQ=P5bn5aZ&C(zIN8BWot@4(vbnwc4Wpc}% zEy|Jl_l4Bmw4K9izRnYn-RD=Q34>nOy&ucxeb=T8-LDm^j$ebHP86V)^th@|Vz@*8 zk|msD+w$`ov#)zj^~9`-sCJIa}P&yu9| zfDUa)X-jj8?|9(bbWVYe(dGxwl@=YN4wGw2Z43!Uw5CXH1yuQ|y--bRSS!@6#t`$>aiRa&NW%p+P&>L+w+q zFN1j#3N?j_b)GKr%}VaJLsz{QlGu` z)o4>xQ(SiUy7E-BGT>Ssh+4@~Kc|<~BOWQ;Tk`zJ;wq#UCErYCN`KqFHpAEKf&bUNNv5OGo8-cDp=^LPmT{!-n2u3X+R8e_1A){oYfbptly31}Y3P@RC59BH$CE${VP z4>2p6))PojGkO&>Y^o#1y_Y8MUMl{WE1(4z!GWD!SjT zS<8#xA4}g-WWLe$L3jV>+LDjxm*{B8D8Dj2aCQXQ?tA4dZGWL#8+}FPQmKQ-Ozr3$ zM~!fV&Z#<^6_VgIBBg4x)S-eA`QhTNI68=|>Avy1W7+(<8c;1*;>bJT$J!=fw;ui` zHXJJc6cGmDO0~-DbaUhTo=l1Rve%fll+T(`RC=!3Cv&Q6X6(G!=>2lB?ABQzt;e-I zG-*jMj$F6gV;$f8Z%z211aytkCsaNK-}TKHbIUTHh&m<@EEq~!Nj-_&_K$n)>*%N5 z1vJd8(!I+M$8z{Vi&U5SY+xk)-7qCe%Ce4k-dHa_biA0CTkP`>3vAzVH#bn)GhZEh ze6XFKh^36EH7zXb#7(q0>z3nQ^VBZ!lYfvLcN5SLRn5M>mUaKZ$I|-CzMJU4oto*H zmmWKYzg>g8HScN;58{DM^ z@aKJoPfk-Srij(GQ|P_<#F(lZ1u?VJF(?H+ zmw3F-eby+ZAbgd&@w7yP~n<8+QFZ{TG@w~o7k+FYL#q<{k`7F)pGfaI(D2*~Ow0eG?>C7fERufB zIvcgy!Vow)_r}2mYCiol7MY?G12qNHTIZ8(OtM?KuA=eC{&TmCGbd7MWhc%SZAv_3 z{%YFs+qe+1fMj|%c^V2dn7}xN*_!e0UHn71Y+>Be?E<}RWzdx5yvr^sTh{bC-VD$? z4kzv2e_{?;4~RHDi3tABRJk0+BO*UfVs z;J>0#V}T~WFVOhzNx$>mOc!w{Ov9p_iTkVt8W7$vK9cC2LqyhlPVuYIy^Q4#??$dh zykF}oGFDTy4auhQAhPk`5zssCYQfFNt^EVhx~=_N#q%*b2Z=M? zW(E6n?9fm}LF`mY@%@%aiOIp2YVfh~lNaa@$c3p(+Ycw4&mLqSyE-%)pBtMpKKi^Y zDz5_%R(!6Yc+bl*7I~8@+{j6LC*$a$6j;1IFU$qjT*@nel%TCKa0!+ie_I0tD@tyG-f-@|y+#0(h z@zWlwsKXgd(gm_ceJfbghpfFff37CW#f#&`e~#Niho1Z9qE!rEi_aGC?wY`4ya$n2 z&B0%9Edxg^x6mV&%&R?7>d%<^t^ee^x`@1tc8z;DOV;o+?nAkzfQ{+GKQ8#DyMq8tq?uME=RSE82d0=$do>S(P>#5A-=HZqB`QDC5cs;i(#N)W37&mb$4d101{z>Y zRs2v+a_^0QkrMAyVWcYCaxICRC%4py3t=ELAo)&9r^V{s%C1ehY81l^5&s&wh0ax9 zT}E)@_3{R7$;*ecX|S+=z9~?t(UUMs0gt24R_JV7zPB-@LMMh5Qi2t38Drw<0ShLV zz)-1hnvW-YX;kN*%QGwJj`Im{*lwh4Z*ZaEUW(B<_mmn5jVgtl`rkLw01n-s=(h7j zA8H@Zh)!|yiN)9YUp#C!p&8=taxI8`A8+j;*s7ZQ#0-Na+g<3K92)5K+f7c7c+QYa zjF}rA?dpx2&mY%Y6n0>6GNd~~#=xdye6tMwCCOXp3mPVU{vQ33>S@EmZpFauom1Wy zpoX6U)e5t8c(*;;!(rQ;<~^fVln$?iF)=T?e0&y>v=?tv@hFlmrRnL6Gowp)eZHcs zy21E%yH)w;@GSm`h>yQ3N7sFQKyJmCI&b=&Y0@pLkGKQWqQ(0${E4{!xia;X(yx)+ zgucJ;3dQx|S>NmBEno!<7F+uR?g~pTbW|Mg~pprdT@8-@O-Y z-LxSZ-K|X1DVGg%z&7-}>g6HyMlAOsp0rb`KnBnqE0@ zdt`;bCR`p5-?0!+l<}~kex)>6J`N=ASCU_%+tvCmt$vYYbm?H}+`(oB6k;uSDB0_X z)*oumQ{-_CR0Q2ngj(BlW#882KfXLS(EQ7X7&`gD2?T1w&8)Vwu_C{^X05iGIMxVm z^zg{v!ezG^WWv-JorY1lA7m9DWF|!8;q{nw1!{vi{o{tT-QbGvN5jKHcfA9WsRv8# z9(gB`uW{SNPky)_Auzx4ck^$5$1q7dY;p}aFWTYBTLH#cm!^DI_HNL)F!1A$$+jUe zl$U`__GKV9R`EG+56^@Co6b2sSp@Z%*dP5K6VF^5uA zH{N4hR|TE2MfLApd!pw@`A0&+Vey8u|XIAv-3iPyI-j`m*Mc>}`Vj^qaOl$E`fy4WMK zMun__$s%CC?F&|}#1&k)5%A*BODihz+5+GIPOU5d^1JeKnmVV{#VNBY^`6Aa!_4;_ z0QvjngRPz0)M0gqY8-S&RR3(i#lG%ue&Pb4cs-wX7}rTnXwGTv+NJ@*|1SDJ9Vs7$ zzfWXcj5c`$A0!|$01x*n*|o|)r|9cUD>HoV5?D7BW zB5ME3>J_6JMR)s*K=Ho=mkjjbO&pU(&?UBbT>h^~rHzGmpZw1qRmm}G0g~r)gpB%s z7euW%X>z5mPamp9K&qvUyyPvt&B$(k>9FVj*vNnHq((mcr4HXN^2ukpT!%P%WYeqv zYr7;PsyJx%s5Ll>{wd6luxh^aPhZ(TF`DFh47mUEE|dy?O(T8_{j@MqEiESUZUn>j zXVq`tcPeMm=ZN*&@&_)+ZeElX(^QTL{;9T-Jh<)XrO(r7F+z; zAHycs8v-%<@15~mCB?g568sJB3+7{g?*Ld?-)sRoc$=UitIn7HaK2ds<~ah{k!oV_XsgEMilGSo10HH~&?3MY z^|J0E6B-q-L5lv3Kf(BX*TxD+iL5Wh<+4MLPBcI;%}>&pXmaILR(pk9Oqz?M%X4v~ zV&yjzL}NBuevl`X$zm+K)e@#K0PJtPG$E3YN2vg3qlcigAs1wTPFZ&}vVvS1K2Kd@1 zAKISTygIe@)62rCm}1mJsK))$ZfV1G6i-zGa!Nu1+(p`jA5)C_EPF=C zY16(RfEJ2~vFcQN*XB4{{Lu>Ai5|v?cmb}_w)wx*rkIcdeA0JpybqlibOi#lZO1Ne ze(S(LR4}+Xt%)OYWb$d%3k-0$`O$Ni!>v)XbZTdH7Rx@#p&;&7%$o5PV?Pf3Sp7Tt zD(iPuu+sNUytOPg{wLU_T0wBR_0+yl(8mXOz3RxKJ>;y#FOL7{?Lodm0Lwj!7<(yMm&WN( zIZnWm&NrRNC9m!qw1{lF%v^_Wr^V%;p4w*ruS-VGOs!&3+BB18pI6akyK@fjSNV44 zC;wo0L=RY8de9D&PXjsK_c&|6`UdfkT^+WUx7 z>Yn3^d0%}D$kQ(#40yre&sd~4%RDZ<7y5z+#~bX2Fcji8U$E;u7;G6ZE+x77OXtKn zHF3;!xDgi?h}KQ$8w2~$Yw@cZ_nty6}8 zmzE-xd+rgtH=H?(`?0Riv{gP?9Ok-*#&v4$0Ik>iB^w7uFo-mAiUgT8U*x+jYrCE8 z)2?le6IyqO(gTN{vta((AGHrB{$9zt9XRKK7IOgK-c^+zcUl<@CGr=QcRek>o0!VL zB5_@x2K1ua{r16>R~5BKbEeI_Rlo90n>?+u{<`0oibbjJb2eKpy$vv@%QH&x@>Oe% zLjVtQUV;=AwpWMKTFq@G81%$UXx%D0)U93h9IJ=LOKxOo<;2-#uX@mY+%NLM-)HS_L-`m=-Pf;#2>ssL+Xe4 z&NeO1H$uN(F4We5%Itd3e>r!!`~BGI$GMT$IUHHlzMD#?2<0A(62SstGzH^#kyNur zEsvV<((luk&+ob|2Nf}uyXzqlGrRdT((!ZwtroGZ5SBQ|Q~RL2`e~>dA$72#jmDiP zu~^Ar%c8h)dK|v;WDw&gkbEp9828HGA1#_TA3j%lonLRC@d2RGwon17#^awZf^-(?dhm%i=HWbaa(=Ym^9)_eniCVq zuE-h0xezdn+2t@+JK2pDPL4)So9FiIwe_^e+x`HlG2R9%g8UTeL7aQL1;K_n=l+|H zF)eST@2@(;lorL>O|)aAfVGHQNKr;5tANZhUZxM zUq^D^-g3;GM8%ZtwJToJe`w6`f4wC3{LEGP(^f&*u35z^MRN&B3cRZLhn{Ynn%)#i zx6(4P+7p`m4DpYcd=i7!*4O_mJ6g+ zmK%v}^P2N%)H(pVu_!$!@+Q>2-Dm~}B1Z4e>w1xmS-(4;m$WP9ta9gX(EzeX>oK2I zwgftz;Mm{r_H6Q&djjp5Z%v=l>+0MEC-DaM{va`P-#|Z9 zX@7m)Hxg43;mn|>=^c6LX4%yB3+5hyo1@dtjRgxj6y1?%Uw&=M$a4>T1`=JvMVfk>&-v(|R6yl4fEp&!WyMOvPybn+`vj`mL(wGWbQVQoS-` zwXF?fn8w4>ligEOcJ{N?jxvtL7S~8;mTXPdW8#{_giM736$3euy%B4 zjYX-;!o+*@D~2%cbOK|uewbilsgs0N;r)vy`6QM@IrMU{&QuV8vRNIu_!=v?eVaGB4eOhS zOBLTr@opLwRah{m1@I6v^JCRWX=_s6SLIlQjo;MLpZ`Y3@x=Dq9>pGJbFiefF|ci+ zje3c-acJ@2l=#CD)2?(2hQ48e6kb=Jk%Ype&tA64zNDCuHtB6IFE5c!-kN;my2-@K zlAGtEUhjNBYwGz&+?MYGr+=LbrN*w|0a)7TTV#~rH6yZ_NZQmu!wd4M$uuLqdCOO$B~zb7Z2LH?F9t=@mzh{Z z^;27^6n5&txrEO5HQay=OK-H-jqH+?RQVO1`eBq!%}ZH}4$UH8Ybu422FE%^gF_+j zcFh+@{?2M$a&bPv9<@awc(md7Wwhr>aQ_%3qNA_H` zyG2b#d`mB4PV-NE^8nr~wsI$1895zE0B0GZIC3%jj=gkIo>cd@oeaq?wAU5Pmi_u< zzacGFU2cdYA&exgS^cSUbsOe%ESNoa?ZKITAxxmNrZhTBxhhLoh9zamE@zdFLoCjQ zV5joj^57&aV{4G|lSev6g=^^KymlJ=lkC#+lM9Qn6H#Ul&OU?fYc#o2m?~J0;QaVE zWS;I#1W4u5xVO7!Zu>r4;td%7yc1mAyjQO@Vt;krC4HGIZPq|gZwT$n83s9*kvJz< z))y#?Dd;QJZ@dvrq^_k1(#6Nj>PmN3)#UXAU;9||Nv)|M-Rp7H!NYaJS!Zy+;Y>MT z)&7qpqkzrL-%IwS>15GPlT_$wG-)erFMJX<79v)Rt$rQQR!FeP%_Tc`7AJq$zOZD* zqOrhFV_Cr;@o$MsXI;SkcbC+2dY>Nh8T=oc{&*q&<40eI=F)eXDg=WbEb-|g9fJ&^ zeTA|Aqnx#Tn#eT9EV^UrNzK>~bc=HiQR;jczX#(f8tLf{ftFa#M)` z-3T6e9~YH%XH;E_BKXe77Qb-cKyY?>?EPJQIV$7^Txff`^=(Gu3f3f8nq>6(XvFoC z-KUfLRp&82S~~na2^9t#W=hrEC)^}Ls;<8as%Ug(x%~&1veB=3S5nkF8o47 z5X24xd3%uT{NbO>z=NDdJtuc)G{3_+(n-NP1Is%EphX zf=pSOwzxACzV*)m{-z9GO4Z2fXGwK8mBG)az#odgrp4DpyJTAZ2PJj+Q&oaa+)4;J zt~n*eghRYf*c9X9VOwxL4H1xm#QT0n2u!vG?v$F+GlqbZR)4?qvsGkLevKa2r8WMb z`?RnVG`{BIlbSw(CS0OF;nQ13k99kn6JP#~Wq&C9LzFU(^vL`-R*{%lPxG#6!FND7iBu?Aeko5b| z1kjaT0>3tffQ1fpJzF70jHQ+Qb~6jSIYoCsv%ECc!K3J_!~jFrHd;@O5qPw-pfXq* zW6$JU_9|X>&x6k3YQwzZ)VW*=pGJkcQd%4cUM<#NJ4u1pzh1F8`o@P@n^Q0`GHtt| zNcT8qMZKV48Le%{qebm4UE+r3%QmQ0mC%A@L!M0io)O%Ltx) z-K%u-!tDBS^u=u;d$v@?68LtNh(GL1p3GNK0br?~d@oo6f_|!~a9loK3Hbfi9_s*2<=|YU@T1}CQ9XxWRk)aIXX+1?s(xB3pN%4co zlZG(!EE^8$%l8HmErn9v?m&>(=F&I?fpqduhS1tT(dlajneDWmdFuSF`#|EC!Fzz09STomsEFMJG#`_JBBlI#obIr95KvOURhFh5!o?4aF= z&ec6t`Y}tm3NOz^gm4fb1xLgtD;dJ^2%oc(HzPv0lB_2#P+bHkR}ws?mKRyaqGrVr z(g1b7XCqXeYzg+P#r)ACle5w;>MIZ-y}Hf@;@xuTAq9a9fo}~m zY7iE#P3~`}A+VX$QIUsQg4JqyTdGEc=IAp8!73-BXQm1C84SOhDeHFbRAO*C3-QWf zw3b8ekd?Lr$DJp;eG*~WcM**JJTupwT$q(M)j+WZxn!C+u(KUF(k~!^va=M2y}g?z zK;-upCqgn$gK*cEbo zS6$ihs#moH?sqge5_uR!NG;u&CETp1Xk7%1(<<;dcrUb~OlZ7u4syh@TV~?g7-|=+ zp-Ihq7YwrH@yM9`j5zvxM$i{ZqqYa7pUm26j1W_cZOX0?VEn8?w?k2|Q^@{u-2t4_ zMqYm!>2~T=3UbB#&hn8b7OCpq$+LJ#fV%E-JGi5Mv@tzaeTaj^vY|F$i;ZB=5&3&> zD7OIrI}UP{DZtW8#v)pLu0HgCSgOw+%qx{P+tdmpHAu#HtUkHw^cWV+?~66|mRcUi zWm~IVMaCkhGJOm~c%gITi3?f(+lIJK0SlsC*OeD^=p(#L-O~O>Jf99W#m;R(tL{%H z^0IZutnj`-ka}XnIJ@(&Phm_ot_?8<7);G2@^XGc#zV(>)mi@XZd`!K{-P)iYA((=k}A3=w;30@wDI}o@hQn!qr?Y052 z50Bj;T?WTK7ZS$zZ4e#pS5x{ZOX(j6le_93&S7z&@@Pfjqt}_M3_*J+&*SiP29$M* z*H5kzq2`>7Q2yBia{zK2WFDCaeb+|XG!~z7-o!CyUn5#doFN)vTu`sB5Tl2^8WSM{ zoiRq4nlP?QYrXw|aEl7Gv;d+pY=vCxWG`PwCwY~|u1C^Y(3`Z>{djUI`rNV%?1KS0 zl&UKtr^{4aqiV@3Dvo9Touf2HJa}t8oji_-Xe6M*R$E!pn9kBGnD)0Ht63%hn#e51 za+zCm(td5|y@nJtw0a@MlIDUe;fr;2VAlv3{3T7^Tn#hujnX79q45&YYD&j+jCNZv z;kY9aK3xy^v%gyb-&cCzmEg99Nk>=I`)2svTTMn7xuP3?sf`6(_nJ~1^+p(!AJ-=f z3<;QNEvq;tZMLu=1XISEEd7rk_(c5XBd)kEj#hqb6TniA%0hHwE%-e0`S^N3;kh`? zQTR04AkFO63+qDQv||Te-)+J`gyLbvW(TK&Y5-+2YomC_3pBN8=pA={KW+3PKP)6aL2O zgK&Dyn$m_WVRQ(%qWWyW+wfCY%yf`@p!s}(|GlbVT zisA(XwH}#`r`T`-yBKsLU=%9z^;PUP@KIMp*&y2hS%Qmbc?Yb4t5#E*gE7y~$iTJ( z6DM?z^i}4gdn1M=p|rdL?82%k6(frdxrR837)Oa^E213a75ix98=MH&Qn_0a%tX!v zG9#y}^O?97ABONEM-j1r;2Q_Q_)r#<*nC-ZKpMLah8 z#a|u3i*t}K+Djv^84~3<@q2^eHL7hvlI=MLIhs#|>}f$du5vMwY>_cF?z|S6nw91q zC_9hAl(Qot(AtG19%-F-E$<2PeUZhr&WSECKV^B(K=BS|p0*E)W>~=hTkeWh9r?b? zk;fd_2;RXxdxp?0MR)h8KJte-WIZFT@~AWzzZkH55sfm{AULu$cAceVhJbzoC!e<1 z{^Ja-3?34XCBZ?EJVc z3i%^VdKyyE3&Tjgtep)=>cUSKNObJr@o8g;(R9oAdWudY6ZqmV!-jM}_Haw4RJRW= zn!OKt=KAFA&(qzhu8n zaM`|97VOW|6Na!2u{I8lSj#MPkk`4rw3Ig!I+j2_D84Q6HUrmeJbPp^L+Fj3wAL#e zy{N84Rd5n;_Za+3F0aSp#a;{Zy2SF(o4J$WY>nK7hv-ewr6D4{=zPl6#Dn{xqIY$^>O(Q#m%C(uQ&C52~10e7b5; z&ge!U=;i?GKyn2`*n`;fmh3vT4#$RI80TJq_e#FNA21L45VeeGY>nkGy1bKP*g*Q1 zjI453>J1j&^uLAro7mjw3zC zQJ>&+xojxB*Bk_P}8t`vic~z z#9j2o;HhDTR895a5rNuz;QJzh-Ae<=OziXlzfdxQMbL$~DUpFlb;prh^V~sz9~2O1 z;Yg~FsC40{NK_w=85_a^U;8g~@mbu>BK+Qo2Ywu7Dvq=(AHP$AqawZ(eQ$!l9_1Y< zlPEI-c>IYdOHfAgAwLr3C_@_Gmd4{-pnL}vuLia+L^;5aMrmQYHp-{5-2~hHP`1Z* zKWyKL@;O6V%W+D#1@Q^69wu9Tp?^W_j>P^fyte;Hmm+r_eGLZJE8S8>@5a!p!%w05cWgT7 ns-4CUU_`{1gy=$W-KcPR;*xbcuKjQa@Ne#{g)_?n1R4Jyd9zVm diff --git a/mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png b/mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png deleted file mode 100644 index 81c68a68c4138627192149b6f1f66b0ae59d152a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19127 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 diff --git a/mod/data/mac-fallback-assets/range_poi.png b/mod/data/mac-fallback-assets/range_poi.png deleted file mode 100644 index 5a282adb3a6a5f2a524b878032b466cb29a15285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10364 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 diff --git a/mod/data/mac-fallback-assets/range_smokeToken.png b/mod/data/mac-fallback-assets/range_smokeToken.png deleted file mode 100644 index 5a282adb3a6a5f2a524b878032b466cb29a15285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10364 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 diff --git a/mod/data/mac-fallback-assets/range_token.png b/mod/data/mac-fallback-assets/range_token.png deleted file mode 100644 index 5a282adb3a6a5f2a524b878032b466cb29a15285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10364 zcmdUVd0bQ1x9>VhAb?0BIG`efC^%KY36KDB#1T`qYFmdmfYymvkb)u{!Gcx`Xs?A< zl=u@Z*j7-S5X56q(24N&3o;& zhqbmU{Cqo_^)Mr%j*}*in?WQ(Q$!}l_=)T_Fq){}*h%9?&k9X`a%c3;p1w!NUyVAE zdSrISk?WgBjvRkv?x4=>xPX|Pvq8p@B@N=?EA2Zy4WV^mLi+u5)4PW1&xa+Rz1#HZ zyFj9duU=RA2x&~=hAzDYR6CuBb+&%_a*Gk^tQn#v|J%iVEr}(PONA+)iv(n8LbQEx zRhHXYV*5j`TdvR3$JmhCiXrx>`Nq?h#*{XZNG)2D@^BnOv#p5SckPp$MZfztB5u+4 zjinPhlh#B)l;f(eu#O_yASEh2UmANH2z&by>7>ziE38Cx%9ltx)7a;QlL_f|K(r$g zhj9$MN?*JU&*y%|#Wi?$2>+MZnEiCnf^mDr3nr=-zD%Zs$) z*-{a$wOJ!awbf`+4`xVhzuzuNZcb_AVcp+q#7ENO#a@+^#9EVTH`>t^l9EIz={yBL*?Mf6Za_TOKXBup_UOH-2DY5qs4<5>^z^OjF_}#`sW% zrGWiHL`w7hcI^Hw42MZ9*;^suKHW^}VGJ$5tP?A{I8Y5{?K9UmOxgril6%twq`2CP_A8z@E#{@JMfLQmPXfF;*pf;e3DUGFcjkgQjVrfNlFHBRNzCZ z;nf{$iaWx0#jcbYRYoP>As%#ZBDv8RdU0& zxHK{BqvSbiZ#PoA*rk%(ca2a!4RU=9!X>+c%&%1$Q|C&d6(yZtO=!L@mt zM%^Y>5bh!%Q%HXt|6G8?_Fo4{ez|Wfmbh3q}>=H;Vc5Pzl#M-zh{vXf! zO-7Vy2XpwWAubMV*^M3x&qau+bgRUf*d^}E&UuF$(SjIHOD}=IRfok(6i#{eU@CTT`M5j#n=ucpQiajXAL;da5d43 zih+!3cTdt8RoaodTeaJ%X~gyn55-88jX0&g3+XwrF>y9v>jSn=t(%Fm+{@2R7f|En zSRrv!HDS6vMBbf=3WDwm&oXj%JH*GZ#=(?+8|xO6m2k-;VU~b)$X+mX)I+j!BH72q zdJ&bociUMINYZ?vEwP^i!4wElK;Y&icbkN8wl~&;C|VMcUmiCKZd^4P!)cukARkhx(i7qs-AjKO4jGbb|==+ zBXH;@l5emrAWA-${au}iKC71+lXj{mCHO%GmFyG`A^Q1s2PU}En4a*;&eG1Cj8*Y# z=tTi3w$}IGV?xI&D$GbeUXvc|KqXPJ1BiBcWc&fTVYUZ}g0;6NuM^OLuq_BUTk8kz zGNu2xUxB#gt2r{OFDZ7$A|~wdD4ucE|D_Mztyd#*{CIg`VDm3cZk*d1QpE5U7^lge zikDA>;ajGp^5hzFHaWG9Q*9DbCePG<`_bL*hno+MuD!Iw2szP3iW7+@kIxvZ!8U zO!UgbEoeByjt$#_2wa(*;;|Fkmn;|fVVbLmRa&X-X9)Ov>WBMEsdP_l7X&6v_K#;w zsrL7=*u5Jn!>{ZWQAlM4{NQbnCSZtwa&!s-v6b$DSz?Aw46_F1XUREXyNH`2+YHJw zO?mDZQVxS7lY2^#`{$;#>fSyf(PQ`ctgngtQ?`mo=~eyZ-oB*kEa^`0@Yl)9Oh{Ig zZ$$KHtzY%W0+KvaU^_mYd?Rc#ar13qXGKbo=PMHmzcd!Iyir9|55)e9Lxq%1P4dD{ z6_H$-4UJ~n!U!da^|F*sr2jS+6kk-&EA|!8SzQA5UytOcVbe+LV+(t_#0Po%m{4O% zEGQM-&n<>?*urX3F58Z=PmTrkDpgal6N%~{h>4Yj$g#`f6PVDxSw2J;{Uu8+Nf4F= zvlA{O1%ol6mDc%uMZtyrl8GFf0i; z`$smIMK{kS7L3v)a55&LUqgYpfTO$E9Js5saZMujL9QtbnpJ9t$;Bs?)AFesA(kc} z_C;SBet(*n=$FOuFZ$5%sw*(UF9C@!9BFvvG_+?eN_o+n$C4AH?z9eyr4{%+981f8 z+YM3DeqZo#G%e?LgK(N=O*&3s2C?L82k=wz^}89nNs;6xLHpZYGZRViA8w#fvde2G zwgY^Oh>f~76bCNPoP)NE+By(N9HrUYgIyW5G!WCI?BV$nwikRb{P?uRP-#|!Uq^wQ zxoeJga7gib0k`-ryh3jFcG*X5kR8$&qNj)F@?MOx(~$K<_Pe}}#qeZ!jz-r8qi4_j zU~fjdbU_t5%WwKtTasVZ1qi1b==o5FnJ~DQ-@jIXX1Hd-s8~`7h6F&ljGNO6aUcQ( zmQ*fXI>6$t4SyaHvBI!tK$8{5jf&%kc}~%3v(YFkomnU)3+)9MOxGi4MPwWGkb^To zy*5AZRw7PbL;OH}F*|z7^d-sS; zAA{jxH#d~G`K}@w9R)eJTBPW+MM48`0++|E)Hm@qAWEFEnMz~Ty}B*h{$(w^Q5k33SvRR`=rJ*a}7+v<{@z87u$vy2Xpi z3nRaA3#2*o8!KU(kIpG;5egRue1c;pH2?5Sv=ScPQl2~kuQ6DqWZ%E|#&LbbN0{Ii+1BIe zOYC6J6Ty5BH8}8pN8TktH_GLh3!PP=dtDNd?EXk*HwOH z&y#inu4xspw0yyavcFR#gW`f(OA2r1RgTLltox8eL&U8TRh~J0ww;cl{abac4)c%w zfKaU*R#{L4@#fN;To?ZVFm@Wlz2Z%x-1AY+?GK13>(wga^$j5@A0W`mN7jWJ$sT;J zJuCA2>I1fwv}v;5_Rixq03?KE{-^yENQ}sb_(@skb+oLV!Yy;o@+jD4_`fD9H`4R# znH1$`-qrox@+{xm3N}&FYJxZV6@~w-fx#0JY^c$&sA{UO@BNS(Ee`f~S<)FUg&+@` z=Uw*lpB4K%qATgGcnM3&3pYOfC$U?X*x!wm(3QNKzLf)0o$<=E`~$Pq&Yl0xl4nOW ziP64!RhF`{xj+8l?ncyZY`4%_tZL6gesFUBSAxvo@}}aKPfz;HX&wtKwh~uPe690i z&$s_wO8*n)oZXGH{l1ZrKOl%bKPYDt1+1NbkI(hFNAuemA=HEPxDMik`}D7O?KW(J@s$pq|s4mi9EuuBt-a@rpQ{ae{j4F2?3Y%9_f zA^SPcxli_>+vnP?fk6_=>hisJIWiuITJO8^t(5tu2On;xmY?ZsSj$pN8gPxb zbkgasE*mx$t$WKN@7IM6&1*R%d!960ooI|eeqaTJn9i88!_Bvkd@r8cN|0U*@h_$%E@5>j1zG%C3*!Q8}^a1EwdTRKs^j{4s zfX1l5@G3?RP7iOp#pq-PQi5aVbm#D=7<0LNpknaqmb*j(l^i*NzLN3-4{cYs2#vnu zr}@4=54^ulp)cET#VS1Tx@i@EDkTTB^gB-`^h{80F$$A-^^>ao>h7SMhL| z6Ykmyn2d8B9Q{>QjiGO|h`sa@KES@47xT36S(}Of9LPtBjBL^S8}4F(e9RS|U|3?p z8S9oi{-?8OV)`fm${tuAv)XU4Wa>8^Xp-R~T{d{pTbIE+7x}*~Qo`&owDh0cdw&TZ zNqw&Km!!LkUny77#HJy=i0uFB8!Db)2oDoG+0jn^(j?zG<^646PKr%HDaa{peG!mS z(*a|MRkZZ!MrVwWoEUAdPvAbvuAWbw8Le;#G-12f<}5VCc5H_F&6DB1CA;Up@-(Q6 zv$7kXOKW|+$kUF8jY_ul`f4{%52qg1^O_@Q*w&uRXrmFzeghhc-FHG#NM}F93By0u z9xrV1S~-PA9pZ<#?P+jtV=;Y3B8aWXEZ*x#tF+@`@|L$5o&_Z6nLZ7Tm8DC{-qYCh z2sBiKJy&@$+VKm*G2HU}g9emTiU+{wxg)iQZ;;9>6*SJf${P{I6D_+x=25HA* zI#RQ?Y1$@B+39!(yw~_kes!QhRpT+=sYjx(8y1B0o9$n6&}P z&Tn)9eEaBEE81=iiY)_R)2U0UuQ?!J#Cz)0MdjCeQ|BuFsvo@U20w=e-PHY#Bka`t zzBhdm^()#9teS$j^eNO&vm1_A^IL>rDu^Y4T*$v{D)!u&kwPV#-Qe_!U1$D?rjjjg zu#V!}J%7aVGZxHbau%j|7xH%9FcI27(TA}7_3QTGqT6N<9ShE!)p?HY)+qFog- zaUmo!oUMPHSq{%>d8`$NTA|xF%kzyD#pnVspK*%=i|on#-ZXe!?G;VZSRM;vaV~~; z;BIdhcV(3Jfipn#laLEC0jIe83npTA!}+NKPI>nj+F7+*vPEn~ZWz<{^b8VM119Qw zu+Xd0j`gPGkoyBhKd=4TTp*VQt^hShPls5N79hz)`g7hFdz0w76HHSZB@@W|1%|Y} zh=UZSNILbUu=Ua0KH1Hu?JZsFfMF&X51e`*2^Lh+<*<}M5j9orLw~({b$v8(Lu7bI zQ(9DSxNF6~;X~!tH~f_6nb0&Zx%%~X;-(p1`s0ISC)?ahJLM6mzo=-`%n?!Db1B4s zlU$bVOIim*1|hM2RJ@czlVZ(qb*@a->=)7Adlj(mI`@=%bB6sX>>_NlUUOeIg_Kqj zs5!SVEcqi5jjS&Lt$L5xEJKNgT*MTUbCPtWpmX z0Rh7fm33w)>7qyCfWbdEGrAuhmj^~3Zo(0e8)zPR2zk1lGA#d^qOk7~`hI_2f@Hg6 z_1r_2)bC0@a`}GA3)82PS|<6JXq0Nr0p$O>%BCW%SroR*LPWQ83L{eYuAYU2{qhU> zU5K5WJpJJ~QumTzYaO5(d|>zGN0F3!kH1R)+>GPRQrE_Zzjs8kPiNGU>~lgfK&%VV zGF4rwC&PA~XHHzFw<0|QTJS@DOiR)&_>E3HJKKa@V ziQqZ#-$z3mE08JIj`p5~z2r;3XzVyTksUVKG&kHB#6+_!PDwpAC8z_Xa|tvhGcp zKHzKb4aaTEf;+3QaJ1g}cKllba_8M!bLU8;pXU?lhO9ag=^gZwAyS288BEeuw@6H> zLAFu#&~{rkrn!%6iF8)XH$-Iyn7! z9wj(HKh}#c5%5ON^KkMKwUZmEnUh$#7Uq#@$j2?`V*4X(*PC6dHb!a-5m7N8QK;5K6aY?sD;&I}n#u{?)aHg|2(P-B7(5iJnzhP?Vg*E6qP3IpctR3=UvF8T1w;*UN?568?nu&py9CYO?+L4t|!7=fJ@`1 zS11^9LVNwt#!ba0NR)!Z3sik9eCEh<^DV59G%a4KwFUZ5Jo<6#CJr%AX$6$)Kv{;$ zl_{GMvJxvKt1$05P*yuP#z%*QPw6f&e+wXlFU|o#q2E?WYNw&R1=TEEka?uGpMX?m zpp+M&7y4qQ&qRVzhk6&jNNSLSh!Qk9vBrsL3i?xB8#f`Rtrj^D`_Md6IRa%k<4us- z%ePB|%#&fi8h{@{mCPUDbsFF}DXEPB{>VI1JqkrW09OON2H>|Wg*5jPSE$7oA*oOzGu4@B?RjK+T;6O#eSvg} z3DUWG;Z_lmD=HVRN>Z__-|Zxz2PlxqLwk)>NN>=Wdsdu?Ut?XWitpO&y0hcYV-%B@ncaR^Wy<-Old zI~QT#66&&2P@l!uW_8%g$a|xDsw2v}Ok63cs75>&_oRLk&B+OcQY&x{`o>Dg`T_hj z=m&@xwPm!O8mBz9t1Hb#aaPSFBI|Kdic{)@MTqR?eT^vts1JZT#zer)F^^!eHL!{( zS~^V6!1ZVgBSqQRMJT1~|1qr}*1+f|VkJ97(X{}bP!$hPc8a23{B=SdB0hU(i$G<* z#E$g>s?7u`yV^X0;=oBY;A4vLuNCq|Qmo}>=HRdw%v?B*deF!0;q4n~0QSY@zSe59 zv39H<>^-5AxODdp3D;Qw+n;CFxISklf(&<76qRir&cCN?`}ZF0Rp3Rk2?!X`Ta3!O eza-s>oMz^npIP1k8?TQD-=y(=;|`BeM*Syc!aU*t diff --git a/mod/data/mac-fallback-assets/range_tokenRangeTwo.png b/mod/data/mac-fallback-assets/range_tokenRangeTwo.png deleted file mode 100644 index 4a8a2f488e72d64ace0344269757f910d16f74ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12344 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 diff --git a/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d b/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d deleted file mode 100644 index 15bb9258a1c225572de9b686b6595455abfe184f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10922 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 Mcz6b}|N5b`;!*7d "mac" | "windows" -deploymentMode = deploymentMode or "mac" -- table-wide setting - -function gGetMode(params) - return playerOverlayMode[params.color] or "mac" -end - -function gGetDeploymentMode() - return deploymentMode -end - -function gToggleDeploymentMode() - deploymentMode = (deploymentMode == "mac") and "windows" or "mac" - macRefreshModeUI() -end - -function gCohesionTrigger(params) - if not params or not params.figGUID then return end - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local mode = playerOverlayMode[params.playerColor] or "mac" - if mode == "windows" then - -- Object may not have the original alias (e.g., hovered non-fig object). - -- pcall guards against "no such function" errors that would otherwise - -- propagate out of the hotkey/button callback. - local ok = pcall(function() fig.call("spawnCohesionRulerOriginal", fig) end) - if not ok then gToggleCohesion({figGUID = params.figGUID}) end - else - gToggleCohesion({figGUID = params.figGUID}) - end -end - -function gRangeTrigger(params) - if not params or not params.figGUID then return end - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local mode = playerOverlayMode[params.playerColor] or "mac" - if mode == "windows" then - local ok = pcall(function() fig.call("spawnRangeRulerOriginal", fig) end) - if not ok then gToggleRange({figGUID = params.figGUID}) end - else - gToggleRange({figGUID = params.figGUID}) - end -end - --- UI: floating panel with one toggle button per seated player -function macModeClick(player, _, id) - local color = id:sub(9) -- "macmode_Red" -> "Red" - if player.color ~= color then - broadcastToColor("Only the player at this seat can toggle their mode.", - player.color, {1, 0.5, 0.5}) - return - end - local cur = playerOverlayMode[color] or "mac" - playerOverlayMode[color] = (cur == "mac") and "windows" or "mac" - -- Wipe any active Cohesion/Range overlays for this player so the next - -- trigger redraws in the chosen mode without stale visuals. - macRefreshModeUI() -end - -local function macFindNodeById(tree, id) - for _, node in ipairs(tree) do - if node.attributes and node.attributes.id == id then return node, tree end - if node.children then - local found, parent = macFindNodeById(node.children, id) - if found then return found, parent end - end - end - return nil, nil -end - --- Tracks the panel's desired active state across refreshes. Persists so a --- click on the close X before the initial refresh is honored. -macModePanelActive = (macModePanelActive == nil) and true or macModePanelActive - -function macModeToggleVisibility() - macModePanelActive = not macModePanelActive - local tree = UI.getXmlTable() or {} - local panel = macFindNodeById(tree, "macModePanel") - if panel then - panel.attributes.active = macModePanelActive and "true" or "false" - UI.setXmlTable(tree) - else - macRefreshModeUI() - end -end - -function macRefreshModeUI() - local seated = Player.getPlayers() - local rows = {{ - tag = "Panel", - attributes = { - color = "transparent", - preferredHeight = "24", - }, - children = { - { - tag = "Text", - attributes = { - text = "Mac TTS U6 Patch", - fontSize = "16", - color = "white", - alignment = "MiddleLeft", - rectAlignment = "MiddleLeft", - }, - }, - { - tag = "Button", - attributes = { - id = "macModePanelClose", - text = "X", - onClick = "macModeToggleVisibility", - color = "#3a1e1e", - textColor = "white", - fontSize = "12", - width = "24", - height = "20", - rectAlignment = "MiddleRight", - }, - }, - }, - }, { - tag = "Text", - attributes = { - text = "Cohesion & Range: pick your renderer", - fontSize = "12", - color = "#bbbbbb", - alignment = "MiddleCenter", - }, - }} - if #seated == 0 then - table.insert(rows, { - tag = "Text", - attributes = { - text = "(no seated players)", - fontSize = "12", - color = "#888888", - alignment = "MiddleCenter", - }, - }) - end - for _, p in ipairs(seated) do - local mode = playerOverlayMode[p.color] or "mac" - local label = p.color .. " - " .. - (mode == "mac" and "MAC FALLBACK" or "WINDOWS ORIGINAL") - local bg = (mode == "mac") and "#1e7a3a" or "#7a3a1e" - table.insert(rows, { - tag = "Button", - attributes = { - id = "macmode_" .. p.color, - text = label, - onClick = "macModeClick", - color = bg, - fontSize = "13", - textColor = "white", - preferredHeight = "28", - }, - }) - end - -- Deployment is table-wide (everyone sees the same zones); use a single - -- global toggle rather than per-seat. - table.insert(rows, { - tag = "Text", - attributes = { - text = "Deployment (table-wide)", - fontSize = "12", - color = "#bbbbbb", - alignment = "MiddleCenter", - }, - }) - table.insert(rows, { - tag = "Button", - attributes = { - id = "macmode_deployment", - text = (deploymentMode == "mac") and "DEPLOYMENT: MAC FALLBACK" - or "DEPLOYMENT: WINDOWS ORIGINAL", - onClick = "macDeploymentClick", - color = (deploymentMode == "mac") and "#1e3a7a" or "#7a3a1e", - fontSize = "13", - textColor = "white", - preferredHeight = "28", - }, - }) - -- Build my panel (will be merged into the existing UI tree below so we - -- don't clobber legionFloatingMenu / Welcome / Chess Clocks etc.). - local panel = { - tag = "Panel", - attributes = { - id = "macModePanel", - active = macModePanelActive and "true" or "false", - rectAlignment = "MiddleRight", - offsetXY = "-10 80", - width = "260", - height = "320", - color = "rgba(0.06,0.06,0.06,0.9)", - padding = "8 8 8 8", - outlineSize = "1 1", - outline = "#303030", - }, - children = {{ - tag = "VerticalLayout", - attributes = { - spacing = "4", - childForceExpandHeight = "false", - childForceExpandWidth = "true", - }, - children = rows, - }}, - } - - local tree = UI.getXmlTable() or {} - -- Remove any stale macModePanel before reinserting the fresh one. - for i = #tree, 1, -1 do - if tree[i].attributes and tree[i].attributes.id == "macModePanel" then - table.remove(tree, i) - end - end - table.insert(tree, panel) - - -- Inject a "Mac Patch" button into the bottom-right legionFloatingMenu - -- (replaces the first interactable=false placeholder button). - local menu = macFindNodeById(tree, "legionFloatingMenu") - if menu and menu.children then - local hasMine = false - for _, c in ipairs(menu.children) do - if c.attributes and c.attributes.id == "macModeMenuButton" then - hasMine = true; break - end - end - if not hasMine then - for i, c in ipairs(menu.children) do - if c.attributes and c.attributes.interactable == "false" then - menu.children[i] = { - tag = "Button", - attributes = { - id = "macModeMenuButton", - fontSize = "10", - onClick = "macModeToggleVisibility", - tooltip = "Toggle Mac TTS U6 Patch panel", - }, - value = "Mac Patch", - } - break - end - end - end - end - - UI.setXmlTable(tree) -end - -function macDeploymentClick(_, _, _) - gToggleDeploymentMode() -end - -function onPlayerChangeColor(_) macRefreshModeUI() end -function onPlayerConnect(_) macRefreshModeUI() end -function onPlayerDisconnect(_) macRefreshModeUI() end - -Wait.time(macRefreshModeUI, 2) - --- Override hotkey init functions to capture playerColor and route through --- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys --- here SHADOWS the originals - when the original onLoad runs init*(), our --- versions register the hotkey instead. - -function initCohesionHotkeys() - addHotkey("Show Cohesion On Hovered Model", - function(playerColor, hoverObject, _) - if not hoverObject or not hoverObject.interactable then return end - gCohesionTrigger({ - figGUID = hoverObject.getGUID(), - playerColor = playerColor, - }) - end) -end - -function initRangebandHotkeys() - addHotkey("Show Range On Hovered Model", - function(playerColor, hoverObject, _) - if not hoverObject or not hoverObject.interactable then return end - gRangeTrigger({ - figGUID = hoverObject.getGUID(), - playerColor = playerColor, - }) - end) -end - --- Legacy global-scope wrappers (for any code that still calls these directly). --- Default to mac mode when no color context is available. -function showCohesionOnHoveredModel(hoverObject) - if not hoverObject or not hoverObject.interactable then return end - gToggleCohesion({ figGUID = hoverObject.getGUID() }) -end - -function spawnCohesionRuler(cohesionSourceObject) - if not cohesionSourceObject then return end - gSpawnCohesion({ figGUID = cohesionSourceObject.getGUID() }) -end - -function clearCohesionRuler() - for key, entry in pairs(activeOverlays) do - if entry.type == "cohesion" then activeOverlays[key] = nil end - end - macRedrawAll() -end - -function showRangeOnHoveredModel(hoverObject) - if not hoverObject or not hoverObject.interactable then return end - gToggleRange({ figGUID = hoverObject.getGUID() }) -end - -function spawnRangeRuler(rangeSourceObject, _) - if not rangeSourceObject then return end - gSpawnRange({ figGUID = rangeSourceObject.getGUID() }) -end - -function clearRangeRulers() - gClearAllRange() -end - diff --git a/mod/src/includes/RangeRulers.ttslua b/mod/src/includes/RangeRulers.ttslua index c51676e0..6df6e15a 100644 --- a/mod/src/includes/RangeRulers.ttslua +++ b/mod/src/includes/RangeRulers.ttslua @@ -80,33 +80,3 @@ function spawnRangeRuler(rangeSourceObject, projectorBundleOverride) noRulers = false end - --- ============================================ --- Mac fallback per-seat router (appended). --- Mirrors the Cohesion pattern. See !/Overlays for the manager. --- ============================================ -spawnRangeRulerOriginal = spawnRangeRuler -clearRangeRulersOriginal = clearRangeRulers - -function spawnRangeRuler(rangeSourceObject, _) - if not rangeSourceObject then return end - Global.call("gRangeTrigger", { - figGUID = rangeSourceObject.getGUID(), - playerColor = nil, - }) -end - -function clearRangeRulers() - Global.call("gClearAllRange", {}) - -- Also run the original (destroys the bundle Object spawned in Windows - -- mode). Skip if rangeRuler was never set (pure-Mac session). - if rangeRuler ~= nil then - pcall(clearRangeRulersOriginal) - end -end - -function clearRangeRuler() - if self and self.getGUID and self.getGUID() ~= "-1" then - Global.call("gClearRange", { figGUID = self.getGUID() }) - end -end diff --git a/src/index.ts b/src/index.ts index ba63a1e4..45d99efd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -103,31 +103,25 @@ export async function compileSaveFile( } } -function defaultTTSHomeDir(): string { - const platform = os.platform(); - if (platform === 'win32') { - return steam.homeDir.win32(process.env); - } - if (platform === 'darwin') { - return path.join(os.homedir(), 'Library', 'Tabletop Simulator'); - } - if (platform === 'linux') { - return path.join(os.homedir(), '.local', 'share', 'Tabletop Simulator'); - } - throw new Error(`Unsupported platform: ${platform}`); -} - export async function destroySymlink(homeDir?: string): Promise { + // TODO: Add non-win32 support. if (!homeDir) { - homeDir = defaultTTSHomeDir(); + if (os.platform() !== 'win32') { + throw new Error(`Unsupported platform: ${os.platform()}`); + } + homeDir = steam.homeDir.win32(process.env); } const from = path.join(homeDir, 'Saves', 'TTSDevLink'); return fs.remove(from); } export async function createSymlink(homeDir?: string): Promise { + // TODO: Add non-win32 support. if (!homeDir) { - homeDir = defaultTTSHomeDir(); + if (os.platform() !== 'win32') { + throw new Error(`Unsupported platform: ${os.platform()}`); + } + homeDir = steam.homeDir.win32(process.env); } await destroySymlink(homeDir); const from = path.join(homeDir, 'Saves', 'TTSDevLink'); From de80abaa80cb522c4547fc0ef80abe1d837392a4 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 20 May 2026 00:55:20 -0400 Subject: [PATCH 90/93] Revert "Revert "Mac projector fallback"" --- mac-patcher/.gitignore | 6 + mac-patcher/README.md | 74 + mac-patcher/SHADER_INVENTORY.md | 59 + mac-patcher/design-refactor.md | 330 +++ mac-patcher/extract_projector_specs.py | 198 ++ mac-patcher/generate_overlay_assets.py | 282 +++ mac-patcher/inspect_cohesion.py | 83 + mac-patcher/inspect_externals.py | 51 + mac-patcher/materials-reference.md | 170 ++ mac-patcher/patch_cohesion_rename.py | 81 + mac-patcher/patch_save_for_mac.py | 2171 +++++++++++++++++ mac-patcher/requirements.txt | 3 + mac-patcher/retro-cohesion.md | 170 ++ mac-patcher/retro-deployment.md | 76 + mac-patcher/retro-movement.md | 122 + mac-patcher/retro-range.md | 86 + mac-patcher/scan_bundles.py | 166 ++ .../mac-fallback-assets/cohesion_halo.png | Bin 0 -> 22585 bytes .../mac-fallback-assets/deployment_blue.png | Bin 0 -> 856 bytes .../mac-fallback-assets/deployment_red.png | Bin 0 -> 856 bytes .../mac-fallback-assets/max_move_cyan.png | Bin 0 -> 4124 bytes .../mac-fallback-assets/range_bombCart.png | Bin 0 -> 12426 bytes .../range_fig_leader_epic.png | Bin 0 -> 19173 bytes .../range_fig_leader_huge.png | Bin 0 -> 18811 bytes .../range_fig_leader_laat.png | Bin 0 -> 19058 bytes .../range_fig_leader_large.png | Bin 0 -> 18753 bytes .../range_fig_leader_long_stadium.png | Bin 0 -> 19025 bytes .../range_fig_leader_medium.png | Bin 0 -> 18599 bytes .../range_fig_leader_small.png | Bin 0 -> 18475 bytes .../range_fig_leader_snail_stadium.png | Bin 0 -> 19127 bytes mod/data/mac-fallback-assets/range_poi.png | Bin 0 -> 10364 bytes .../mac-fallback-assets/range_smokeToken.png | Bin 0 -> 10364 bytes mod/data/mac-fallback-assets/range_token.png | Bin 0 -> 10364 bytes .../range_tokenRangeTwo.png | Bin 0 -> 12344 bytes .../silhouette_mac_fallback.unity3d | Bin 0 -> 10922 bytes mod/src/StarWarsLegion.lua | 1 + mod/src/StarWarsLegion/Order_Token.a57c41.lua | 97 +- .../SETUP_CONTROLLER.1cb552.lua | 32 +- mod/src/includes/Cohesion.ttslua | 28 +- mod/src/includes/Overlays.ttslua | 929 +++++++ mod/src/includes/RangeRulers.ttslua | 30 + src/index.ts | 26 +- 42 files changed, 5218 insertions(+), 53 deletions(-) create mode 100644 mac-patcher/.gitignore create mode 100644 mac-patcher/README.md create mode 100644 mac-patcher/SHADER_INVENTORY.md create mode 100644 mac-patcher/design-refactor.md create mode 100644 mac-patcher/extract_projector_specs.py create mode 100644 mac-patcher/generate_overlay_assets.py create mode 100644 mac-patcher/inspect_cohesion.py create mode 100644 mac-patcher/inspect_externals.py create mode 100644 mac-patcher/materials-reference.md create mode 100644 mac-patcher/patch_cohesion_rename.py create mode 100644 mac-patcher/patch_save_for_mac.py create mode 100644 mac-patcher/requirements.txt create mode 100644 mac-patcher/retro-cohesion.md create mode 100644 mac-patcher/retro-deployment.md create mode 100644 mac-patcher/retro-movement.md create mode 100644 mac-patcher/retro-range.md create mode 100644 mac-patcher/scan_bundles.py create mode 100644 mod/data/mac-fallback-assets/cohesion_halo.png create mode 100644 mod/data/mac-fallback-assets/deployment_blue.png create mode 100644 mod/data/mac-fallback-assets/deployment_red.png create mode 100644 mod/data/mac-fallback-assets/max_move_cyan.png create mode 100644 mod/data/mac-fallback-assets/range_bombCart.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_epic.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_huge.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_laat.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_large.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_long_stadium.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_medium.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_small.png create mode 100644 mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png create mode 100644 mod/data/mac-fallback-assets/range_poi.png create mode 100644 mod/data/mac-fallback-assets/range_smokeToken.png create mode 100644 mod/data/mac-fallback-assets/range_token.png create mode 100644 mod/data/mac-fallback-assets/range_tokenRangeTwo.png create mode 100644 mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d create mode 100644 mod/src/includes/Overlays.ttslua diff --git a/mac-patcher/.gitignore b/mac-patcher/.gitignore new file mode 100644 index 00000000..34bac69b --- /dev/null +++ b/mac-patcher/.gitignore @@ -0,0 +1,6 @@ +venv/ +__pycache__/ +*.pyc +*.bak +*.bak-* +scan_bundles_*.out diff --git a/mac-patcher/README.md b/mac-patcher/README.md new file mode 100644 index 00000000..420bd388 --- /dev/null +++ b/mac-patcher/README.md @@ -0,0 +1,74 @@ +# mac-patcher + +Python patcher that injects Mac-compatibility fallbacks into a SWL TTS save +file. Works around the TTS Unity 6 (Berserk) shader bug where vanilla +AssetBundle Projectors (range rulers, cohesion halos, movement templates, +deployment zones, silhouettes) render as a magenta rectangle on macOS +because the custom shader bundled by Allen White (Dicewrench) fails to load +under Unity 6 Metal. + +## What it does + +For each affected object script in a `TS_Save_N.json`: + +- **Cohesion / Range / Maximum Move / Deployment / Silhouette**: routes the + visualization through a Mac-friendly path (vector lines + decal PNGs) + built into the Global script. The vanilla bundle path stays available + for per-seat opt-in (see "Mac TTS U6 Patch" floating panel). + +- **SIL button**: renames `click_function = "toggleSilhouettes"` to + `"macToggleSil"` + injects a forwarder. Empirically the engine silently + swallows the original click_function name in Mac mode (the button plays + its sound/animation but the function is never invoked). The rename + bypasses the issue entirely. + +- **clearSilhouette nil guard**: upstream `clearSilhouette()` derefs + `removeAttachments()[1]` which is nil when `silhouetteState` is true but + the physical silhouette attachment has been lost across save reloads. + Patched to guard the nil case + reset state in onload. + +- **Order Token Speed/Move button overrides**: inlines vanilla bodies of + `changeSpeed1/2/3` and `moveForward/Backwards/Left/Right` to capture + the clicking player's color (`macActivePlayerForMove`) for per-seat + routing. Inlined rather than wrapped because the List Builder copies the + block to Command Token Custom_Models which lack the vanilla + Order_Token function bodies (wrap+call_orig would crash with "attempt + to call a nil value"). + +## Usage + +```bash +# Setup once +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt + +# Patch a save +python3 patch_save_for_mac.py [--reload] +``` + +The patcher also mirrors the patched save into +`~/Library/Tabletop Simulator/Saves/TS_Save_N.json` (next free slot, or the +existing `SWL BETA - MAC PATCH` slot if present) so TTS picks it up in +Games -> Save & Load. + +`--reload` hot-pushes the patched scripts to a running TTS instance via the +External Editor API (no need to restart TTS). + +## Idempotence + +All patches are idempotent: a re-run on an already-patched save strips the +previous Mac patch block from Global (via marker regex) and re-injects +fresh. Per-object wrappers are also stripped before re-injection. + +## Files + +- `patch_save_for_mac.py` - main patcher +- `scan_bundles.py` - UnityPy scanner for the local TTS bundle cache +- `extract_projector_specs.py` - dump Projector material properties from a + bundle (used to discover the `_Arc` shader keyword for firing arc lines) +- `generate_overlay_assets.py` - regenerate the range/cohesion/etc. PNG + decals from authoritative SWL geometry +- `inspect_*.py` - debug helpers +- `retro-*.md` - retrospective notes on each subsystem rewrite +- `SHADER_INVENTORY.md` - list of custom shaders in the vanilla bundles diff --git a/mac-patcher/SHADER_INVENTORY.md b/mac-patcher/SHADER_INVENTORY.md new file mode 100644 index 00000000..926e86db --- /dev/null +++ b/mac-patcher/SHADER_INVENTORY.md @@ -0,0 +1,59 @@ +# SW Legion TTS Mod — Shader Inventory + +Scan effectué le 2026-05-07 sur `~/Library/Tabletop Simulator/Mods/Assetbundles/` (40 bundles `.unity3d`). +Outil : UnityPy 1.25.0, script `scan_bundles.py`. Output brut : `scan_bundles_2026-05-07.out`. + +## TL;DR +**9 shaders custom distincts**, **tous embarqués dans le `.unity3d` qui les utilise**. Donc : +- L'astuce "Always Included Shaders" du player TTS est **non-applicable** : ces shaders ne sont pas chargés depuis le pool du player, ils vivent dans les bundles. +- Le bug magenta vient du fait que le bytecode/code source de ces shaders custom ne fonctionne plus correctement sous Unity 6 + Metal sur Mac. +- Les overlays magenta listés par Martin (cohésion, range, deployment, movement) correspondent **exactement** aux 4 shaders `BucketheadBits/Projector/*`. + +## Inventaire complet (par catégorie) + +### Shaders Projector (overlays affectés par le bug magenta) +| Shader | Materials | Bundles | +|---|---|---| +| `BucketheadBits/Projector/Movement` | `ProjectorMaterial_27mm_speed1_single`, `ProjectorMaterial_27mm_speed2_single`, `ProjectorMaterial_50mm_speed2_single` | 3 | +| `BucketheadBits/Projector/Deployment` | `Projector_Deployment_Blue` (×2), `Projector_Deployment_Red` (×2) | 4 | +| `BucketheadBits/Projector/Range` | `ProjectorMaterial_25mm_token`, `ProjectorMaterial_27mm`, `ProjectorMaterial_50mm` | 3 | +| `BucketheadBits/Projector/Cohesion` | `Cohesion_27mm` | 1 | + +→ 11 materials projector au total, 4 shaders distincts à porter en priorité. + +### Outils / autres custom +| Shader | Materials | Bundles | +|---|---|---| +| `BucketheadBits/MoveTool` | `Speed1`, `Speed2` | 2 | +| `BucketheadBits/Tokens/Lambert Channel Mask` | `unitIDtoken_1` à `unitIDtoken_10` | 10 | +| `BucketheadBits/Silhouette` | `BucketheadBits_Silhouette` | 1 | +| `BucketheadBits/Units/Color Replacer` | `Ewok_SharedMat` | 1 | +| `DWD/LightenSkybox` | `Skybox` | 1 | + +→ Le shader `Tokens/Lambert Channel Mask` correspond aux 10 unitIDtokens loggés en console par TTS lors du bug : "Shader didn't load correctly for AssetBundle material unitIDtoken_X". Donc ce shader est **explicitement confirmé cassé**. + +## Auteurs +- Préfixe **`BucketheadBits/`** sur 8/9 shaders → ancien contributeur "Buckethead". Aucun repo GitHub public trouvé sous ce handle. Possible que ce soit un handle alternatif de Tieren (crédité ailleurs comme "original mod creator"). +- Préfixe **`DWD/`** sur 1 shader (LightenSkybox) → autre auteur, juste un tint skybox. + +## Implications pour la conversation avec Ben + +1. **L'angle "Always Included Shaders" n'est pas la bonne piste** pour ce bug, parce que les shaders custom sont embarqués dans les bundles. Si Ben a essayé cette piste, c'est cohérent que ça n'ait rien donné. + +2. **Le scope minimum réaliste** : porter / remplacer ces 9 shaders sous Unity 6 (compilation Metal-compatible). Soit en partant des sources s'il les a, soit en réécrivant des équivalents stock. + +3. **Materials = 11 projectors + 10 tokens + 4 outils + 1 skybox = 26 materials affectés au total** dans 27 bundles différents. Volumétrie raisonnable si les shaders sont swappable d'un coup. + +4. **Type technique des shaders** : + - Les `Projector/*` sont des shaders Unity Projector (transparent, projection avec bandes alternées) — historiquement tricky à porter Metal. + - Le `Tokens/Lambert Channel Mask` est un shader de coloration par canaux (R/G/B/A) — utilisé pour appliquer la couleur du joueur sur un mesh à canaux. Classique mais codé custom. + - Les autres (MoveTool, Silhouette, Color Replacer, LightenSkybox) sont des shaders d'effets simples. + +## Sources non-trouvées +- Aucun repo public sous `swlegion`, `swlegion-dev`, `matanlurey`, `halestom15`, `Buckethead` ne contient les sources Unity de ces shaders. +- Le projet Unity vit donc en privé sur la machine d'un contributeur (probablement Decaf ou Ben). + +## Prochaine étape +Attendre Ben pour confirmer si les sources sont disponibles. Cet inventaire répond déjà partiellement à ses questions 1 et 2 : +- Q1 ("custom shaders updated or just retargeted ?") : on sait maintenant exactement ce qu'il faut updater. +- Q2 ("which shaders are referenced ?") : liste complète ci-dessus. diff --git a/mac-patcher/design-refactor.md b/mac-patcher/design-refactor.md new file mode 100644 index 00000000..a94b2d03 --- /dev/null +++ b/mac-patcher/design-refactor.md @@ -0,0 +1,330 @@ +# Design refactor — Overlays Projector SWL TTS (Mac/Unity 6) + +**Status** : draft 15 mai 2026. Spec pour le patch event-driven hybride (decals plats + vector lines drapants). Base : 4 retro-*.md + materials-reference.md + shaders Allen White lus. + +## 1. Contexte (résumé exécutif) + +Le passage du player TTS à Unity 6 a strippé les variants Standard receiver des Projectors legacy. Sur Mac, **tous les overlays Projector du mod SWL s'affichent en magenta** : Cohesion Ruler, Range Ruler, Maximum Move, Deployment Boundary. Validation empirique session 14-15 mai : aucun fix côté bundle/shader ne marche (le strip est player-side TTS U6). Fix Berserk = ticket à ouvrir mais sans garantie de calendrier. + +**Solution mod-side validée** : remplacer le pattern Custom_AssetBundle (Projector) par un combo **Decal plat (PNG hébergé) + Vector lines (drapant via raycast)** orchestré par un manager Global event-driven. + +## 1bis. Fallback per-seat avec auto-test au boot + +Le refactor ne **remplace pas** le Projector legacy, il l'**augmente** d'un fallback per-seat. Chaque joueur a un compat flag détecté au boot, et le rendu s'adapte : + +- **Seats non-Mac** (TTS U6 OK) → voient le **Projector legacy** (rendu original préservé) +- **Seats Mac** (bug magenta) → voient le **patch hybride** (decal + vector lines) + +Quand Berserk corrigera le bug, l'auto-test au boot suivant désactivera le compat pour les seats Mac → retour automatique au comportement legacy, zéro action utilisateur. + +### 1bis.1 État par-seat (Global script) + +```lua +compatBySeat = {} -- {Red=true|false, Blue=true|false, ...} true = Mac/needs patch + +function onLoad(script_state) + if script_state ~= "" then + compatBySeat = JSON.decode(script_state) or {} + end + testCompatForAllSeated() +end + +function onSave() + return JSON.encode(compatBySeat) +end + +function getMacSeats() + local out = {} + for color, isMac in pairs(compatBySeat) do + if isMac then table.insert(out, color) end + end + return out +end + +function getNonMacSeats() + local out = {} + for color, isMac in pairs(compatBySeat) do + if not isMac then table.insert(out, color) end + end + return out +end +``` + +### 1bis.2 Test compat au boot + +```lua +function testCompatForAllSeated() + for _, player in pairs(Player.getPlayers()) do + if player.seated then + offerCompatTest(player) + end + end +end + +function offerCompatTest(player) + -- Spawn un canary Projector en zone hors-jeu (sous la table) + local canary = spawnObject({ + type="Custom_AssetBundle", + position={-50, -10, -50}, + scale={0,0,0} + }) + canary.setCustomObject({assetbundle = COHESION_27MM_URL}) + + -- Wait quelques secondes le temps que le bundle charge + Wait.time(function() + -- UI popup pour ce joueur uniquement + player.broadcast("Test rendu : cliquez sur ce que vous voyez sur le canary (Y=-10)") + -- Buttons "Couleurs OK" et "Magenta/rose" + -- onClick stocke compatBySeat[player.color] = true|false + -- destruct canary + end, 3) +end +``` + +### 1bis.3 Spawn d'un overlay (pattern unifié) + +```lua +function spawnOverlay(type, fig, params) + local macSeats = getMacSeats() + local nonMacSeats = getNonMacSeats() + local entry = {type=type, fig=fig, params=params, objects={}} + + -- 1. Projector legacy pour les seats non-Mac + if #nonMacSeats > 0 then + local projector = spawnObject({type="Custom_AssetBundle", ...}) + projector.setCustomObject({assetbundle = bundleURLs[type][fig.baseSize]}) + projector.setLock(true) + projector.setName(type .. "_legacy") + if #macSeats > 0 then projector.setInvisibleTo(macSeats) end + entry.objects.projector = projector + end + + -- 2. Custom_Tile (PNG plat) pour les seats Mac + if #macSeats > 0 then + local tile = spawnObject({type="Custom_Tile", position=fig.pos+Y0.1, scale=...}) + tile.setCustomObject({image = pngURLs[type][fig.baseSize]}) + tile.setLock(true) + tile.setName(type .. "_patch") + if #nonMacSeats > 0 then tile.setInvisibleTo(nonMacSeats) end + entry.objects.tile = tile + end + + -- 3. Vector lines (drape relief) pour les seats Mac + if #macSeats > 0 then + entry.lines = buildVectorLines(type, fig, params, macSeats) + -- ajoutées au batch global + end + + activeOverlays[fig.getGUID() .. ":" .. type] = entry + redrawAll() +end +``` + +### 1bis.4 Bug Berserk fix → reset auto + +Quand Berserk corrigera les variants Standard receiver dans le player TTS U6 : +- Au prochain boot de partie, `testCompatForAllSeated` re-run pour chaque seat +- Les Mac users verront maintenant le canary correctement → cliquent "Couleurs OK" → `compatBySeat[color] = false` +- Le patch ne s'active plus pour eux, comportement legacy restauré +- Aucun changement de code requis côté mod + +**Re-test manuel** : un bouton "Re-test compatibility" dans l'UI Notes du mod permet de forcer un nouveau test à tout moment (utile si le user veut vérifier après une mise à jour TTS). + +### 1bis.5 Incertitudes à valider empiriquement au POC + +| Incertitude | Plan B si KO | +|---|---| +| `Object.setInvisibleTo` cache-t-il le **rendu de la projection** d'un Projector ou seulement le GameObject parent ? | Si KO : si au moins 1 Mac seat présent, on ne spawn PAS le Projector du tout (tout le monde voit le patch — compromis : 1 Mac dans la partie = tout le monde adopte le rendu patch) | +| `Global.setDecals()` supporte-t-il un filtre `players` per entry ? | Si KO : utiliser `Custom_Tile` Objects (déjà ce qu'on a au-dessus, avec `setInvisibleTo`). Plus propre et déjà compatible per-seat | + +À tester en début de phase d'implémentation, avant de pousser tout le code. + +## 2. Architecture event-driven (manager Global) + +### 2.1 État global unique + +```lua +-- mod/src/includes/Overlays.ttslua (nouveau) +local activeOverlays = {} -- clé = "{guid}:{type}" → {type, fig, params} +local hiddenWhilePickedUp = {} -- buffer pour les overlays cachés temporairement +``` + +### 2.2 API publique unifiée + +```lua +-- Spawn / clear par type, scopé à une fig +function spawnOverlay(type, fig, params) +function clearOverlayForFig(type, fig) +function clearAllOverlays() -- reset complet + +-- Adapters pour conserver l'API existante des callers +function spawnCohesionRuler(fig) -- → spawnOverlay("cohesion", fig, {}) +function clearCohesionRuler() -- → clearOverlayForFig("cohesion", self) +function spawnRangeRuler(fig, override) -- → spawnOverlay("range", fig, {override=override}) +function clearRangeRulers() -- → clearOverlayForFig("range", selectedUnitObj) +-- etc. +``` + +### 2.3 Event handlers (Global) + +```lua +function onObjectPickUp(player_color, obj) + -- buffer tous les overlays attachés à obj puis les retirer du draw + hidePickedUp(obj) + redrawAll() +end + +function onObjectDrop(player_color, obj) + -- restaurer les overlays de obj depuis le buffer + restorePickedUp(obj) + redrawAll() +end + +function onObjectDestroy(obj) + -- nettoyage définitif + purgeForFig(obj) + redrawAll() +end +``` + +### 2.4 Rendu unique par tick d'event + +```lua +function redrawAll() + local lines, decals = {}, {} + for _, entry in pairs(activeOverlays) do + local l, d = builders[entry.type](entry.fig, entry.params) + for _, line in ipairs(l) do table.insert(lines, line) end + for _, decal in ipairs(d) do table.insert(decals, decal) end + end + Global.setVectorLines(lines) + Global.setDecals(decals) +end +``` + +**Important** : `setVectorLines` et `setDecals` sont des **singletons** côté TTS — chaque appel remplace toute la collection. D'où le manager global qui agrège. + +## 3. Spec par overlay + +### 3.1 Cohesion + +| Composant | Détail | +|---|---| +| Trigger spawn | Hotkey "Show Cohesion On Hovered Model" / bouton COHESION Order Token / dropCoroutine post-mouvement (`moveState=true`) | +| Trigger clear | Mêmes triggers (toggle) + onPickedUp (auto) + standby/reload globaux | +| Decal PNG | `cohesion_halo.png` — gradient radial blanc fade depuis le centre, fond transparent, 512×512px | +| Decal params | `position = fig.pos + Y0.1`, `rotation = (90, 0, 0)` (face vers le haut), `scale = (baseRadius+1, baseRadius+1, baseRadius+1) × 2` | +| Vector line | 1 cercle filaire blanc à `r = base_radius + 0.5"` du centre, drapé par raycast 32 segments | +| Couleur | #FFFFFF α=0.6 | + +API conservée : `showCohesionOnHoveredModel`, `spawnCohesionRuler`, `clearCohesionRuler`. Compatible avec les 3 sites de require existants (Global, Unit_Leader, Order_Token) car redirigés vers le manager global. + +### 3.2 Range Ruler + +| Composant | Détail | +|---|---| +| Trigger spawn | Hotkey "Show Range On Hovered Model" / bouton RANGE Order Token (targetingMode/attackMode) / bouton R sur POI/tokens | +| Trigger clear | Mêmes triggers (toggle) + clearTemplates (post-drop mouvement) + exit modes | +| Decal PNG | `range_bands.png` (universel) — 4 anneaux concentriques à 6/12/18/24 inches relatifs, couleurs jaune/orange/rouge/magenta foncé, fond transparent, 1024×1024px | +| Decal params | `position = fig.pos + Y0.1`, `scale = (60, 60, 60)` (couvre 24" de rayon + marge) | +| Vector lines | 4 cercles filaires aux rayons absolus **6", 12", 18", 24"** du centre, mêmes couleurs que les bandes du PNG | +| Couleur (par anneau) | R1 #FFC300, R2 #FF7500, R3 #FF1400, R4 #C20042 (alpha 0.6 sur les lignes) | +| Variantes | Tokens (smokeToken/token/tokenRangeTwo/bombCart/POI) → moins d'anneaux (1 ou 2), PNG distincts ou même PNG avec scale ajusté | + +API conservée : `showRangeOnHoveredModel`, `spawnRangeRuler(fig, override)`, `clearRangeRuler/clearRangeRulers`. + +Pour les variantes, le `override` permet de passer un type explicite (`"smokeToken"`, `"poi"`, etc.) au lieu du baseSize de la fig. + +### 3.3 Maximum Move + +| Composant | Détail | +|---|---| +| Trigger spawn | Lors du clic sur bouton speed (1/2/3) sur Order Token (flow mouvement) | +| Trigger clear | clearMovementTemplates (drop final) | +| Decal PNG | `max_move_disk.png` — disque plein bleu ciel #55CCFF α=0.48, fond transparent, 512×512px | +| Decal params | `position = fig.pos + Y0.1`, `scale = ProjectorRadius × 2` selon (baseSize, speed) | +| Vector line | 1 cercle filaire bleu ciel au rayon = ProjectorRadius (drape relief) + 1 anneau base blanc fin à r = baseRadius + 0.5" | +| Couleur | Cercle principal #55CCFF α=0.6, anneau base #FFFFFF α=0.5 | +| Rayons | Lookup table `maxMoveRadius[baseSize][speed]` (ex: `27mm.speed1=4.55", speed2=6.52", speed3=8.48"`) — extraits de `_ProjectorRadius` dans les materials Movement | + +API conservée : flow existant dans `Order_Token.a57c41.lua:552-574`, remplacement du `spawnObject Custom_AssetBundle` par `spawnOverlay("maxmove", fig, {speed=selectedSpeed, baseSize=unitData.baseSize})`. + +### 3.4 Deployment Boundary + +| Composant | Détail | +|---|---| +| Trigger spawn | `spawnDeploymentBoundary(matrix)` au setup (depuis menu UI Deployment) | +| Trigger clear | `clearDeploymentBoundary()` (menu UI Remove Overlay) + standby global | +| Decal PNG | `deployment_red.png` + `deployment_blue.png` — rectangle plein uniforme α=0.5, 256×256px | +| Decal params | Par cellule de la matrix, `position = grid pos + offset`, `scale = (6, 6, 6)` (taille standard d'une cellule SWL), rotation selon `deployRotations[cell]` | +| Vector lines | Contour rectangle drapant (4 côtés × N segments via raycast) par cellule, couleur match decal | +| Couleur | Rouge #FF0000 / Bleu #0000FF α=0.5 | +| Variantes | Half/L/Corner/Round → combinaisons de cellules dans la matrix, le manager gère chaque cellule individuellement (1 decal + 1 contour par cellule) | + +API conservée : `spawnDeploymentBoundary(matrix)`, `clearDeploymentBoundary()`. Internement, chaque cellule est enregistrée comme un overlay distinct avec un GUID synthétique (ex `"deployment:bs:5:3"`). + +## 4. Assets PNG à générer + +| Fichier | Dimensions | Description | Hosting | +|---|---|---|---| +| `cohesion_halo.png` | 512×512 | Gradient radial blanc centre → transparent bord, RGBA | iron-squadron.fr/tts-assets/ | +| `range_bands.png` | 1024×1024 | 4 anneaux concentriques aux rayons relatifs 0.25/0.5/0.75/1.0, gradient discret (pas blend), couleurs jaune/orange/rouge/magenta, alpha 0.6 | iron-squadron.fr/tts-assets/ | +| `range_smoke.png` | 256×256 | 1 anneau à rayon relatif 1.0, blanc opaque | idem | +| `range_token.png` | 256×256 | 1 anneau à rayon relatif 1.0, jaune | idem | +| `range_tokenRangeTwo.png` | 256×256 | 2 anneaux, jaune + orange | idem | +| `range_poi.png` | 256×256 | 1 anneau pour POI (range 0.5 / 3") | idem | +| `range_bombCart.png` | 256×256 | 2 anneaux | idem | +| `max_move_disk.png` | 512×512 | Disque plein bleu ciel #55CCFF α=0.48, RGBA | idem | +| `deployment_red.png` | 256×256 | Carré rouge plein #FF0000 α=0.5 | idem | +| `deployment_blue.png` | 256×256 | Carré bleu plein #0000FF α=0.5 | idem | + +**Génération** : script Python Pillow `generate_overlay_assets.py` qui produit tous les PNG d'un coup. Reproductible, versionnable dans `swlegion-tts/tool/`. + +**Upload** : `rsync` vers le VPS Iron Squadron, dossier `/var/www/iron-squadron/tts-assets/` (ou similaire). URL stable indépendante de Steam. + +## 5. Edge cases et invariants + +| Cas | Gestion | +|---|---| +| Multi-overlays simultanés (plusieurs figs avec cohesion + range) | Cohabitent dans la table `activeOverlays`, un `redrawAll()` au moindre changement | +| Fig détruite (mort en combat) | `onObjectDestroy` → `purgeForFig` → redraw | +| Pickup pendant overlay actif | `onObjectPickUp` → buffer dans `hiddenWhilePickedUp`, retire du rendu | +| Drop après pickup | `onObjectDrop` → restore depuis buffer, redraw (positions recalculées par les builders) | +| Pickup d'un objet sans overlay | Handler skip silencieux (test sur présence dans la table) | +| Reload de partie | `activeOverlays` se reset au boot (variable Lua locale du Global), OK | +| Filtres `standbyTokens` / `removeLockedRulers` (scans `getAllObjects()` par nom) | Deviennent caducs (plus d'Object physique). À supprimer ou laisser comme no-op | +| Test multi-joueurs réseau | Le manager Global est synchronisé par TTS (Lua sync), donc tous les joueurs voient les mêmes overlays | +| Drape impossible sur certains terrains custom | Limitation acceptable (déjà flag par Ben). Le decal plat sert de fallback visuel | + +## 6. Chemin de migration (séquentiel, testable étape par étape) + +1. **Setup Manager Global** : créer `mod/src/includes/Overlays.ttslua`. Aucun caller encore branché. Test : `npm run compile && load mod`, no-op confirmé +2. **Cohesion** (60 lignes, le plus simple) : réécrire `mod/src/includes/Cohesion.ttslua` pour rediriger vers le manager. Test : spawn via hotkey + bouton + dropCoroutine sur Mac +3. **Range Ruler** : réécrire `mod/src/includes/RangeRulers.ttslua` + adapter `POI_Token.lua` + `TokenWithRangeRuler.ttslua`. Test : 4 anneaux + tokens +4. **Maximum Move** : adapter `Order_Token.a57c41.lua:552-574` (spawn inline → call manager) + `clearMovementTemplates`. Test : flow mouvement complet sur Mac +5. **Deployment** : réécrire `SETUP_CONTROLLER.1cb552.lua:243-323`. Test : sélection scenario + spawn de la zone +6. **Cleanup** : supprimer les filtres `standbyTokens` / `removeLockedRulers` pour les types qui ne sont plus des Objects. Garder le filtre pour Movement Template (mesh, toujours Object) +7. **PR sur swlegion/tts** : branche `mac-overlays-refactor`, description du bug + solution + diff. Test communauté + +## 7. Compatibilité conservée + +- **Tous les sites de require existants** continuent à fonctionner sans modif (`require('!/Cohesion')`, `require('!/RangeRulers')`) +- **Toutes les fonctions globales conservent leur signature** (`spawnCohesionRuler(fig)`, `clearRangeRulers()`, etc.) +- **Pas de change d'API publique** → callers (Unit_Leader, Order_Token, POI_Token, etc.) inchangés +- **Variables d'état globales** comme `selectedUnitObj` conservées pour compat avec les filtres et la logique de toggle + +## 8. Risques connus à surveiller + +| Risque | Mitigation | +|---|---| +| Performance multi-overlays (N décals + N×64 vectors) | Tester empiriquement sur table chargée. Si lag, sparse raycast (32 segments) ou skip raycast si table plate détectée | +| Synchronisation decal/vector lines pas pixel-perfect | Vector lines au-dessus du decal (Y légèrement supérieur), tolérance visuelle | +| URL Iron Squadron offline | Hosting sur 2 destinations (VPS + GitHub Pages backup) ou inclure le CDN dans le repo si TTS supporte les data URIs | +| Bug U6 sur les decals aussi (improbable, POC validé le 14 mai) | Garder Path B = ticket Berserk en parallèle | +| Refactor casse un caller obscur | Test exhaustif des 5 sites de spawn + tous les triggers connus | + +## 9. Annexes + +- `materials-reference.md` : tous les params visuels extraits +- `retro-cohesion.md`, `retro-range.md`, `retro-deployment.md`, `retro-movement.md` : déclencheurs et call graphs +- `swlegion-tts/mod/src/includes/` : code source des modules à patcher diff --git a/mac-patcher/extract_projector_specs.py b/mac-patcher/extract_projector_specs.py new file mode 100644 index 00000000..e699f8f1 --- /dev/null +++ b/mac-patcher/extract_projector_specs.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +""" +Scan Unity .mat and .prefab YAML files in UnityProject-U6/Assets/Projectors/ +and extract critical parameters for the SWL TTS mod refactor. + +Output: materials-reference.md with structured tables per overlay. +""" +import os +import re +from pathlib import Path +from collections import defaultdict + +ROOT = Path("/Users/martinpourrat/MARTIN/Star Wars Legion/Mod TTS SWL/UnityProject-U6/Assets/Projectors") +OUT = Path("/Users/martinpourrat/MARTIN/Star Wars Legion/Mod TTS SWL/materials-reference.md") + +OVERLAYS = ["Cohesion", "Range", "Deployment", "Movement"] + + +def read(p: Path) -> str: + return p.read_text(encoding="utf-8", errors="replace") + + +def parse_material(path: Path) -> dict: + """Extract critical fields from a Unity .mat YAML file.""" + text = read(path) + out = {"path": str(path.relative_to(ROOT)), "name": path.stem} + + m = re.search(r"m_ShaderKeywords:\s*(.*)", text) + out["keywords"] = m.group(1).strip() if m else "" + + m = re.search(r"m_Shader:\s*\{fileID:\s*\d+,\s*guid:\s*([0-9a-f]+)", text) + out["shader_guid"] = m.group(1) if m else "" + + # Floats — capture key params + floats = {} + for fname in ["_BaseSize", "_ProjectorRadius", "_BandSize", "_BandContrast", + "_MaxRange", "_Arc", "_RangeSize", "_GradScaler", + "_OneTuner", "_TwoTuner", "_ThreeTuner", "_FourTuner", + "_FiveTuner", "_InfinityTuner"]: + m = re.search(rf"-\s*{re.escape(fname)}:\s*(-?[\d.]+)", text) + if m: + floats[fname] = m.group(1) + out["floats"] = floats + + # Colors — capture all _Color* / _Range* + colors = {} + for m in re.finditer(r"-\s*(_\w+):\s*\{r:\s*([\d.]+),\s*g:\s*([\d.]+),\s*b:\s*([\d.]+),\s*a:\s*([\d.]+)\}", text): + name, r, g, b, a = m.groups() + if name in ("_BumpScale",): # not a color + continue + colors[name] = (float(r), float(g), float(b), float(a)) + out["colors"] = colors + + return out + + +def parse_prefab(path: Path) -> dict: + """Extract critical fields from a Unity .prefab YAML file (Projector components).""" + text = read(path) + out = {"path": str(path.relative_to(ROOT)), "name": path.stem} + + # Count Projector components (Unity type id 119) + projectors = re.findall(r"---\s*!u!119\s*&\d+\s*\nProjector:(.*?)(?=\n---|\Z)", text, re.DOTALL) + out["n_projectors"] = len(projectors) + + proj_data = [] + for body in projectors: + d = {} + for fname in ["m_OrthographicSize", "m_FarClipPlane", "m_NearClipPlane", + "m_FieldOfView", "m_AspectRatio", "m_Orthographic"]: + m = re.search(rf"{fname}:\s*(-?[\d.]+)", body) + if m: + d[fname] = m.group(1) + m = re.search(r"m_Material:\s*\{fileID:\s*\d+,\s*guid:\s*([0-9a-f]+)", body) + if m: + d["material_guid"] = m.group(1) + proj_data.append(d) + out["projectors"] = proj_data + + # Transforms (LocalPosition Y, rotation) + transforms = [] + for body in re.findall(r"---\s*!u!4\s*&\d+\s*\nTransform:(.*?)(?=\n---|\Z)", text, re.DOTALL): + d = {} + m = re.search(r"m_LocalPosition:\s*\{x:\s*(-?[\d.]+),\s*y:\s*(-?[\d.]+),\s*z:\s*(-?[\d.]+)\}", body) + if m: + d["pos"] = (float(m.group(1)), float(m.group(2)), float(m.group(3))) + m = re.search(r"m_LocalEulerAnglesHint:\s*\{x:\s*(-?[\d.]+),\s*y:\s*(-?[\d.]+),\s*z:\s*(-?[\d.]+)\}", body) + if m: + d["rot"] = (float(m.group(1)), float(m.group(2)), float(m.group(3))) + transforms.append(d) + out["transforms"] = transforms + + return out + + +def rgba_to_hex(r, g, b, a): + """Convert 0-1 RGBA to hex + alpha note.""" + rh, gh, bh = int(r * 255), int(g * 255), int(b * 255) + return f"#{rh:02X}{gh:02X}{bh:02X} (α={a:.2f})" + + +def fmt_colors(colors): + """Format dict of colors as compact markdown.""" + if not colors: + return "—" + parts = [] + for name, (r, g, b, a) in colors.items(): + if a == 0 and r == 0 and g == 0 and b == 0: + parts.append(f"`{name}` transparent") + else: + parts.append(f"`{name}` {rgba_to_hex(r, g, b, a)}") + return "
".join(parts) + + +def fmt_floats(floats): + """Format dict of floats.""" + if not floats: + return "—" + return ", ".join(f"`{k}`={v}" for k, v in floats.items()) + + +def main(): + materials_by_overlay = defaultdict(list) + prefabs_by_overlay = defaultdict(list) + + for overlay in OVERLAYS: + for p in sorted((ROOT / overlay).rglob("*.mat")): + if p.suffix == ".meta": + continue + materials_by_overlay[overlay].append(parse_material(p)) + for p in sorted((ROOT / overlay).rglob("*.prefab")): + if p.suffix == ".meta": + continue + prefabs_by_overlay[overlay].append(parse_prefab(p)) + + # Build the master doc + lines = [] + lines.append("# Materials & Prefabs Reference — SWL TTS Projector Overlays") + lines.append("") + lines.append("Extraction directe des `.mat` et `.prefab` Unity (YAML) dans `UnityProject-U6/Assets/Projectors/`. Source de vérité pour reproduire le rendu en vector lines.") + lines.append("") + lines.append("Notation : couleurs en hex + alpha. Champs `Floats` clés du shader projector (paramètres procéduraux des anneaux).") + lines.append("") + + for overlay in OVERLAYS: + lines.append(f"## {overlay}") + lines.append("") + + # Materials table + lines.append("### Materials") + lines.append("") + lines.append("| Material | Shader keywords | Floats clés | Couleurs |") + lines.append("|---|---|---|---|") + for m in materials_by_overlay[overlay]: + kw = m["keywords"] or "—" + lines.append(f"| `{m['name']}` | `{kw}` | {fmt_floats(m['floats'])} | {fmt_colors(m['colors'])} |") + lines.append("") + + # Prefabs table + lines.append("### Prefabs") + lines.append("") + lines.append("| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid |") + lines.append("|---|---|---|---|---|") + for p in prefabs_by_overlay[overlay]: + n = p["n_projectors"] + proj_sizes = [pr.get("m_OrthographicSize", "?") for pr in p["projectors"]] + proj_far = [pr.get("m_FarClipPlane", "?") for pr in p["projectors"]] + proj_summary = "
".join(f"size={s}, far={f}" for s, f in zip(proj_sizes, proj_far)) if proj_sizes else "—" + + tr_summary = "
".join( + f"pos=({t.get('pos', ('?',)*3)[0]}, {t.get('pos', ('?',)*3)[1]}, {t.get('pos', ('?',)*3)[2]}) rot=({t.get('rot', ('?',)*3)[0]}, {t.get('rot', ('?',)*3)[1]}, {t.get('rot', ('?',)*3)[2]})" + for t in p["transforms"] + ) if p["transforms"] else "—" + + mat_guids = [pr.get("material_guid", "?") for pr in p["projectors"]] + mat_summary = "
".join(g[:12] + "…" for g in mat_guids) if mat_guids else "—" + + lines.append(f"| `{p['name']}` | {n} | {proj_summary} | {tr_summary} | {mat_summary} |") + lines.append("") + + # Final notes + lines.append("## Notes globales") + lines.append("") + lines.append("- Cohesion : double set `Materials/cohesion_*` et `_Revamp/halfCohesion_*`. Probable : `_Revamp/` est la version actuellement utilisée (le bundle live s'appelle `halfcohesion_27mm.unity3d` cf shader-inventory).") + lines.append("- Range : shader procédural avec jusqu'à 5 bandes (`_RangeOne` à `_RangeFive`) + `_RangeInfinity`. Le keyword `_MAXRANGE_RANGEFIVE` ou `_MAXRANGE_RANGEFOUR` détermine la dernière bande visible.") + lines.append("- Movement : un seul Projector par prefab, paramétré par `_BaseSize` + speed. Le `_Arc` doit contrôler la portion d'arc visible.") + lines.append("- Deployment : 6 materials uniques réutilisés par 12 prefabs (rotations différentes pour matérialiser corner/half/L/round/score).") + + OUT.write_text("\n".join(lines), encoding="utf-8") + print(f"Wrote {OUT}") + # Stats + total_mats = sum(len(v) for v in materials_by_overlay.values()) + total_prefabs = sum(len(v) for v in prefabs_by_overlay.values()) + print(f"Scanned {total_mats} materials and {total_prefabs} prefabs across {len(OVERLAYS)} overlays.") + + +if __name__ == "__main__": + main() diff --git a/mac-patcher/generate_overlay_assets.py b/mac-patcher/generate_overlay_assets.py new file mode 100644 index 00000000..d11b1932 --- /dev/null +++ b/mac-patcher/generate_overlay_assets.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +""" +Generate PNG assets for the Mac Cohesion + Range overlay fallback. +""" +from pathlib import Path +from PIL import Image, ImageDraw +import math + +OUT_DIR = Path(__file__).parent / "tts-assets" +OUT_DIR.mkdir(exist_ok=True) + + +# Range band colors (R, G, B in 0..255) — from BB_RangeProjector defaults +RANGE_COLORS = { + "half": (255, 255, 255), # 0.5 / 3" — white + "one": (255, 195, 0), # R1 / 6" — yellow + "two": (255, 117, 0), # R2 / 12" — orange + "three": (255, 20, 0), # R3 / 18" — red + "four": (195, 0, 66), # R4 / 24" — magenta + "five": (134, 0, 112), # R5 / 30" — violet +} + +# Configurations per overlay type (bands = list of (radius_inches, color_key)) +# Edge-based convention: each band's effective world radius is band_r + base_r. +RANGE_BAND_CONFIGS = { + "fig_leader": { + "bands": [(3, "half"), (6, "one"), (12, "two"), + (18, "three"), (24, "four"), (30, "five")], + "max_radius": 30, + }, + # SWL ranges in inches: Range 1 = 6", Range 2 = 12", Range 0.5 (poi) = 3". + "smokeToken": {"bands": [(6, "one")], "max_radius": 6}, + "token": {"bands": [(6, "one")], "max_radius": 6}, + "tokenRangeTwo": {"bands": [(6, "one"), (12, "two")], "max_radius": 12}, + "poi": {"bands": [(3, "one")], "max_radius": 3}, + "bombCart": {"bands": [(6, "one"), (12, "two")], "max_radius": 12}, +} + +# Base radii (inches, half-diameter) per ROUND fig base size. Oblong bases +# (long, snail) live in OBLONG_DIMENSIONS_MM below — they don't use a single +# radius and don't generate round PNGs. +FIG_BASE_RADIUS_IN = { + "small": 27 / 2 / 25.4, + "medium": 50 / 2 / 25.4, + "large": 70 / 2 / 25.4, + "huge": 100 / 2 / 25.4, + "laat": 120 / 2 / 25.4, + "epic": 150 / 2 / 25.4, +} + +# Token base radii (inches), only needed for multi-band tokens. +TOKEN_BASE_RADIUS_IN = { + "tokenRangeTwo": 25.1 / 2 / 25.4, + "bombCart": 50.0 / 2 / 25.4, +} + + +def _with_base_offset(config: dict, base_r: float) -> dict: + """Shift band radii outward by base_r and grow max_radius accordingly so + the PNG band proportions match the world positions of the vector-line + rings (which are drawn at band_r + base_r).""" + return { + "bands": [(r + base_r, c) for r, c in config["bands"]], + "max_radius": config["max_radius"] + base_r, + } + + +def generate_cohesion_halo(size: int = 512) -> Path: + """Halo radial blanc fade transparent — reproduit le rendu Cohesion bundle. + + Le shader BB_CohesionProjector fait `rangeOne = (1 - grad)` puis multiplie + par alpha global. On reproduit ce fade : opaque au centre, transparent au + bord. + """ + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + pixels = img.load() + cx, cy = size / 2, size / 2 + max_r = size / 2 + + # Alpha global du material Cohesion 27mm = 0.37 ; on multiplie par le fade + base_alpha = 0.37 + + for y in range(size): + for x in range(size): + dx, dy = x - cx, y - cy + dist = math.sqrt(dx * dx + dy * dy) + r_norm = dist / max_r # [0, 1+] depuis le centre + if r_norm > 1.0: + continue # transparent au-delà du rayon + + # Fade radial : (1 - r) avec courbe douce + # Le shader fait (1 - grad) linéaire, on garde la même chose + fade = max(0.0, 1.0 - r_norm) + alpha = int(base_alpha * fade * 255) + pixels[x, y] = (255, 255, 255, alpha) + + out = OUT_DIR / "cohesion_halo.png" + img.save(out) + return out + + +def generate_range_band_png(name: str, config: dict, size: int = 1024, + alpha: float = 0.2) -> Path: + """Generate a circular range overlay with concentric bands. + + Drawing from outer-to-inner: each filled ellipse overlays the previous, + producing clean rings. + """ + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + cx = cy = size / 2 + max_r_pix = size / 2 + a = int(alpha * 255) + + for band_r, color_key in reversed(config["bands"]): + r_pix = (band_r / config["max_radius"]) * max_r_pix + r, g, b = RANGE_COLORS[color_key] + draw.ellipse( + [(cx - r_pix, cy - r_pix), (cx + r_pix, cy + r_pix)], + fill=(r, g, b, a), + ) + + out = OUT_DIR / f"range_{name}.png" + img.save(out) + return out + + +# Oblong base dimensions (mm). Used to render stadium-shape range overlays +# whose 6 bands stay concentric to a rectangular footprint with rounded caps. +OBLONG_DIMENSIONS_MM = { + "long": {"width": 100, "length": 175}, + "snail": {"width": 100, "length": 200}, +} + + +def generate_range_band_stadium_png(base_size: str, dims_mm: dict, + config: dict, alpha: float = 0.2, + pixels_per_inch: int = 16) -> Path: + """Generate a stadium-shape PNG with 6 concentric range bands. + + The PNG dimensions encode the *outermost* band's bounding box (so the + decal can be stretched in TTS using scale = {shortSpan, longSpan, 1} and + the bands stay aligned with the matching vector stadium outlines). + + A stadium = rounded rectangle whose corner radius equals half the + short dimension, producing a pill shape with semicircular caps on the + long-axis ends. Each band is one rounded_rectangle of width + 2*(halfWid + band_r) and length 2*(halfLen + band_r), corner radius + halfWid + band_r. Drawn outer-to-inner so each band overpaints the next. + """ + halfWid_in = (dims_mm["width"] / 2) / 25.4 + halfLen_in = (dims_mm["length"] / 2) / 25.4 + max_dist = config["max_radius"] + + shortSpan_in = 2 * (halfWid_in + max_dist) + longSpan_in = 2 * (halfLen_in + max_dist) + + W = int(round(shortSpan_in * pixels_per_inch)) + H = int(round(longSpan_in * pixels_per_inch)) + cx, cy = W / 2, H / 2 + + img = Image.new("RGBA", (W, H), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + a = int(alpha * 255) + + for band_r, color_key in reversed(config["bands"]): + half_w_px = (halfWid_in + band_r) * pixels_per_inch + half_h_px = (halfLen_in + band_r) * pixels_per_inch + r, g, b = RANGE_COLORS[color_key] + draw.rounded_rectangle( + [(cx - half_w_px, cy - half_h_px), + (cx + half_w_px, cy + half_h_px)], + radius=half_w_px, # = half of the short side → semicircle caps + fill=(r, g, b, a), + ) + + out = OUT_DIR / f"range_fig_leader_{base_size}_stadium.png" + img.save(out) + return out + + +def generate_filled_disk(name: str, color_rgb: tuple, size: int = 512, + alpha: float = 0.2) -> Path: + """Generate a simple filled disk PNG (used for Maximum Move overlay).""" + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + cx = cy = size / 2 + r = size / 2 + a = int(alpha * 255) + draw.ellipse([(cx - r, cy - r), (cx + r, cy + r)], + fill=(color_rgb[0], color_rgb[1], color_rgb[2], a)) + out = OUT_DIR / f"{name}.png" + img.save(out) + return out + + +def generate_filled_square(name: str, color_rgb: tuple, size: int = 256, + alpha: float = 0.3) -> Path: + """Generate a filled square PNG (used for Deployment Boundary cells).""" + img = Image.new("RGBA", (size, size), + (color_rgb[0], color_rgb[1], color_rgb[2], int(alpha * 255))) + out = OUT_DIR / f"{name}.png" + img.save(out) + return out + + +def generate_silhouette_top_disc(size: int = 512) -> Path: + """Hologram-style top cap for the Mac silhouette cylinder. + + Radial gradient: bright green-cyan center fades to transparent at the + rim. Combined with the vector wireframe cylinder this gives the + Star Wars hologram briefing look (Leia "Help me Obi-Wan" projection). + """ + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + pixels = img.load() + cx, cy = size / 2, size / 2 + max_r = size / 2 + + r_center, g_center, b_center = 102, 255, 178 # bright hologram cyan-green + a_center = 0.85 + + for y in range(size): + for x in range(size): + dx, dy = x - cx, y - cy + dist = math.sqrt(dx * dx + dy * dy) + r_norm = dist / max_r + if r_norm > 1.0: + continue + # Smooth radial falloff (1-r squared) for a soft hologram glow + fade = (1.0 - r_norm) ** 1.5 + alpha = int(a_center * fade * 255) + pixels[x, y] = (r_center, g_center, b_center, alpha) + + out = OUT_DIR / "silhouette_top.png" + img.save(out) + return out + + +def main(): + p = generate_cohesion_halo() + print(f"Generated {p} ({p.stat().st_size} bytes)") + + # Fig leaders (round bases): one PNG per base size, bands shifted by base radius. + fig_cfg = RANGE_BAND_CONFIGS["fig_leader"] + for base_size, br in FIG_BASE_RADIUS_IN.items(): + out = generate_range_band_png(f"fig_leader_{base_size}", + _with_base_offset(fig_cfg, br)) + print(f"Generated {out} ({out.stat().st_size} bytes)") + + # Oblong bases: stadium-shape PNGs (rectangular footprint + rounded caps). + for base_size, dims in OBLONG_DIMENSIONS_MM.items(): + out = generate_range_band_stadium_png(base_size, dims, fig_cfg) + print(f"Generated {out} ({out.stat().st_size} bytes)") + + # Multi-band tokens: bake in their base radius for alignment. + for name, br in TOKEN_BASE_RADIUS_IN.items(): + cfg = RANGE_BAND_CONFIGS[name] + out = generate_range_band_png(name, _with_base_offset(cfg, br)) + print(f"Generated {out} ({out.stat().st_size} bytes)") + + # Single-band tokens: no offset adjustment needed (band sits at outer edge). + for name in ("smokeToken", "token", "poi"): + out = generate_range_band_png(name, RANGE_BAND_CONFIGS[name]) + print(f"Generated {out} ({out.stat().st_size} bytes)") + + # Maximum Move: cyan filled disk (#55CCFF) + p = generate_filled_disk("max_move_cyan", (85, 204, 255), alpha=0.2) + print(f"Generated {p} ({p.stat().st_size} bytes)") + + # Deployment Boundary: red and blue filled squares (alpha 0.5 = match bundle) + p = generate_filled_square("deployment_red", (255, 0, 0), alpha=0.3) + print(f"Generated {p} ({p.stat().st_size} bytes)") + p = generate_filled_square("deployment_blue", (0, 0, 255), alpha=0.3) + print(f"Generated {p} ({p.stat().st_size} bytes)") + + # Silhouette top cap: hologram-style green/cyan radial gradient. + p = generate_silhouette_top_disc() + print(f"Generated {p} ({p.stat().st_size} bytes)") + + +if __name__ == "__main__": + main() diff --git a/mac-patcher/inspect_cohesion.py b/mac-patcher/inspect_cohesion.py new file mode 100644 index 00000000..e26becbe --- /dev/null +++ b/mac-patcher/inspect_cohesion.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Inspect Cohesion bundle structure: list objects, materials, shaders, and the m_Shader PPtr.""" +import os +import sys +import UnityPy + +BUNDLE = os.path.expanduser( + "~/Library/Tabletop Simulator/Mods/Assetbundles/" + "httpssteamusercontentaakamaihdnetugc2482129948496305632EBBE2560D4336E6C96317EDDA787E225CF0E5B48.unity3d" +) + +env = UnityPy.load(BUNDLE) + +print("=" * 80) +print("BUNDLE METADATA") +print("=" * 80) +for f in env.files.values(): + print(f" file: {type(f).__name__}") + if hasattr(f, "unity_version"): + print(f" unity_version: {f.unity_version}") + if hasattr(f, "target_platform"): + print(f" target_platform: {f.target_platform}") + if hasattr(f, "externals"): + print(f" externals (cross-bundle refs):") + for ext in getattr(f, "externals", []): + print(f" - {ext}") + +print() +print("=" * 80) +print("ALL OBJECTS") +print("=" * 80) +for obj in env.objects: + path_id = getattr(obj, "path_id", "?") + type_name = obj.type.name + try: + data = obj.read() + name = getattr(data, "m_Name", "") or "" + except Exception as e: + name = f"" + print(f" PathID={path_id:<6} type={type_name:<15} name={name!r}") + +print() +print("=" * 80) +print("MATERIAL DETAILS (looking for Cohesion_27mm)") +print("=" * 80) +for obj in env.objects: + if obj.type.name != "Material": + continue + data = obj.read() + print(f"\nMaterial PathID={obj.path_id} name={data.m_Name!r}") + sref = data.m_Shader + print(f" m_Shader PPtr:") + print(f" file_id = {getattr(sref, 'file_id', '?')} " + f"(0 = internal to this bundle, !=0 = external file)") + print(f" path_id = {getattr(sref, 'path_id', '?')}") + # Try to resolve + try: + sobj = sref.deref() + if sobj: + sd = sobj.read() + sname = ( + getattr(getattr(sd, "m_ParsedForm", None), "m_Name", None) + or getattr(sd, "m_Name", None) + ) + print(f" resolved -> Shader name = {sname!r}") + else: + print(f" resolved -> None (external or missing)") + except Exception as e: + print(f" resolve error: {e!r}") + +print() +print("=" * 80) +print("SHADER DETAILS") +print("=" * 80) +for obj in env.objects: + if obj.type.name != "Shader": + continue + data = obj.read() + sname = ( + getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) + or getattr(data, "m_Name", None) + ) + print(f" PathID={obj.path_id} Shader name={sname!r}") diff --git a/mac-patcher/inspect_externals.py b/mac-patcher/inspect_externals.py new file mode 100644 index 00000000..c9307a21 --- /dev/null +++ b/mac-patcher/inspect_externals.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""For each bundle: dump its externals (referenced external files) and list materials with external m_Shader PPtr.""" +import os +import sys +import UnityPy + +BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") + +bundles = sorted(f for f in os.listdir(BUNDLE_DIR) if f.endswith(".unity3d")) + +# We are most interested in bundles whose materials use external shaders (file_id != 0) +print("=" * 90) +print("BUNDLES WITH EXTERNAL m_Shader REFERENCES") +print("=" * 90) + +for fname in bundles: + path = os.path.join(BUNDLE_DIR, fname) + short_id = fname.replace("httpssteamusercontentaakamaihdnetugc", "")[:20] + env = UnityPy.load(path) + + external_mats = [] + for obj in env.objects: + if obj.type.name != "Material": + continue + try: + data = obj.read() + sref = data.m_Shader + fid = getattr(sref, "file_id", None) + pid = getattr(sref, "path_id", None) + if fid and fid != 0: + external_mats.append((data.m_Name, fid, pid)) + except Exception: + pass + + if not external_mats: + continue + + print(f"\n{short_id}:") + # Dump externals table + for f in env.files.values(): + if hasattr(f, "externals"): + print(f" externals table:") + for i, ext in enumerate(getattr(f, "externals", [])): + # The externals entry usually has a 'path' and 'guid' + ext_path = getattr(ext, "path", "?") + ext_guid = getattr(ext, "guid", "?") + # index i+1 because file_id is 1-based for externals (file_id=0 means self) + print(f" file_id={i+1} path={ext_path!r} guid={ext_guid}") + print(f" materials with external m_Shader:") + for name, fid, pid in external_mats: + print(f" material={name!r} m_Shader=(file_id={fid}, path_id={pid})") diff --git a/mac-patcher/materials-reference.md b/mac-patcher/materials-reference.md new file mode 100644 index 00000000..eabaae36 --- /dev/null +++ b/mac-patcher/materials-reference.md @@ -0,0 +1,170 @@ +# Materials & Prefabs Reference — SWL TTS Projector Overlays + +Extraction directe des `.mat` et `.prefab` Unity (YAML) dans `UnityProject-U6/Assets/Projectors/`. Source de vérité pour reproduire le rendu en vector lines. + +Notation : couleurs en hex + alpha. Champs `Floats` clés du shader projector (paramètres procéduraux des anneaux). + +## Cohesion + +### Materials + +| Material | Shader keywords | Floats clés | Couleurs | +|---|---|---|---| +| `Cohesion_27mm` | `_ARC_OFF _MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=4.479488, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.37)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #000000 (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_50mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=5.470629, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_70mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=6.850394, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_27mm` | `_ARC_OFF _MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=3.531496, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.37)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #000000 (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_50mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=3.984252, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | +| `Cohesion_70mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=4.3779526, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | + +### Prefabs + +| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | +|---|---|---|---|---| +| `cohesion_27mm` | 1 | size=4.547244, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | ebe2e870a59f… | +| `cohesion_50mm` | 1 | size=5.669291, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 406845c49cf4… | +| `cohesion_70mm` | 1 | size=6.850394, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3a57b0b27e32… | +| `halfCohesion_27mm` | 1 | size=3.531496, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 0e1c90ce1ef6… | +| `halfCohesion_50mm` | 1 | size=3.984252, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 2e05a24ab516… | +| `halfCohesion_70mm` | 1 | size=4.3779526, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 7cee44cc736f… | + +## Range + +### Materials + +| Material | Shader keywords | Floats clés | Couleurs | +|---|---|---|---| +| `ProjectorMaterial_100mm` | `_ARC_ON _MAXRANGE_RANGEFIVE _RANGE_FIVE` | `_BaseSize`=100, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.0183, `_TwoTuner`=0.0062, `_ThreeTuner`=0.0245, `_FourTuner`=0.0123, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_100mm_oblong` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=47.4, `_ProjectorRadius`=30, `_BandSize`=0.34, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=-0.022, `_TwoTuner`=-0.0421, `_ThreeTuner`=-0.0324, `_FourTuner`=-0.0525, `_FiveTuner`=0.073, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_100mm_snail` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=47.4, `_ProjectorRadius`=30, `_BandSize`=0.34, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=-0.0216, `_TwoTuner`=-0.0421, `_ThreeTuner`=-0.0324, `_FourTuner`=-0.0525, `_FiveTuner`=0.073, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_120mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=120, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.022, `_TwoTuner`=0.0074, `_ThreeTuner`=0.0292, `_FourTuner`=0.0146, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_150mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=150, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.027, `_TwoTuner`=0.009, `_ThreeTuner`=0.0358, `_FourTuner`=0.018, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_18mm_smokeToken` | `_ARC_OFF _MAXRANGE_RANGEONE` | `_BaseSize`=18.8, `_ProjectorRadius`=6, `_BandSize`=-0.11, `_BandContrast`=0.46, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #3A3145 (α=0.59)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_25mm_token` | `_ARC_OFF _MAXRANGE_RANGEONE` | `_BaseSize`=27, `_ProjectorRadius`=2.97, `_BandSize`=-0.45, `_BandContrast`=3, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_25mm_tokenRangeTwo` | `_ARC_OFF _MAXRANGE_RANGETWO` | `_BaseSize`=28.12, `_ProjectorRadius`=14.9, `_BandSize`=0.04, `_BandContrast`=3, `_MaxRange`=1, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0062, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_27mm` | `_ARC_OFF _MAXRANGE_RANGEFIVE` | `_BaseSize`=27, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=0, `_RangeSize`=-76.5, `_GradScaler`=0.0034, `_OneTuner`=0.0053, `_TwoTuner`=0.0019, `_ThreeTuner`=0.0072, `_FourTuner`=0.0037, `_FiveTuner`=0, `_InfinityTuner`=0.0034 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_50mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=50, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_GradScaler`=0, `_OneTuner`=0.0098, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0127, `_FourTuner`=0.0063, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_50mm_bombCart` | `_ARC_OFF _MAXRANGE_RANGETWO` | `_BaseSize`=50, `_ProjectorRadius`=12, `_BandSize`=0.01, `_BandContrast`=3, `_MaxRange`=1, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `ProjectorMaterial_70mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=70, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=2.87, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.0132, `_TwoTuner`=0.0045, `_ThreeTuner`=0.0175, `_FourTuner`=0.009, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.35)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | +| `Projector_BoardGuide` | `m_LightmapFlags: 4` | — | `_CameraFadeParams` transparent
`_Color` #FFFFFF (α=1.00)
`_ColorAddSubDiff` transparent
`_EmissionColor` #000000 (α=1.00)
`_SoftParticleFadeParams` transparent
`_TintColor` #7F7F7F (α=0.50) | + +### Prefabs + +| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | +|---|---|---|---|---| +| `projector_100mm` | 1 | size=31.9685, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 4b37ff3d68c5… | +| `projector_100mm_oblong` | 1 | size=33.4, far=45 | pos=(0.0, -1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 6c0556b105e8… | +| `projector_120mm` | 1 | size=32.3622, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | ab767c454926… | +| `projector_150mm` | 1 | size=32.95276, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3a33cb678c17… | +| `projector_200mm_oblong` | 1 | size=33.4, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0)
pos=(-0.0, -0.26, -0.0) rot=(0.0, 0.0, 90.0) | cccc683b4b18… | +| `projector_27mm` | 1 | size=30.5314, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | b1c5ef617632… | +| `projector_50mm` | 1 | size=30.984253, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 20ce39a8e0e1… | +| `projector_70mm` | 1 | size=31.37795, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 977480df799a… | +| `projector_POIGuide` | 1 | size=36, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | a0fb1f64d52e… | +| `projector_bomb_cart` | 1 | size=12.98425, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 417ae88dafb1… | +| `projector_smokeToken` | 1 | size=6.3700786, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3ef1e0f12697… | +| `projector_token` | 1 | size=7.06299, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 94ad9a7a030f… | +| `projector_tokenRangeTwo` | 1 | size=12.6, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 0905fd93d0b7… | + +## Deployment + +### Materials + +| Material | Shader keywords | Floats clés | Couleurs | +|---|---|---|---| +| `Projector_Deployment_Blue` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Deployment_Blue_Round` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Deployment_Red` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Deployment_Red_Round` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Score_Blue` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | +| `Projector_Score_Red` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | + +### Prefabs + +| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | +|---|---|---|---|---| +| `projector_blue_score` | 1 | size=6, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | fe66dad2ac93… | +| `projector_blue_spawn` | 1 | size=3, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 23c778da5a02… | +| `projector_blue_spawn_L` | 2 | size=1.5, far=25
size=3, far=25 | pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0)
pos=(1.5, 1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(0.0, -1.5000002, 0.0) rot=(0.0, 0.0, 90.0) | 23c778da5a02…
23c778da5a02… | +| `projector_blue_spawn_corner` | 1 | size=1.5, far=25 | pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0)
pos=(-1.5, -1.5, 0.0) rot=(0.0, 0.0, 90.0) | 23c778da5a02… | +| `projector_blue_spawn_half` | 1 | size=3, far=25 | pos=(0.0, -1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0) | 23c778da5a02… | +| `projector_blue_spawn_round2` | 1 | size=12, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 34786d447192… | +| `projector_red_score` | 1 | size=6, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 90fdc53ff83c… | +| `projector_red_spawn` | 1 | size=3, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 93e068805588… | +| `projector_red_spawn_L` | 2 | size=3, far=25
size=1.5, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0)
pos=(0.0, 1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(-1.5, -1.5, -0.0) rot=(0.0, 0.0, 90.0) | 93e068805588…
93e068805588… | +| `projector_red_spawn_corner` | 1 | size=1.5, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0)
pos=(1.5, 1.5, -0.0) rot=(0.0, 0.0, 90.0) | 93e068805588… | +| `projector_red_spawn_half` | 1 | size=3, far=25 | pos=(0.0, 1.5, -0.0) rot=(0.0, 0.0, 90.0)
pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 93e068805588… | +| `projector_red_spawn_round2` | 1 | size=12, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 54824e17d05f… | + +## Movement + +### Materials + +| Material | Shader keywords | Floats clés | Couleurs | +|---|---|---|---| +| `ProjectorMaterial_100mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=8.622047, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_100mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=10.590551, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_100mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=12.559055, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_120mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=9.803149, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_120mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=11.771653, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_120mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.740157, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_150mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=11.574803, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_150mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.543307, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_150mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=15.511811, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed1_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=8.45, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=4.547244, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed2_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=12.42748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=6.515748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed3_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=16.42748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_27mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=8.484252, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_50mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=5.6692915, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_50mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=7.6377954, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_50mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=9.606299, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_70mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=6.850394, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_70mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=8.818897, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_70mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=10.787401, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_oblong_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.051181, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_oblong_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=15.019685, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_oblong_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=16.98819, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_snail_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=14.527559, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_snail_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=16.496063, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | +| `ProjectorMaterial_snail_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=18.464567, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | + +### Prefabs + +| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | +|---|---|---|---|---| +| `movement_100mm_speed1_single` | 1 | size=8.622047, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 406c1ddb0995… | +| `movement_100mm_speed2_single` | 1 | size=10.590551, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 0aa471d50563… | +| `movement_100mm_speed3_single` | 1 | size=12.559055, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 8fe5008dea10… | +| `movement_120mm_speed1_single` | 1 | size=9.803149, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 21bc9fa53646… | +| `movement_120mm_speed2_single` | 1 | size=11.771653, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 6d02674694a9… | +| `movement_120mm_speed3_single` | 1 | size=13.740157, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 895a4d7a1f02… | +| `movement_150mm_speed1_single` | 1 | size=11.574803, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | fef40c5dadca… | +| `movement_150mm_speed2_single` | 1 | size=13.543307, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | c9d6b007a730… | +| `movement_150mm_speed3_single` | 1 | size=15.511811, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | d08a6fca8013… | +| `movement_27mm_speed1_double` | 1 | size=8.45, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 7037d7245d82… | +| `movement_27mm_speed1_single` | 1 | size=4.547244, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 61da0f6e009e… | +| `movement_27mm_speed2_double` | 1 | size=12.42748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 362084914234… | +| `movement_27mm_speed2_single` | 1 | size=6.515748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 6de57d74ffda… | +| `movement_27mm_speed3_double` | 1 | size=16.42748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | a0dce2ad51d5… | +| `movement_27mm_speed3_single` | 1 | size=8.484252, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | b65b690fde07… | +| `movement_50mm_speed1_single` | 1 | size=5.6692915, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 38f1f5f6a60c… | +| `movement_50mm_speed2_single` | 1 | size=7.6377954, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 3d2f279db06b… | +| `movement_50mm_speed3_single` | 1 | size=9.606299, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | ca54633a2d92… | +| `movement_70mm_speed1_single` | 1 | size=6.850394, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 3d2faeaaaaff… | +| `movement_70mm_speed2_single` | 1 | size=8.818897, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 948d0799a0c3… | +| `movement_70mm_speed3_single` | 1 | size=10.787401, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | a986001c0bc6… | +| `movement_oblong_speed1_single` | 1 | size=13.051181, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | b9540fc3dad6… | +| `movement_oblong_speed2_single` | 1 | size=15.019685, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 672e50dcd47c… | +| `movement_oblong_speed3_single` | 1 | size=16.98819, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | a2468c9fe07d… | +| `movement_snail_speed1_single` | 1 | size=14.527559, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | b7c44d9516fd… | +| `movement_snail_speed2_single` | 1 | size=16.496063, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | d27d5e30c83c… | +| `movement_snail_speed3_single` | 1 | size=18.464567, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | ecdda5f21c4c… | + +## Notes globales + +- Cohesion : double set `Materials/cohesion_*` et `_Revamp/halfCohesion_*`. Probable : `_Revamp/` est la version actuellement utilisée (le bundle live s'appelle `halfcohesion_27mm.unity3d` cf shader-inventory). +- Range : shader procédural avec jusqu'à 5 bandes (`_RangeOne` à `_RangeFive`) + `_RangeInfinity`. Le keyword `_MAXRANGE_RANGEFIVE` ou `_MAXRANGE_RANGEFOUR` détermine la dernière bande visible. +- Movement : un seul Projector par prefab, paramétré par `_BaseSize` + speed. Le `_Arc` doit contrôler la portion d'arc visible. +- Deployment : 6 materials uniques réutilisés par 12 prefabs (rotations différentes pour matérialiser corner/half/L/round/score). \ No newline at end of file diff --git a/mac-patcher/patch_cohesion_rename.py b/mac-patcher/patch_cohesion_rename.py new file mode 100644 index 00000000..065a14a9 --- /dev/null +++ b/mac-patcher/patch_cohesion_rename.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +PATCH ATTEMPT #1 — Rename embedded Shader m_Name from +'BucketheadBits/Projector/Cohesion' to 'Standard'. + +Theory: at AssetBundle.LoadAsset, Unity may resolve the shader by name via +Shader.Find() before falling back to the embedded bytecode. If so, TTS picks +up its own built-in Standard shader and renders the Cohesion projector with +it, ignoring the broken embedded shader. + +Writes to .patched.unity3d alongside the original. Does NOT touch +the live bundle in ~/Library/Tabletop Simulator/Mods/Assetbundles/ yet. +""" +import os +import sys +import shutil +import UnityPy + +BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") +BUNDLE_NAME = "httpssteamusercontentaakamaihdnetugc2482129948496305632EBBE2560D4336E6C96317EDDA787E225CF0E5B48.unity3d" +SRC = os.path.join(BUNDLE_DIR, BUNDLE_NAME) +DST = os.path.join("/tmp", "cohesion.patched.unity3d") + +print(f"Loading: {SRC}") +env = UnityPy.load(SRC) + +modified = False +for obj in env.objects: + if obj.type.name != "Shader": + continue + data = obj.read() + current = getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) or getattr(data, "m_Name", None) + print(f" Found Shader PathID={obj.path_id} current m_Name={current!r}") + if current != "BucketheadBits/Projector/Cohesion": + print(" -> Not our target, skipping.") + continue + + # Modify via typetree (safest cross-version path with UnityPy) + tree = obj.read_typetree() + # m_ParsedForm.m_Name is the canonical name for parsed shaders. + # m_Name may also exist for the asset itself; we try both. + if "m_ParsedForm" in tree and "m_Name" in tree["m_ParsedForm"]: + old = tree["m_ParsedForm"]["m_Name"] + tree["m_ParsedForm"]["m_Name"] = "Standard" + print(f" -> renamed m_ParsedForm.m_Name {old!r} -> 'Standard'") + modified = True + if "m_Name" in tree and tree.get("m_Name"): + old = tree["m_Name"] + tree["m_Name"] = "Standard" + print(f" -> renamed m_Name {old!r} -> 'Standard'") + modified = True + + obj.save_typetree(tree) + +if not modified: + print("ERROR: no shader matched. Aborting.") + sys.exit(1) + +# Write out the patched bundle +print(f"\nSaving patched bundle to: {DST}") +with open(DST, "wb") as f: + f.write(env.file.save(packer="lz4")) + +print(f" size: {os.path.getsize(DST)} bytes (original was {os.path.getsize(SRC)})") + +# Verify roundtrip +print("\nVerifying patched bundle is re-loadable...") +env2 = UnityPy.load(DST) +for obj in env2.objects: + if obj.type.name == "Shader": + d = obj.read() + n = getattr(getattr(d, "m_ParsedForm", None), "m_Name", None) or getattr(d, "m_Name", None) + print(f" After patch: Shader PathID={obj.path_id} m_Name={n!r}") + elif obj.type.name == "Material": + d = obj.read() + sref = d.m_Shader + print(f" Material {d.m_Name!r} m_Shader=(file_id={sref.file_id}, path_id={sref.path_id})") + +print("\nDone. Patched file ready at:", DST) +print("Run this command to replace the live bundle (only when TTS is closed):") +print(f" cp {DST!r} {SRC!r}") diff --git a/mac-patcher/patch_save_for_mac.py b/mac-patcher/patch_save_for_mac.py new file mode 100644 index 00000000..24bd5e22 --- /dev/null +++ b/mac-patcher/patch_save_for_mac.py @@ -0,0 +1,2171 @@ +#!/usr/bin/env python3 +""" +Patch a TTS save JSON (SWL mod beta) to add Mac Cohesion fallback. + +Two-part patch: +1. Append manager + event handlers to the Global LuaScript. +2. Replace the inline Cohesion block in Unit_Leader (99f1c8) and Order_Token + (a57c41) with delegated stubs that call the Global manager via Global.call. + +The native hotkey "Show Cohesion On Hovered Model" and the Order Token +"COHESION" button automatically use the patched code afterwards - they +spawn vector-lines rings instead of legacy Custom_AssetBundle Projectors. + +Usage: + python3 patch_save_for_mac.py +""" +import json +import re +import socket +import sys +from pathlib import Path + +# ------------------------------------------------------------ +# 1) Code appended to Global LuaScript (manager + event handlers) +# ------------------------------------------------------------ +GLOBAL_PATCH_LUA = r""" + +-- ============================================ +-- MAC COHESION FALLBACK (auto-injected for TTS U6 magenta bug) +-- Manager Global + event handlers + vector-lines rendering. +-- Called by Unit_Leader / Order_Token via Global.call from their patched +-- Cohesion functions. +-- ============================================ + +-- Capture the ORIGINAL Global functions loaded by !/RangeRulers and +-- !/Cohesion BEFORE we overwrite them below. We also capture the inner +-- spawn/clear pair because the toggle wrapper resolves them by NAME at +-- call time - without aliasing those too, the original toggle would route +-- back into our Mac versions, defeating Windows mode entirely. +spawnRangeRulerOriginalGlobal = spawnRangeRuler +clearRangeRulersOriginalGlobal = clearRangeRulers +spawnCohesionRulerOriginalGlobal = spawnCohesionRuler +clearCohesionRulerOriginalGlobal = clearCohesionRuler + +activeOverlays = activeOverlays or {} +hiddenWhilePickedUp = hiddenWhilePickedUp or {} + +local ASSETS_BASE = "https://raw.githubusercontent.com/ironsquadronfr-hub/tts/mac-projector-fallback/mod/data/mac-fallback-assets/" +local COHESION_HALO_URL = ASSETS_BASE .. "cohesion_halo.png" +local MAX_MOVE_CYAN_URL = ASSETS_BASE .. "max_move_cyan.png" +local DEPLOYMENT_RED_URL = ASSETS_BASE .. "deployment_red.png" +local DEPLOYMENT_BLUE_URL = ASSETS_BASE .. "deployment_blue.png" +local RANGE_DECAL_URLS = { + smokeToken = ASSETS_BASE .. "range_smokeToken.png", + token = ASSETS_BASE .. "range_token.png", + tokenRangeTwo = ASSETS_BASE .. "range_tokenRangeTwo.png", + poi = ASSETS_BASE .. "range_poi.png", + bombCart = ASSETS_BASE .. "range_bombCart.png", + -- Fig leaders: per-base-size PNGs (bands pre-offset by the base radius so + -- the decal bands line up with the vector-line rings). + fig_leader = { + small = ASSETS_BASE .. "range_fig_leader_small.png", + medium = ASSETS_BASE .. "range_fig_leader_medium.png", + large = ASSETS_BASE .. "range_fig_leader_large.png", + huge = ASSETS_BASE .. "range_fig_leader_huge.png", + laat = ASSETS_BASE .. "range_fig_leader_laat.png", + epic = ASSETS_BASE .. "range_fig_leader_epic.png", + long = ASSETS_BASE .. "range_fig_leader_long_stadium.png", + snail = ASSETS_BASE .. "range_fig_leader_snail_stadium.png", + }, +} +local RANGE_MAX_RADIUS = { + fig_leader = 30, -- R5 max + -- SWL: Range 1 = 6", Range 2 = 12", Range 0.5 = 3". + smokeToken = 6, + token = 6, + tokenRangeTwo = 12, + poi = 3, + bombCart = 12, +} + +-- Range band colors (RGBA) extracted from BB_RangeProjector material defaults +-- + demi-portee blanche pour SWL Range 0.5 (3 inches) +local RANGE_COLORS = { + half = {1.0, 1.0, 1.0, 0.6}, -- 0.5 = 3" white + one = {1.0, 0.764, 0.0, 0.7}, -- R1 yellow + two = {1.0, 0.459, 0.0, 0.7}, -- R2 orange + three = {1.0, 0.079, 0.0, 0.7}, -- R3 red + four = {0.764, 0.0, 0.259, 0.7}, -- R4 magenta + five = {0.528, 0.0, 0.443, 0.7}, -- R5 violet +} + +-- Per-rangeKey configurations: list of {radius_inches, color_name} +local RANGE_CONFIGS = { + -- Fig leaders: half + 4 bands at SWL standard ranges 3/6/12/18/24" + small = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + medium = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + large = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + huge = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + laat = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + epic = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + long = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + snail = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, + -- Token rangeKeys (1 to 2 bands depending on use). SWL ranges in inches: + -- Range 1 = 6", Range 2 = 12", Range 0.5 (poi) = 3". + smokeToken = {{r=6,c="one"}}, + token = {{r=6,c="one"}}, + tokenRangeTwo = {{r=6,c="one"},{r=12,c="two"}}, + poi = {{r=3,c="one"}}, + bombCart = {{r=6,c="one"},{r=12,c="two"}}, +} + +-- Base footprint dimensions in mm. width <= length; equal means round. +-- Used to drive round vs stadium overlay shape. +local BASE_DIMENSIONS = { + small = {width = 27, length = 27}, + medium = {width = 50, length = 50}, + large = {width = 70, length = 70}, + huge = {width = 100, length = 100}, + laat = {width = 120, length = 120}, + epic = {width = 150, length = 150}, + long = {width = 100, length = 175}, -- oblong + snail = {width = 100, length = 200}, -- oblong +} + +-- Base radius (inches) for round overlays. For oblong bases we use a +-- stadium shape instead (see macBuildStadium), so the radius here is the +-- half-width that drives the cap arcs. +local FIG_BASE_RADIUS_IN = {} +for k, d in pairs(BASE_DIMENSIONS) do + FIG_BASE_RADIUS_IN[k] = (d.width / 2) / 25.4 +end + +local function macIsOblong(baseSize) + local d = BASE_DIMENSIONS[baseSize] + return d ~= nil and d.length > d.width +end + +-- Returns the stadium half-dimensions (inches) and yaw for an oblong base, +-- or nil if the fig's base is round. Callers should branch on the return. +local function macOblongDims(fig, baseSize) + if not macIsOblong(baseSize) then return nil end + local d = BASE_DIMENSIONS[baseSize] + return { + halfLen = (d.length / 2) / 25.4, + halfWid = (d.width / 2) / 25.4, + rotY = fig.getRotation().y, + } +end + +-- Resolve baseSize for a fig. The mod stores it as a script-local Var on the +-- fig itself (set at spawn from the unit's container). unitData.baseSize is +-- only populated on the Unit Leader, not on minis/vehicles - so getVar is +-- the canonical read, with unitData as fallback for safety. +local function macGetBaseSize(fig) + if not fig then return nil end + local ok, bs = pcall(function() return fig.getVar("baseSize") end) + if ok and bs then return bs end + local data = fig.getTable("unitData") + if data and data.baseSize then return data.baseSize end + return nil +end + +local function macFigBaseRadius(fig) + if not fig then return 0.5315 end + local bs = macGetBaseSize(fig) + return (bs and FIG_BASE_RADIUS_IN[bs]) or 0.5315 +end + +-- Token base radii (inches) - measured from token edge for range bands +local TOKEN_BASE_RADIUS = { + smokeToken = 18.8 / 2 / 25.4, -- 0.370" + token = 25.1 / 2 / 25.4, -- 0.494" + tokenRangeTwo = 25.1 / 2 / 25.4, -- 0.494" + poi = 50.8 / 2 / 25.4, -- 1.000" + bombCart = 50.0 / 2 / 25.4, -- 0.984" +} + +-- Hotkey-driven range on tokens renders fig-leader-style bands using the +-- closest equivalent base size (so the visual matches what a unit of that +-- footprint would see). Token R button keeps the single ring (token-spec). +local TOKEN_TO_BASESIZE = { + smokeToken = "small", + token = "small", + tokenRangeTwo = "small", + poi = "medium", + bombCart = "medium", +} + +-- Maximum Move radii (inches), indexed by baseSize then speed 1..3 +-- Source: ProjectorRadius from BB_MovementProjector materials (single variants) +local MAX_MOVE_RADIUS = { + small = { 4.547244, 6.515748, 8.484252}, -- 27mm + medium = { 5.669292, 7.637795, 9.606299}, -- 50mm + large = { 6.850394, 8.818897, 10.787401}, -- 70mm + huge = { 8.622047, 10.590551, 12.559055}, -- 100mm + laat = { 9.803149, 11.771653, 13.740157}, -- 120mm + epic = {11.574803, 13.543307, 15.511811}, -- 150mm + long = {13.051181, 15.019685, 16.988190}, -- oblong + snail = {14.527559, 16.496063, 18.464567}, -- snail +} + +-- Deployment cell dimensions (width X, depth Z, inches). +-- All cells live on a 6"x6" grid; sub-cells are halves/quarters of that cell. +local DEPLOYMENT_CELL_SIZES = { + r = {6, 6}, b = {6, 6}, -- full cell + rl = {6, 6}, bl = {6, 6}, -- large variant (also full cell) + rh = {6, 3}, bh = {6, 3}, -- horizontal half (3" deep) + rs = {3, 6}, bs = {3, 6}, -- vertical half (3" wide), x-offset +1.5 + rss = {3, 6}, bss = {3, 6}, -- vertical half (3" wide), x-offset -1.5 + rc = {3, 3}, bc = {3, 3}, -- corner quarter + rcc = {3, 3}, bcc = {3, 3}, -- opposite corner quarter +} + +local function macRayGroundY(x, z, offset) + local hits = Physics.cast({ + origin = {x, 30, z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 50, + }) + for _, h in ipairs(hits) do + return h.point.y + (offset or 0.05) + end + return offset or 0.05 +end + +local function macBuildRect(cx, cz, hw, hd, color, thickness) + local segPerSide = 8 + local pts = {} + local corners = { + {cx - hw, cz + hd}, {cx + hw, cz + hd}, + {cx + hw, cz - hd}, {cx - hw, cz - hd}, + } + for i = 1, 4 do + local a = corners[i] + local b = corners[i % 4 + 1] + for s = 0, segPerSide - 1 do + local t = s / segPerSide + local x = a[1] + (b[1] - a[1]) * t + local z = a[2] + (b[2] - a[2]) * t + table.insert(pts, {x, macRayGroundY(x, z), z}) + end + end + local c1 = corners[1] + table.insert(pts, {c1[1], macRayGroundY(c1[1], c1[2]), c1[2]}) + return {points = pts, color = color, thickness = thickness or 0.06} +end + +local function macBuildRing(centerPos, radius, color, thickness, ignoreObj) + local nSeg = 64 + local pts = {} + for i = 0, nSeg do + local a = (i / nSeg) * 2 * math.pi + local x = centerPos.x + radius * math.cos(a) + local z = centerPos.z + radius * math.sin(a) + local hits = Physics.cast({ + origin = {x, centerPos.y + 10, z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local y = centerPos.y + 0.05 + for _, h in ipairs(hits) do + if h.hit_object ~= ignoreObj then + y = h.point.y + 0.05 + break + end + end + table.insert(pts, {x, y, z}) + end + return {points = pts, color = color, thickness = thickness or 0.05} +end + +-- Stadium = rectangle with two semicircular caps on the long-axis ends. +-- halfLen, halfWid in inches (halfLen >= halfWid). dist = expansion outwards +-- from the base edge in inches (so the overlay sits at dist outside the base). +-- rotYdeg rotates the stadium around Y to match the fig's orientation; the +-- long axis is local Z (front-to-back of the model). +local function macBuildStadium(centerPos, halfLen, halfWid, rotYdeg, dist, color, thickness, ignoreObj) + local rotRad = math.rad(rotYdeg or 0) + local cosR, sinR = math.cos(rotRad), math.sin(rotRad) + + local capCenter = halfLen - halfWid -- distance from center to cap arc center, along long axis + local capR = halfWid + dist -- cap arc radius (= straight-side offset from long axis) + + local nSegArc = 32 -- segments per semicircle cap + local pts = {} + + local function addLocal(lx, lz) + local wx = centerPos.x + lx * cosR + lz * sinR + local wz = centerPos.z - lx * sinR + lz * cosR + local hits = Physics.cast({ + origin = {wx, centerPos.y + 10, wz}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local y = centerPos.y + 0.05 + for _, h in ipairs(hits) do + if h.hit_object ~= ignoreObj then + y = h.point.y + 0.05 + break + end + end + table.insert(pts, {wx, y, wz}) + end + + -- Top cap: angle 0 to pi (right edge -> top -> left edge), centered at z=+capCenter + for i = 0, nSegArc do + local a = (i / nSegArc) * math.pi + addLocal(capR * math.cos(a), capCenter + capR * math.sin(a)) + end + -- The straight left side is implicit: the polyline jumps from + -- (-capR, +capCenter) directly to (-capR, -capCenter) - TTS draws a + -- straight segment between consecutive points. + -- Bottom cap: angle pi to 2pi, centered at z=-capCenter + for i = 0, nSegArc do + local a = math.pi + (i / nSegArc) * math.pi + addLocal(capR * math.cos(a), -capCenter + capR * math.sin(a)) + end + -- Close the loop back to the top-cap start point. + addLocal(capR, capCenter) + + return {points = pts, color = color, thickness = thickness or 0.05} +end + +local macBuilders = {} + +macBuilders.range = function(fig, params) + local pos = fig.getPosition() + -- Resolve rangeKey for token-specific single-ring rendering (R button on + -- a token). The hotkey path sets params.forceFigMode = true to bypass + -- this and always render the fig-leader 6-band overlay, using the token's + -- equivalent baseSize for sizing. + local forceFig = params and params.forceFigMode + local rangeKey = (not forceFig) and ((params and params.rangeKey) or fig.getVar("rangeKey")) or nil + local config, decalURL, maxR, br + local oblong, halfLen, halfWid, rotY + local baseSize -- hoisted: used below for firing-arc decision + if rangeKey and RANGE_CONFIGS[rangeKey] then + config = RANGE_CONFIGS[rangeKey] + decalURL = RANGE_DECAL_URLS[rangeKey] + maxR = RANGE_MAX_RADIUS[rangeKey] + br = TOKEN_BASE_RADIUS[rangeKey] or 0 + else + baseSize = macGetBaseSize(fig) + local hasBaseSize = baseSize ~= nil + if not hasBaseSize then + -- Token hit from hotkey: map rangeKey to base size for sizing. + local tk = fig.getVar("rangeKey") + baseSize = (tk and TOKEN_TO_BASESIZE[tk]) or "small" + end + config = RANGE_CONFIGS[baseSize] or RANGE_CONFIGS.small + decalURL = RANGE_DECAL_URLS.fig_leader[baseSize] + or RANGE_DECAL_URLS.fig_leader.small + maxR = RANGE_MAX_RADIUS.fig_leader + if hasBaseSize then + br = macFigBaseRadius(fig) + local od = macOblongDims(fig, baseSize) + if od then + oblong, halfLen, halfWid, rotY = true, od.halfLen, od.halfWid, od.rotY + end + else + -- Token in forceFig mode: use token's measured radius for offset. + local tk = fig.getVar("rangeKey") + br = (tk and TOKEN_BASE_RADIUS[tk]) or FIG_BASE_RADIUS_IN[baseSize] or 0.5315 + end + end + + -- SWL convention: all distances are measured from the base/token edge. + + -- Vector contours: 6 concentric rings for round bases, 6 concentric + -- stadium outlines for oblong bases (each band sits at band.r outwards + -- from the base edge - same rule, just a different shape). + local lines = {} + for _, band in ipairs(config) do + if oblong then + table.insert(lines, macBuildStadium(pos, halfLen, halfWid, rotY, band.r, + RANGE_COLORS[band.c], 0.1, fig)) + else + table.insert(lines, macBuildRing(pos, band.r + br, RANGE_COLORS[band.c], 0.1, fig)) + end + end + + -- Firing arc lines (4 radial lines dividing into Front/R/Rear/L quadrants). + -- Mirrors the vanilla Projector shader: ProjectorMaterial_ sets + -- _Arc=1 / shader keyword _ARC_ON for every non-27mm base (medium, large, + -- huge, laat, epic, long, snail). 27mm sets _Arc=0. + -- Round bases : lines emanate from the base edge, radial from center. + -- Oblong bases: lines emanate from the centers of the two end + -- semicircles (front cap + rear cap), front lines at +/-45 from the + -- long-axis forward, rear lines at +/-135. + local arcColor = {1, 1, 1, 0.6} + if baseSize and baseSize ~= "small" then + if oblong then + local capOffset = (halfLen or 0) - (halfWid or 0) + local rad = math.rad(rotY) + local fwdX, fwdZ = math.sin(rad), math.cos(rad) + local frontX = pos.x + capOffset * fwdX + local frontZ = pos.z + capOffset * fwdZ + local rearX = pos.x - capOffset * fwdX + local rearZ = pos.z - capOffset * fwdZ + local reach = (halfWid or 0) + (maxR or 6) + local arcs = { + {frontX, frontZ, 45}, -- front-right boundary + {frontX, frontZ, -45}, -- front-left boundary + {rearX, rearZ, 135}, -- rear-right boundary + {rearX, rearZ, -135}, -- rear-left boundary + } + for _, e in ipairs(arcs) do + local cx, cz, deg = e[1], e[2], e[3] + local a = math.rad(rotY + deg) + local sinA, cosA = math.sin(a), math.cos(a) + table.insert(lines, { + points = { + {cx, pos.y + 0.1, cz}, + {cx + reach * sinA, pos.y + 0.1, cz + reach * cosA}, + }, + color = arcColor, + thickness = 0.05, + }) + end + else + local figRotY = fig.getRotation().y + local outerR = (maxR or 6) + br + for _, deg in ipairs({45, 135, 225, 315}) do + local a = math.rad(deg + figRotY) + local sinA, cosA = math.sin(a), math.cos(a) + table.insert(lines, { + points = { + {pos.x + br * sinA, pos.y + 0.1, pos.z + br * cosA}, + {pos.x + outerR * sinA, pos.y + 0.1, pos.z + outerR * cosA}, + }, + color = arcColor, + thickness = 0.05, + }) + end + end + end + + -- Decal halo (filled bands) - flat on the ground under the fig + local hits = Physics.cast({ + origin = {pos.x, pos.y + 10, pos.z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local groundY = pos.y + for _, h in ipairs(hits) do + if h.hit_object ~= fig then groundY = h.point.y; break end + end + + if oblong then + -- Stadium-shape PNGs are authored per oblong base size: the 6 bands + -- inside the PNG already match the stadium vector outlines because + -- the PNG was generated with the same halfWid/halfLen + dist math. + -- Decal scale = footprint of the outermost band (2*(half + maxR)) + -- in each axis, so the PNG stretches to exactly the right size. + local longSpan = 2 * (halfLen + maxR) + local shortSpan = 2 * (halfWid + maxR) + return { + lines = lines, + decals = { + { + name = "range_decal_" .. fig.getGUID(), + url = decalURL, + position = {pos.x, groundY + 0.02, pos.z}, + rotation = {90, rotY, 0}, + scale = {shortSpan, longSpan, 1}, + } + }, + } + end + + local effectiveMaxR = maxR + br + return { + lines = lines, + decals = { + { + name = "range_decal_" .. fig.getGUID(), + url = decalURL, + position = {pos.x, groundY + 0.02, pos.z}, -- below cohesion halo + rotation = {90, 0, 0}, + scale = {effectiveMaxR * 2, effectiveMaxR * 2, effectiveMaxR * 2}, + } + }, + } +end + +macBuilders.maxmove = function(fig, params) + -- Use the anchor position stored at spawn time so the ring stays put + -- when the fig slides toward the destination, instead of tracking it. + local pos = (params and params.anchorPos) or fig.getPosition() + local baseSize = (params and params.baseSize) or "small" + local speed = (params and params.speed) or 1 + local radii = MAX_MOVE_RADIUS[baseSize] or MAX_MOVE_RADIUS.small + local r = radii[speed] or radii[1] + local color = {0.333, 0.800, 1.000, 0.7} + -- Base ring: small white outline matching the figure's base + -- (BB_MovementProjector renders a base ring on top of the cyan max-move). + local br = FIG_BASE_RADIUS_IN[baseSize] or FIG_BASE_RADIUS_IN.small + local baseColor = {1, 1, 1, 0.8} + -- Oblong base dimensions for the stadium base ring. Use the rotation + -- captured at spawn-time so the ring keeps its orientation even after + -- the fig pivots toward its destination. + local od = macOblongDims(fig, baseSize) + local oblong = od ~= nil + local halfLen, halfWid, rotY + if oblong then + halfLen, halfWid = od.halfLen, od.halfWid + rotY = (params and params.anchorRot) or od.rotY + end + + -- Ground raycast for decal position + local hits = Physics.cast({ + origin = {pos.x, pos.y + 10, pos.z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local groundY = pos.y + for _, h in ipairs(hits) do + if h.hit_object ~= fig then groundY = h.point.y; break end + end + + if oblong then + -- Oblong base: the cyan max-move envelope stays a round ring (isotropic + -- - any rotation reaches the same radius from the pivot), but the + -- inner white base ring follows the rectangular footprint as a + -- stadium hugging the actual base edge. + return { + lines = { + macBuildRing(pos, r, color, 0.1, fig), + macBuildStadium(pos, halfLen, halfWid, rotY, 0, + baseColor, 0.06, fig), + }, + decals = { + { + name = "maxmove_decal_" .. fig.getGUID(), + url = MAX_MOVE_CYAN_URL, + position = {pos.x, groundY + 0.025, pos.z}, + rotation = {90, 0, 0}, + scale = {r * 2, r * 2, r * 2}, + } + }, + } + end + + return { + lines = { + macBuildRing(pos, r, color, 0.1, fig), -- outer cyan max-move + macBuildRing(pos, br, baseColor, 0.06, fig), -- inner white base ring + }, + decals = { + { + name = "maxmove_decal_" .. fig.getGUID(), + url = MAX_MOVE_CYAN_URL, + position = {pos.x, groundY + 0.025, pos.z}, + rotation = {90, 0, 0}, + scale = {r * 2, r * 2, r * 2}, + } + }, + } +end + +macBuilders.deployment = function(_, params) + local pos = params.pos -- {x, y, z} cell center (after offset) + local cell = params.cell -- "r"/"b"/"rh"/... key + local size = DEPLOYMENT_CELL_SIZES[cell] or {6, 6} + local w, d = size[1], size[2] + local cx, cz = pos[1], pos[3] + local hw, hd = w / 2, d / 2 + + local isRed = cell:sub(1, 1) == "r" + local lineColor = isRed and {1, 0.15, 0.15, 0.9} or {0.15, 0.4, 1, 0.9} + local url = isRed and DEPLOYMENT_RED_URL or DEPLOYMENT_BLUE_URL + + local groundY = macRayGroundY(cx, cz, 0) + + return { + lines = { macBuildRect(cx, cz, hw, hd, lineColor, 0.08) }, + decals = { + { + name = "deployment_decal_" .. cell .. "_" .. tostring(cx) .. "_" .. tostring(cz), + url = url, + position = {cx, groundY + 0.03, cz}, + rotation = {90, 0, 0}, + scale = {w, d, 1}, + } + }, + } +end + +macBuilders.cohesion = function(fig, params) + local pos = fig.getPosition() + -- Cohesion overlay = base edge + 3 inches (half Range 1). Round bases get + -- a ring; oblong bases (long/snail) get a stadium-shape concentric to the + -- footprint so the offset stays a constant 3" from the edge. + local rangeKey = fig.getVar("rangeKey") + local oblong, halfLen, halfWid, rotY, r + if rangeKey and TOKEN_BASE_RADIUS[rangeKey] then + r = TOKEN_BASE_RADIUS[rangeKey] + 3.0 + else + local baseSize = macGetBaseSize(fig) or "small" + local od = macOblongDims(fig, baseSize) + if od then + oblong, halfLen, halfWid, rotY = true, od.halfLen, od.halfWid, od.rotY + else + r = (FIG_BASE_RADIUS_IN[baseSize] or FIG_BASE_RADIUS_IN.small) + 3.0 + end + end + if params and params.radius then + oblong, r = false, params.radius + end + + -- Find ground level under the fig so the flat decal sits on the table, + -- not at the fig pivot (which is above the base). + local hits = Physics.cast({ + origin = {pos.x, pos.y + 10, pos.z}, + direction = {0, -1, 0}, + type = 1, + max_distance = 20, + }) + local groundY = pos.y + for _, h in ipairs(hits) do + if h.hit_object ~= fig then + groundY = h.point.y + break + end + end + + if oblong then + -- Halo decal: stretch the round halo PNG into an ellipse aligned with + -- the fig's long axis. Approximates the stadium fade (a true stadium + -- halo would need a custom PNG); good enough visually since the + -- vector stadium outline carries the precise boundary. + local longSpan = 2 * (halfLen + 3.0) + local shortSpan = 2 * (halfWid + 3.0) + return { + lines = { macBuildStadium(pos, halfLen, halfWid, rotY, 3.0, + {1, 1, 1, 0.8}, 0.06, fig) }, + decals = { + { + name = "cohesion_halo_" .. fig.getGUID(), + url = COHESION_HALO_URL, + position = {pos.x, groundY + 0.04, pos.z}, + -- 90 deg pitch lays the decal flat on the table; rotY (yaw) + -- aligns its local Y with the fig's long axis. + rotation = {90, rotY, 0}, + scale = {shortSpan, longSpan, 1}, + } + }, + } + end + + return { + lines = { macBuildRing(pos, r, {1, 1, 1, 0.8}, 0.06, fig) }, + decals = { + { + name = "cohesion_halo_" .. fig.getGUID(), + url = COHESION_HALO_URL, + position = {pos.x, groundY + 0.04, pos.z}, + rotation = {90, 0, 0}, + scale = {r * 2, r * 2, r * 2}, + } + }, + } +end + +function macRedrawAll() + local lines, decals = {}, {} + -- Preload all texture URLs (invisible decals far below the table) so TTS + -- keeps them cached and we never get the white-square flash on respawn. + local preloadURLs = { + COHESION_HALO_URL, + RANGE_DECAL_URLS.smokeToken, + RANGE_DECAL_URLS.token, RANGE_DECAL_URLS.tokenRangeTwo, + RANGE_DECAL_URLS.poi, RANGE_DECAL_URLS.bombCart, + RANGE_DECAL_URLS.fig_leader.small, RANGE_DECAL_URLS.fig_leader.medium, + RANGE_DECAL_URLS.fig_leader.large, RANGE_DECAL_URLS.fig_leader.huge, + RANGE_DECAL_URLS.fig_leader.laat, RANGE_DECAL_URLS.fig_leader.epic, + RANGE_DECAL_URLS.fig_leader.long, RANGE_DECAL_URLS.fig_leader.snail, + MAX_MOVE_CYAN_URL, + DEPLOYMENT_RED_URL, DEPLOYMENT_BLUE_URL, + } + for i, url in ipairs(preloadURLs) do + table.insert(decals, { + name = "preload_" .. i, + url = url, + position = {0, -200 - i * 0.01, 0}, + rotation = {90, 0, 0}, + scale = {0.01, 0.01, 0.01}, + }) + end + local stale = {} + for key, entry in pairs(activeOverlays) do + local b = macBuilders[entry.type] + if b then + -- Deployment entries have no fig (params-driven). Others need a + -- live Object - drop them silently if destroyed/invalid. + local needsFig = (entry.type ~= "deployment") + if needsFig and (not entry.fig + or type(entry.fig.getPosition) ~= "function") then + stale[#stale + 1] = key + else + local ok, out = pcall(b, entry.fig, entry.params) + if ok and out then + for _, l in ipairs(out.lines or {}) do + table.insert(lines, l) + end + for _, d in ipairs(out.decals or {}) do + table.insert(decals, d) + end + else + stale[#stale + 1] = key + end + end + end + end + for _, k in ipairs(stale) do activeOverlays[k] = nil end + Global.setVectorLines(lines) + Global.setDecals(decals) +end + +function gSpawnCohesion(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":cohesion"] = { + type = "cohesion", fig = fig, params = params or {} + } + macRedrawAll() +end + +function gClearCohesion(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":cohesion"] = nil + macRedrawAll() +end + +function gToggleCohesion(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local key = fig.getGUID() .. ":cohesion" + if activeOverlays[key] then + gClearCohesion(params) + else + gSpawnCohesion(params) + end +end + +-- Range polling: a slow timer (5 frames = ~12 fps) that re-draws active +-- range rulers so they follow figures in real-time. Stops automatically when +-- no range overlay is active anymore - avoids the overnight memory leak. +macRangePollingActive = macRangePollingActive or false + +function macRangePoll() + local hasRange = false + for _, e in pairs(activeOverlays) do + if e.type == "range" then hasRange = true; break end + end + if hasRange then + macRedrawAll() + Wait.frames(macRangePoll, 5) + else + macRangePollingActive = false + end +end + +function gSpawnRange(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":range"] = { + type = "range", fig = fig, params = params or {} + } + macRedrawAll() + if not macRangePollingActive then + macRangePollingActive = true + Wait.frames(macRangePoll, 5) + end +end + +function gClearRange(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":range"] = nil + macRedrawAll() +end + +function gToggleRange(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local key = fig.getGUID() .. ":range" + if activeOverlays[key] then + gClearRange(params) + else + gSpawnRange(params) + end +end + +function gSpawnDeployment(params) + -- params: { cell = "r" | "b" | ..., pos = {x, y, z} } + if not params or not params.cell or not params.pos then return end + local key = "deploy:" .. params.cell .. ":" + .. tostring(params.pos[1]) .. ":" .. tostring(params.pos[3]) + activeOverlays[key] = {type = "deployment", fig = nil, params = params} + macRedrawAll() +end + +function gClearAllDeployment() + for k, e in pairs(activeOverlays) do + if e.type == "deployment" then activeOverlays[k] = nil end + end + macRedrawAll() +end + +function gSpawnMaxMove(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + -- Capture the spawn-time position + yaw. MaxMove is anchored to where + -- the move STARTED - it must not follow the fig as it slides toward + -- the destination. The yaw matters for oblong stadium overlays so the + -- white base ring keeps its orientation while the fig pivots. + local p = (params and params.params) or params or {} + p.anchorPos = fig.getPosition() + p.anchorRot = fig.getRotation().y + activeOverlays[fig.getGUID() .. ":maxmove"] = { + type = "maxmove", fig = fig, params = p + } + macRedrawAll() +end + +function gClearMaxMove(params) + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + activeOverlays[fig.getGUID() .. ":maxmove"] = nil + macRedrawAll() +end + +function onObjectPickUp(player_color, obj) + if not obj or not obj.getGUID then return end + local guid = obj.getGUID() + local prefix = guid .. ":" + local plen = #prefix + local changed = false + for key, entry in pairs(activeOverlays) do + if key:sub(1, plen) == prefix then + -- Cohesion: static, hide during pickup, restore on drop. + -- Range: keep visible; the polling timer redraws each tick so + -- the ruler follows the figure in real-time during the drag. + if entry.type == "cohesion" then + hiddenWhilePickedUp[key] = entry + activeOverlays[key] = nil + changed = true + end + end + end + if changed then macRedrawAll() end +end + +function onObjectDrop(player_color, obj) + if not obj or not obj.getGUID then return end + local guid = obj.getGUID() + local prefix = guid .. ":" + local plen = #prefix + + -- Defer the overlay restore until the fig has come to rest. Without this, + -- a flick-drop with residual inertia leaves the overlay anchored at the + -- release position while the fig continues to slide across the table. + local function tryRestore() + local fig = getObjectFromGUID(guid) + if not fig then return end -- destroyed during slide + if fig.held_by_color then return end -- picked up again, hold off + local v = fig.getVelocity and fig.getVelocity() + if v then + local speed = math.sqrt((v.x or 0)^2 + (v.y or 0)^2 + (v.z or 0)^2) + if speed > 0.05 then + Wait.frames(tryRestore, 3) + return + end + end + local changed = false + for key, entry in pairs(hiddenWhilePickedUp) do + if key:sub(1, plen) == prefix then + activeOverlays[key] = entry + hiddenWhilePickedUp[key] = nil + changed = true + end + end + if changed then macRedrawAll() end + end + tryRestore() +end + +function onObjectDestroy(obj) + if not obj or not obj.getGUID then return end + local guid = obj.getGUID() + local prefix = guid .. ":" + local plen = #prefix + local changed = false + for key in pairs(activeOverlays) do + if key:sub(1, plen) == prefix then + activeOverlays[key] = nil + changed = true + end + end + for key in pairs(hiddenWhilePickedUp) do + if key:sub(1, plen) == prefix then + hiddenWhilePickedUp[key] = nil + end + end + if changed then macRedrawAll() end +end + +-- Texture preload is handled by macRedrawAll which always includes all PNG +-- URLs as invisible decals far below the table. No separate preload needed. + +-- ============================================ +-- PER-SEAT MODE TOGGLE (Cohesion + Range) +-- Each seated player picks their renderer: "mac" (this patch) or +-- "windows" (original bundle Projector). When a player triggers an overlay, +-- the router checks THEIR mode and uses that renderer. The rendered overlay +-- is visible to everyone (TTS engine limitation). +-- ============================================ + +playerOverlayMode = playerOverlayMode or {} -- color -> "mac" | "windows" +-- Default = "windows" (majority of TTS users). Mac users toggle their seat +-- via the floating panel. Same for deployment (table-wide setting). +deploymentMode = deploymentMode or "windows" + +function gGetMode(params) + return playerOverlayMode[params.color] or "windows" +end + +function gGetDeploymentMode() + return deploymentMode +end + +function gToggleDeploymentMode() + deploymentMode = (deploymentMode == "mac") and "windows" or "mac" + -- Wipe any active deployment zones so the next setup uses the new mode. + gClearAllDeployment() + macDeferRefresh() +end + +-- Replicates the original showRangeOnHoveredModel toggle, calling the +-- aliased Global originals so our Mac overrides don't recapture the path. +function gWindowsRangeToggle(fig) + if not fig then return end + if rangeRuler ~= nil then + pcall(clearRangeRulersOriginalGlobal) + if selectedUnitObj == fig then + selectedUnitObj = nil + return + end + end + if fig.interactable then + pcall(function() spawnRangeRulerOriginalGlobal(fig) end) + selectedUnitObj = fig + end +end + +-- Replicates the original showCohesionOnHoveredModel toggle. Uses Global +-- aliased originals + Global cohesionRuler state (set by the original). +function gWindowsCohesionToggle(fig) + if not fig then return end + if fig.interactable and selectedUnitObj == fig and cohesionRuler ~= nil then + pcall(clearCohesionRulerOriginalGlobal) + selectedUnitObj = nil + return + end + pcall(clearCohesionRulerOriginalGlobal) + pcall(function() spawnCohesionRulerOriginalGlobal(fig) end) + selectedUnitObj = fig +end + +function gCohesionTrigger(params) + if not params or not params.figGUID then return end + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local mode = playerOverlayMode[params.playerColor] or "windows" + if mode == "windows" then + gWindowsCohesionToggle(fig) + else + gToggleCohesion({figGUID = params.figGUID}) + end +end + +function gRangeTrigger(params) + if not params or not params.figGUID then return end + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local mode = playerOverlayMode[params.playerColor] or "windows" + if mode == "windows" then + gWindowsRangeToggle(fig) + else + gToggleRange({ + figGUID = params.figGUID, + forceFigMode = params.forceFigMode, + rangeKey = params.rangeKey, + }) + end +end + +-- UI: floating panel with one toggle button per seated player +function macModeClick(player, _, id) + local color = id:sub(9) -- "macmode_Red" -> "Red" + if player.color ~= color then + broadcastToColor("Only the player at this seat can toggle their mode.", + player.color, {1, 0.5, 0.5}) + return + end + local cur = playerOverlayMode[color] or "windows" + playerOverlayMode[color] = (cur == "mac") and "windows" or "mac" + -- Wipe Mac-side overlays so stale visuals don't linger after the toggle. + -- We don't track per-color ownership, so this clears Cohesion+Range for + -- everyone - acceptable since each player can re-trigger their hotkey. + -- Windows-side overlays (fig-scoped cohesionRuler/rangeRuler) are + -- cleared by iterating objects with active state. + for k, e in pairs(activeOverlays) do + if e.type == "cohesion" or e.type == "range" then + activeOverlays[k] = nil + end + end + macRedrawAll() + for _, obj in ipairs(getAllObjects()) do + if obj.getVar and obj.getVar("cohesionRuler") then + pcall(function() obj.call("clearCohesionRulerOriginal", obj) end) + end + if obj.getVar and obj.getVar("rangeRuler") then + pcall(function() obj.call("clearRangeRulersOriginal", obj) end) + end + end + macDeferRefresh() +end + +local function macFindNodeById(tree, id) + for _, node in ipairs(tree) do + if node.attributes and node.attributes.id == id then return node, tree end + if node.children then + local found, parent = macFindNodeById(node.children, id) + if found then return found, parent end + end + end + return nil, nil +end + +-- Tracks the panel's desired active state across refreshes. Persists so a +-- click on the close X before the initial refresh is honored. Hidden at first +-- load (default = false); the floating "Mac Patch" menu button reveals it. +macModePanelActive = (macModePanelActive == nil) and false or macModePanelActive + +function macModeToggleVisibility() + macModePanelActive = not macModePanelActive + -- Defer the XML rebuild: TTS occasionally throws a UTF-8 byte-buffer + -- encoding error if UI.setXmlTable runs synchronously inside a UI + -- click handler. Deferring one frame + pcall has been reliable. + Wait.frames(function() + pcall(function() + local tree = UI.getXmlTable() or {} + local panel = macFindNodeById(tree, "macModePanel") + if panel then + panel.attributes.active = macModePanelActive and "true" or "false" + UI.setXmlTable(tree) + else + macRefreshModeUI() + end + end) + end, 1) +end + +function macRefreshModeUI() + local seated = Player.getPlayers() + local rows = {{ + tag = "Panel", + attributes = { + color = "transparent", + preferredHeight = "24", + }, + children = { + { + tag = "Text", + attributes = { + text = "Mac TTS U6 Patch", + fontSize = "16", + color = "white", + alignment = "MiddleLeft", + rectAlignment = "MiddleLeft", + }, + }, + { + tag = "Button", + attributes = { + id = "macModePanelClose", + text = "X", + onClick = "macModeToggleVisibility", + color = "#3a1e1e", + textColor = "white", + fontSize = "12", + width = "24", + height = "20", + rectAlignment = "MiddleRight", + }, + }, + }, + }, { + tag = "Text", + attributes = { + text = "Cohesion & Range: pick your renderer", + fontSize = "12", + color = "#bbbbbb", + alignment = "MiddleCenter", + }, + }} + if #seated == 0 then + table.insert(rows, { + tag = "Text", + attributes = { + text = "(no seated players)", + fontSize = "12", + color = "#888888", + alignment = "MiddleCenter", + }, + }) + end + for _, p in ipairs(seated) do + local mode = playerOverlayMode[p.color] or "windows" + local label = p.color .. " - " .. + (mode == "mac" and "MAC FALLBACK" or "WINDOWS ORIGINAL") + local bg = (mode == "mac") and "#1e7a3a" or "#7a3a1e" + table.insert(rows, { + tag = "Button", + attributes = { + id = "macmode_" .. p.color, + text = label, + onClick = "macModeClick", + color = bg, + fontSize = "13", + textColor = "white", + preferredHeight = "28", + }, + }) + end + -- Deployment is table-wide (everyone sees the same zones); use a single + -- global toggle rather than per-seat. + table.insert(rows, { + tag = "Text", + attributes = { + text = "Deployment (table-wide)", + fontSize = "12", + color = "#bbbbbb", + alignment = "MiddleCenter", + }, + }) + table.insert(rows, { + tag = "Button", + attributes = { + id = "macmode_deployment", + text = (deploymentMode == "mac") and "DEPLOYMENT: MAC FALLBACK" + or "DEPLOYMENT: WINDOWS ORIGINAL", + onClick = "macDeploymentClick", + color = (deploymentMode == "mac") and "#1e3a7a" or "#7a3a1e", + fontSize = "13", + textColor = "white", + preferredHeight = "28", + }, + }) + -- Build my panel (will be merged into the existing UI tree below so we + -- don't clobber legionFloatingMenu / Welcome / Chess Clocks etc.). + local panel = { + tag = "Panel", + attributes = { + id = "macModePanel", + active = macModePanelActive and "true" or "false", + rectAlignment = "MiddleRight", + offsetXY = "-10 80", + width = "260", + height = "320", + color = "rgba(0.06,0.06,0.06,0.9)", + padding = "8 8 8 8", + outlineSize = "1 1", + outline = "#303030", + }, + children = {{ + tag = "VerticalLayout", + attributes = { + spacing = "4", + childForceExpandHeight = "false", + childForceExpandWidth = "true", + }, + children = rows, + }}, + } + + local tree = UI.getXmlTable() or {} + -- Remove any stale macModePanel before reinserting the fresh one. + for i = #tree, 1, -1 do + if tree[i].attributes and tree[i].attributes.id == "macModePanel" then + table.remove(tree, i) + end + end + table.insert(tree, panel) + + -- Inject a "Mac Patch" button into the bottom-right legionFloatingMenu + -- (replaces the first interactable=false placeholder button). + local menu = macFindNodeById(tree, "legionFloatingMenu") + if menu and menu.children then + local hasMine = false + for _, c in ipairs(menu.children) do + if c.attributes and c.attributes.id == "macModeMenuButton" then + hasMine = true; break + end + end + if not hasMine then + for i, c in ipairs(menu.children) do + if c.attributes and c.attributes.interactable == "false" then + menu.children[i] = { + tag = "Button", + attributes = { + id = "macModeMenuButton", + fontSize = "10", + onClick = "macModeToggleVisibility", + tooltip = "Toggle Mac TTS U6 Patch panel", + }, + value = "Mac Patch", + } + break + end + end + end + end + + UI.setXmlTable(tree) +end + +function macDeploymentClick(_, _, _) + gToggleDeploymentMode() +end + +-- Defer one frame: TTS throws a UTF-8 byte-buffer encoding error if we +-- rebuild the UI XML synchronously inside a click handler / seat change. +-- pcall guards against any residual race. Promoted to a global function +-- so macModeClick / macModeToggleVisibility / gToggleDeploymentMode (all +-- defined before this point) can call it via name lookup at call time. +function macDeferRefresh() + Wait.frames(function() pcall(macRefreshModeUI) end, 1) +end +function onPlayerChangeColor(_) macDeferRefresh() end +function onPlayerConnect(_) macDeferRefresh() end +function onPlayerDisconnect(_) macDeferRefresh() end + +-- Initial UI build, deferred + pcalled like the rest. +Wait.time(function() pcall(macRefreshModeUI) end, 2) + +-- Override hotkey init functions to capture playerColor and route through +-- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys +-- here SHADOWS the originals - when the original onLoad runs init*(), our +-- versions register the hotkey instead. + +function initCohesionHotkeys() + addHotkey("Show Cohesion On Hovered Model", + function(playerColor, hoverObject, _) + if not hoverObject or not hoverObject.interactable then return end + gCohesionTrigger({ + figGUID = hoverObject.getGUID(), + playerColor = playerColor, + }) + end) +end + +function initRangebandHotkeys() + addHotkey("Show Range On Hovered Model", + function(playerColor, hoverObject, _) + if not hoverObject or not hoverObject.interactable then return end + gRangeTrigger({ + figGUID = hoverObject.getGUID(), + playerColor = playerColor, + forceFigMode = true, -- hotkey always shows 6-band fig overlay + }) + end) +end + +-- The vanilla showRangeOnHoveredModel calls spawnRangeRuler(fig) which spawns +-- the Range AssetBundle directly. On Mac, the "Oblong Range" shader used by +-- long/snail bundles isn't supported (magenta). Override the global function +-- to route through gToggleRange so our stadium overlay renders instead. +-- Vanilla Range bundle still works for round bases via gWindowsRangeToggle +-- when the player explicitly chose Windows mode. +function showRangeOnHoveredModel(hoverObject) + if not hoverObject or not hoverObject.interactable then return end + gToggleRange({figGUID = hoverObject.getGUID(), forceFigMode = true}) +end + +-- Note: we deliberately do NOT override the global spawnRangeRuler / +-- clearRangeRulers / showRangeOnHoveredModel chain at the Lua scope where +-- the vanilla mod defines it. Overriding spawnRangeRuler from a token +-- script reproducibly SIGSEGV'd TTS on Mac in May 2026 (Mono GC stack +-- corruption). Mac routing happens via the g* functions called from our +-- own hotkey/button wrappers; the originals stay reachable for +-- Windows-mode players via spawnRangeRulerOriginalGlobal aliases above. + +-- END MAC COHESION FALLBACK +""" + + +# ------------------------------------------------------------ +# 2) Wrapper appended AFTER the original Cohesion include (per-seat router) +# ------------------------------------------------------------ +# Strategy: keep the ORIGINAL Cohesion/RangeRulers expansion intact, then +# append wrappers that alias originals + route through the Global router. +# This preserves the original Custom_AssetBundle Projector spawn for +# Windows-mode players (called back as spawnCohesionRulerOriginal). + +COHESION_WRAPPER = r""" +-- MAC PATCH per-seat router (Cohesion) +-- Aliases the original spawn so Windows-mode players still get their bundle. +spawnCohesionRulerOriginal = spawnCohesionRuler +clearCohesionRulerOriginal = clearCohesionRuler + +function spawnCohesionRuler(cohesionSourceObject) + if not cohesionSourceObject then return end + Global.call("gCohesionTrigger", { + figGUID = cohesionSourceObject.getGUID(), + playerColor = nil, + }) +end + +function clearCohesionRuler() + if self and self.getGUID and self.getGUID() ~= "-1" then + Global.call("gClearCohesion", { figGUID = self.getGUID() }) + end + if cohesionRuler ~= nil then + pcall(clearCohesionRulerOriginal) + end +end + +-- Vanilla Order Token's clearTemplates() calls clearCohesionRulers (plural), +-- but the save was built from an older mod version that ships only the +-- singular clearCohesionRuler. Provide the plural as a delegating stub so +-- standby/clearTemplates/etc. don't nil-error on click. +function clearCohesionRulers() + if selectedUnitObj then + pcall(function() selectedUnitObj.setVar("moveState", false) end) + pcall(function() selectedUnitObj.call("clearCohesionRuler") end) + end +end + +-- Same root cause as clearCohesionRulers: newer Order Token code paths in +-- the live save (e.g. moveUnit -> stopAttack) call helpers the older bundled +-- script doesn't ship. Stub them so MOVE/AIM/etc don't nil-error. The bodies +-- mirror the current vanilla source but pcall every dependency so we don't +-- chain-error if anything else is also missing. +function clearAttackLine() + if attackLine then + for k, v in pairs(attackLine) do + pcall(destroyObject, v) + end + attackLine = nil + end +end + +function exitTargetingMode() + enemyHighlighted = false + attackModeOn = false + pcall(clearRangeRulers) + pcall(unhighlightEnemies) + pcall(clearAttackLine) +end + +function exitAttackMode() + enemyHighlighted = false + attackModeOn = false + pcall(clearRangeRulers) + pcall(unhighlightEnemies) +end +-- END MAC PATCH per-seat router (Cohesion) +""" + + +COHESION_BLOCK_RE = re.compile( + r"----#include !/Cohesion\r?\n.*?----#include !/Cohesion\r?\n", + re.DOTALL +) + +RANGE_BLOCK_RE = re.compile( + r"----#include !/RangeRulers\r?\n.*?----#include !/RangeRulers\r?\n", + re.DOTALL +) + +# Maximum Move spawn block in Order_Token (a57c41), inline (not an include). +# Captures from `local maxMoveBundles = getMovementLinks()` up to and +# including the two closing ends of the original `if isDeploy / if ... ~= nil` +# scaffold. The replacement is self-contained (opens 3 ifs, closes all 3), +# so re-applying yields the same canonical block - idempotent. (Earlier +# versions only matched up to setName("Maximum Move") and re-matched their +# own output, accumulating orphan else/end copies on each re-run.) +MAXMOVE_SPAWN_RE = re.compile( + r"local maxMoveBundles = getMovementLinks\(\).*?" + r"\r?\n end\r?\n end\r?\n", + re.DOTALL +) + +MAXMOVE_SPAWN_REPLACEMENT = r"""local maxMoveBundles = getMovementLinks() + local baseSizeMoveBundles = maxMoveBundles[unitData.baseSize] + local maxMoveTemplateBundleToSpawn = baseSizeMoveBundles and baseSizeMoveBundles[unitData.selectedSpeed] + + -- MAC PATCH: permissive condition (covers nil + false) so the overlay + -- also fires when changeSpeed2/3 calls moveUnit() with no isDeploy arg. + if isDeploy ~= true then + if maxMoveTemplateBundleToSpawn ~= nil then + local _macMode = Global.call("gGetMode", {color = macActivePlayerForMove}) + if _macMode == "windows" then + -- WINDOWS ORIGINAL: spawn Custom_AssetBundle Projector + maxMoveTemplate = spawnObject({ + type = "Custom_AssetBundle", + position = {basePos.x, basePos.y + 20, basePos.z}, + rotation = {0, basePos.y, 0}, + scale = {0,0,0} + }) + maxMoveTemplate.setCustomObject({ + type = 0, + assetbundle = maxMoveTemplateBundleToSpawn + }) + maxMoveTemplate.setLock(true) + maxMoveTemplate.use_gravity = false + maxMoveTemplate.setName("Maximum Move") + else + -- MAC FALLBACK: route through the Global Overlays manager + Global.call("gSpawnMaxMove", { + figGUID = selectedUnitObj.getGUID(), + baseSize = unitData.baseSize, + speed = unitData.selectedSpeed, + }) + maxMoveTemplate = nil + end + end + end +""" + +# clearMovementTemplates function: needs to also clear our managed maxmove +# overlay. We replace the whole function body. +# Match up to the first un-indented `end` (= the function's closing end), so +# the regex is idempotent - it matches both the vanilla body and any +# previously patched body shape (which has extra lines past the inner `end`). +CLEAR_MOVEMENT_RE = re.compile( + r"function clearMovementTemplates\(\).*?\r?\nend\b", + re.DOTALL +) + +CLEAR_MOVEMENT_REPLACEMENT = r"""function clearMovementTemplates() + if templateA ~= nil then + destroyObject(templateA) + end + if templateB ~= nil then + destroyObject(templateB) + end + -- MAC PATCH per-seat: destroy bundle Object if present (Windows-mode + -- spawn) AND clear the manager entry (Mac fallback). Either may be set. + if maxMoveTemplate ~= nil then + pcall(destroyObject, maxMoveTemplate) + end + -- Clear only THIS token's MaxMove (keyed by the attached unit), not + -- everyone's - gClearAllMaxMove was killing other tokens' rings when + -- two players had MOVE open at the same time. + if selectedUnitObj then + Global.call("gClearMaxMove", { figGUID = selectedUnitObj.getGUID() }) + end + maxMoveTemplate = nil +end""" + +# Deployment Boundary: replace the Custom_AssetBundle Projector spawn inside +# spawnBoundaryCell (SETUP_CONTROLLER 1cb552) with a Global.call to the manager. +# Idempotent: matches both the vanilla form AND a previously-injected patched +# form (between the MAC PATCH dual-path marker and the closing ` end`). The +# patched alternative is listed first so re-runs prefer it. +DEPLOYMENT_SPAWN_RE = re.compile( + r"(?:" + r"-- MAC PATCH dual-path:.*?\r?\n end" + r"|" + r"local projector = spawnObject\(\{\r?\n" + r" type = \"Custom_AssetBundle\",.*?" + r"projector\.setCustomObject\(\{\r?\n" + r" assetbundle = asset,\r?\n" + r" \}\)" + r")", + re.DOTALL +) + +DEPLOYMENT_SPAWN_REPLACEMENT = r"""-- MAC PATCH dual-path: branch on the table-wide deploymentMode toggle. + local _macDeployMode = Global.call("gGetDeploymentMode") + local projector = nil + if _macDeployMode == "windows" then + projector = spawnObject({ + type = "Custom_AssetBundle", + position = pos, + scale = {0, 0, 0}, + rotation = {0, deployRotations[cell], 0} + }) + projector.setName("Deployment Boundary") + projector.setLock(true) + projector.setCustomObject({ + assetbundle = asset, + }) + else + Global.call("gSpawnDeployment", { cell = cell, pos = pos }) + end""" + +# clearDeploymentBoundary: also clear manager entries. +CLEAR_DEPLOYMENT_RE = re.compile( + r"function clearDeploymentBoundary\(\)\r?\n" + r" local battlefieldObjs = battlefieldZone\.getObjects\(\).*?\r?\n" + r" end\r?\nend", + re.DOTALL +) + +CLEAR_DEPLOYMENT_REPLACEMENT = r"""function clearDeploymentBoundary() + -- MAC PATCH: clear Global Overlays manager entries first. + Global.call("gClearAllDeployment", {}) + local battlefieldObjs = battlefieldZone.getObjects() + for _, obj in pairs(battlefieldObjs) do + if obj.getName() == "Deployment Boundary" then + destroyObject(obj) + end + end +end""" + +# Silhouette URL swap to our Unity-6-compiled bundle. +# +# The upstream BucketheadBits_Silhouette bundle uses Allen White's custom +# Silhouette shader compiled under Unity 2019.4. TTS U6 on Mac fails to +# load that shader at runtime (console: "Shader didn't load correctly for +# AssetBundle material BucketheadBits_Silhouette. Assigning Standard +# shader.") and falls back to Standard, which renders the cylinder opaque. +# +# Our fork-hosted bundle uses the SAME prefab + material + shader source +# (Assets/Sihl/), only the compile target differs (Unity 6 instead of +# Unity 2019.4). Path A from Dicewrench's analysis. +# +# We swap ALL silhouette URLs (default cylinder, snail, long) to the same +# fork URL. Snail/long visual fidelity is acceptable for now (cylinder +# placeholder); per-base meshes are a separate follow-up. +SILHOUETTE_BUNDLE_URL = ( + "https://raw.githubusercontent.com/ironsquadronfr-hub/tts/" + "mac-projector-fallback/mod/data/mac-fallback-assets/" + "silhouette_mac_fallback.unity3d" +) +SILHOUETTE_URL_RE = re.compile( + r'(silhouetteData\s*=\s*)"https?://[^"]*/ugc/[^"]+"' +) +SILHOUETTE_URL_REPLACEMENT = r'\1"' + SILHOUETTE_BUNDLE_URL + '"' + + +# Cleanup of debug prints and corrupted signatures from earlier sessions. +# Safe no-op on a clean save. Matches both the legacy `print("[SIL DEBUG] ...)` +# form AND the chat-visible `printToAll("[SIL] ...", {...})` form. +SIL_DEBUG_STRIP_RE = re.compile( + # Match leading horizontal whitespace only (NOT \s* which would also eat + # the newline before, mashing the function signature against the next + # statement - that was a real bug we hit earlier this session). + r'[ \t]*(?:print\("\[SIL DEBUG\][^\n]*\)|' + r'printToAll\("\[SIL\][^"]*",\s*\{[^}]*\}\))\r?\n' +) +# Repair function signatures that were mashed by the previous \s*-glob bug. +# Idempotent: target the specific mashed patterns; matches are zero on a +# clean save. +SIL_REPAIR_TOGGLE_RE = re.compile( + r'(function toggleSilhouettes\(\))( if silhouetteState then)' +) +SIL_REPAIR_SHOW_RE = re.compile( + r'(function showSilhouette\(\))( for k, guid in pairs\(miniGUIDs)' +) +SIL_REPAIR_SPAWN_RE = re.compile( + r'(function spawnSilhouette\(obj, pos, rot\))( local globals)' +) + + +# Silhouette state-desync fix (upstream bug). +# +# Upstream clearSilhouette assumes removeAttachments()[1] always returns an +# object, but the state variable `silhouetteState` is persisted across saves +# while the physical attachments are not - so a save made with silhouettes +# visible reloads with state=true but no attachments. First SIL click then +# calls clearSilhouette which dereferences nil and crashes. +# +# Two-part patch: +# (1) clearSilhouette: guard the nil case (skip the destruct if no +# attachment), so re-clicks don't crash. +# (2) onload: force silhouetteState=false on every load, since the load +# cannot restore the physical silhouettes anyway. +# +# Also fixes the "two silhouettes stacked" symptom: that happens when +# showSilhouette runs while state is desynced, spawning a second set on top +# of one whose handles have been forgotten. +CLEAR_SILHOUETTE_RE = re.compile( + r"function clearSilhouette\(\).*?silhouetteState = false\r?\nend", + re.DOTALL +) +CLEAR_SILHOUETTE_REPLACEMENT = r"""function clearSilhouette() + -- MAC PATCH: guard nil silToDestroy. silhouetteState can be persisted as + -- true across saves while the physical attachments are not, so a load + -- with state=true + no attachments would otherwise crash here. + for k, guid in pairs(miniGUIDs or {}) do + local obj = getObjectFromGUID(guid) + if obj then + local silToDestroy = obj.removeAttachments()[1] + if silToDestroy then silToDestroy.destruct() end + end + end + silhouetteState = false +end""" + +# Match the existing `function onload()` body (one-line empty or actual +# code). Idempotent: re-runs detect the marker `MAC PATCH: silhouette reset` +# and skip. +SIL_ONLOAD_RESET_LINE = ( + ' -- MAC PATCH: silhouette reset on load (attachments lost across saves)\n' + ' silhouetteState = false\n' +) +ONLOAD_OPEN_RE = re.compile(r"function onload\(\)\r?\n") + + +# SIL button rename (root cause: TTS engine intercepts the click_function +# name "toggleSilhouettes" silently in Mac mode; LCK/R buttons unaffected +# because their click_function names differ. Confirmed empirically 17 mai +# 2026: renaming to "macToggleSil" + forwarder unblocks Mac mode entirely). +# +# We swap the click_function name on the SIL button definition AND inject a +# forwarder function that simply calls toggleSilhouettes(). The original +# toggleSilhouettes is untouched, so Windows mode behavior is identical. +# Idempotent: re-runs detect both markers and skip. +SIL_BUTTON_CLICKFN_OLD = 'click_function = "toggleSilhouettes"' +SIL_BUTTON_CLICKFN_NEW = 'click_function = "macToggleSil"' +SIL_FORWARDER = ''' +-- MAC PATCH: rebuilt SIL button click handler. The original click_function +-- name "toggleSilhouettes" is silently intercepted by TTS in Mac mode (the +-- SIL button plays its click sound/animation but the function is never +-- called). Renaming the click_function on the button definition + adding +-- this forwarder restores the routing. +function macToggleSil() + toggleSilhouettes() +end +-- END MAC PATCH SIL forwarder +''' +SIL_FORWARDER_MARKER = 'function macToggleSil()' + + +RANGE_WRAPPER = r""" +-- MAC PATCH per-seat router (Range) - ALIAS ONLY. +-- +-- HISTORY (do not re-introduce): we previously overrode spawnRangeRuler / +-- clearRangeRulers / clearRangeRuler on the ~20 token scripts. That +-- reproducibly SIGSEGV'd TTS Mac (Mono GC_clear_stack_inner) when a fig +-- hotkey spawned the vanilla Range bundle. Overrides REMOVED - per-seat +-- Range routing for fig hotkey lives in Global (gRangeTrigger via our +-- initRangebandHotkeys shadow). Tokens' R buttons keep vanilla behavior +-- (magenta on Mac, native on Windows) until a per-token Mac fallback +-- ships - see TOKEN_BUTTON_WRAPPER for the per-token override path. +-- +-- The single alias below is REQUIRED: Global macModeClick calls +-- obj.call("clearRangeRulersOriginal", obj) on every object holding a +-- rangeRuler state, to wipe the vanilla ruler when toggling modes. +clearRangeRulersOriginal = clearRangeRulers +-- END MAC PATCH per-seat router (Range) +""" + +# Order_Token-only overrides: intercept the UI button click handlers +# (toggleCohesionRuler, targetingMode, changeSpeedN, moveX) to capture +# playerColor passed by TTS for per-seat routing. +ORDER_TOKEN_BUTTON_OVERRIDES = r""" +-- MAC PATCH per-seat router (Order_Token button click overrides) +function toggleCohesionRuler(_, playerColor) + if not rulerOn then + Global.call("gCohesionTrigger", { + figGUID = selectedUnitObj.getGUID(), + playerColor = playerColor, + }) + rulerOn = true + else + selectedUnitObj.call("clearCohesionRuler") + rulerOn = false + end +end + +function targetingMode(_, playerColor) + if not enemyHighlighted then + exitAttackMode() + highlightEnemies() + Global.call("gRangeTrigger", { + figGUID = selectedUnitObj.getGUID(), + playerColor = playerColor, + }) + enemyHighlighted = true + resetRangeButtons() + else + exitTargetingMode() + end +end + +-- Capture playerColor for Maximum Move per-seat routing. moveUnit() doesn't +-- receive the click color, so each entry-point button stashes it in a +-- script-global var that the spawn block reads. Defaults to nil -> Mac mode. + +-- initMove / initDeploy: redefine the body INLINE rather than wrap, because +-- wrapping (capturing the original via a local + calling it) reproducibly +-- broke every Order Token button click in testing (root cause unknown; +-- suspected Lua chunk-level upvalue interaction with TTS engine click +-- dispatch). The inline body mirrors the vanilla logic + the playerColor +-- capture so the MAXMOVE_SPAWN block can route to Mac/Windows per seat. +function initMove(obj, playerColor) + if not selectedUnitObj then return end + macActivePlayerForMove = playerColor + initPos = selectedUnitObj.getPosition() + initRot = selectedUnitObj.getRotation() + selectedUnitObj.call("setStartPos") + moveUnit(false) +end +function initDeploy(obj, playerColor) + if not selectedUnitObj then return end + macActivePlayerForMove = playerColor + initPos = selectedUnitObj.getPosition() + initRot = selectedUnitObj.getRotation() + selectedUnitObj.call("setStartPos") + moveUnit(true) +end + +-- INLINE the vanilla bodies (mirror Order_Token.a57c41.lua) rather than +-- wrap. The List Builder copies this block to Command Token Custom_Models +-- which DO NOT carry the vanilla Order_Token function bodies - so a +-- wrap-then-call pattern hits `_macOrigChangeSpeed1 = nil` and crashes the +-- moment a Speed/Move button is pressed. Inlining keeps the behavior +-- identical on both Order Tokens (vanilla body re-defined) and Command +-- Tokens (helper fns setTemplateVariables/clearTemplates/moveUnit exist +-- on both because they're part of the include set the List Builder emits). +function changeSpeed1(_, playerColor) + macActivePlayerForMove = playerColor + unitData.selectedSpeed = 1 + setTemplateVariables() + clearTemplates() + moveUnit() +end +function changeSpeed2(_, playerColor) + macActivePlayerForMove = playerColor + unitData.selectedSpeed = 2 + setTemplateVariables() + clearTemplates() + moveUnit() +end +function changeSpeed3(_, playerColor) + macActivePlayerForMove = playerColor + unitData.selectedSpeed = 3 + setTemplateVariables() + clearTemplates() + moveUnit() +end +function moveForward(_, playerColor) + macActivePlayerForMove = playerColor + self.editButton({ + index = 11, click_function = "moveBackwards", + label = "B", tooltip = "Move Backwards" + }) + moveDirection = "forward" + moveUnit() +end +function moveBackwards(_, playerColor) + macActivePlayerForMove = playerColor + self.editButton({ + index = 11, click_function = "moveForward", + label = "F", tooltip = "Move Forward" + }) + moveDirection = "backwards" + moveUnit() +end +function moveLeft(_, playerColor) + macActivePlayerForMove = playerColor + moveDirection = "left" + moveUnit() +end +function moveRight(_, playerColor) + macActivePlayerForMove = playerColor + moveDirection = "right" + moveUnit() +end +-- END MAC PATCH per-seat router (Order_Token button click overrides) +""" + +# Wrapper appended to objects that have `function toggleRangeRuler()` - +# the R button on tokens (POI, Smoke, Bomb Cart, Objective). Per-seat +# routing: Mac players get our vector bands (TOKEN-spec via rangeKey), +# Windows players get the vanilla token bundle (spawnTokenRangeRuler). +# The vanilla function is aliased BEFORE override so Windows-mode clicks +# can fall through to it. +TOKEN_BUTTON_WRAPPER = r""" +-- MAC PATCH per-seat router (token R button) +-- Aliases the vanilla toggleRangeRuler before overriding so Windows-mode +-- players keep spawning the token-specific Range bundle (POI / Smoke / +-- bomb_cart / objective). Mac-mode players get our vector overlay via +-- gRangeTrigger which dispatches to macBuilders.range with the token's +-- rangeKey var. +local _macToggleRangeRulerOriginal = toggleRangeRuler +function toggleRangeRuler(_, playerColor) + local mode = Global.call("gGetMode", { color = playerColor }) + if mode == "windows" then + if _macToggleRangeRulerOriginal then + return _macToggleRangeRulerOriginal() + end + return + end + if rangeOn then + Global.call("gClearRange", { figGUID = self.getGUID() }) + rangeOn = false + else + Global.call("gRangeTrigger", { + figGUID = self.getGUID(), + playerColor = playerColor, + }) + rangeOn = true + end +end +-- END MAC PATCH per-seat router (token R button) +""" + +TOKEN_BUTTON_WRAPPER_RE = re.compile( + r"\r?\n-- MAC PATCH per-seat router \(token R button\).*?" + r"-- END MAC PATCH per-seat router \(token R button\)\r?\n", + re.DOTALL +) + +# List Builder _loadArmyFromJson nil-guard. Stock SWL never nil-checks the +# result of JSON.decode(text) inside importFromText, so any blur of the +# import InputField with empty/invalid text crashes _loadArmyFromJson at +# `data.armyFaction`. The guard is upstream-able. Idempotent via the +# negative lookahead on the marker comment. +LIST_BUILDER_GUARD_RE = re.compile( + r"(function _loadArmyFromJson\(data\)\r?\n)(?! -- MAC PATCH: guard )" +) +LIST_BUILDER_GUARD_REPLACEMENT = ( + r"\1" + " -- MAC PATCH: guard nil/invalid JSON from onEndEdit blur (upstream bug)\r\n" + " if not data then return end\r\n" +) + +# Idempotent marker - used to detect and remove an existing Mac patch in the +# Global LuaScript before applying a fresh one. +GLOBAL_PATCH_MARKER_RE = re.compile( + r"\r?\n-- ={5,}\r?\n-- MAC COHESION FALLBACK.*?-- END MAC COHESION FALLBACK\r?\n", + re.DOTALL +) + +# Idempotent markers for the per-seat wrappers added to object scripts. +# Strip these before re-applying to avoid double-wrapping. +COHESION_WRAPPER_RE = re.compile( + r"\r?\n-- MAC PATCH per-seat router \(Cohesion\).*?" + r"-- END MAC PATCH per-seat router \(Cohesion\)\r?\n", + re.DOTALL +) +RANGE_WRAPPER_RE = re.compile( + r"\r?\n-- MAC PATCH per-seat router \(Range\).*?" + r"-- END MAC PATCH per-seat router \(Range\)\r?\n", + re.DOTALL +) +ORDER_TOKEN_OVERRIDES_RE = re.compile( + r"\r?\n-- MAC PATCH per-seat router \(Order_Token button click overrides\).*?" + r"-- END MAC PATCH per-seat router \(Order_Token button click overrides\)\r?\n", + re.DOTALL +) + + +TARGET_GUIDS = {"99f1c8", "a57c41"} # Objects with the Cohesion block to replace + + +def collect_patched_scripts(data: dict) -> list: + """Return script_states for the TTS External Editor API (Save & Play). + + Includes the Global script and any object script we patched (matching + TARGET_GUIDS). Format: list of dicts with name, guid, script. + """ + states = [ + {"name": "Global", "guid": "-1", "script": data.get("LuaScript", "")} + ] + + def walk(o): + if isinstance(o, dict): + guid = o.get("GUID", "").lower() + if guid in TARGET_GUIDS and "LuaScript" in o: + states.append({ + "name": o.get("Name", guid), + "guid": guid, + "script": o["LuaScript"], + }) + for v in o.values(): + walk(v) + elif isinstance(o, list): + for x in o: + walk(x) + + walk(data) + return states + + +def send_to_tts(script_states: list) -> bool: + """Send a Save & Play (messageID 1) to TTS via the External Editor API. + + Returns True on success, False if TTS isn't listening (likely the API + is disabled in TTS Configuration). + """ + msg = {"messageID": 1, "scriptStates": script_states} + payload = json.dumps(msg).encode("utf-8") + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(3) + s.connect(("127.0.0.1", 39999)) + s.sendall(payload) + s.close() + return True + except (ConnectionRefusedError, socket.timeout, OSError) as e: + print(f" WARN: could not connect to TTS on port 39999 ({e}).") + print(f" -> Enable Configuration -> Game Tweaks -> 'External Editor API' in TTS.") + return False + + +def patch_object_scripts(data: dict) -> tuple: + """Walk the JSON tree, replace Cohesion and Range blocks in object scripts. + + Cohesion block: only on TARGET_GUIDS (Unit_Leader, Order_Token). + Range block: on every object containing the !/RangeRulers include + (Order_Token, Tokens, POI, bomb_cart, etc. - ~20 objects). + + Returns (n_cohesion_patched, n_range_patched, n_dep_patched). + """ + n_coh, n_rng, n_dep = 0, 0, 0 + + def walk(o): + nonlocal n_coh, n_rng, n_dep + if isinstance(o, dict): + guid = o.get("GUID", "").lower() + if "LuaScript" in o: + ls = o["LuaScript"] + changed = False + + # Strip any previous per-seat wrappers so re-runs are idempotent. + for rx in (COHESION_WRAPPER_RE, RANGE_WRAPPER_RE, + ORDER_TOKEN_OVERRIDES_RE, TOKEN_BUTTON_WRAPPER_RE): + new_ls, n = rx.subn("", ls) + if n > 0: + ls = new_ls + changed = True + + # Cleanup: strip stale debug prints + repair any signatures + # mashed by an earlier (overzealous) strip pass. Both are + # no-ops on a clean save. + new_ls, n = SIL_DEBUG_STRIP_RE.subn("", ls) + if n > 0: + ls = new_ls + changed = True + for rx in (SIL_REPAIR_TOGGLE_RE, SIL_REPAIR_SHOW_RE, SIL_REPAIR_SPAWN_RE): + new_ls, n = rx.subn(r'\1\n\2', ls) + if n > 0: + ls = new_ls + changed = True + + # (Diagnostic SIL prints removed 17 mai 2026 once mute Mac + # mode confirmed. SIL_DEBUG_STRIP_RE above stays as a cleanup + # pass for older saves that still carry the prints.) + + # Silhouette URL swap to our Unity-6-compiled bundle. + # Applies to any object that defines spawnSilhouette + # (Unit_Leader 99f1c8, Bomb Cart b497e1, POI Token 761483). + # Idempotent: the regex only matches Steam UGC URLs (/ugc/), + # not our fork URL, so re-runs are no-ops. + new_ls, n = SILHOUETTE_URL_RE.subn(SILHOUETTE_URL_REPLACEMENT, ls) + if n > 0: + ls = new_ls + changed = True + + # SIL button rename + forwarder. Bypasses the TTS engine + # bug where the click_function name "toggleSilhouettes" is + # silently muted in Mac mode. Idempotent: replace() is no-op + # once swap is done, marker check prevents double-injection. + if SIL_BUTTON_CLICKFN_OLD in ls: + ls = ls.replace(SIL_BUTTON_CLICKFN_OLD, SIL_BUTTON_CLICKFN_NEW) + changed = True + if SIL_BUTTON_CLICKFN_NEW in ls and SIL_FORWARDER_MARKER not in ls: + ls = ls.rstrip() + '\n' + SIL_FORWARDER + changed = True + + # Cohesion: KEEP the original include block, append per-seat + # wrapper after it. Only Unit_Leader / Order_Token have it. + if guid in TARGET_GUIDS: + def coh_repl(m): + return m.group(0) + COHESION_WRAPPER + new_ls, n = COHESION_BLOCK_RE.subn(coh_repl, ls, count=1) + if n > 0: + ls = new_ls + n_coh += 1 + changed = True + + # Silhouette desync fix (any object with clearSilhouette). + # Guard the nil-attachment case and reset silhouetteState in + # onload. Idempotent: the regex matches both the vanilla + # body and our previously-patched body (same shape). + if "function clearSilhouette()" in ls: + new_ls, n = CLEAR_SILHOUETTE_RE.subn(CLEAR_SILHOUETTE_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + changed = True + # Inject the state reset at the top of onload. + if "MAC PATCH: silhouette reset on load" not in ls: + new_ls, n = ONLOAD_OPEN_RE.subn( + "function onload()\n" + SIL_ONLOAD_RESET_LINE, + ls, count=1, + ) + if n > 0: + ls = new_ls + changed = True + + # Range: KEEP block, append wrapper. ~21 objects. + def rng_repl(m): + return m.group(0) + RANGE_WRAPPER + new_ls, n = RANGE_BLOCK_RE.subn(rng_repl, ls, count=1) + if n > 0: + ls = new_ls + n_rng += 1 + changed = True + + # List Builder import guard. Applies to BLUE/RED LIST + # BUILDER (any object that defines _loadArmyFromJson). + # Fixes a pre-existing upstream crash where blurring the + # import InputField with empty/invalid text fires + # onEndEdit -> importFromText -> JSON.decode("") -> nil -> + # data.armyFaction crash. Idempotent. + if "function _loadArmyFromJson(data)" in ls: + new_ls, n = LIST_BUILDER_GUARD_RE.subn( + LIST_BUILDER_GUARD_REPLACEMENT, ls, count=1 + ) + if n > 0: + ls = new_ls + changed = True + + # Maximum Move spawn (only in Order_Token a57c41) - Mac only + if guid == "a57c41": + new_ls, n = MAXMOVE_SPAWN_RE.subn(MAXMOVE_SPAWN_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + changed = True + new_ls, n = CLEAR_MOVEMENT_RE.subn(CLEAR_MOVEMENT_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + changed = True + # Button click overrides (per-seat capture of playerColor) + ls = ls + ORDER_TOKEN_BUTTON_OVERRIDES + changed = True + + # Token R button: append toggleRangeRuler override on any + # object that defines that function (POI, Smoke, BombCart, + # Objective, etc.). Routes the click to the Mac fallback + # Range overlay sized via TOKEN_BASE_RADIUS. (Already-applied + # case is handled by the strip loop above.) + if "function toggleRangeRuler()" in ls: + ls = ls + TOKEN_BUTTON_WRAPPER + changed = True + + # Deployment Boundary (only in SETUP_CONTROLLER 1cb552) - Mac only + if guid == "1cb552": + new_ls, n = DEPLOYMENT_SPAWN_RE.subn(DEPLOYMENT_SPAWN_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + n_dep += 1 + changed = True + new_ls, n = CLEAR_DEPLOYMENT_RE.subn(CLEAR_DEPLOYMENT_REPLACEMENT, ls, count=1) + if n > 0: + ls = new_ls + changed = True + + if changed: + o["LuaScript"] = ls + + for v in o.values(): + walk(v) + elif isinstance(o, list): + for x in o: + walk(x) + + walk(data) + return n_coh, n_rng, n_dep + + +def main(): + args = [a for a in sys.argv[1:] if not a.startswith("--")] + flags = [a for a in sys.argv[1:] if a.startswith("--")] + if len(args) != 2: + print(f"Usage: {sys.argv[0]} [--reload]") + sys.exit(1) + src = Path(args[0]) + dst = Path(args[1]) + reload_tts = "--reload" in flags + + with src.open("r") as f: + data = json.load(f) + + # Rename so the patched save is recognizable in TTS load list. + data["SaveName"] = "SWL BETA - MAC PATCH" + + # 1. Append manager + handlers to Global LuaScript (idempotent: remove + # any previous Mac patch block first, then append fresh). + original_global_len = len(data.get("LuaScript", "")) + existing = data.get("LuaScript", "") + cleaned, n_removed = GLOBAL_PATCH_MARKER_RE.subn("", existing) + data["LuaScript"] = cleaned + GLOBAL_PATCH_LUA + new_global_len = len(data["LuaScript"]) + note = " (replaced existing patch)" if n_removed else "" + print(f"Global LuaScript: {original_global_len} -> {new_global_len} bytes{note}") + print() + + # 2. Replace Cohesion + Range + Deployment blocks in object scripts. + print("Object script patches:") + n_coh, n_rng, n_dep = patch_object_scripts(data) + print(f" Cohesion blocks replaced: {n_coh}") + print(f" Range blocks replaced: {n_rng}") + print(f" Deployment blocks replaced: {n_dep}") + print() + + dst.parent.mkdir(parents=True, exist_ok=True) + with dst.open("w") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + + print(f"Written: {dst}") + print(f" Save Name: {data.get('SaveName', '?').strip()}") + print(f" Version: {data.get('VersionNumber', '?')}") + + # Mirror to TTS Saves folder so it appears in Games -> Save & Load. + # Picks the next free TS_Save_N.json slot (TTS scans these on load). + saves_dir = Path.home() / "Library" / "Tabletop Simulator" / "Saves" + if saves_dir.exists(): + existing = sorted( + int(p.stem.split("_")[-1]) + for p in saves_dir.glob("TS_Save_*.json") + if p.stem.split("_")[-1].isdigit() + ) + # Reuse the highest existing slot if its content matches the prefix + # "SWL BETA - MAC PATCH"; else pick next free number. + target = None + for n in reversed(existing): + p = saves_dir / f"TS_Save_{n}.json" + try: + with p.open("r") as f: + if '"SWL BETA - MAC PATCH"' in f.read(512): + target = p + break + except OSError: + continue + if target is None: + next_n = (existing[-1] if existing else 0) + 1 + target = saves_dir / f"TS_Save_{next_n}.json" + with target.open("w") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + print(f" Mirrored to: {target}") + + if reload_tts: + print() + print("Hot-reloading into TTS via External Editor API...") + states = collect_patched_scripts(data) + print(f" Sending {len(states)} script(s): " + + ", ".join(f"{s['name']}({s['guid']})" for s in states)) + if send_to_tts(states): + print(" OK Sent. TTS should restart Lua with the new code " + "(table/objects state preserved).") + + +if __name__ == "__main__": + main() diff --git a/mac-patcher/requirements.txt b/mac-patcher/requirements.txt new file mode 100644 index 00000000..fd6ee21e --- /dev/null +++ b/mac-patcher/requirements.txt @@ -0,0 +1,3 @@ +UnityPy>=1.10 +Pillow>=10.0 +requests>=2.31 diff --git a/mac-patcher/retro-cohesion.md b/mac-patcher/retro-cohesion.md new file mode 100644 index 00000000..a67ea58c --- /dev/null +++ b/mac-patcher/retro-cohesion.md @@ -0,0 +1,170 @@ +# Rétro-ingé Cohesion Ruler + +Rédigé en mode prod, base pour le refactor event-driven vector lines (15 mai 2026, post-finding session 14-15 mai). + +## 1. Définition fonctionnelle (rappel règles SWL) + +Le **Cohesion Ruler** matérialise la **distance de cohésion d'unité** : tous les minis d'une même unité doivent rester à une distance maximale du leader d'unité (sinon malus / dispersion). Le ruler aide à vérifier visuellement cette contrainte pendant le tour. + +Distance de cohésion : **0.5 inch** (1,27 cm) entre les bords des socles, règle officielle SWL. + +## 2. Visuel attendu + +3 tailles, indexées par `baseSize` du leader : + +| Clé | Base size physique | Bundle | +|---|---|---| +| `small` | 27 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305632/...` | +| `medium` | 50 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305877/...` | +| `large` | 70 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305957/...` | + +Rendu côté Windows (TTS Unity 2019.4) : cercle semi-transparent vert/cyan projeté au sol via Unity Projector, centré sur la fig. **À extraire empiriquement** via UnityPy + référence Workshop pour la couleur/alpha exacts (en TODO ci-dessous). + +Comportement Projector : drape sur le relief de la table (rochers, élévations) grâce au Projector legacy. + +## 3. Déclencheurs (qui peut spawn/clear le ruler ?) + +### Spawn + +1. **Hotkey global "Show Cohesion On Hovered Model"** — `StarWarsLegion.lua:611-619` + - Bind via `addHotkey()` côté Global + - Appelle `showCohesionOnHoveredModel(hoverObject)` qui : + - Si déjà selected → clear + reset `selectedUnitObj` + - Sinon → `clearCohesionRuler() + spawnCohesionRuler(hoverObject)` + - **État global** `selectedUnitObj` tracké côté Global (variable du script `StarWarsLegion.lua` via `require('!/Cohesion')`) + +2. **Bouton "COHESION" sur Order Token** — `Order_Token.a57c41.lua:347-352, 369-378` + - Bouton créé pendant l'activation d'une unité + - `toggleCohesionRuler()` toggle via flag `rulerOn` local au Order Token + - Appelle `selectedUnitObj.call("spawnCohesionRuler", selectedUnitObj)` → s'exécute dans le script de la **fig sélectionnée** (Unit_Leader.99f1c8) + +3. **Re-spawn automatique après mouvement** — `Unit_Leader.99f1c8.lua:278-286` + - `dropCoroutine()` attend que `getVelocity().y == 0` (fig au sol) + - Si `moveState == true` (= en cours de mesure de mouvement) → `spawnCohesionRuler(self)` + +### Clear + +1. **Hotkey re-press** — toggle via `showCohesionOnHoveredModel` (idem spawn, second appel) + +2. **Bouton COHESION re-press** — toggle via `toggleCohesionRuler` (idem) + +3. **Bouton invisible "unitID" sur Unit_Leader** — `Unit_Leader.99f1c8.lua:45` + - Bouton au centre de la fig avec label = numéro d'unité, couleur alpha 0.01 (quasi-invisible) + - `click_function = "clearCohesionRuler"` + - C'est l'overlay cliquable du numéro affiché sur chaque fig + +4. **onPickedUp sur Unit_Leader** — `Unit_Leader.99f1c8.lua:274-276` + - **Event-driven déjà existant** : pickup la fig → clear automatique + +5. **`standbyTokens()` global** — `StarWarsLegion.lua:562-571` + - Boucle sur `getAllObjects()` et destroy tout objet nommé `"Cohesion Ruler"` (et "Range Ruler", "Movement Template", "Deployment Boundary") + - Déclenché par... à tracer (probablement reset de partie ou hotkey debug) + +6. **`removeLockedRulers()` GAME_CONTROLLER** — `GAME_CONTROLLER.623b03.lua:148-155` + - Boucle sur `getAllObjects()` et destroy "Cohesion Ruler" / "Range Ruler" + - Appelé par la fonction de reload (`reloadObj` voisine) + +7. **`clearCohesionRulers()` (pluriel) Order Token** — `Order_Token.a57c41.lua:788-793` + - Appelé par `clearTemplates()` lors du reset après mouvement + - `selectedUnitObj.setVar("moveState", false)` + `selectedUnitObj.call("clearCohesionRuler")` + +## 4. Lifecycle complet + +``` +[Spawn] + showCohesionOnHoveredModel(fig) [hotkey hover] + toggleCohesionRuler() [via Order Token bouton] + dropCoroutine() [auto si moveState] + ↓ + spawnCohesionRuler(fig): + 1. unitData = fig.getTable("unitData") + 2. bundleURL = getCohesionLinks()[unitData.baseSize] + 3. spawnObject(Custom_AssetBundle, pos = fig.pos + Y+20, scale = 0, rot = (0, fig.rot.y, 0)) + 4. setCustomObject({type=0, assetbundle=bundleURL}) + 5. setLock(true), use_gravity=false, setName("Cohesion Ruler") + ↓ +[Ruler affiché — STATIQUE, ne suit pas la fig] + Note: pas de onFixedUpdate. Si fig bouge sans pickup (impossible normalement + car les figs sont lockables), le ruler reste à l'ancienne position. + +[Clear] + showCohesionOnHoveredModel(same fig) [hotkey re-press toggle] + toggleCohesionRuler() [re-press toggle] + onPickedUp(fig) [pickup auto] + clearCohesionRuler() [click invisible "unitID" button] + standbyTokens() [global reset] + removeLockedRulers() [reload] + clearCohesionRulers() [via Order Token clearTemplates] + ↓ + clearCohesionRuler(): + 1. destroyObject(cohesionRuler) + 2. cohesionRuler = nil + +[Re-spawn auto post-drop] + onDropped → checkVelocity → si moveState → startLuaCoroutine(dropCoroutine) + ↓ + dropCoroutine: + while velocity.y ~= 0: yield + if moveState == true: spawnCohesionRuler(self) +``` + +## 5. État scopé : ATTENTION pattern hétérogène + +`cohesionRuler` est une variable Lua **scopée au script Lua qui fait le require**. Chaque objet qui require `!/Cohesion` a SA propre `cohesionRuler`. Trois scopes coexistent : + +| Site require | Scope `cohesionRuler` | +|---|---| +| `StarWarsLegion.lua:8` (Global) | Script Global | +| `Unit_Leader.99f1c8.lua:1` (chaque fig leader) | Par-fig | +| `Order_Token.a57c41.lua:4` (chaque Order Token) | Par-Order-Token | + +**Conséquence pratique** : +- Hotkey hover → spawn dans le scope Global → `cohesionRuler` du Global mis à jour, **pas celui de la fig** +- Bouton COHESION → `selectedUnitObj.call("spawnCohesionRuler", selectedUnitObj)` → exécute dans le scope de la fig → `cohesionRuler` de la fig mis à jour +- `onPickedUp(fig)` → clear dans le scope de la fig → ne clear PAS un ruler spawn via hotkey hover (mais le bouton invisible "unitID" est sur la fig, donc dans le scope fig...) + +**Bug latent connu (non reproduit)** : si tu spawn via hotkey hover, puis tu pickup une autre fig, le ruler du Global subsiste. À garder en tête pour le refactor (à corriger ou pas selon scope). + +## 6. Filtres de cleanup global + +Deux fonctions scannent `getAllObjects()` et destroy par nom : + +| Fonction | Fichier | Cible | +|---|---|---| +| `standbyTokens()` | StarWarsLegion.lua:562 | "Cohesion Ruler" + "Range Ruler" + "Movement Template" + "Deployment Boundary" | +| `removeLockedRulers()` | GAME_CONTROLLER.623b03.lua:148 | "Cohesion Ruler" + "Range Ruler" | + +**Impact refactor** : si on remplace l'Object Custom_AssetBundle par des vector lines (qui ne sont pas des Objects), ces deux filtres deviennent **caducs**. Il faudra leur substituer un clear de la table d'état globale + `Global.setVectorLines({})`. + +## 7. Paramètres visuels exacts — TODO + +À extraire empiriquement via UnityPy sur les bundles small/medium/large : + +- [ ] Bundle `halfcohesion_27mm.unity3d` (ou équivalent small) : material color, alpha, orthographicSize du Projector, animation éventuelle +- [ ] Bundle medium (50mm) : idem +- [ ] Bundle large (70mm) : idem +- [ ] Screenshot référence côté Workshop (rendu attendu côté Windows TTS 2019.4 / U6 non-magenta) + +Scripts UnityPy disponibles dans `Mod TTS SWL/` (`inspect_cohesion.py`, `inspect_externals.py`). À adapter pour extraire les params materials. + +## 8. Implications pour le refactor + +### Bonnes nouvelles +- API publique petite et conservable : `showCohesionOnHoveredModel`, `spawnCohesionRuler`, `clearCohesionRuler` +- 80% du pattern event-driven déjà en place (`onPickedUp`, `dropCoroutine`) +- Lifecycle bien défini, déclencheurs énumérés exhaustivement + +### Décisions à prendre dans le design doc +1. **Unifier les scopes** : faire que `cohesionRuler` soit une table globale indexée par GUID (Variante A) plutôt que par-script. Résout le bug latent (§5) et permet le pattern multi-figs simultanées. +2. **Remplacer les filtres de cleanup** (§6) par des appels à la fonction de clear globale. +3. **Reproduire le drape sur relief** via `Physics.cast()` par segment, comme le POC validé du 14-15 mai. +4. **Compatibilité Order Token bouton** : conserver la sémantique toggle `rulerOn` ou simplifier. + +(Note : esthétique du rendu vector lines hors specs — on traitera une fois le dossier refactor clos.) + +## 9. Références + +- Code : `swlegion-tts/mod/src/includes/Cohesion.ttslua`, `data/CohesionLinks.ttslua`, `StarWarsLegion.lua`, `StarWarsLegion/Unit_Leader.99f1c8.lua`, `StarWarsLegion/Order_Token.a57c41.lua`, `StarWarsLegion/GAME_CONTROLLER.623b03.lua` +- Bundles : cache TTS local, sources Unity dans `Mod TTS SWL/UnityProject-U6/Assets/` +- Session 14-15 mai validation empirique : `~/.claude/projects/-Users-martinpourrat-MARTIN-Star-Wars-Legion/memory/mod-tts-swl/session-14-mai.md` +- Plan B identifié : `~/.claude/projects/-Users-martinpourrat-MARTIN-Star-Wars-Legion/memory/mod-tts-swl/paths-forward.md` diff --git a/mac-patcher/retro-deployment.md b/mac-patcher/retro-deployment.md new file mode 100644 index 00000000..20d1741e --- /dev/null +++ b/mac-patcher/retro-deployment.md @@ -0,0 +1,76 @@ +# Rétro-ingé Deployment Boundary + +## Visuel attendu + +Zones de déploiement rouge/bleu peintes au sol au début de la partie selon le scenario. Pattern matriciel avec 14 codes de cellule : + +| Code | Sens | +|---|---| +| `r` / `b` | Base red / blue | +| `rh` / `bh` | Home (zone arrière) | +| `rs` / `bs` | Side (zone latérale) | +| `rss` / `bss` | Side stretched | +| `rl` / `bl` | Long (zone allongée, spawn 2 cellules : sX + ccX) | +| `rc` / `bc` | Corner | +| `rcc` / `bcc` | Corner-corner (autre orientation) | + +URLs bundles dans `deployLinks` (`SETUP_CONTROLLER.1cb552.lua:188-202`). + +Plusieurs codes partagent la même URL (e.g. `rh = rs = rss` → même bundle, rotation différente). Distinct rotations : `r=0, rh=0, rs=90, rss=90, ...` (`deployRotations` ligne 204). + +Rendu côté Windows : grand rectangle/zone projeté au sol via Projector. Couleurs rouge/bleu. **À extraire via UnityPy** (task #13). + +## Spawn + +**1 seul déclencheur** : `spawnDeploymentBoundary(matrix)` — `SETUP_CONTROLLER.1cb552.lua:267-314` + +- Reçoit une matrice 12×N (codes par cellule) inversée puis lue ligne par ligne +- Pour chaque cellule non-vide : `spawnBoundaryCell(cell, x, z)` qui spawn 1 Custom_AssetBundle au pos calculé `{xStart + 6*(x-1), yValue, zStart - 6*(z-1)}` + `deployOffset[cell]` +- Cas spécial `bl`/`rl` : spawn 2 cellules (sX + ccX) pour matérialiser le L + +Appelé par `SETUP_CONTROLLER.1cb552.lua:369` (probablement après sélection de la carte Deployment via UI menu). + +## Clear + +**1 seul déclencheur** : `clearDeploymentBoundary()` — ligne 316-323 +- Boucle sur `battlefieldZone.getObjects()` et `destroyObject` chaque objet nommé `"Deployment Boundary"` + +Aussi via : +- **`standbyTokens()`** — `StarWarsLegion.lua:567` — destroy global par nom + +(Pas dans `removeLockedRulers` de GAME_CONTROLLER : Deployment n'est pas dans la liste.) + +## Lifecycle + +``` +[Setup partie] + Menu UI "Mount Deployment" → checkDeployment() → spawnDeploymentBoundary(matrix) + ↓ + Loop sur matrix → pour chaque cell : spawnBoundaryCell + ↓ + Custom_AssetBundle spawn statique (scale 0, locked, no gravity) + +[Clear] + Menu UI "Remove Overlay" → clearDeploymentBoundary() + ↓ + Boucle battlefieldZone → destroyObject par nom + +[Aussi : standbyTokens() global au reset] +``` + +**Pattern : 100% statique.** Pas de suivi, pas d'event. Spawn au setup, clear quand on remove. Plus simple que Cohesion et Range. + +## Différences clés vs Cohesion/Range + +1. **Pas de fig source** — c'est un overlay de zone, pas un overlay attaché à un objet mobile. Pas besoin de drape sur relief si la table est plate (à confirmer : sur certaines tables custom avec relief, est-ce que la zone de déploiement doit draper ? probablement oui pour cohérence visuelle). +2. **Multi-cellules** par scenario — peut-être 10-20 Custom_AssetBundle spawn simultanément (vs 1 pour Cohesion/Range). +3. **Pas de variable d'état scopée** — pas de `deploymentRuler = nil` ; le code itère sur `battlefieldZone.getObjects()` filtré par nom pour le cleanup. + +## Implications refactor + +- API publique à conserver : `spawnDeploymentBoundary(matrix)`, `clearDeploymentBoundary()` +- 14 bundle URLs (mais visuellement ~6 zones distinctes vu les doublons URL) → 6 sets de params visuels à extraire (task #13) +- Pas d'event-driven nécessaire — statique simple +- En vector lines : chaque cellule = un polygone fermé `setVectorLines({{points={p1,p2,p3,p4,p1}, ...}, ...})`. Pas de Physics.cast si table plate, sinon raycast par segment du contour +- Filtre cleanup `standbyTokens` à adapter pour clear la table d'état globale (ou laisser un `Global.setVectorLines({})` de la collection deployment) +- **Décision design** : Deployment partage-t-il la même collection `Global.setVectorLines()` que Cohesion/Range, ou faut-il un canal séparé ? `Global.setVectorLines` est singleton donc tout doit cohabiter dans la même collection. Implication : la table d'état globale doit indexer par type (`cohesion` / `range` / `deployment`) pour gérer les clears partiels. diff --git a/mac-patcher/retro-movement.md b/mac-patcher/retro-movement.md new file mode 100644 index 00000000..579aaca3 --- /dev/null +++ b/mac-patcher/retro-movement.md @@ -0,0 +1,122 @@ +# Rétro-ingé Movement Template + Maximum Move + +**Correction** : Movement implique **2 objets visuels** spawn ensemble lors de l'activation de mouvement. Un seul des deux est cassé magenta. + +## Composant 1 : Movement Template A/B (templates 3D courbes) — NON AFFECTÉ + +Code : `Order_Token.a57c41.lua:480-529` + +```lua +templateA = spawnObject({ type = "Custom_AssetBundle", scale = {1,1,1} ... }) +templateA.setCustomObject({ assetbundle = ..., material = 1 }) +templateA.setColorTint(...) +templateA.setName("Movement Template (A)") +``` + +- **scale = {1,1,1}** → mesh visible (pas un Projector caché) +- **`material = 1`** + **`setColorTint()`** → material standard avec colorTint TTS exposé +- Bundles `longBundle` / `shortBundle` / `sharedBundle` par speed (templateInfo) +- **Pas affecté par bug magenta** : c'est un mesh classique, pas un Projector receiver + +## Composant 2 : Maximum Move (cercle de portée) — CASSÉ MAGENTA ✅ + +Code : `Order_Token.a57c41.lua:552-574` + +```lua +maxMoveTemplate = spawnObject({ + type = "Custom_AssetBundle", + position = {basePos.x, basePos.y + 20, basePos.z}, + scale = {0,0,0} -- ← scale 0, pattern Projector +}) +maxMoveTemplate.setCustomObject({ + type = 0, + assetbundle = maxMoveTemplateBundleToSpawn +}) +maxMoveTemplate.setLock(true) +maxMoveTemplate.use_gravity = false +maxMoveTemplate.setName("Maximum Move") -- ← nom différent de "Movement Template" +``` + +- **scale = {0,0,0}** → Projector legacy (même pattern que Cohesion/Range/Deployment) +- **Pas de setColorTint** sur ce spawn +- **CASSÉ MAGENTA** sur Mac confirmé par Martin + +### Bundles (`includes/data/MovementLinks.ttslua`) + +`getMovementLinks()` retourne table indexée `[baseSize][selectedSpeed]` : + +| baseSize | Taille | Speeds | +|---|---|---| +| `small` | 27mm | 3 bundles (speed 1/2/3) | +| `medium` | 50mm | 3 | +| `large` | 70mm | 3 | +| `huge` | 100mm | 3 | +| `laat` | 120mm | 3 | +| `epic` | 150mm | 3 | +| `long` | 100×175mm oblong | 3 | +| `snail` | 100×200mm oblong | 3 | + +**Total : 24 bundles uniques** pour le cercle de portée max. + +Rendu visuel attendu côté Windows : cercle/anneau projeté au sol matérialisant la portée maximale de mouvement à cette vitesse. **Couleur/alpha à extraire via UnityPy** (task #13). + +## Déclencheurs + +### Spawn +**1 seul déclencheur** : flow d'activation mouvement via Order Token +- Bouton speed (1/2/3) cliqué sur Order Token → set `unitData.selectedSpeed` +- Une fonction de mouvement (autour de `Order_Token.a57c41.lua:480+`) spawn `templateA`, `templateB`, ET `maxMoveTemplate` ensemble +- Conditional : `if isDeploy == false` → pas de Maximum Move pendant la phase de déploiement (ligne 556) + +### Clear +**Fonction `clearMovementTemplates()`** — `Order_Token.a57c41.lua:776-786` +```lua +function clearMovementTemplates() + if templateA ~= nil then destroyObject(templateA) end + if templateB ~= nil then destroyObject(templateB) end + if maxMoveTemplate ~= nil then destroyObject(maxMoveTemplate) end +end +``` + +Appelé par : +- `clearTemplates()` (ligne 770-774) → appelé après le drop final du mouvement +- Aucun cleanup global (Maximum Move PAS dans `standbyTokens()` ni `removeLockedRulers()`) + +## Lifecycle + +``` +[Spawn] + Order Token bouton speed (1/2/3) → spawn flow + ↓ + templateA, templateB (mesh visible) + maxMoveTemplate (Projector) + ↓ + L'utilisateur déplace templateA jusqu'à la position cible + → contrôle visuel "ma fig peut-elle aller là" avec le cercle Maximum Move + +[Clear] + Drop final (fig placée) → clearTemplates() → destruct 3 objets ensemble +``` + +**Pattern : statique au spawn, pas de suivi de fig**. Le cercle Maximum Move est positionné une fois à `basePos + Y20` et reste là pendant que tu manipules les templates de mouvement. + +## État scopé + +`maxMoveTemplate` est une variable par Order Token (`require('!/Cohesion')` ligne 4 mais maxMoveTemplate vient du flow Order_Token directement, pas d'include). Pas de partage Global / Unit_Leader. + +## Implications refactor + +- API publique à conserver : le spawn et `clearMovementTemplates()` (mais cette dernière mélange templates A/B + maxMoveTemplate) +- 24 bundles cercle de portée → 8 sets de params visuels distincts par baseSize (les 3 speeds partagent probablement le même material avec rayon différent ; à extraire task #13) +- Pattern statique simple : spawn une fois, clear ensemble avec templates A/B +- En vector lines : 1 cercle par maxMoveTemplate, dessiné via setVectorLines + Physics.cast par segment pour drape relief +- Cleanup propre : `clearMovementTemplates` doit clear la table d'état globale spécifique au maxMove de cet Order Token +- **Templates A/B (mesh) restent inchangés** — pas dans le scope du refactor + +## Couleur attendue + +Le cercle Maximum Move devrait visuellement matcher la couleur du template de speed correspondant : +- Speed 1 : couleur 1 (vert ?) +- Speed 2 : couleur 2 (jaune ?) +- Speed 3 : couleur 3 (rouge ?) + +Note `templateInfo.moveTemplate[selectedSpeed].colorTint` — c'est le tint des templates A/B, mais le cercle Maximum Move utilise un bundle distinct sans colorTint (le bundle embarque sa propre couleur). À vérifier empiriquement / via UnityPy quelle est la couleur baked-in de chaque bundle Maximum Move par speed. diff --git a/mac-patcher/retro-range.md b/mac-patcher/retro-range.md new file mode 100644 index 00000000..a4569069 --- /dev/null +++ b/mac-patcher/retro-range.md @@ -0,0 +1,86 @@ +# Rétro-ingé Range Ruler + +## Visuel attendu + +12 tailles dans `getRangeRulerLinks()` (`mod/src/includes/data/RangeRulerLinks.ttslua`) : + +| Clé | Cible | Range | +|---|---|---| +| `small` | base 27mm | range mini | +| `medium` | base 50mm | range mini | +| `large` | base 70mm | range mini | +| `huge` | base 100mm | range véhicule | +| `laat` | base 120mm | range LAAT | +| `epic` | base 150mm | range épique | +| `long` | base 100mm oblong | range véhicule long | +| `snail` | base 100x200mm | range escargot | +| `bombCart` | bomb cart | range spécifique | +| `smokeToken` | jeton fumée 18.8mm | range 1 | +| `token` | jeton charge/objectif/condition 25.1mm | range 1 | +| `tokenRangeTwo` | jeton graffiti 25.1mm | range 2 | +| `poi` | POI token 50.8mm | range 0.5 (3") | + +Rendu côté Windows : **4 cercles concentriques** projetés au sol via Unity Projector legacy (1 bundle = N Projectors embarqués pour les bandes de portée 1/2/3/4). Couleurs/alphas/rayons par bande **à extraire via UnityPy** (task #13). + +**Configurations par overlay** : +- Rulers de fig (small/med/large/huge/laat/epic/long/snail) : **4 cercles** (range 1/2/3/4) +- `smokeToken` : 1 cercle (range 1, effet smoke 1 pouce) +- `token` (charge/obj/cond) : 1 cercle (range 1) +- `tokenRangeTwo` (graffiti) : 2 cercles (range 1 + 2) +- `poi` : 1 cercle (range 0.5 = 3") +- `bombCart` : à confirmer empiriquement + +## Déclencheurs + +### Spawn +1. **Hotkey "Show Range On Hovered Model"** — `StarWarsLegion.lua:601-608` → `showRangeOnHoveredModel(hoverObject)` +2. **Bouton "RANGE" sur Order Token** — `Order_Token.a57c41.lua:354-365` → `targetingMode()` ligne 922 → `spawnRangeRuler(selectedUnitObj)` +3. **Bouton "RANGE" via attackMode** — `Order_Token.a57c41.lua:934` → `spawnRangeRuler(selectedUnitObj)` +4. **Bouton "R" sur POI Token** — `POI_Tokens/POI_Token.761483.lua:23,41-46` → `toggleRangeRuler()` → `spawnTokenRangeRuler()` → `spawnRangeRuler(self, tokenRulerBundle)` avec override +5. **Bouton "R" sur autres tokens** — `includes/TokenWithRangeRuler.ttslua:37-43,52-56` (smokeToken, token, tokenRangeTwo via `rangeKey` scopé par-token) + +### Clear +1. **Hotkey re-press** — toggle via `showRangeOnHoveredModel` +2. **`clearRangeRulers()` (pluriel)** — Order Token : appelé par `clearTemplates`, `exitTargetingMode`, `exitAttackMode`, `attackMenu` +3. **`clearRangeRuler()` (singulier)** — POI/Token : appelé par `toggleRangeRuler` et `onDestroy` +4. **`standbyTokens()` global** — `StarWarsLegion.lua:567` — destroy tout objet nommé `"Range Ruler"` +5. **`removeLockedRulers()`** — `GAME_CONTROLLER.623b03.lua:151` — destroy par nom + +## Différence clé vs Cohesion : suivi temps réel + +`RangeRulers.ttslua:78-79` : + +```lua +luaScript = "targetGUID = '"..rangeSourceObject.getGUID().."'\n" + .. "function onFixedUpdate()\n" + .. " if targetGUID ~= nil then\n" + .. " targetObj = getObjectFromGUID(targetGUID)\n" + .. " local targetPosition = targetObj.getPosition()\n" + .. " self.setPosition({targetPosition.x, targetPosition.y + 20, targetPosition.z})\n" + .. " self.setRotation({0,targetObj.getRotation().y,0})\n" + .. " end\n" + .. "end" +rangeRuler.setLuaScript(luaScript) +``` + +→ Le ruler **se repositionne 60 fois/sec** côté objet ruler (pas côté Lua de la fig). Différent de Cohesion qui est statique. + +**Implication refactor** : pour Range, le suivi temps réel est utile gameplay (tu mesures pendant que tu déplaces la fig pour évaluer si tu vas être en range). On ne peut pas simplement passer en pur event-driven sans perte UX. Le drape sur relief recalculé doit suivre — soit redraw à chaque tick visible (avec mémoization de position pour éviter le leak), soit accepter une dégradation UX (redraw au drop seulement). + +## État scopé (même problème que Cohesion §5) + +Variable `rangeRuler` scopée par script-objet (Global, chaque fig leader, chaque POI Token, chaque token générique). Pattern identique à Cohesion : 3+ scopes peuvent désaccorder. + +`selectedUnitObj` global aussi tracké côté Range (`showRangeOnHoveredModel` ligne 7-17 de RangeRulers.ttslua). + +## Override bundle (paramètre `projectorBundleOverride`) + +`spawnRangeRuler(rangeSourceObject, projectorBundleOverride)` — le 2e paramètre permet de fournir une URL bundle au lieu de la lookuper via `unitData.baseSize`. Utilisé par POI Token et TokenWithRangeRuler qui passent `rangeRulerTable[rangeKey]` directement. À conserver dans le refactor. + +## Implications refactor + +- API publique à conserver : `showRangeOnHoveredModel`, `spawnRangeRuler` (avec override), `clearRangeRulers` (Order Token), `clearRangeRuler` (POI/Token) +- 12 bundle URLs → 12 sets de params visuels à extraire (chacun avec **1 à 4 cercles concentriques** selon le type) et reproduire en vector lines (task #13) +- Pattern suivi temps réel → décision design (redraw par tick avec mémoization vs redraw event-driven uniquement) +- Filtres cleanup global (`standbyTokens`, `removeLockedRulers`) à adapter pour la table d'état +- Override `projectorBundleOverride` → s'inscrit naturellement dans une table d'état `{fig=obj, rangeKey="poi"}` ou similaire diff --git a/mac-patcher/scan_bundles.py b/mac-patcher/scan_bundles.py new file mode 100644 index 00000000..eb5edfd2 --- /dev/null +++ b/mac-patcher/scan_bundles.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +"""Scan TTS AssetBundles for material -> shader references.""" +import UnityPy +import os +import sys +from collections import defaultdict, Counter + +BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") +STOCK_PREFIXES = ( + "Standard", + "Hidden/", + "Mobile/", + "Particles/", + "UI/", + "Sprites/", + "Skybox/", + "Legacy Shaders/", + "Universal Render Pipeline/", + "URP/", + "Unlit/", + "GUI/", + "Lightmap", + "VertexLit", + "Reflective/", + "Self-Illumin/", + "Transparent/", + "FX/", + "Nature/", + "TextMeshPro/", + "TextMesh Pro/", +) + +def is_stock(name: str) -> bool: + return any(name.startswith(p) for p in STOCK_PREFIXES) + +shader_to_bundles = defaultdict(set) +shader_material_count = Counter() +bundle_to_shaders = defaultdict(set) +embedded_shaders_per_bundle = defaultdict(set) +errors = [] +bundle_summary = [] + +bundles = sorted(f for f in os.listdir(BUNDLE_DIR) if f.endswith(".unity3d")) +print(f"Scanning {len(bundles)} bundles in {BUNDLE_DIR}\n", file=sys.stderr) + +for fname in bundles: + path = os.path.join(BUNDLE_DIR, fname) + short_id = fname.replace("httpssteamusercontentaakamaihdnetugc", "")[:20] + fsize = os.path.getsize(path) + n_mat = 0 + n_shader = 0 + n_tex = 0 + try: + env = UnityPy.load(path) + for obj in env.objects: + t = obj.type.name + if t == "Material": + n_mat += 1 + try: + data = obj.read() + shader_name = None + try: + sref = data.m_Shader + # Try multiple ways to resolve PPtr + try: + shader_obj = sref.deref() + except Exception: + shader_obj = None + if shader_obj is None: + try: + shader_obj_read = sref.read() + if shader_obj_read is not None: + shader_name = ( + getattr(getattr(shader_obj_read, "m_ParsedForm", None), "m_Name", None) + or getattr(shader_obj_read, "m_Name", None) + ) + except Exception: + pass + else: + sd = shader_obj.read() + shader_name = ( + getattr(getattr(sd, "m_ParsedForm", None), "m_Name", None) + or getattr(sd, "m_Name", None) + ) + except Exception as e: + errors.append(f"{short_id} mat shader resolve: {e!r}") + if not shader_name: + # PPtr fields + try: + file_id = getattr(data.m_Shader, "file_id", "?") + path_id = getattr(data.m_Shader, "path_id", "?") + shader_name = f"" + except Exception: + shader_name = "" + # Material name + mat_name = getattr(data, "m_Name", "") or "" + shader_to_bundles[shader_name].add(short_id) + shader_material_count[shader_name] += 1 + bundle_to_shaders[short_id].add((shader_name, mat_name)) + except Exception as e: + errors.append(f"{short_id} mat read err: {e!r}") + elif t == "Shader": + n_shader += 1 + try: + data = obj.read() + sname = ( + getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) + or getattr(data, "m_Name", None) + ) + if sname: + embedded_shaders_per_bundle[short_id].add(sname) + except Exception as e: + errors.append(f"{short_id} shader read: {e!r}") + elif t in ("Texture2D", "Texture"): + n_tex += 1 + except Exception as e: + errors.append(f"{short_id}: load failed: {e!r}") + + bundle_summary.append((short_id, fsize, n_mat, n_shader, n_tex)) + +# === REPORT === +print("=" * 90) +print("BUNDLE SUMMARY (id_prefix, size_bytes, n_materials, n_shaders_embedded, n_textures)") +print("=" * 90) +for short_id, fsize, n_mat, n_shader, n_tex in bundle_summary: + print(f" {short_id} {fsize:>10} mats={n_mat:>3} shaders={n_shader:>2} tex={n_tex:>3}") + +print() +print("=" * 90) +print("SHADER FREQUENCY ACROSS ALL MATERIALS (sorted by usage)") +print("=" * 90) +for sname, count in shader_material_count.most_common(): + flag = " [CUSTOM?]" if not is_stock(sname) and not sname.startswith("4} mats / {n_bundles:>2} bundles {sname}{flag}") + +print() +print("=" * 90) +print("EMBEDDED SHADERS (shaders bundled INSIDE a .unity3d, sorted)") +print("=" * 90) +all_embedded = set() +for shaders in embedded_shaders_per_bundle.values(): + all_embedded.update(shaders) +print(f"\nDistinct embedded shaders: {len(all_embedded)}") +for s in sorted(all_embedded): + bundles_with = [b for b, ss in embedded_shaders_per_bundle.items() if s in ss] + print(f" - {s} ({len(bundles_with)} bundle(s): {','.join(bundles_with[:3])}{'...' if len(bundles_with) > 3 else ''})") + +print() +print("=" * 90) +print("BUNDLES REFERENCING NON-STOCK SHADERS (with material names)") +print("=" * 90) +for short_id, items in sorted(bundle_to_shaders.items()): + custom = [(s, m) for (s, m) in items if not is_stock(s) and not s.startswith("A2>%liFU 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/deployment_red.png b/mod/data/mac-fallback-assets/deployment_red.png new file mode 100644 index 0000000000000000000000000000000000000000..a8e05c5e28d935439f57cf4d2f709049cdbfa337 GIT binary patch literal 856 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJD{c~2L|kcv5PuNX2iDDWKE z&}jGPHlw7BugI5IyNuHtmi)D5_;H*;;mjx+4S^9C0tvh1F7Idd-q+F|3d}JKp00i_ I>zopr0J6>oo&W#< literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/max_move_cyan.png b/mod/data/mac-fallback-assets/max_move_cyan.png new file mode 100644 index 0000000000000000000000000000000000000000..dc017745ade7c63aa858f7608a3ced343b7a245f GIT binary patch literal 4124 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

Bd<4L18 zPhy1_KviTWqb66G4Wh}(ygXz&9|O)f3uwdFCa!4?KlGMbAr;%khN$<(Y752wfi=7~ zWG4G*^5@_P?P@?2+hFLeI(3EmX(Km6l@0{RXC?DJIj|^xzb^^HF3sM z9~PS`6uUoN8GIH{0(P50y^o{>`Mwj7&yB=yv(@-6K(qgn8eWbySkKes&)x$Z9RE3* za02qV^R>uF*@S3(l2A;Yjo6Tj5aKKL4 zu=%7aeAiN__!24|{fT8`0nNkY9a?!V z2v_uGfr~Z0(yKMiEmatc!CA%v8h^#3B;(YCdOu0pdwj|uQwv`&QSUVCoau))5ypqA zv!Qu!vUoYgeZX8+>#Pj9vozsWiEZWlGfHL1JVgkn?@1cm$X4=nb3n3m3_fi@+k@bR zI+`%|Q{6+MP!xim!pt~U7Y=Q%(Yk-1WiTd4I^Gn3Bdu+VLTH}4D-^r-vkRISV5>kH z_so){ZNVQx)jgnCB}StZF&z|CWw3a>bf&5kaM)@1jK#ho#=CL?JWzdsqc%RefUiho z&(!|!c?lYt*PzcFW#g{)7KJFotcVp!ipMD&aeI^AG+})$1IEA4q{DHC!~98D?POGG ziYr^^Nql-nabO~oy#5I`Mb_#;?vq;tp5A1rqFQs-Xh0gm=h5H=FXm`Kh8GlDr#Yb$ zI)?Tu0v8aVDbMqfY_-$m`gwx=l6{5EabF9YCmj`u(w9C9 zQZ#cX>%Uh@9X5^i#>>R5QkyYzu{v%{TK_?U5GF2utp#f)5!r0$iXzi=g}_5*K@Fi zOA0w^<2+1nMESu5Oob^6aVXb9`9zevpj;W{T$H1^h)b1$D|H0EVLCs634DvniQJ$~8psD0W)Fkby;G_juxYBqbDUNaWx{ zaP)=HJLF^!rWA0qJxI816Nu#l~@=dv@RHBjpU2((sL j_or@ySp6?@>S;C418-OBbs(nX|BS)Y)z4*}Q$iB}(dK-T literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_huge.png b/mod/data/mac-fallback-assets/range_fig_leader_huge.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad39ebcba76a691f394fefe08f950cb75aa9228 GIT binary patch literal 18811 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_laat.png b/mod/data/mac-fallback-assets/range_fig_leader_laat.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2ac1d221b1d13e9c357efadb6afdb7fda6c4dd GIT binary patch literal 19058 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_large.png b/mod/data/mac-fallback-assets/range_fig_leader_large.png new file mode 100644 index 0000000000000000000000000000000000000000..c1baafb4cda99568d4f54cd6317be0e17ec5320f GIT binary patch literal 18753 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png b/mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png new file mode 100644 index 0000000000000000000000000000000000000000..81c68a68c4138627192149b6f1f66b0ae59d152a GIT binary patch literal 19127 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_poi.png b/mod/data/mac-fallback-assets/range_poi.png new file mode 100644 index 0000000000000000000000000000000000000000..5a282adb3a6a5f2a524b878032b466cb29a15285 GIT binary patch literal 10364 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_smokeToken.png b/mod/data/mac-fallback-assets/range_smokeToken.png new file mode 100644 index 0000000000000000000000000000000000000000..5a282adb3a6a5f2a524b878032b466cb29a15285 GIT binary patch literal 10364 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_token.png b/mod/data/mac-fallback-assets/range_token.png new file mode 100644 index 0000000000000000000000000000000000000000..5a282adb3a6a5f2a524b878032b466cb29a15285 GIT binary patch literal 10364 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/range_tokenRangeTwo.png b/mod/data/mac-fallback-assets/range_tokenRangeTwo.png new file mode 100644 index 0000000000000000000000000000000000000000..4a8a2f488e72d64ace0344269757f910d16f74ed GIT binary patch literal 12344 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 literal 0 HcmV?d00001 diff --git a/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d b/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d new file mode 100644 index 0000000000000000000000000000000000000000..15bb9258a1c225572de9b686b6595455abfe184f GIT binary patch literal 10922 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 Mcz6b}|N5b`;!*7d "mac" | "windows" +deploymentMode = deploymentMode or "mac" -- table-wide setting + +function gGetMode(params) + return playerOverlayMode[params.color] or "mac" +end + +function gGetDeploymentMode() + return deploymentMode +end + +function gToggleDeploymentMode() + deploymentMode = (deploymentMode == "mac") and "windows" or "mac" + macRefreshModeUI() +end + +function gCohesionTrigger(params) + if not params or not params.figGUID then return end + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local mode = playerOverlayMode[params.playerColor] or "mac" + if mode == "windows" then + -- Object may not have the original alias (e.g., hovered non-fig object). + -- pcall guards against "no such function" errors that would otherwise + -- propagate out of the hotkey/button callback. + local ok = pcall(function() fig.call("spawnCohesionRulerOriginal", fig) end) + if not ok then gToggleCohesion({figGUID = params.figGUID}) end + else + gToggleCohesion({figGUID = params.figGUID}) + end +end + +function gRangeTrigger(params) + if not params or not params.figGUID then return end + local fig = getObjectFromGUID(params.figGUID) + if not fig then return end + local mode = playerOverlayMode[params.playerColor] or "mac" + if mode == "windows" then + local ok = pcall(function() fig.call("spawnRangeRulerOriginal", fig) end) + if not ok then gToggleRange({figGUID = params.figGUID}) end + else + gToggleRange({figGUID = params.figGUID}) + end +end + +-- UI: floating panel with one toggle button per seated player +function macModeClick(player, _, id) + local color = id:sub(9) -- "macmode_Red" -> "Red" + if player.color ~= color then + broadcastToColor("Only the player at this seat can toggle their mode.", + player.color, {1, 0.5, 0.5}) + return + end + local cur = playerOverlayMode[color] or "mac" + playerOverlayMode[color] = (cur == "mac") and "windows" or "mac" + -- Wipe any active Cohesion/Range overlays for this player so the next + -- trigger redraws in the chosen mode without stale visuals. + macRefreshModeUI() +end + +local function macFindNodeById(tree, id) + for _, node in ipairs(tree) do + if node.attributes and node.attributes.id == id then return node, tree end + if node.children then + local found, parent = macFindNodeById(node.children, id) + if found then return found, parent end + end + end + return nil, nil +end + +-- Tracks the panel's desired active state across refreshes. Persists so a +-- click on the close X before the initial refresh is honored. +macModePanelActive = (macModePanelActive == nil) and true or macModePanelActive + +function macModeToggleVisibility() + macModePanelActive = not macModePanelActive + local tree = UI.getXmlTable() or {} + local panel = macFindNodeById(tree, "macModePanel") + if panel then + panel.attributes.active = macModePanelActive and "true" or "false" + UI.setXmlTable(tree) + else + macRefreshModeUI() + end +end + +function macRefreshModeUI() + local seated = Player.getPlayers() + local rows = {{ + tag = "Panel", + attributes = { + color = "transparent", + preferredHeight = "24", + }, + children = { + { + tag = "Text", + attributes = { + text = "Mac TTS U6 Patch", + fontSize = "16", + color = "white", + alignment = "MiddleLeft", + rectAlignment = "MiddleLeft", + }, + }, + { + tag = "Button", + attributes = { + id = "macModePanelClose", + text = "X", + onClick = "macModeToggleVisibility", + color = "#3a1e1e", + textColor = "white", + fontSize = "12", + width = "24", + height = "20", + rectAlignment = "MiddleRight", + }, + }, + }, + }, { + tag = "Text", + attributes = { + text = "Cohesion & Range: pick your renderer", + fontSize = "12", + color = "#bbbbbb", + alignment = "MiddleCenter", + }, + }} + if #seated == 0 then + table.insert(rows, { + tag = "Text", + attributes = { + text = "(no seated players)", + fontSize = "12", + color = "#888888", + alignment = "MiddleCenter", + }, + }) + end + for _, p in ipairs(seated) do + local mode = playerOverlayMode[p.color] or "mac" + local label = p.color .. " - " .. + (mode == "mac" and "MAC FALLBACK" or "WINDOWS ORIGINAL") + local bg = (mode == "mac") and "#1e7a3a" or "#7a3a1e" + table.insert(rows, { + tag = "Button", + attributes = { + id = "macmode_" .. p.color, + text = label, + onClick = "macModeClick", + color = bg, + fontSize = "13", + textColor = "white", + preferredHeight = "28", + }, + }) + end + -- Deployment is table-wide (everyone sees the same zones); use a single + -- global toggle rather than per-seat. + table.insert(rows, { + tag = "Text", + attributes = { + text = "Deployment (table-wide)", + fontSize = "12", + color = "#bbbbbb", + alignment = "MiddleCenter", + }, + }) + table.insert(rows, { + tag = "Button", + attributes = { + id = "macmode_deployment", + text = (deploymentMode == "mac") and "DEPLOYMENT: MAC FALLBACK" + or "DEPLOYMENT: WINDOWS ORIGINAL", + onClick = "macDeploymentClick", + color = (deploymentMode == "mac") and "#1e3a7a" or "#7a3a1e", + fontSize = "13", + textColor = "white", + preferredHeight = "28", + }, + }) + -- Build my panel (will be merged into the existing UI tree below so we + -- don't clobber legionFloatingMenu / Welcome / Chess Clocks etc.). + local panel = { + tag = "Panel", + attributes = { + id = "macModePanel", + active = macModePanelActive and "true" or "false", + rectAlignment = "MiddleRight", + offsetXY = "-10 80", + width = "260", + height = "320", + color = "rgba(0.06,0.06,0.06,0.9)", + padding = "8 8 8 8", + outlineSize = "1 1", + outline = "#303030", + }, + children = {{ + tag = "VerticalLayout", + attributes = { + spacing = "4", + childForceExpandHeight = "false", + childForceExpandWidth = "true", + }, + children = rows, + }}, + } + + local tree = UI.getXmlTable() or {} + -- Remove any stale macModePanel before reinserting the fresh one. + for i = #tree, 1, -1 do + if tree[i].attributes and tree[i].attributes.id == "macModePanel" then + table.remove(tree, i) + end + end + table.insert(tree, panel) + + -- Inject a "Mac Patch" button into the bottom-right legionFloatingMenu + -- (replaces the first interactable=false placeholder button). + local menu = macFindNodeById(tree, "legionFloatingMenu") + if menu and menu.children then + local hasMine = false + for _, c in ipairs(menu.children) do + if c.attributes and c.attributes.id == "macModeMenuButton" then + hasMine = true; break + end + end + if not hasMine then + for i, c in ipairs(menu.children) do + if c.attributes and c.attributes.interactable == "false" then + menu.children[i] = { + tag = "Button", + attributes = { + id = "macModeMenuButton", + fontSize = "10", + onClick = "macModeToggleVisibility", + tooltip = "Toggle Mac TTS U6 Patch panel", + }, + value = "Mac Patch", + } + break + end + end + end + end + + UI.setXmlTable(tree) +end + +function macDeploymentClick(_, _, _) + gToggleDeploymentMode() +end + +function onPlayerChangeColor(_) macRefreshModeUI() end +function onPlayerConnect(_) macRefreshModeUI() end +function onPlayerDisconnect(_) macRefreshModeUI() end + +Wait.time(macRefreshModeUI, 2) + +-- Override hotkey init functions to capture playerColor and route through +-- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys +-- here SHADOWS the originals - when the original onLoad runs init*(), our +-- versions register the hotkey instead. + +function initCohesionHotkeys() + addHotkey("Show Cohesion On Hovered Model", + function(playerColor, hoverObject, _) + if not hoverObject or not hoverObject.interactable then return end + gCohesionTrigger({ + figGUID = hoverObject.getGUID(), + playerColor = playerColor, + }) + end) +end + +function initRangebandHotkeys() + addHotkey("Show Range On Hovered Model", + function(playerColor, hoverObject, _) + if not hoverObject or not hoverObject.interactable then return end + gRangeTrigger({ + figGUID = hoverObject.getGUID(), + playerColor = playerColor, + }) + end) +end + +-- Legacy global-scope wrappers (for any code that still calls these directly). +-- Default to mac mode when no color context is available. +function showCohesionOnHoveredModel(hoverObject) + if not hoverObject or not hoverObject.interactable then return end + gToggleCohesion({ figGUID = hoverObject.getGUID() }) +end + +function spawnCohesionRuler(cohesionSourceObject) + if not cohesionSourceObject then return end + gSpawnCohesion({ figGUID = cohesionSourceObject.getGUID() }) +end + +function clearCohesionRuler() + for key, entry in pairs(activeOverlays) do + if entry.type == "cohesion" then activeOverlays[key] = nil end + end + macRedrawAll() +end + +function showRangeOnHoveredModel(hoverObject) + if not hoverObject or not hoverObject.interactable then return end + gToggleRange({ figGUID = hoverObject.getGUID() }) +end + +function spawnRangeRuler(rangeSourceObject, _) + if not rangeSourceObject then return end + gSpawnRange({ figGUID = rangeSourceObject.getGUID() }) +end + +function clearRangeRulers() + gClearAllRange() +end + diff --git a/mod/src/includes/RangeRulers.ttslua b/mod/src/includes/RangeRulers.ttslua index 6df6e15a..c51676e0 100644 --- a/mod/src/includes/RangeRulers.ttslua +++ b/mod/src/includes/RangeRulers.ttslua @@ -80,3 +80,33 @@ function spawnRangeRuler(rangeSourceObject, projectorBundleOverride) noRulers = false end + +-- ============================================ +-- Mac fallback per-seat router (appended). +-- Mirrors the Cohesion pattern. See !/Overlays for the manager. +-- ============================================ +spawnRangeRulerOriginal = spawnRangeRuler +clearRangeRulersOriginal = clearRangeRulers + +function spawnRangeRuler(rangeSourceObject, _) + if not rangeSourceObject then return end + Global.call("gRangeTrigger", { + figGUID = rangeSourceObject.getGUID(), + playerColor = nil, + }) +end + +function clearRangeRulers() + Global.call("gClearAllRange", {}) + -- Also run the original (destroys the bundle Object spawned in Windows + -- mode). Skip if rangeRuler was never set (pure-Mac session). + if rangeRuler ~= nil then + pcall(clearRangeRulersOriginal) + end +end + +function clearRangeRuler() + if self and self.getGUID and self.getGUID() ~= "-1" then + Global.call("gClearRange", { figGUID = self.getGUID() }) + end +end diff --git a/src/index.ts b/src/index.ts index 45d99efd..ba63a1e4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -103,25 +103,31 @@ export async function compileSaveFile( } } +function defaultTTSHomeDir(): string { + const platform = os.platform(); + if (platform === 'win32') { + return steam.homeDir.win32(process.env); + } + if (platform === 'darwin') { + return path.join(os.homedir(), 'Library', 'Tabletop Simulator'); + } + if (platform === 'linux') { + return path.join(os.homedir(), '.local', 'share', 'Tabletop Simulator'); + } + throw new Error(`Unsupported platform: ${platform}`); +} + export async function destroySymlink(homeDir?: string): Promise { - // TODO: Add non-win32 support. if (!homeDir) { - if (os.platform() !== 'win32') { - throw new Error(`Unsupported platform: ${os.platform()}`); - } - homeDir = steam.homeDir.win32(process.env); + homeDir = defaultTTSHomeDir(); } const from = path.join(homeDir, 'Saves', 'TTSDevLink'); return fs.remove(from); } export async function createSymlink(homeDir?: string): Promise { - // TODO: Add non-win32 support. if (!homeDir) { - if (os.platform() !== 'win32') { - throw new Error(`Unsupported platform: ${os.platform()}`); - } - homeDir = steam.homeDir.win32(process.env); + homeDir = defaultTTSHomeDir(); } await destroySymlink(homeDir); const from = path.join(homeDir, 'Saves', 'TTSDevLink'); From fc82bc1990c3de560ace2ec29821779dee9cac3a Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 20 May 2026 01:23:37 -0400 Subject: [PATCH 91/93] Revert "Revert "Revert "Mac projector fallback""" --- mac-patcher/.gitignore | 6 - mac-patcher/README.md | 74 - mac-patcher/SHADER_INVENTORY.md | 59 - mac-patcher/design-refactor.md | 330 --- mac-patcher/extract_projector_specs.py | 198 -- mac-patcher/generate_overlay_assets.py | 282 --- mac-patcher/inspect_cohesion.py | 83 - mac-patcher/inspect_externals.py | 51 - mac-patcher/materials-reference.md | 170 -- mac-patcher/patch_cohesion_rename.py | 81 - mac-patcher/patch_save_for_mac.py | 2171 ----------------- mac-patcher/requirements.txt | 3 - mac-patcher/retro-cohesion.md | 170 -- mac-patcher/retro-deployment.md | 76 - mac-patcher/retro-movement.md | 122 - mac-patcher/retro-range.md | 86 - mac-patcher/scan_bundles.py | 166 -- .../mac-fallback-assets/cohesion_halo.png | Bin 22585 -> 0 bytes .../mac-fallback-assets/deployment_blue.png | Bin 856 -> 0 bytes .../mac-fallback-assets/deployment_red.png | Bin 856 -> 0 bytes .../mac-fallback-assets/max_move_cyan.png | Bin 4124 -> 0 bytes .../mac-fallback-assets/range_bombCart.png | Bin 12426 -> 0 bytes .../range_fig_leader_epic.png | Bin 19173 -> 0 bytes .../range_fig_leader_huge.png | Bin 18811 -> 0 bytes .../range_fig_leader_laat.png | Bin 19058 -> 0 bytes .../range_fig_leader_large.png | Bin 18753 -> 0 bytes .../range_fig_leader_long_stadium.png | Bin 19025 -> 0 bytes .../range_fig_leader_medium.png | Bin 18599 -> 0 bytes .../range_fig_leader_small.png | Bin 18475 -> 0 bytes .../range_fig_leader_snail_stadium.png | Bin 19127 -> 0 bytes mod/data/mac-fallback-assets/range_poi.png | Bin 10364 -> 0 bytes .../mac-fallback-assets/range_smokeToken.png | Bin 10364 -> 0 bytes mod/data/mac-fallback-assets/range_token.png | Bin 10364 -> 0 bytes .../range_tokenRangeTwo.png | Bin 12344 -> 0 bytes .../silhouette_mac_fallback.unity3d | Bin 10922 -> 0 bytes mod/src/StarWarsLegion.lua | 1 - mod/src/StarWarsLegion/Order_Token.a57c41.lua | 97 +- .../SETUP_CONTROLLER.1cb552.lua | 32 +- mod/src/includes/Cohesion.ttslua | 28 +- mod/src/includes/Overlays.ttslua | 929 ------- mod/src/includes/RangeRulers.ttslua | 30 - src/index.ts | 26 +- 42 files changed, 53 insertions(+), 5218 deletions(-) delete mode 100644 mac-patcher/.gitignore delete mode 100644 mac-patcher/README.md delete mode 100644 mac-patcher/SHADER_INVENTORY.md delete mode 100644 mac-patcher/design-refactor.md delete mode 100644 mac-patcher/extract_projector_specs.py delete mode 100644 mac-patcher/generate_overlay_assets.py delete mode 100644 mac-patcher/inspect_cohesion.py delete mode 100644 mac-patcher/inspect_externals.py delete mode 100644 mac-patcher/materials-reference.md delete mode 100644 mac-patcher/patch_cohesion_rename.py delete mode 100644 mac-patcher/patch_save_for_mac.py delete mode 100644 mac-patcher/requirements.txt delete mode 100644 mac-patcher/retro-cohesion.md delete mode 100644 mac-patcher/retro-deployment.md delete mode 100644 mac-patcher/retro-movement.md delete mode 100644 mac-patcher/retro-range.md delete mode 100644 mac-patcher/scan_bundles.py delete mode 100644 mod/data/mac-fallback-assets/cohesion_halo.png delete mode 100644 mod/data/mac-fallback-assets/deployment_blue.png delete mode 100644 mod/data/mac-fallback-assets/deployment_red.png delete mode 100644 mod/data/mac-fallback-assets/max_move_cyan.png delete mode 100644 mod/data/mac-fallback-assets/range_bombCart.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_epic.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_huge.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_laat.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_large.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_long_stadium.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_medium.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_small.png delete mode 100644 mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png delete mode 100644 mod/data/mac-fallback-assets/range_poi.png delete mode 100644 mod/data/mac-fallback-assets/range_smokeToken.png delete mode 100644 mod/data/mac-fallback-assets/range_token.png delete mode 100644 mod/data/mac-fallback-assets/range_tokenRangeTwo.png delete mode 100644 mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d delete mode 100644 mod/src/includes/Overlays.ttslua diff --git a/mac-patcher/.gitignore b/mac-patcher/.gitignore deleted file mode 100644 index 34bac69b..00000000 --- a/mac-patcher/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -venv/ -__pycache__/ -*.pyc -*.bak -*.bak-* -scan_bundles_*.out diff --git a/mac-patcher/README.md b/mac-patcher/README.md deleted file mode 100644 index 420bd388..00000000 --- a/mac-patcher/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# mac-patcher - -Python patcher that injects Mac-compatibility fallbacks into a SWL TTS save -file. Works around the TTS Unity 6 (Berserk) shader bug where vanilla -AssetBundle Projectors (range rulers, cohesion halos, movement templates, -deployment zones, silhouettes) render as a magenta rectangle on macOS -because the custom shader bundled by Allen White (Dicewrench) fails to load -under Unity 6 Metal. - -## What it does - -For each affected object script in a `TS_Save_N.json`: - -- **Cohesion / Range / Maximum Move / Deployment / Silhouette**: routes the - visualization through a Mac-friendly path (vector lines + decal PNGs) - built into the Global script. The vanilla bundle path stays available - for per-seat opt-in (see "Mac TTS U6 Patch" floating panel). - -- **SIL button**: renames `click_function = "toggleSilhouettes"` to - `"macToggleSil"` + injects a forwarder. Empirically the engine silently - swallows the original click_function name in Mac mode (the button plays - its sound/animation but the function is never invoked). The rename - bypasses the issue entirely. - -- **clearSilhouette nil guard**: upstream `clearSilhouette()` derefs - `removeAttachments()[1]` which is nil when `silhouetteState` is true but - the physical silhouette attachment has been lost across save reloads. - Patched to guard the nil case + reset state in onload. - -- **Order Token Speed/Move button overrides**: inlines vanilla bodies of - `changeSpeed1/2/3` and `moveForward/Backwards/Left/Right` to capture - the clicking player's color (`macActivePlayerForMove`) for per-seat - routing. Inlined rather than wrapped because the List Builder copies the - block to Command Token Custom_Models which lack the vanilla - Order_Token function bodies (wrap+call_orig would crash with "attempt - to call a nil value"). - -## Usage - -```bash -# Setup once -python3 -m venv venv -source venv/bin/activate -pip install -r requirements.txt - -# Patch a save -python3 patch_save_for_mac.py [--reload] -``` - -The patcher also mirrors the patched save into -`~/Library/Tabletop Simulator/Saves/TS_Save_N.json` (next free slot, or the -existing `SWL BETA - MAC PATCH` slot if present) so TTS picks it up in -Games -> Save & Load. - -`--reload` hot-pushes the patched scripts to a running TTS instance via the -External Editor API (no need to restart TTS). - -## Idempotence - -All patches are idempotent: a re-run on an already-patched save strips the -previous Mac patch block from Global (via marker regex) and re-injects -fresh. Per-object wrappers are also stripped before re-injection. - -## Files - -- `patch_save_for_mac.py` - main patcher -- `scan_bundles.py` - UnityPy scanner for the local TTS bundle cache -- `extract_projector_specs.py` - dump Projector material properties from a - bundle (used to discover the `_Arc` shader keyword for firing arc lines) -- `generate_overlay_assets.py` - regenerate the range/cohesion/etc. PNG - decals from authoritative SWL geometry -- `inspect_*.py` - debug helpers -- `retro-*.md` - retrospective notes on each subsystem rewrite -- `SHADER_INVENTORY.md` - list of custom shaders in the vanilla bundles diff --git a/mac-patcher/SHADER_INVENTORY.md b/mac-patcher/SHADER_INVENTORY.md deleted file mode 100644 index 926e86db..00000000 --- a/mac-patcher/SHADER_INVENTORY.md +++ /dev/null @@ -1,59 +0,0 @@ -# SW Legion TTS Mod — Shader Inventory - -Scan effectué le 2026-05-07 sur `~/Library/Tabletop Simulator/Mods/Assetbundles/` (40 bundles `.unity3d`). -Outil : UnityPy 1.25.0, script `scan_bundles.py`. Output brut : `scan_bundles_2026-05-07.out`. - -## TL;DR -**9 shaders custom distincts**, **tous embarqués dans le `.unity3d` qui les utilise**. Donc : -- L'astuce "Always Included Shaders" du player TTS est **non-applicable** : ces shaders ne sont pas chargés depuis le pool du player, ils vivent dans les bundles. -- Le bug magenta vient du fait que le bytecode/code source de ces shaders custom ne fonctionne plus correctement sous Unity 6 + Metal sur Mac. -- Les overlays magenta listés par Martin (cohésion, range, deployment, movement) correspondent **exactement** aux 4 shaders `BucketheadBits/Projector/*`. - -## Inventaire complet (par catégorie) - -### Shaders Projector (overlays affectés par le bug magenta) -| Shader | Materials | Bundles | -|---|---|---| -| `BucketheadBits/Projector/Movement` | `ProjectorMaterial_27mm_speed1_single`, `ProjectorMaterial_27mm_speed2_single`, `ProjectorMaterial_50mm_speed2_single` | 3 | -| `BucketheadBits/Projector/Deployment` | `Projector_Deployment_Blue` (×2), `Projector_Deployment_Red` (×2) | 4 | -| `BucketheadBits/Projector/Range` | `ProjectorMaterial_25mm_token`, `ProjectorMaterial_27mm`, `ProjectorMaterial_50mm` | 3 | -| `BucketheadBits/Projector/Cohesion` | `Cohesion_27mm` | 1 | - -→ 11 materials projector au total, 4 shaders distincts à porter en priorité. - -### Outils / autres custom -| Shader | Materials | Bundles | -|---|---|---| -| `BucketheadBits/MoveTool` | `Speed1`, `Speed2` | 2 | -| `BucketheadBits/Tokens/Lambert Channel Mask` | `unitIDtoken_1` à `unitIDtoken_10` | 10 | -| `BucketheadBits/Silhouette` | `BucketheadBits_Silhouette` | 1 | -| `BucketheadBits/Units/Color Replacer` | `Ewok_SharedMat` | 1 | -| `DWD/LightenSkybox` | `Skybox` | 1 | - -→ Le shader `Tokens/Lambert Channel Mask` correspond aux 10 unitIDtokens loggés en console par TTS lors du bug : "Shader didn't load correctly for AssetBundle material unitIDtoken_X". Donc ce shader est **explicitement confirmé cassé**. - -## Auteurs -- Préfixe **`BucketheadBits/`** sur 8/9 shaders → ancien contributeur "Buckethead". Aucun repo GitHub public trouvé sous ce handle. Possible que ce soit un handle alternatif de Tieren (crédité ailleurs comme "original mod creator"). -- Préfixe **`DWD/`** sur 1 shader (LightenSkybox) → autre auteur, juste un tint skybox. - -## Implications pour la conversation avec Ben - -1. **L'angle "Always Included Shaders" n'est pas la bonne piste** pour ce bug, parce que les shaders custom sont embarqués dans les bundles. Si Ben a essayé cette piste, c'est cohérent que ça n'ait rien donné. - -2. **Le scope minimum réaliste** : porter / remplacer ces 9 shaders sous Unity 6 (compilation Metal-compatible). Soit en partant des sources s'il les a, soit en réécrivant des équivalents stock. - -3. **Materials = 11 projectors + 10 tokens + 4 outils + 1 skybox = 26 materials affectés au total** dans 27 bundles différents. Volumétrie raisonnable si les shaders sont swappable d'un coup. - -4. **Type technique des shaders** : - - Les `Projector/*` sont des shaders Unity Projector (transparent, projection avec bandes alternées) — historiquement tricky à porter Metal. - - Le `Tokens/Lambert Channel Mask` est un shader de coloration par canaux (R/G/B/A) — utilisé pour appliquer la couleur du joueur sur un mesh à canaux. Classique mais codé custom. - - Les autres (MoveTool, Silhouette, Color Replacer, LightenSkybox) sont des shaders d'effets simples. - -## Sources non-trouvées -- Aucun repo public sous `swlegion`, `swlegion-dev`, `matanlurey`, `halestom15`, `Buckethead` ne contient les sources Unity de ces shaders. -- Le projet Unity vit donc en privé sur la machine d'un contributeur (probablement Decaf ou Ben). - -## Prochaine étape -Attendre Ben pour confirmer si les sources sont disponibles. Cet inventaire répond déjà partiellement à ses questions 1 et 2 : -- Q1 ("custom shaders updated or just retargeted ?") : on sait maintenant exactement ce qu'il faut updater. -- Q2 ("which shaders are referenced ?") : liste complète ci-dessus. diff --git a/mac-patcher/design-refactor.md b/mac-patcher/design-refactor.md deleted file mode 100644 index a94b2d03..00000000 --- a/mac-patcher/design-refactor.md +++ /dev/null @@ -1,330 +0,0 @@ -# Design refactor — Overlays Projector SWL TTS (Mac/Unity 6) - -**Status** : draft 15 mai 2026. Spec pour le patch event-driven hybride (decals plats + vector lines drapants). Base : 4 retro-*.md + materials-reference.md + shaders Allen White lus. - -## 1. Contexte (résumé exécutif) - -Le passage du player TTS à Unity 6 a strippé les variants Standard receiver des Projectors legacy. Sur Mac, **tous les overlays Projector du mod SWL s'affichent en magenta** : Cohesion Ruler, Range Ruler, Maximum Move, Deployment Boundary. Validation empirique session 14-15 mai : aucun fix côté bundle/shader ne marche (le strip est player-side TTS U6). Fix Berserk = ticket à ouvrir mais sans garantie de calendrier. - -**Solution mod-side validée** : remplacer le pattern Custom_AssetBundle (Projector) par un combo **Decal plat (PNG hébergé) + Vector lines (drapant via raycast)** orchestré par un manager Global event-driven. - -## 1bis. Fallback per-seat avec auto-test au boot - -Le refactor ne **remplace pas** le Projector legacy, il l'**augmente** d'un fallback per-seat. Chaque joueur a un compat flag détecté au boot, et le rendu s'adapte : - -- **Seats non-Mac** (TTS U6 OK) → voient le **Projector legacy** (rendu original préservé) -- **Seats Mac** (bug magenta) → voient le **patch hybride** (decal + vector lines) - -Quand Berserk corrigera le bug, l'auto-test au boot suivant désactivera le compat pour les seats Mac → retour automatique au comportement legacy, zéro action utilisateur. - -### 1bis.1 État par-seat (Global script) - -```lua -compatBySeat = {} -- {Red=true|false, Blue=true|false, ...} true = Mac/needs patch - -function onLoad(script_state) - if script_state ~= "" then - compatBySeat = JSON.decode(script_state) or {} - end - testCompatForAllSeated() -end - -function onSave() - return JSON.encode(compatBySeat) -end - -function getMacSeats() - local out = {} - for color, isMac in pairs(compatBySeat) do - if isMac then table.insert(out, color) end - end - return out -end - -function getNonMacSeats() - local out = {} - for color, isMac in pairs(compatBySeat) do - if not isMac then table.insert(out, color) end - end - return out -end -``` - -### 1bis.2 Test compat au boot - -```lua -function testCompatForAllSeated() - for _, player in pairs(Player.getPlayers()) do - if player.seated then - offerCompatTest(player) - end - end -end - -function offerCompatTest(player) - -- Spawn un canary Projector en zone hors-jeu (sous la table) - local canary = spawnObject({ - type="Custom_AssetBundle", - position={-50, -10, -50}, - scale={0,0,0} - }) - canary.setCustomObject({assetbundle = COHESION_27MM_URL}) - - -- Wait quelques secondes le temps que le bundle charge - Wait.time(function() - -- UI popup pour ce joueur uniquement - player.broadcast("Test rendu : cliquez sur ce que vous voyez sur le canary (Y=-10)") - -- Buttons "Couleurs OK" et "Magenta/rose" - -- onClick stocke compatBySeat[player.color] = true|false - -- destruct canary - end, 3) -end -``` - -### 1bis.3 Spawn d'un overlay (pattern unifié) - -```lua -function spawnOverlay(type, fig, params) - local macSeats = getMacSeats() - local nonMacSeats = getNonMacSeats() - local entry = {type=type, fig=fig, params=params, objects={}} - - -- 1. Projector legacy pour les seats non-Mac - if #nonMacSeats > 0 then - local projector = spawnObject({type="Custom_AssetBundle", ...}) - projector.setCustomObject({assetbundle = bundleURLs[type][fig.baseSize]}) - projector.setLock(true) - projector.setName(type .. "_legacy") - if #macSeats > 0 then projector.setInvisibleTo(macSeats) end - entry.objects.projector = projector - end - - -- 2. Custom_Tile (PNG plat) pour les seats Mac - if #macSeats > 0 then - local tile = spawnObject({type="Custom_Tile", position=fig.pos+Y0.1, scale=...}) - tile.setCustomObject({image = pngURLs[type][fig.baseSize]}) - tile.setLock(true) - tile.setName(type .. "_patch") - if #nonMacSeats > 0 then tile.setInvisibleTo(nonMacSeats) end - entry.objects.tile = tile - end - - -- 3. Vector lines (drape relief) pour les seats Mac - if #macSeats > 0 then - entry.lines = buildVectorLines(type, fig, params, macSeats) - -- ajoutées au batch global - end - - activeOverlays[fig.getGUID() .. ":" .. type] = entry - redrawAll() -end -``` - -### 1bis.4 Bug Berserk fix → reset auto - -Quand Berserk corrigera les variants Standard receiver dans le player TTS U6 : -- Au prochain boot de partie, `testCompatForAllSeated` re-run pour chaque seat -- Les Mac users verront maintenant le canary correctement → cliquent "Couleurs OK" → `compatBySeat[color] = false` -- Le patch ne s'active plus pour eux, comportement legacy restauré -- Aucun changement de code requis côté mod - -**Re-test manuel** : un bouton "Re-test compatibility" dans l'UI Notes du mod permet de forcer un nouveau test à tout moment (utile si le user veut vérifier après une mise à jour TTS). - -### 1bis.5 Incertitudes à valider empiriquement au POC - -| Incertitude | Plan B si KO | -|---|---| -| `Object.setInvisibleTo` cache-t-il le **rendu de la projection** d'un Projector ou seulement le GameObject parent ? | Si KO : si au moins 1 Mac seat présent, on ne spawn PAS le Projector du tout (tout le monde voit le patch — compromis : 1 Mac dans la partie = tout le monde adopte le rendu patch) | -| `Global.setDecals()` supporte-t-il un filtre `players` per entry ? | Si KO : utiliser `Custom_Tile` Objects (déjà ce qu'on a au-dessus, avec `setInvisibleTo`). Plus propre et déjà compatible per-seat | - -À tester en début de phase d'implémentation, avant de pousser tout le code. - -## 2. Architecture event-driven (manager Global) - -### 2.1 État global unique - -```lua --- mod/src/includes/Overlays.ttslua (nouveau) -local activeOverlays = {} -- clé = "{guid}:{type}" → {type, fig, params} -local hiddenWhilePickedUp = {} -- buffer pour les overlays cachés temporairement -``` - -### 2.2 API publique unifiée - -```lua --- Spawn / clear par type, scopé à une fig -function spawnOverlay(type, fig, params) -function clearOverlayForFig(type, fig) -function clearAllOverlays() -- reset complet - --- Adapters pour conserver l'API existante des callers -function spawnCohesionRuler(fig) -- → spawnOverlay("cohesion", fig, {}) -function clearCohesionRuler() -- → clearOverlayForFig("cohesion", self) -function spawnRangeRuler(fig, override) -- → spawnOverlay("range", fig, {override=override}) -function clearRangeRulers() -- → clearOverlayForFig("range", selectedUnitObj) --- etc. -``` - -### 2.3 Event handlers (Global) - -```lua -function onObjectPickUp(player_color, obj) - -- buffer tous les overlays attachés à obj puis les retirer du draw - hidePickedUp(obj) - redrawAll() -end - -function onObjectDrop(player_color, obj) - -- restaurer les overlays de obj depuis le buffer - restorePickedUp(obj) - redrawAll() -end - -function onObjectDestroy(obj) - -- nettoyage définitif - purgeForFig(obj) - redrawAll() -end -``` - -### 2.4 Rendu unique par tick d'event - -```lua -function redrawAll() - local lines, decals = {}, {} - for _, entry in pairs(activeOverlays) do - local l, d = builders[entry.type](entry.fig, entry.params) - for _, line in ipairs(l) do table.insert(lines, line) end - for _, decal in ipairs(d) do table.insert(decals, decal) end - end - Global.setVectorLines(lines) - Global.setDecals(decals) -end -``` - -**Important** : `setVectorLines` et `setDecals` sont des **singletons** côté TTS — chaque appel remplace toute la collection. D'où le manager global qui agrège. - -## 3. Spec par overlay - -### 3.1 Cohesion - -| Composant | Détail | -|---|---| -| Trigger spawn | Hotkey "Show Cohesion On Hovered Model" / bouton COHESION Order Token / dropCoroutine post-mouvement (`moveState=true`) | -| Trigger clear | Mêmes triggers (toggle) + onPickedUp (auto) + standby/reload globaux | -| Decal PNG | `cohesion_halo.png` — gradient radial blanc fade depuis le centre, fond transparent, 512×512px | -| Decal params | `position = fig.pos + Y0.1`, `rotation = (90, 0, 0)` (face vers le haut), `scale = (baseRadius+1, baseRadius+1, baseRadius+1) × 2` | -| Vector line | 1 cercle filaire blanc à `r = base_radius + 0.5"` du centre, drapé par raycast 32 segments | -| Couleur | #FFFFFF α=0.6 | - -API conservée : `showCohesionOnHoveredModel`, `spawnCohesionRuler`, `clearCohesionRuler`. Compatible avec les 3 sites de require existants (Global, Unit_Leader, Order_Token) car redirigés vers le manager global. - -### 3.2 Range Ruler - -| Composant | Détail | -|---|---| -| Trigger spawn | Hotkey "Show Range On Hovered Model" / bouton RANGE Order Token (targetingMode/attackMode) / bouton R sur POI/tokens | -| Trigger clear | Mêmes triggers (toggle) + clearTemplates (post-drop mouvement) + exit modes | -| Decal PNG | `range_bands.png` (universel) — 4 anneaux concentriques à 6/12/18/24 inches relatifs, couleurs jaune/orange/rouge/magenta foncé, fond transparent, 1024×1024px | -| Decal params | `position = fig.pos + Y0.1`, `scale = (60, 60, 60)` (couvre 24" de rayon + marge) | -| Vector lines | 4 cercles filaires aux rayons absolus **6", 12", 18", 24"** du centre, mêmes couleurs que les bandes du PNG | -| Couleur (par anneau) | R1 #FFC300, R2 #FF7500, R3 #FF1400, R4 #C20042 (alpha 0.6 sur les lignes) | -| Variantes | Tokens (smokeToken/token/tokenRangeTwo/bombCart/POI) → moins d'anneaux (1 ou 2), PNG distincts ou même PNG avec scale ajusté | - -API conservée : `showRangeOnHoveredModel`, `spawnRangeRuler(fig, override)`, `clearRangeRuler/clearRangeRulers`. - -Pour les variantes, le `override` permet de passer un type explicite (`"smokeToken"`, `"poi"`, etc.) au lieu du baseSize de la fig. - -### 3.3 Maximum Move - -| Composant | Détail | -|---|---| -| Trigger spawn | Lors du clic sur bouton speed (1/2/3) sur Order Token (flow mouvement) | -| Trigger clear | clearMovementTemplates (drop final) | -| Decal PNG | `max_move_disk.png` — disque plein bleu ciel #55CCFF α=0.48, fond transparent, 512×512px | -| Decal params | `position = fig.pos + Y0.1`, `scale = ProjectorRadius × 2` selon (baseSize, speed) | -| Vector line | 1 cercle filaire bleu ciel au rayon = ProjectorRadius (drape relief) + 1 anneau base blanc fin à r = baseRadius + 0.5" | -| Couleur | Cercle principal #55CCFF α=0.6, anneau base #FFFFFF α=0.5 | -| Rayons | Lookup table `maxMoveRadius[baseSize][speed]` (ex: `27mm.speed1=4.55", speed2=6.52", speed3=8.48"`) — extraits de `_ProjectorRadius` dans les materials Movement | - -API conservée : flow existant dans `Order_Token.a57c41.lua:552-574`, remplacement du `spawnObject Custom_AssetBundle` par `spawnOverlay("maxmove", fig, {speed=selectedSpeed, baseSize=unitData.baseSize})`. - -### 3.4 Deployment Boundary - -| Composant | Détail | -|---|---| -| Trigger spawn | `spawnDeploymentBoundary(matrix)` au setup (depuis menu UI Deployment) | -| Trigger clear | `clearDeploymentBoundary()` (menu UI Remove Overlay) + standby global | -| Decal PNG | `deployment_red.png` + `deployment_blue.png` — rectangle plein uniforme α=0.5, 256×256px | -| Decal params | Par cellule de la matrix, `position = grid pos + offset`, `scale = (6, 6, 6)` (taille standard d'une cellule SWL), rotation selon `deployRotations[cell]` | -| Vector lines | Contour rectangle drapant (4 côtés × N segments via raycast) par cellule, couleur match decal | -| Couleur | Rouge #FF0000 / Bleu #0000FF α=0.5 | -| Variantes | Half/L/Corner/Round → combinaisons de cellules dans la matrix, le manager gère chaque cellule individuellement (1 decal + 1 contour par cellule) | - -API conservée : `spawnDeploymentBoundary(matrix)`, `clearDeploymentBoundary()`. Internement, chaque cellule est enregistrée comme un overlay distinct avec un GUID synthétique (ex `"deployment:bs:5:3"`). - -## 4. Assets PNG à générer - -| Fichier | Dimensions | Description | Hosting | -|---|---|---|---| -| `cohesion_halo.png` | 512×512 | Gradient radial blanc centre → transparent bord, RGBA | iron-squadron.fr/tts-assets/ | -| `range_bands.png` | 1024×1024 | 4 anneaux concentriques aux rayons relatifs 0.25/0.5/0.75/1.0, gradient discret (pas blend), couleurs jaune/orange/rouge/magenta, alpha 0.6 | iron-squadron.fr/tts-assets/ | -| `range_smoke.png` | 256×256 | 1 anneau à rayon relatif 1.0, blanc opaque | idem | -| `range_token.png` | 256×256 | 1 anneau à rayon relatif 1.0, jaune | idem | -| `range_tokenRangeTwo.png` | 256×256 | 2 anneaux, jaune + orange | idem | -| `range_poi.png` | 256×256 | 1 anneau pour POI (range 0.5 / 3") | idem | -| `range_bombCart.png` | 256×256 | 2 anneaux | idem | -| `max_move_disk.png` | 512×512 | Disque plein bleu ciel #55CCFF α=0.48, RGBA | idem | -| `deployment_red.png` | 256×256 | Carré rouge plein #FF0000 α=0.5 | idem | -| `deployment_blue.png` | 256×256 | Carré bleu plein #0000FF α=0.5 | idem | - -**Génération** : script Python Pillow `generate_overlay_assets.py` qui produit tous les PNG d'un coup. Reproductible, versionnable dans `swlegion-tts/tool/`. - -**Upload** : `rsync` vers le VPS Iron Squadron, dossier `/var/www/iron-squadron/tts-assets/` (ou similaire). URL stable indépendante de Steam. - -## 5. Edge cases et invariants - -| Cas | Gestion | -|---|---| -| Multi-overlays simultanés (plusieurs figs avec cohesion + range) | Cohabitent dans la table `activeOverlays`, un `redrawAll()` au moindre changement | -| Fig détruite (mort en combat) | `onObjectDestroy` → `purgeForFig` → redraw | -| Pickup pendant overlay actif | `onObjectPickUp` → buffer dans `hiddenWhilePickedUp`, retire du rendu | -| Drop après pickup | `onObjectDrop` → restore depuis buffer, redraw (positions recalculées par les builders) | -| Pickup d'un objet sans overlay | Handler skip silencieux (test sur présence dans la table) | -| Reload de partie | `activeOverlays` se reset au boot (variable Lua locale du Global), OK | -| Filtres `standbyTokens` / `removeLockedRulers` (scans `getAllObjects()` par nom) | Deviennent caducs (plus d'Object physique). À supprimer ou laisser comme no-op | -| Test multi-joueurs réseau | Le manager Global est synchronisé par TTS (Lua sync), donc tous les joueurs voient les mêmes overlays | -| Drape impossible sur certains terrains custom | Limitation acceptable (déjà flag par Ben). Le decal plat sert de fallback visuel | - -## 6. Chemin de migration (séquentiel, testable étape par étape) - -1. **Setup Manager Global** : créer `mod/src/includes/Overlays.ttslua`. Aucun caller encore branché. Test : `npm run compile && load mod`, no-op confirmé -2. **Cohesion** (60 lignes, le plus simple) : réécrire `mod/src/includes/Cohesion.ttslua` pour rediriger vers le manager. Test : spawn via hotkey + bouton + dropCoroutine sur Mac -3. **Range Ruler** : réécrire `mod/src/includes/RangeRulers.ttslua` + adapter `POI_Token.lua` + `TokenWithRangeRuler.ttslua`. Test : 4 anneaux + tokens -4. **Maximum Move** : adapter `Order_Token.a57c41.lua:552-574` (spawn inline → call manager) + `clearMovementTemplates`. Test : flow mouvement complet sur Mac -5. **Deployment** : réécrire `SETUP_CONTROLLER.1cb552.lua:243-323`. Test : sélection scenario + spawn de la zone -6. **Cleanup** : supprimer les filtres `standbyTokens` / `removeLockedRulers` pour les types qui ne sont plus des Objects. Garder le filtre pour Movement Template (mesh, toujours Object) -7. **PR sur swlegion/tts** : branche `mac-overlays-refactor`, description du bug + solution + diff. Test communauté - -## 7. Compatibilité conservée - -- **Tous les sites de require existants** continuent à fonctionner sans modif (`require('!/Cohesion')`, `require('!/RangeRulers')`) -- **Toutes les fonctions globales conservent leur signature** (`spawnCohesionRuler(fig)`, `clearRangeRulers()`, etc.) -- **Pas de change d'API publique** → callers (Unit_Leader, Order_Token, POI_Token, etc.) inchangés -- **Variables d'état globales** comme `selectedUnitObj` conservées pour compat avec les filtres et la logique de toggle - -## 8. Risques connus à surveiller - -| Risque | Mitigation | -|---|---| -| Performance multi-overlays (N décals + N×64 vectors) | Tester empiriquement sur table chargée. Si lag, sparse raycast (32 segments) ou skip raycast si table plate détectée | -| Synchronisation decal/vector lines pas pixel-perfect | Vector lines au-dessus du decal (Y légèrement supérieur), tolérance visuelle | -| URL Iron Squadron offline | Hosting sur 2 destinations (VPS + GitHub Pages backup) ou inclure le CDN dans le repo si TTS supporte les data URIs | -| Bug U6 sur les decals aussi (improbable, POC validé le 14 mai) | Garder Path B = ticket Berserk en parallèle | -| Refactor casse un caller obscur | Test exhaustif des 5 sites de spawn + tous les triggers connus | - -## 9. Annexes - -- `materials-reference.md` : tous les params visuels extraits -- `retro-cohesion.md`, `retro-range.md`, `retro-deployment.md`, `retro-movement.md` : déclencheurs et call graphs -- `swlegion-tts/mod/src/includes/` : code source des modules à patcher diff --git a/mac-patcher/extract_projector_specs.py b/mac-patcher/extract_projector_specs.py deleted file mode 100644 index e699f8f1..00000000 --- a/mac-patcher/extract_projector_specs.py +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env python3 -""" -Scan Unity .mat and .prefab YAML files in UnityProject-U6/Assets/Projectors/ -and extract critical parameters for the SWL TTS mod refactor. - -Output: materials-reference.md with structured tables per overlay. -""" -import os -import re -from pathlib import Path -from collections import defaultdict - -ROOT = Path("/Users/martinpourrat/MARTIN/Star Wars Legion/Mod TTS SWL/UnityProject-U6/Assets/Projectors") -OUT = Path("/Users/martinpourrat/MARTIN/Star Wars Legion/Mod TTS SWL/materials-reference.md") - -OVERLAYS = ["Cohesion", "Range", "Deployment", "Movement"] - - -def read(p: Path) -> str: - return p.read_text(encoding="utf-8", errors="replace") - - -def parse_material(path: Path) -> dict: - """Extract critical fields from a Unity .mat YAML file.""" - text = read(path) - out = {"path": str(path.relative_to(ROOT)), "name": path.stem} - - m = re.search(r"m_ShaderKeywords:\s*(.*)", text) - out["keywords"] = m.group(1).strip() if m else "" - - m = re.search(r"m_Shader:\s*\{fileID:\s*\d+,\s*guid:\s*([0-9a-f]+)", text) - out["shader_guid"] = m.group(1) if m else "" - - # Floats — capture key params - floats = {} - for fname in ["_BaseSize", "_ProjectorRadius", "_BandSize", "_BandContrast", - "_MaxRange", "_Arc", "_RangeSize", "_GradScaler", - "_OneTuner", "_TwoTuner", "_ThreeTuner", "_FourTuner", - "_FiveTuner", "_InfinityTuner"]: - m = re.search(rf"-\s*{re.escape(fname)}:\s*(-?[\d.]+)", text) - if m: - floats[fname] = m.group(1) - out["floats"] = floats - - # Colors — capture all _Color* / _Range* - colors = {} - for m in re.finditer(r"-\s*(_\w+):\s*\{r:\s*([\d.]+),\s*g:\s*([\d.]+),\s*b:\s*([\d.]+),\s*a:\s*([\d.]+)\}", text): - name, r, g, b, a = m.groups() - if name in ("_BumpScale",): # not a color - continue - colors[name] = (float(r), float(g), float(b), float(a)) - out["colors"] = colors - - return out - - -def parse_prefab(path: Path) -> dict: - """Extract critical fields from a Unity .prefab YAML file (Projector components).""" - text = read(path) - out = {"path": str(path.relative_to(ROOT)), "name": path.stem} - - # Count Projector components (Unity type id 119) - projectors = re.findall(r"---\s*!u!119\s*&\d+\s*\nProjector:(.*?)(?=\n---|\Z)", text, re.DOTALL) - out["n_projectors"] = len(projectors) - - proj_data = [] - for body in projectors: - d = {} - for fname in ["m_OrthographicSize", "m_FarClipPlane", "m_NearClipPlane", - "m_FieldOfView", "m_AspectRatio", "m_Orthographic"]: - m = re.search(rf"{fname}:\s*(-?[\d.]+)", body) - if m: - d[fname] = m.group(1) - m = re.search(r"m_Material:\s*\{fileID:\s*\d+,\s*guid:\s*([0-9a-f]+)", body) - if m: - d["material_guid"] = m.group(1) - proj_data.append(d) - out["projectors"] = proj_data - - # Transforms (LocalPosition Y, rotation) - transforms = [] - for body in re.findall(r"---\s*!u!4\s*&\d+\s*\nTransform:(.*?)(?=\n---|\Z)", text, re.DOTALL): - d = {} - m = re.search(r"m_LocalPosition:\s*\{x:\s*(-?[\d.]+),\s*y:\s*(-?[\d.]+),\s*z:\s*(-?[\d.]+)\}", body) - if m: - d["pos"] = (float(m.group(1)), float(m.group(2)), float(m.group(3))) - m = re.search(r"m_LocalEulerAnglesHint:\s*\{x:\s*(-?[\d.]+),\s*y:\s*(-?[\d.]+),\s*z:\s*(-?[\d.]+)\}", body) - if m: - d["rot"] = (float(m.group(1)), float(m.group(2)), float(m.group(3))) - transforms.append(d) - out["transforms"] = transforms - - return out - - -def rgba_to_hex(r, g, b, a): - """Convert 0-1 RGBA to hex + alpha note.""" - rh, gh, bh = int(r * 255), int(g * 255), int(b * 255) - return f"#{rh:02X}{gh:02X}{bh:02X} (α={a:.2f})" - - -def fmt_colors(colors): - """Format dict of colors as compact markdown.""" - if not colors: - return "—" - parts = [] - for name, (r, g, b, a) in colors.items(): - if a == 0 and r == 0 and g == 0 and b == 0: - parts.append(f"`{name}` transparent") - else: - parts.append(f"`{name}` {rgba_to_hex(r, g, b, a)}") - return "
".join(parts) - - -def fmt_floats(floats): - """Format dict of floats.""" - if not floats: - return "—" - return ", ".join(f"`{k}`={v}" for k, v in floats.items()) - - -def main(): - materials_by_overlay = defaultdict(list) - prefabs_by_overlay = defaultdict(list) - - for overlay in OVERLAYS: - for p in sorted((ROOT / overlay).rglob("*.mat")): - if p.suffix == ".meta": - continue - materials_by_overlay[overlay].append(parse_material(p)) - for p in sorted((ROOT / overlay).rglob("*.prefab")): - if p.suffix == ".meta": - continue - prefabs_by_overlay[overlay].append(parse_prefab(p)) - - # Build the master doc - lines = [] - lines.append("# Materials & Prefabs Reference — SWL TTS Projector Overlays") - lines.append("") - lines.append("Extraction directe des `.mat` et `.prefab` Unity (YAML) dans `UnityProject-U6/Assets/Projectors/`. Source de vérité pour reproduire le rendu en vector lines.") - lines.append("") - lines.append("Notation : couleurs en hex + alpha. Champs `Floats` clés du shader projector (paramètres procéduraux des anneaux).") - lines.append("") - - for overlay in OVERLAYS: - lines.append(f"## {overlay}") - lines.append("") - - # Materials table - lines.append("### Materials") - lines.append("") - lines.append("| Material | Shader keywords | Floats clés | Couleurs |") - lines.append("|---|---|---|---|") - for m in materials_by_overlay[overlay]: - kw = m["keywords"] or "—" - lines.append(f"| `{m['name']}` | `{kw}` | {fmt_floats(m['floats'])} | {fmt_colors(m['colors'])} |") - lines.append("") - - # Prefabs table - lines.append("### Prefabs") - lines.append("") - lines.append("| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid |") - lines.append("|---|---|---|---|---|") - for p in prefabs_by_overlay[overlay]: - n = p["n_projectors"] - proj_sizes = [pr.get("m_OrthographicSize", "?") for pr in p["projectors"]] - proj_far = [pr.get("m_FarClipPlane", "?") for pr in p["projectors"]] - proj_summary = "
".join(f"size={s}, far={f}" for s, f in zip(proj_sizes, proj_far)) if proj_sizes else "—" - - tr_summary = "
".join( - f"pos=({t.get('pos', ('?',)*3)[0]}, {t.get('pos', ('?',)*3)[1]}, {t.get('pos', ('?',)*3)[2]}) rot=({t.get('rot', ('?',)*3)[0]}, {t.get('rot', ('?',)*3)[1]}, {t.get('rot', ('?',)*3)[2]})" - for t in p["transforms"] - ) if p["transforms"] else "—" - - mat_guids = [pr.get("material_guid", "?") for pr in p["projectors"]] - mat_summary = "
".join(g[:12] + "…" for g in mat_guids) if mat_guids else "—" - - lines.append(f"| `{p['name']}` | {n} | {proj_summary} | {tr_summary} | {mat_summary} |") - lines.append("") - - # Final notes - lines.append("## Notes globales") - lines.append("") - lines.append("- Cohesion : double set `Materials/cohesion_*` et `_Revamp/halfCohesion_*`. Probable : `_Revamp/` est la version actuellement utilisée (le bundle live s'appelle `halfcohesion_27mm.unity3d` cf shader-inventory).") - lines.append("- Range : shader procédural avec jusqu'à 5 bandes (`_RangeOne` à `_RangeFive`) + `_RangeInfinity`. Le keyword `_MAXRANGE_RANGEFIVE` ou `_MAXRANGE_RANGEFOUR` détermine la dernière bande visible.") - lines.append("- Movement : un seul Projector par prefab, paramétré par `_BaseSize` + speed. Le `_Arc` doit contrôler la portion d'arc visible.") - lines.append("- Deployment : 6 materials uniques réutilisés par 12 prefabs (rotations différentes pour matérialiser corner/half/L/round/score).") - - OUT.write_text("\n".join(lines), encoding="utf-8") - print(f"Wrote {OUT}") - # Stats - total_mats = sum(len(v) for v in materials_by_overlay.values()) - total_prefabs = sum(len(v) for v in prefabs_by_overlay.values()) - print(f"Scanned {total_mats} materials and {total_prefabs} prefabs across {len(OVERLAYS)} overlays.") - - -if __name__ == "__main__": - main() diff --git a/mac-patcher/generate_overlay_assets.py b/mac-patcher/generate_overlay_assets.py deleted file mode 100644 index d11b1932..00000000 --- a/mac-patcher/generate_overlay_assets.py +++ /dev/null @@ -1,282 +0,0 @@ -#!/usr/bin/env python3 -""" -Generate PNG assets for the Mac Cohesion + Range overlay fallback. -""" -from pathlib import Path -from PIL import Image, ImageDraw -import math - -OUT_DIR = Path(__file__).parent / "tts-assets" -OUT_DIR.mkdir(exist_ok=True) - - -# Range band colors (R, G, B in 0..255) — from BB_RangeProjector defaults -RANGE_COLORS = { - "half": (255, 255, 255), # 0.5 / 3" — white - "one": (255, 195, 0), # R1 / 6" — yellow - "two": (255, 117, 0), # R2 / 12" — orange - "three": (255, 20, 0), # R3 / 18" — red - "four": (195, 0, 66), # R4 / 24" — magenta - "five": (134, 0, 112), # R5 / 30" — violet -} - -# Configurations per overlay type (bands = list of (radius_inches, color_key)) -# Edge-based convention: each band's effective world radius is band_r + base_r. -RANGE_BAND_CONFIGS = { - "fig_leader": { - "bands": [(3, "half"), (6, "one"), (12, "two"), - (18, "three"), (24, "four"), (30, "five")], - "max_radius": 30, - }, - # SWL ranges in inches: Range 1 = 6", Range 2 = 12", Range 0.5 (poi) = 3". - "smokeToken": {"bands": [(6, "one")], "max_radius": 6}, - "token": {"bands": [(6, "one")], "max_radius": 6}, - "tokenRangeTwo": {"bands": [(6, "one"), (12, "two")], "max_radius": 12}, - "poi": {"bands": [(3, "one")], "max_radius": 3}, - "bombCart": {"bands": [(6, "one"), (12, "two")], "max_radius": 12}, -} - -# Base radii (inches, half-diameter) per ROUND fig base size. Oblong bases -# (long, snail) live in OBLONG_DIMENSIONS_MM below — they don't use a single -# radius and don't generate round PNGs. -FIG_BASE_RADIUS_IN = { - "small": 27 / 2 / 25.4, - "medium": 50 / 2 / 25.4, - "large": 70 / 2 / 25.4, - "huge": 100 / 2 / 25.4, - "laat": 120 / 2 / 25.4, - "epic": 150 / 2 / 25.4, -} - -# Token base radii (inches), only needed for multi-band tokens. -TOKEN_BASE_RADIUS_IN = { - "tokenRangeTwo": 25.1 / 2 / 25.4, - "bombCart": 50.0 / 2 / 25.4, -} - - -def _with_base_offset(config: dict, base_r: float) -> dict: - """Shift band radii outward by base_r and grow max_radius accordingly so - the PNG band proportions match the world positions of the vector-line - rings (which are drawn at band_r + base_r).""" - return { - "bands": [(r + base_r, c) for r, c in config["bands"]], - "max_radius": config["max_radius"] + base_r, - } - - -def generate_cohesion_halo(size: int = 512) -> Path: - """Halo radial blanc fade transparent — reproduit le rendu Cohesion bundle. - - Le shader BB_CohesionProjector fait `rangeOne = (1 - grad)` puis multiplie - par alpha global. On reproduit ce fade : opaque au centre, transparent au - bord. - """ - img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) - pixels = img.load() - cx, cy = size / 2, size / 2 - max_r = size / 2 - - # Alpha global du material Cohesion 27mm = 0.37 ; on multiplie par le fade - base_alpha = 0.37 - - for y in range(size): - for x in range(size): - dx, dy = x - cx, y - cy - dist = math.sqrt(dx * dx + dy * dy) - r_norm = dist / max_r # [0, 1+] depuis le centre - if r_norm > 1.0: - continue # transparent au-delà du rayon - - # Fade radial : (1 - r) avec courbe douce - # Le shader fait (1 - grad) linéaire, on garde la même chose - fade = max(0.0, 1.0 - r_norm) - alpha = int(base_alpha * fade * 255) - pixels[x, y] = (255, 255, 255, alpha) - - out = OUT_DIR / "cohesion_halo.png" - img.save(out) - return out - - -def generate_range_band_png(name: str, config: dict, size: int = 1024, - alpha: float = 0.2) -> Path: - """Generate a circular range overlay with concentric bands. - - Drawing from outer-to-inner: each filled ellipse overlays the previous, - producing clean rings. - """ - img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) - draw = ImageDraw.Draw(img) - cx = cy = size / 2 - max_r_pix = size / 2 - a = int(alpha * 255) - - for band_r, color_key in reversed(config["bands"]): - r_pix = (band_r / config["max_radius"]) * max_r_pix - r, g, b = RANGE_COLORS[color_key] - draw.ellipse( - [(cx - r_pix, cy - r_pix), (cx + r_pix, cy + r_pix)], - fill=(r, g, b, a), - ) - - out = OUT_DIR / f"range_{name}.png" - img.save(out) - return out - - -# Oblong base dimensions (mm). Used to render stadium-shape range overlays -# whose 6 bands stay concentric to a rectangular footprint with rounded caps. -OBLONG_DIMENSIONS_MM = { - "long": {"width": 100, "length": 175}, - "snail": {"width": 100, "length": 200}, -} - - -def generate_range_band_stadium_png(base_size: str, dims_mm: dict, - config: dict, alpha: float = 0.2, - pixels_per_inch: int = 16) -> Path: - """Generate a stadium-shape PNG with 6 concentric range bands. - - The PNG dimensions encode the *outermost* band's bounding box (so the - decal can be stretched in TTS using scale = {shortSpan, longSpan, 1} and - the bands stay aligned with the matching vector stadium outlines). - - A stadium = rounded rectangle whose corner radius equals half the - short dimension, producing a pill shape with semicircular caps on the - long-axis ends. Each band is one rounded_rectangle of width - 2*(halfWid + band_r) and length 2*(halfLen + band_r), corner radius - halfWid + band_r. Drawn outer-to-inner so each band overpaints the next. - """ - halfWid_in = (dims_mm["width"] / 2) / 25.4 - halfLen_in = (dims_mm["length"] / 2) / 25.4 - max_dist = config["max_radius"] - - shortSpan_in = 2 * (halfWid_in + max_dist) - longSpan_in = 2 * (halfLen_in + max_dist) - - W = int(round(shortSpan_in * pixels_per_inch)) - H = int(round(longSpan_in * pixels_per_inch)) - cx, cy = W / 2, H / 2 - - img = Image.new("RGBA", (W, H), (0, 0, 0, 0)) - draw = ImageDraw.Draw(img) - a = int(alpha * 255) - - for band_r, color_key in reversed(config["bands"]): - half_w_px = (halfWid_in + band_r) * pixels_per_inch - half_h_px = (halfLen_in + band_r) * pixels_per_inch - r, g, b = RANGE_COLORS[color_key] - draw.rounded_rectangle( - [(cx - half_w_px, cy - half_h_px), - (cx + half_w_px, cy + half_h_px)], - radius=half_w_px, # = half of the short side → semicircle caps - fill=(r, g, b, a), - ) - - out = OUT_DIR / f"range_fig_leader_{base_size}_stadium.png" - img.save(out) - return out - - -def generate_filled_disk(name: str, color_rgb: tuple, size: int = 512, - alpha: float = 0.2) -> Path: - """Generate a simple filled disk PNG (used for Maximum Move overlay).""" - img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) - draw = ImageDraw.Draw(img) - cx = cy = size / 2 - r = size / 2 - a = int(alpha * 255) - draw.ellipse([(cx - r, cy - r), (cx + r, cy + r)], - fill=(color_rgb[0], color_rgb[1], color_rgb[2], a)) - out = OUT_DIR / f"{name}.png" - img.save(out) - return out - - -def generate_filled_square(name: str, color_rgb: tuple, size: int = 256, - alpha: float = 0.3) -> Path: - """Generate a filled square PNG (used for Deployment Boundary cells).""" - img = Image.new("RGBA", (size, size), - (color_rgb[0], color_rgb[1], color_rgb[2], int(alpha * 255))) - out = OUT_DIR / f"{name}.png" - img.save(out) - return out - - -def generate_silhouette_top_disc(size: int = 512) -> Path: - """Hologram-style top cap for the Mac silhouette cylinder. - - Radial gradient: bright green-cyan center fades to transparent at the - rim. Combined with the vector wireframe cylinder this gives the - Star Wars hologram briefing look (Leia "Help me Obi-Wan" projection). - """ - img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) - pixels = img.load() - cx, cy = size / 2, size / 2 - max_r = size / 2 - - r_center, g_center, b_center = 102, 255, 178 # bright hologram cyan-green - a_center = 0.85 - - for y in range(size): - for x in range(size): - dx, dy = x - cx, y - cy - dist = math.sqrt(dx * dx + dy * dy) - r_norm = dist / max_r - if r_norm > 1.0: - continue - # Smooth radial falloff (1-r squared) for a soft hologram glow - fade = (1.0 - r_norm) ** 1.5 - alpha = int(a_center * fade * 255) - pixels[x, y] = (r_center, g_center, b_center, alpha) - - out = OUT_DIR / "silhouette_top.png" - img.save(out) - return out - - -def main(): - p = generate_cohesion_halo() - print(f"Generated {p} ({p.stat().st_size} bytes)") - - # Fig leaders (round bases): one PNG per base size, bands shifted by base radius. - fig_cfg = RANGE_BAND_CONFIGS["fig_leader"] - for base_size, br in FIG_BASE_RADIUS_IN.items(): - out = generate_range_band_png(f"fig_leader_{base_size}", - _with_base_offset(fig_cfg, br)) - print(f"Generated {out} ({out.stat().st_size} bytes)") - - # Oblong bases: stadium-shape PNGs (rectangular footprint + rounded caps). - for base_size, dims in OBLONG_DIMENSIONS_MM.items(): - out = generate_range_band_stadium_png(base_size, dims, fig_cfg) - print(f"Generated {out} ({out.stat().st_size} bytes)") - - # Multi-band tokens: bake in their base radius for alignment. - for name, br in TOKEN_BASE_RADIUS_IN.items(): - cfg = RANGE_BAND_CONFIGS[name] - out = generate_range_band_png(name, _with_base_offset(cfg, br)) - print(f"Generated {out} ({out.stat().st_size} bytes)") - - # Single-band tokens: no offset adjustment needed (band sits at outer edge). - for name in ("smokeToken", "token", "poi"): - out = generate_range_band_png(name, RANGE_BAND_CONFIGS[name]) - print(f"Generated {out} ({out.stat().st_size} bytes)") - - # Maximum Move: cyan filled disk (#55CCFF) - p = generate_filled_disk("max_move_cyan", (85, 204, 255), alpha=0.2) - print(f"Generated {p} ({p.stat().st_size} bytes)") - - # Deployment Boundary: red and blue filled squares (alpha 0.5 = match bundle) - p = generate_filled_square("deployment_red", (255, 0, 0), alpha=0.3) - print(f"Generated {p} ({p.stat().st_size} bytes)") - p = generate_filled_square("deployment_blue", (0, 0, 255), alpha=0.3) - print(f"Generated {p} ({p.stat().st_size} bytes)") - - # Silhouette top cap: hologram-style green/cyan radial gradient. - p = generate_silhouette_top_disc() - print(f"Generated {p} ({p.stat().st_size} bytes)") - - -if __name__ == "__main__": - main() diff --git a/mac-patcher/inspect_cohesion.py b/mac-patcher/inspect_cohesion.py deleted file mode 100644 index e26becbe..00000000 --- a/mac-patcher/inspect_cohesion.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python3 -"""Inspect Cohesion bundle structure: list objects, materials, shaders, and the m_Shader PPtr.""" -import os -import sys -import UnityPy - -BUNDLE = os.path.expanduser( - "~/Library/Tabletop Simulator/Mods/Assetbundles/" - "httpssteamusercontentaakamaihdnetugc2482129948496305632EBBE2560D4336E6C96317EDDA787E225CF0E5B48.unity3d" -) - -env = UnityPy.load(BUNDLE) - -print("=" * 80) -print("BUNDLE METADATA") -print("=" * 80) -for f in env.files.values(): - print(f" file: {type(f).__name__}") - if hasattr(f, "unity_version"): - print(f" unity_version: {f.unity_version}") - if hasattr(f, "target_platform"): - print(f" target_platform: {f.target_platform}") - if hasattr(f, "externals"): - print(f" externals (cross-bundle refs):") - for ext in getattr(f, "externals", []): - print(f" - {ext}") - -print() -print("=" * 80) -print("ALL OBJECTS") -print("=" * 80) -for obj in env.objects: - path_id = getattr(obj, "path_id", "?") - type_name = obj.type.name - try: - data = obj.read() - name = getattr(data, "m_Name", "") or "" - except Exception as e: - name = f"" - print(f" PathID={path_id:<6} type={type_name:<15} name={name!r}") - -print() -print("=" * 80) -print("MATERIAL DETAILS (looking for Cohesion_27mm)") -print("=" * 80) -for obj in env.objects: - if obj.type.name != "Material": - continue - data = obj.read() - print(f"\nMaterial PathID={obj.path_id} name={data.m_Name!r}") - sref = data.m_Shader - print(f" m_Shader PPtr:") - print(f" file_id = {getattr(sref, 'file_id', '?')} " - f"(0 = internal to this bundle, !=0 = external file)") - print(f" path_id = {getattr(sref, 'path_id', '?')}") - # Try to resolve - try: - sobj = sref.deref() - if sobj: - sd = sobj.read() - sname = ( - getattr(getattr(sd, "m_ParsedForm", None), "m_Name", None) - or getattr(sd, "m_Name", None) - ) - print(f" resolved -> Shader name = {sname!r}") - else: - print(f" resolved -> None (external or missing)") - except Exception as e: - print(f" resolve error: {e!r}") - -print() -print("=" * 80) -print("SHADER DETAILS") -print("=" * 80) -for obj in env.objects: - if obj.type.name != "Shader": - continue - data = obj.read() - sname = ( - getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) - or getattr(data, "m_Name", None) - ) - print(f" PathID={obj.path_id} Shader name={sname!r}") diff --git a/mac-patcher/inspect_externals.py b/mac-patcher/inspect_externals.py deleted file mode 100644 index c9307a21..00000000 --- a/mac-patcher/inspect_externals.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -"""For each bundle: dump its externals (referenced external files) and list materials with external m_Shader PPtr.""" -import os -import sys -import UnityPy - -BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") - -bundles = sorted(f for f in os.listdir(BUNDLE_DIR) if f.endswith(".unity3d")) - -# We are most interested in bundles whose materials use external shaders (file_id != 0) -print("=" * 90) -print("BUNDLES WITH EXTERNAL m_Shader REFERENCES") -print("=" * 90) - -for fname in bundles: - path = os.path.join(BUNDLE_DIR, fname) - short_id = fname.replace("httpssteamusercontentaakamaihdnetugc", "")[:20] - env = UnityPy.load(path) - - external_mats = [] - for obj in env.objects: - if obj.type.name != "Material": - continue - try: - data = obj.read() - sref = data.m_Shader - fid = getattr(sref, "file_id", None) - pid = getattr(sref, "path_id", None) - if fid and fid != 0: - external_mats.append((data.m_Name, fid, pid)) - except Exception: - pass - - if not external_mats: - continue - - print(f"\n{short_id}:") - # Dump externals table - for f in env.files.values(): - if hasattr(f, "externals"): - print(f" externals table:") - for i, ext in enumerate(getattr(f, "externals", [])): - # The externals entry usually has a 'path' and 'guid' - ext_path = getattr(ext, "path", "?") - ext_guid = getattr(ext, "guid", "?") - # index i+1 because file_id is 1-based for externals (file_id=0 means self) - print(f" file_id={i+1} path={ext_path!r} guid={ext_guid}") - print(f" materials with external m_Shader:") - for name, fid, pid in external_mats: - print(f" material={name!r} m_Shader=(file_id={fid}, path_id={pid})") diff --git a/mac-patcher/materials-reference.md b/mac-patcher/materials-reference.md deleted file mode 100644 index eabaae36..00000000 --- a/mac-patcher/materials-reference.md +++ /dev/null @@ -1,170 +0,0 @@ -# Materials & Prefabs Reference — SWL TTS Projector Overlays - -Extraction directe des `.mat` et `.prefab` Unity (YAML) dans `UnityProject-U6/Assets/Projectors/`. Source de vérité pour reproduire le rendu en vector lines. - -Notation : couleurs en hex + alpha. Champs `Floats` clés du shader projector (paramètres procéduraux des anneaux). - -## Cohesion - -### Materials - -| Material | Shader keywords | Floats clés | Couleurs | -|---|---|---|---| -| `Cohesion_27mm` | `_ARC_OFF _MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=4.479488, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.37)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #000000 (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_50mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=5.470629, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_70mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=6.850394, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_27mm` | `_ARC_OFF _MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=3.531496, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.37)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #000000 (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_50mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=3.984252, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | -| `Cohesion_70mm` | `_MAXRANGE_RANGEONE _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=4.3779526, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.67)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #FFFFFF (α=0.30)
`_MoveTwo` #FF0000 (α=1.00)
`_RangeFive` #FFFF00 (α=1.00)
`_RangeFour` #00FF00 (α=1.00)
`_RangeInfinity` #7F7F7F (α=1.00)
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF6C00 (α=1.00)
`_RangeTwo` #0000FF (α=1.00) | - -### Prefabs - -| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | -|---|---|---|---|---| -| `cohesion_27mm` | 1 | size=4.547244, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | ebe2e870a59f… | -| `cohesion_50mm` | 1 | size=5.669291, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 406845c49cf4… | -| `cohesion_70mm` | 1 | size=6.850394, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3a57b0b27e32… | -| `halfCohesion_27mm` | 1 | size=3.531496, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 0e1c90ce1ef6… | -| `halfCohesion_50mm` | 1 | size=3.984252, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 2e05a24ab516… | -| `halfCohesion_70mm` | 1 | size=4.3779526, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 7cee44cc736f… | - -## Range - -### Materials - -| Material | Shader keywords | Floats clés | Couleurs | -|---|---|---|---| -| `ProjectorMaterial_100mm` | `_ARC_ON _MAXRANGE_RANGEFIVE _RANGE_FIVE` | `_BaseSize`=100, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.0183, `_TwoTuner`=0.0062, `_ThreeTuner`=0.0245, `_FourTuner`=0.0123, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_100mm_oblong` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=47.4, `_ProjectorRadius`=30, `_BandSize`=0.34, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=-0.022, `_TwoTuner`=-0.0421, `_ThreeTuner`=-0.0324, `_FourTuner`=-0.0525, `_FiveTuner`=0.073, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_100mm_snail` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=47.4, `_ProjectorRadius`=30, `_BandSize`=0.34, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=-0.0216, `_TwoTuner`=-0.0421, `_ThreeTuner`=-0.0324, `_FourTuner`=-0.0525, `_FiveTuner`=0.073, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_120mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=120, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.022, `_TwoTuner`=0.0074, `_ThreeTuner`=0.0292, `_FourTuner`=0.0146, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_150mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=150, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.027, `_TwoTuner`=0.009, `_ThreeTuner`=0.0358, `_FourTuner`=0.018, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_18mm_smokeToken` | `_ARC_OFF _MAXRANGE_RANGEONE` | `_BaseSize`=18.8, `_ProjectorRadius`=6, `_BandSize`=-0.11, `_BandContrast`=0.46, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #3A3145 (α=0.59)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFFFFF (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_25mm_token` | `_ARC_OFF _MAXRANGE_RANGEONE` | `_BaseSize`=27, `_ProjectorRadius`=2.97, `_BandSize`=-0.45, `_BandContrast`=3, `_MaxRange`=0, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_25mm_tokenRangeTwo` | `_ARC_OFF _MAXRANGE_RANGETWO` | `_BaseSize`=28.12, `_ProjectorRadius`=14.9, `_BandSize`=0.04, `_BandContrast`=3, `_MaxRange`=1, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0062, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_27mm` | `_ARC_OFF _MAXRANGE_RANGEFIVE` | `_BaseSize`=27, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=0, `_RangeSize`=-76.5, `_GradScaler`=0.0034, `_OneTuner`=0.0053, `_TwoTuner`=0.0019, `_ThreeTuner`=0.0072, `_FourTuner`=0.0037, `_FiveTuner`=0, `_InfinityTuner`=0.0034 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_50mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=50, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=3, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_GradScaler`=0, `_OneTuner`=0.0098, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0127, `_FourTuner`=0.0063, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_50mm_bombCart` | `_ARC_OFF _MAXRANGE_RANGETWO` | `_BaseSize`=50, `_ProjectorRadius`=12, `_BandSize`=0.01, `_BandContrast`=3, `_MaxRange`=1, `_Arc`=0, `_RangeSize`=-76.5, `_OneTuner`=0, `_TwoTuner`=0.0034, `_ThreeTuner`=0.0034, `_FourTuner`=0.0034, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.60)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `ProjectorMaterial_70mm` | `_ARC_ON _MAXRANGE_RANGEFIVE` | `_BaseSize`=70, `_ProjectorRadius`=30, `_BandSize`=0.278, `_BandContrast`=2.87, `_MaxRange`=4, `_Arc`=1, `_RangeSize`=-76.5, `_OneTuner`=0.0132, `_TwoTuner`=0.0045, `_ThreeTuner`=0.0175, `_FourTuner`=0.009, `_FiveTuner`=0, `_InfinityTuner`=0 | `_Color` #FFFFFF (α=0.35)
`_EmissionColor` #000000 (α=1.00)
`_RangeFive` #860070 (α=1.00)
`_RangeFour` #C20042 (α=1.00)
`_RangeInfinity` transparent
`_RangeOne` #FFC300 (α=1.00)
`_RangeThree` #FF1400 (α=1.00)
`_RangeTwo` #FF7500 (α=1.00) | -| `Projector_BoardGuide` | `m_LightmapFlags: 4` | — | `_CameraFadeParams` transparent
`_Color` #FFFFFF (α=1.00)
`_ColorAddSubDiff` transparent
`_EmissionColor` #000000 (α=1.00)
`_SoftParticleFadeParams` transparent
`_TintColor` #7F7F7F (α=0.50) | - -### Prefabs - -| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | -|---|---|---|---|---| -| `projector_100mm` | 1 | size=31.9685, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 4b37ff3d68c5… | -| `projector_100mm_oblong` | 1 | size=33.4, far=45 | pos=(0.0, -1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 6c0556b105e8… | -| `projector_120mm` | 1 | size=32.3622, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | ab767c454926… | -| `projector_150mm` | 1 | size=32.95276, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3a33cb678c17… | -| `projector_200mm_oblong` | 1 | size=33.4, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0)
pos=(-0.0, -0.26, -0.0) rot=(0.0, 0.0, 90.0) | cccc683b4b18… | -| `projector_27mm` | 1 | size=30.5314, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | b1c5ef617632… | -| `projector_50mm` | 1 | size=30.984253, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 20ce39a8e0e1… | -| `projector_70mm` | 1 | size=31.37795, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 977480df799a… | -| `projector_POIGuide` | 1 | size=36, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | a0fb1f64d52e… | -| `projector_bomb_cart` | 1 | size=12.98425, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 417ae88dafb1… | -| `projector_smokeToken` | 1 | size=6.3700786, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 3ef1e0f12697… | -| `projector_token` | 1 | size=7.06299, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 94ad9a7a030f… | -| `projector_tokenRangeTwo` | 1 | size=12.6, far=45 | pos=(0.0, 20.0, 0.0) rot=(90.0, 0.0, 0.0) | 0905fd93d0b7… | - -## Deployment - -### Materials - -| Material | Shader keywords | Floats clés | Couleurs | -|---|---|---|---| -| `Projector_Deployment_Blue` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Deployment_Blue_Round` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Deployment_Red` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Deployment_Red_Round` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Score_Blue` | `m_LightmapFlags: 4` | — | `_Color` #0000FF (α=0.50)
`_EmissionColor` #000000 (α=1.00) | -| `Projector_Score_Red` | `m_LightmapFlags: 4` | — | `_Color` #FF0000 (α=0.50)
`_EmissionColor` #000000 (α=1.00) | - -### Prefabs - -| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | -|---|---|---|---|---| -| `projector_blue_score` | 1 | size=6, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | fe66dad2ac93… | -| `projector_blue_spawn` | 1 | size=3, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 23c778da5a02… | -| `projector_blue_spawn_L` | 2 | size=1.5, far=25
size=3, far=25 | pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0)
pos=(1.5, 1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(0.0, -1.5000002, 0.0) rot=(0.0, 0.0, 90.0) | 23c778da5a02…
23c778da5a02… | -| `projector_blue_spawn_corner` | 1 | size=1.5, far=25 | pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0)
pos=(-1.5, -1.5, 0.0) rot=(0.0, 0.0, 90.0) | 23c778da5a02… | -| `projector_blue_spawn_half` | 1 | size=3, far=25 | pos=(0.0, -1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(10.65, 10.71, -19.76) rot=(90.0, 0.0, 0.0) | 23c778da5a02… | -| `projector_blue_spawn_round2` | 1 | size=12, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 34786d447192… | -| `projector_red_score` | 1 | size=6, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 90fdc53ff83c… | -| `projector_red_spawn` | 1 | size=3, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 93e068805588… | -| `projector_red_spawn_L` | 2 | size=3, far=25
size=1.5, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0)
pos=(0.0, 1.5, 0.0) rot=(0.0, 0.0, 90.0)
pos=(-1.5, -1.5, -0.0) rot=(0.0, 0.0, 90.0) | 93e068805588…
93e068805588… | -| `projector_red_spawn_corner` | 1 | size=1.5, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0)
pos=(1.5, 1.5, -0.0) rot=(0.0, 0.0, 90.0) | 93e068805588… | -| `projector_red_spawn_half` | 1 | size=3, far=25 | pos=(0.0, 1.5, -0.0) rot=(0.0, 0.0, 90.0)
pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 93e068805588… | -| `projector_red_spawn_round2` | 1 | size=12, far=25 | pos=(0.08, 10.71, -19.42) rot=(90.0, 0.0, 0.0) | 54824e17d05f… | - -## Movement - -### Materials - -| Material | Shader keywords | Floats clés | Couleurs | -|---|---|---|---| -| `ProjectorMaterial_100mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=8.622047, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_100mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=10.590551, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_100mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=12.559055, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_120mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=9.803149, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_120mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=11.771653, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_120mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.740157, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_150mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=11.574803, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_150mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.543307, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_150mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=15.511811, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed1_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=8.45, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=4.547244, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed2_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=12.42748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=6.515748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed3_double` | `_MAXRANGE_RANGEINFINITY _MOVES_DOUBLE` | `_BaseSize`=27, `_ProjectorRadius`=16.42748, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_27mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=27, `_ProjectorRadius`=8.484252, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_50mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=5.6692915, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_50mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=7.6377954, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_50mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=50, `_ProjectorRadius`=9.606299, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_70mm_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=6.850394, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_70mm_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=8.818897, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_70mm_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=10.787401, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_oblong_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=13.051181, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_oblong_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=15.019685, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_oblong_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=16.98819, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_snail_speed1_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=14.527559, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_snail_speed2_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=16.496063, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | -| `ProjectorMaterial_snail_speed3_single` | `_MAXRANGE_RANGEINFINITY _MOVES_SINGLE` | `_BaseSize`=70, `_ProjectorRadius`=18.464567, `_MaxRange`=5, `_RangeSize`=-76.5 | `_Base` #FFFFFF (α=1.00)
`_Color` #FFFFFF (α=0.48)
`_EmissionColor` #000000 (α=1.00)
`_MoveOne` #55CCFF (α=1.00)
`_MoveTwo` #357A97 (α=1.00)
`_RangeFive` #FFFF6D (α=1.00)
`_RangeFour` #FFD600 (α=1.00)
`_RangeInfinity` #FFFFFF (α=0.47)
`_RangeOne` #6C0000 (α=1.00)
`_RangeThree` #FF7B00 (α=1.00)
`_RangeTwo` #DA0000 (α=1.00) | - -### Prefabs - -| Prefab | Nb Projectors | OrthographicSize / FarClip | Transform pos/rot | Material guid | -|---|---|---|---|---| -| `movement_100mm_speed1_single` | 1 | size=8.622047, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 406c1ddb0995… | -| `movement_100mm_speed2_single` | 1 | size=10.590551, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 0aa471d50563… | -| `movement_100mm_speed3_single` | 1 | size=12.559055, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 8fe5008dea10… | -| `movement_120mm_speed1_single` | 1 | size=9.803149, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 21bc9fa53646… | -| `movement_120mm_speed2_single` | 1 | size=11.771653, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 6d02674694a9… | -| `movement_120mm_speed3_single` | 1 | size=13.740157, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 895a4d7a1f02… | -| `movement_150mm_speed1_single` | 1 | size=11.574803, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | fef40c5dadca… | -| `movement_150mm_speed2_single` | 1 | size=13.543307, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | c9d6b007a730… | -| `movement_150mm_speed3_single` | 1 | size=15.511811, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | d08a6fca8013… | -| `movement_27mm_speed1_double` | 1 | size=8.45, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 7037d7245d82… | -| `movement_27mm_speed1_single` | 1 | size=4.547244, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 61da0f6e009e… | -| `movement_27mm_speed2_double` | 1 | size=12.42748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 362084914234… | -| `movement_27mm_speed2_single` | 1 | size=6.515748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | 6de57d74ffda… | -| `movement_27mm_speed3_double` | 1 | size=16.42748, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | a0dce2ad51d5… | -| `movement_27mm_speed3_single` | 1 | size=8.484252, far=25 | pos=(0.08, 13.63, -19.42) rot=(90.0, 0.0, 0.0) | b65b690fde07… | -| `movement_50mm_speed1_single` | 1 | size=5.6692915, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 38f1f5f6a60c… | -| `movement_50mm_speed2_single` | 1 | size=7.6377954, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 3d2f279db06b… | -| `movement_50mm_speed3_single` | 1 | size=9.606299, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | ca54633a2d92… | -| `movement_70mm_speed1_single` | 1 | size=6.850394, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 3d2faeaaaaff… | -| `movement_70mm_speed2_single` | 1 | size=8.818897, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 948d0799a0c3… | -| `movement_70mm_speed3_single` | 1 | size=10.787401, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | a986001c0bc6… | -| `movement_oblong_speed1_single` | 1 | size=13.051181, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | b9540fc3dad6… | -| `movement_oblong_speed2_single` | 1 | size=15.019685, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | 672e50dcd47c… | -| `movement_oblong_speed3_single` | 1 | size=16.98819, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | a2468c9fe07d… | -| `movement_snail_speed1_single` | 1 | size=14.527559, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | b7c44d9516fd… | -| `movement_snail_speed2_single` | 1 | size=16.496063, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | d27d5e30c83c… | -| `movement_snail_speed3_single` | 1 | size=18.464567, far=25 | pos=(0.0, 13.63, 0.0) rot=(90.0, 0.0, 0.0) | ecdda5f21c4c… | - -## Notes globales - -- Cohesion : double set `Materials/cohesion_*` et `_Revamp/halfCohesion_*`. Probable : `_Revamp/` est la version actuellement utilisée (le bundle live s'appelle `halfcohesion_27mm.unity3d` cf shader-inventory). -- Range : shader procédural avec jusqu'à 5 bandes (`_RangeOne` à `_RangeFive`) + `_RangeInfinity`. Le keyword `_MAXRANGE_RANGEFIVE` ou `_MAXRANGE_RANGEFOUR` détermine la dernière bande visible. -- Movement : un seul Projector par prefab, paramétré par `_BaseSize` + speed. Le `_Arc` doit contrôler la portion d'arc visible. -- Deployment : 6 materials uniques réutilisés par 12 prefabs (rotations différentes pour matérialiser corner/half/L/round/score). \ No newline at end of file diff --git a/mac-patcher/patch_cohesion_rename.py b/mac-patcher/patch_cohesion_rename.py deleted file mode 100644 index 065a14a9..00000000 --- a/mac-patcher/patch_cohesion_rename.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python3 -""" -PATCH ATTEMPT #1 — Rename embedded Shader m_Name from -'BucketheadBits/Projector/Cohesion' to 'Standard'. - -Theory: at AssetBundle.LoadAsset, Unity may resolve the shader by name via -Shader.Find() before falling back to the embedded bytecode. If so, TTS picks -up its own built-in Standard shader and renders the Cohesion projector with -it, ignoring the broken embedded shader. - -Writes to .patched.unity3d alongside the original. Does NOT touch -the live bundle in ~/Library/Tabletop Simulator/Mods/Assetbundles/ yet. -""" -import os -import sys -import shutil -import UnityPy - -BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") -BUNDLE_NAME = "httpssteamusercontentaakamaihdnetugc2482129948496305632EBBE2560D4336E6C96317EDDA787E225CF0E5B48.unity3d" -SRC = os.path.join(BUNDLE_DIR, BUNDLE_NAME) -DST = os.path.join("/tmp", "cohesion.patched.unity3d") - -print(f"Loading: {SRC}") -env = UnityPy.load(SRC) - -modified = False -for obj in env.objects: - if obj.type.name != "Shader": - continue - data = obj.read() - current = getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) or getattr(data, "m_Name", None) - print(f" Found Shader PathID={obj.path_id} current m_Name={current!r}") - if current != "BucketheadBits/Projector/Cohesion": - print(" -> Not our target, skipping.") - continue - - # Modify via typetree (safest cross-version path with UnityPy) - tree = obj.read_typetree() - # m_ParsedForm.m_Name is the canonical name for parsed shaders. - # m_Name may also exist for the asset itself; we try both. - if "m_ParsedForm" in tree and "m_Name" in tree["m_ParsedForm"]: - old = tree["m_ParsedForm"]["m_Name"] - tree["m_ParsedForm"]["m_Name"] = "Standard" - print(f" -> renamed m_ParsedForm.m_Name {old!r} -> 'Standard'") - modified = True - if "m_Name" in tree and tree.get("m_Name"): - old = tree["m_Name"] - tree["m_Name"] = "Standard" - print(f" -> renamed m_Name {old!r} -> 'Standard'") - modified = True - - obj.save_typetree(tree) - -if not modified: - print("ERROR: no shader matched. Aborting.") - sys.exit(1) - -# Write out the patched bundle -print(f"\nSaving patched bundle to: {DST}") -with open(DST, "wb") as f: - f.write(env.file.save(packer="lz4")) - -print(f" size: {os.path.getsize(DST)} bytes (original was {os.path.getsize(SRC)})") - -# Verify roundtrip -print("\nVerifying patched bundle is re-loadable...") -env2 = UnityPy.load(DST) -for obj in env2.objects: - if obj.type.name == "Shader": - d = obj.read() - n = getattr(getattr(d, "m_ParsedForm", None), "m_Name", None) or getattr(d, "m_Name", None) - print(f" After patch: Shader PathID={obj.path_id} m_Name={n!r}") - elif obj.type.name == "Material": - d = obj.read() - sref = d.m_Shader - print(f" Material {d.m_Name!r} m_Shader=(file_id={sref.file_id}, path_id={sref.path_id})") - -print("\nDone. Patched file ready at:", DST) -print("Run this command to replace the live bundle (only when TTS is closed):") -print(f" cp {DST!r} {SRC!r}") diff --git a/mac-patcher/patch_save_for_mac.py b/mac-patcher/patch_save_for_mac.py deleted file mode 100644 index 24bd5e22..00000000 --- a/mac-patcher/patch_save_for_mac.py +++ /dev/null @@ -1,2171 +0,0 @@ -#!/usr/bin/env python3 -""" -Patch a TTS save JSON (SWL mod beta) to add Mac Cohesion fallback. - -Two-part patch: -1. Append manager + event handlers to the Global LuaScript. -2. Replace the inline Cohesion block in Unit_Leader (99f1c8) and Order_Token - (a57c41) with delegated stubs that call the Global manager via Global.call. - -The native hotkey "Show Cohesion On Hovered Model" and the Order Token -"COHESION" button automatically use the patched code afterwards - they -spawn vector-lines rings instead of legacy Custom_AssetBundle Projectors. - -Usage: - python3 patch_save_for_mac.py -""" -import json -import re -import socket -import sys -from pathlib import Path - -# ------------------------------------------------------------ -# 1) Code appended to Global LuaScript (manager + event handlers) -# ------------------------------------------------------------ -GLOBAL_PATCH_LUA = r""" - --- ============================================ --- MAC COHESION FALLBACK (auto-injected for TTS U6 magenta bug) --- Manager Global + event handlers + vector-lines rendering. --- Called by Unit_Leader / Order_Token via Global.call from their patched --- Cohesion functions. --- ============================================ - --- Capture the ORIGINAL Global functions loaded by !/RangeRulers and --- !/Cohesion BEFORE we overwrite them below. We also capture the inner --- spawn/clear pair because the toggle wrapper resolves them by NAME at --- call time - without aliasing those too, the original toggle would route --- back into our Mac versions, defeating Windows mode entirely. -spawnRangeRulerOriginalGlobal = spawnRangeRuler -clearRangeRulersOriginalGlobal = clearRangeRulers -spawnCohesionRulerOriginalGlobal = spawnCohesionRuler -clearCohesionRulerOriginalGlobal = clearCohesionRuler - -activeOverlays = activeOverlays or {} -hiddenWhilePickedUp = hiddenWhilePickedUp or {} - -local ASSETS_BASE = "https://raw.githubusercontent.com/ironsquadronfr-hub/tts/mac-projector-fallback/mod/data/mac-fallback-assets/" -local COHESION_HALO_URL = ASSETS_BASE .. "cohesion_halo.png" -local MAX_MOVE_CYAN_URL = ASSETS_BASE .. "max_move_cyan.png" -local DEPLOYMENT_RED_URL = ASSETS_BASE .. "deployment_red.png" -local DEPLOYMENT_BLUE_URL = ASSETS_BASE .. "deployment_blue.png" -local RANGE_DECAL_URLS = { - smokeToken = ASSETS_BASE .. "range_smokeToken.png", - token = ASSETS_BASE .. "range_token.png", - tokenRangeTwo = ASSETS_BASE .. "range_tokenRangeTwo.png", - poi = ASSETS_BASE .. "range_poi.png", - bombCart = ASSETS_BASE .. "range_bombCart.png", - -- Fig leaders: per-base-size PNGs (bands pre-offset by the base radius so - -- the decal bands line up with the vector-line rings). - fig_leader = { - small = ASSETS_BASE .. "range_fig_leader_small.png", - medium = ASSETS_BASE .. "range_fig_leader_medium.png", - large = ASSETS_BASE .. "range_fig_leader_large.png", - huge = ASSETS_BASE .. "range_fig_leader_huge.png", - laat = ASSETS_BASE .. "range_fig_leader_laat.png", - epic = ASSETS_BASE .. "range_fig_leader_epic.png", - long = ASSETS_BASE .. "range_fig_leader_long_stadium.png", - snail = ASSETS_BASE .. "range_fig_leader_snail_stadium.png", - }, -} -local RANGE_MAX_RADIUS = { - fig_leader = 30, -- R5 max - -- SWL: Range 1 = 6", Range 2 = 12", Range 0.5 = 3". - smokeToken = 6, - token = 6, - tokenRangeTwo = 12, - poi = 3, - bombCart = 12, -} - --- Range band colors (RGBA) extracted from BB_RangeProjector material defaults --- + demi-portee blanche pour SWL Range 0.5 (3 inches) -local RANGE_COLORS = { - half = {1.0, 1.0, 1.0, 0.6}, -- 0.5 = 3" white - one = {1.0, 0.764, 0.0, 0.7}, -- R1 yellow - two = {1.0, 0.459, 0.0, 0.7}, -- R2 orange - three = {1.0, 0.079, 0.0, 0.7}, -- R3 red - four = {0.764, 0.0, 0.259, 0.7}, -- R4 magenta - five = {0.528, 0.0, 0.443, 0.7}, -- R5 violet -} - --- Per-rangeKey configurations: list of {radius_inches, color_name} -local RANGE_CONFIGS = { - -- Fig leaders: half + 4 bands at SWL standard ranges 3/6/12/18/24" - small = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - medium = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - large = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - huge = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - laat = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - epic = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - long = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - snail = {{r=3,c="half"},{r=6,c="one"},{r=12,c="two"},{r=18,c="three"},{r=24,c="four"},{r=30,c="five"}}, - -- Token rangeKeys (1 to 2 bands depending on use). SWL ranges in inches: - -- Range 1 = 6", Range 2 = 12", Range 0.5 (poi) = 3". - smokeToken = {{r=6,c="one"}}, - token = {{r=6,c="one"}}, - tokenRangeTwo = {{r=6,c="one"},{r=12,c="two"}}, - poi = {{r=3,c="one"}}, - bombCart = {{r=6,c="one"},{r=12,c="two"}}, -} - --- Base footprint dimensions in mm. width <= length; equal means round. --- Used to drive round vs stadium overlay shape. -local BASE_DIMENSIONS = { - small = {width = 27, length = 27}, - medium = {width = 50, length = 50}, - large = {width = 70, length = 70}, - huge = {width = 100, length = 100}, - laat = {width = 120, length = 120}, - epic = {width = 150, length = 150}, - long = {width = 100, length = 175}, -- oblong - snail = {width = 100, length = 200}, -- oblong -} - --- Base radius (inches) for round overlays. For oblong bases we use a --- stadium shape instead (see macBuildStadium), so the radius here is the --- half-width that drives the cap arcs. -local FIG_BASE_RADIUS_IN = {} -for k, d in pairs(BASE_DIMENSIONS) do - FIG_BASE_RADIUS_IN[k] = (d.width / 2) / 25.4 -end - -local function macIsOblong(baseSize) - local d = BASE_DIMENSIONS[baseSize] - return d ~= nil and d.length > d.width -end - --- Returns the stadium half-dimensions (inches) and yaw for an oblong base, --- or nil if the fig's base is round. Callers should branch on the return. -local function macOblongDims(fig, baseSize) - if not macIsOblong(baseSize) then return nil end - local d = BASE_DIMENSIONS[baseSize] - return { - halfLen = (d.length / 2) / 25.4, - halfWid = (d.width / 2) / 25.4, - rotY = fig.getRotation().y, - } -end - --- Resolve baseSize for a fig. The mod stores it as a script-local Var on the --- fig itself (set at spawn from the unit's container). unitData.baseSize is --- only populated on the Unit Leader, not on minis/vehicles - so getVar is --- the canonical read, with unitData as fallback for safety. -local function macGetBaseSize(fig) - if not fig then return nil end - local ok, bs = pcall(function() return fig.getVar("baseSize") end) - if ok and bs then return bs end - local data = fig.getTable("unitData") - if data and data.baseSize then return data.baseSize end - return nil -end - -local function macFigBaseRadius(fig) - if not fig then return 0.5315 end - local bs = macGetBaseSize(fig) - return (bs and FIG_BASE_RADIUS_IN[bs]) or 0.5315 -end - --- Token base radii (inches) - measured from token edge for range bands -local TOKEN_BASE_RADIUS = { - smokeToken = 18.8 / 2 / 25.4, -- 0.370" - token = 25.1 / 2 / 25.4, -- 0.494" - tokenRangeTwo = 25.1 / 2 / 25.4, -- 0.494" - poi = 50.8 / 2 / 25.4, -- 1.000" - bombCart = 50.0 / 2 / 25.4, -- 0.984" -} - --- Hotkey-driven range on tokens renders fig-leader-style bands using the --- closest equivalent base size (so the visual matches what a unit of that --- footprint would see). Token R button keeps the single ring (token-spec). -local TOKEN_TO_BASESIZE = { - smokeToken = "small", - token = "small", - tokenRangeTwo = "small", - poi = "medium", - bombCart = "medium", -} - --- Maximum Move radii (inches), indexed by baseSize then speed 1..3 --- Source: ProjectorRadius from BB_MovementProjector materials (single variants) -local MAX_MOVE_RADIUS = { - small = { 4.547244, 6.515748, 8.484252}, -- 27mm - medium = { 5.669292, 7.637795, 9.606299}, -- 50mm - large = { 6.850394, 8.818897, 10.787401}, -- 70mm - huge = { 8.622047, 10.590551, 12.559055}, -- 100mm - laat = { 9.803149, 11.771653, 13.740157}, -- 120mm - epic = {11.574803, 13.543307, 15.511811}, -- 150mm - long = {13.051181, 15.019685, 16.988190}, -- oblong - snail = {14.527559, 16.496063, 18.464567}, -- snail -} - --- Deployment cell dimensions (width X, depth Z, inches). --- All cells live on a 6"x6" grid; sub-cells are halves/quarters of that cell. -local DEPLOYMENT_CELL_SIZES = { - r = {6, 6}, b = {6, 6}, -- full cell - rl = {6, 6}, bl = {6, 6}, -- large variant (also full cell) - rh = {6, 3}, bh = {6, 3}, -- horizontal half (3" deep) - rs = {3, 6}, bs = {3, 6}, -- vertical half (3" wide), x-offset +1.5 - rss = {3, 6}, bss = {3, 6}, -- vertical half (3" wide), x-offset -1.5 - rc = {3, 3}, bc = {3, 3}, -- corner quarter - rcc = {3, 3}, bcc = {3, 3}, -- opposite corner quarter -} - -local function macRayGroundY(x, z, offset) - local hits = Physics.cast({ - origin = {x, 30, z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 50, - }) - for _, h in ipairs(hits) do - return h.point.y + (offset or 0.05) - end - return offset or 0.05 -end - -local function macBuildRect(cx, cz, hw, hd, color, thickness) - local segPerSide = 8 - local pts = {} - local corners = { - {cx - hw, cz + hd}, {cx + hw, cz + hd}, - {cx + hw, cz - hd}, {cx - hw, cz - hd}, - } - for i = 1, 4 do - local a = corners[i] - local b = corners[i % 4 + 1] - for s = 0, segPerSide - 1 do - local t = s / segPerSide - local x = a[1] + (b[1] - a[1]) * t - local z = a[2] + (b[2] - a[2]) * t - table.insert(pts, {x, macRayGroundY(x, z), z}) - end - end - local c1 = corners[1] - table.insert(pts, {c1[1], macRayGroundY(c1[1], c1[2]), c1[2]}) - return {points = pts, color = color, thickness = thickness or 0.06} -end - -local function macBuildRing(centerPos, radius, color, thickness, ignoreObj) - local nSeg = 64 - local pts = {} - for i = 0, nSeg do - local a = (i / nSeg) * 2 * math.pi - local x = centerPos.x + radius * math.cos(a) - local z = centerPos.z + radius * math.sin(a) - local hits = Physics.cast({ - origin = {x, centerPos.y + 10, z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local y = centerPos.y + 0.05 - for _, h in ipairs(hits) do - if h.hit_object ~= ignoreObj then - y = h.point.y + 0.05 - break - end - end - table.insert(pts, {x, y, z}) - end - return {points = pts, color = color, thickness = thickness or 0.05} -end - --- Stadium = rectangle with two semicircular caps on the long-axis ends. --- halfLen, halfWid in inches (halfLen >= halfWid). dist = expansion outwards --- from the base edge in inches (so the overlay sits at dist outside the base). --- rotYdeg rotates the stadium around Y to match the fig's orientation; the --- long axis is local Z (front-to-back of the model). -local function macBuildStadium(centerPos, halfLen, halfWid, rotYdeg, dist, color, thickness, ignoreObj) - local rotRad = math.rad(rotYdeg or 0) - local cosR, sinR = math.cos(rotRad), math.sin(rotRad) - - local capCenter = halfLen - halfWid -- distance from center to cap arc center, along long axis - local capR = halfWid + dist -- cap arc radius (= straight-side offset from long axis) - - local nSegArc = 32 -- segments per semicircle cap - local pts = {} - - local function addLocal(lx, lz) - local wx = centerPos.x + lx * cosR + lz * sinR - local wz = centerPos.z - lx * sinR + lz * cosR - local hits = Physics.cast({ - origin = {wx, centerPos.y + 10, wz}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local y = centerPos.y + 0.05 - for _, h in ipairs(hits) do - if h.hit_object ~= ignoreObj then - y = h.point.y + 0.05 - break - end - end - table.insert(pts, {wx, y, wz}) - end - - -- Top cap: angle 0 to pi (right edge -> top -> left edge), centered at z=+capCenter - for i = 0, nSegArc do - local a = (i / nSegArc) * math.pi - addLocal(capR * math.cos(a), capCenter + capR * math.sin(a)) - end - -- The straight left side is implicit: the polyline jumps from - -- (-capR, +capCenter) directly to (-capR, -capCenter) - TTS draws a - -- straight segment between consecutive points. - -- Bottom cap: angle pi to 2pi, centered at z=-capCenter - for i = 0, nSegArc do - local a = math.pi + (i / nSegArc) * math.pi - addLocal(capR * math.cos(a), -capCenter + capR * math.sin(a)) - end - -- Close the loop back to the top-cap start point. - addLocal(capR, capCenter) - - return {points = pts, color = color, thickness = thickness or 0.05} -end - -local macBuilders = {} - -macBuilders.range = function(fig, params) - local pos = fig.getPosition() - -- Resolve rangeKey for token-specific single-ring rendering (R button on - -- a token). The hotkey path sets params.forceFigMode = true to bypass - -- this and always render the fig-leader 6-band overlay, using the token's - -- equivalent baseSize for sizing. - local forceFig = params and params.forceFigMode - local rangeKey = (not forceFig) and ((params and params.rangeKey) or fig.getVar("rangeKey")) or nil - local config, decalURL, maxR, br - local oblong, halfLen, halfWid, rotY - local baseSize -- hoisted: used below for firing-arc decision - if rangeKey and RANGE_CONFIGS[rangeKey] then - config = RANGE_CONFIGS[rangeKey] - decalURL = RANGE_DECAL_URLS[rangeKey] - maxR = RANGE_MAX_RADIUS[rangeKey] - br = TOKEN_BASE_RADIUS[rangeKey] or 0 - else - baseSize = macGetBaseSize(fig) - local hasBaseSize = baseSize ~= nil - if not hasBaseSize then - -- Token hit from hotkey: map rangeKey to base size for sizing. - local tk = fig.getVar("rangeKey") - baseSize = (tk and TOKEN_TO_BASESIZE[tk]) or "small" - end - config = RANGE_CONFIGS[baseSize] or RANGE_CONFIGS.small - decalURL = RANGE_DECAL_URLS.fig_leader[baseSize] - or RANGE_DECAL_URLS.fig_leader.small - maxR = RANGE_MAX_RADIUS.fig_leader - if hasBaseSize then - br = macFigBaseRadius(fig) - local od = macOblongDims(fig, baseSize) - if od then - oblong, halfLen, halfWid, rotY = true, od.halfLen, od.halfWid, od.rotY - end - else - -- Token in forceFig mode: use token's measured radius for offset. - local tk = fig.getVar("rangeKey") - br = (tk and TOKEN_BASE_RADIUS[tk]) or FIG_BASE_RADIUS_IN[baseSize] or 0.5315 - end - end - - -- SWL convention: all distances are measured from the base/token edge. - - -- Vector contours: 6 concentric rings for round bases, 6 concentric - -- stadium outlines for oblong bases (each band sits at band.r outwards - -- from the base edge - same rule, just a different shape). - local lines = {} - for _, band in ipairs(config) do - if oblong then - table.insert(lines, macBuildStadium(pos, halfLen, halfWid, rotY, band.r, - RANGE_COLORS[band.c], 0.1, fig)) - else - table.insert(lines, macBuildRing(pos, band.r + br, RANGE_COLORS[band.c], 0.1, fig)) - end - end - - -- Firing arc lines (4 radial lines dividing into Front/R/Rear/L quadrants). - -- Mirrors the vanilla Projector shader: ProjectorMaterial_ sets - -- _Arc=1 / shader keyword _ARC_ON for every non-27mm base (medium, large, - -- huge, laat, epic, long, snail). 27mm sets _Arc=0. - -- Round bases : lines emanate from the base edge, radial from center. - -- Oblong bases: lines emanate from the centers of the two end - -- semicircles (front cap + rear cap), front lines at +/-45 from the - -- long-axis forward, rear lines at +/-135. - local arcColor = {1, 1, 1, 0.6} - if baseSize and baseSize ~= "small" then - if oblong then - local capOffset = (halfLen or 0) - (halfWid or 0) - local rad = math.rad(rotY) - local fwdX, fwdZ = math.sin(rad), math.cos(rad) - local frontX = pos.x + capOffset * fwdX - local frontZ = pos.z + capOffset * fwdZ - local rearX = pos.x - capOffset * fwdX - local rearZ = pos.z - capOffset * fwdZ - local reach = (halfWid or 0) + (maxR or 6) - local arcs = { - {frontX, frontZ, 45}, -- front-right boundary - {frontX, frontZ, -45}, -- front-left boundary - {rearX, rearZ, 135}, -- rear-right boundary - {rearX, rearZ, -135}, -- rear-left boundary - } - for _, e in ipairs(arcs) do - local cx, cz, deg = e[1], e[2], e[3] - local a = math.rad(rotY + deg) - local sinA, cosA = math.sin(a), math.cos(a) - table.insert(lines, { - points = { - {cx, pos.y + 0.1, cz}, - {cx + reach * sinA, pos.y + 0.1, cz + reach * cosA}, - }, - color = arcColor, - thickness = 0.05, - }) - end - else - local figRotY = fig.getRotation().y - local outerR = (maxR or 6) + br - for _, deg in ipairs({45, 135, 225, 315}) do - local a = math.rad(deg + figRotY) - local sinA, cosA = math.sin(a), math.cos(a) - table.insert(lines, { - points = { - {pos.x + br * sinA, pos.y + 0.1, pos.z + br * cosA}, - {pos.x + outerR * sinA, pos.y + 0.1, pos.z + outerR * cosA}, - }, - color = arcColor, - thickness = 0.05, - }) - end - end - end - - -- Decal halo (filled bands) - flat on the ground under the fig - local hits = Physics.cast({ - origin = {pos.x, pos.y + 10, pos.z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local groundY = pos.y - for _, h in ipairs(hits) do - if h.hit_object ~= fig then groundY = h.point.y; break end - end - - if oblong then - -- Stadium-shape PNGs are authored per oblong base size: the 6 bands - -- inside the PNG already match the stadium vector outlines because - -- the PNG was generated with the same halfWid/halfLen + dist math. - -- Decal scale = footprint of the outermost band (2*(half + maxR)) - -- in each axis, so the PNG stretches to exactly the right size. - local longSpan = 2 * (halfLen + maxR) - local shortSpan = 2 * (halfWid + maxR) - return { - lines = lines, - decals = { - { - name = "range_decal_" .. fig.getGUID(), - url = decalURL, - position = {pos.x, groundY + 0.02, pos.z}, - rotation = {90, rotY, 0}, - scale = {shortSpan, longSpan, 1}, - } - }, - } - end - - local effectiveMaxR = maxR + br - return { - lines = lines, - decals = { - { - name = "range_decal_" .. fig.getGUID(), - url = decalURL, - position = {pos.x, groundY + 0.02, pos.z}, -- below cohesion halo - rotation = {90, 0, 0}, - scale = {effectiveMaxR * 2, effectiveMaxR * 2, effectiveMaxR * 2}, - } - }, - } -end - -macBuilders.maxmove = function(fig, params) - -- Use the anchor position stored at spawn time so the ring stays put - -- when the fig slides toward the destination, instead of tracking it. - local pos = (params and params.anchorPos) or fig.getPosition() - local baseSize = (params and params.baseSize) or "small" - local speed = (params and params.speed) or 1 - local radii = MAX_MOVE_RADIUS[baseSize] or MAX_MOVE_RADIUS.small - local r = radii[speed] or radii[1] - local color = {0.333, 0.800, 1.000, 0.7} - -- Base ring: small white outline matching the figure's base - -- (BB_MovementProjector renders a base ring on top of the cyan max-move). - local br = FIG_BASE_RADIUS_IN[baseSize] or FIG_BASE_RADIUS_IN.small - local baseColor = {1, 1, 1, 0.8} - -- Oblong base dimensions for the stadium base ring. Use the rotation - -- captured at spawn-time so the ring keeps its orientation even after - -- the fig pivots toward its destination. - local od = macOblongDims(fig, baseSize) - local oblong = od ~= nil - local halfLen, halfWid, rotY - if oblong then - halfLen, halfWid = od.halfLen, od.halfWid - rotY = (params and params.anchorRot) or od.rotY - end - - -- Ground raycast for decal position - local hits = Physics.cast({ - origin = {pos.x, pos.y + 10, pos.z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local groundY = pos.y - for _, h in ipairs(hits) do - if h.hit_object ~= fig then groundY = h.point.y; break end - end - - if oblong then - -- Oblong base: the cyan max-move envelope stays a round ring (isotropic - -- - any rotation reaches the same radius from the pivot), but the - -- inner white base ring follows the rectangular footprint as a - -- stadium hugging the actual base edge. - return { - lines = { - macBuildRing(pos, r, color, 0.1, fig), - macBuildStadium(pos, halfLen, halfWid, rotY, 0, - baseColor, 0.06, fig), - }, - decals = { - { - name = "maxmove_decal_" .. fig.getGUID(), - url = MAX_MOVE_CYAN_URL, - position = {pos.x, groundY + 0.025, pos.z}, - rotation = {90, 0, 0}, - scale = {r * 2, r * 2, r * 2}, - } - }, - } - end - - return { - lines = { - macBuildRing(pos, r, color, 0.1, fig), -- outer cyan max-move - macBuildRing(pos, br, baseColor, 0.06, fig), -- inner white base ring - }, - decals = { - { - name = "maxmove_decal_" .. fig.getGUID(), - url = MAX_MOVE_CYAN_URL, - position = {pos.x, groundY + 0.025, pos.z}, - rotation = {90, 0, 0}, - scale = {r * 2, r * 2, r * 2}, - } - }, - } -end - -macBuilders.deployment = function(_, params) - local pos = params.pos -- {x, y, z} cell center (after offset) - local cell = params.cell -- "r"/"b"/"rh"/... key - local size = DEPLOYMENT_CELL_SIZES[cell] or {6, 6} - local w, d = size[1], size[2] - local cx, cz = pos[1], pos[3] - local hw, hd = w / 2, d / 2 - - local isRed = cell:sub(1, 1) == "r" - local lineColor = isRed and {1, 0.15, 0.15, 0.9} or {0.15, 0.4, 1, 0.9} - local url = isRed and DEPLOYMENT_RED_URL or DEPLOYMENT_BLUE_URL - - local groundY = macRayGroundY(cx, cz, 0) - - return { - lines = { macBuildRect(cx, cz, hw, hd, lineColor, 0.08) }, - decals = { - { - name = "deployment_decal_" .. cell .. "_" .. tostring(cx) .. "_" .. tostring(cz), - url = url, - position = {cx, groundY + 0.03, cz}, - rotation = {90, 0, 0}, - scale = {w, d, 1}, - } - }, - } -end - -macBuilders.cohesion = function(fig, params) - local pos = fig.getPosition() - -- Cohesion overlay = base edge + 3 inches (half Range 1). Round bases get - -- a ring; oblong bases (long/snail) get a stadium-shape concentric to the - -- footprint so the offset stays a constant 3" from the edge. - local rangeKey = fig.getVar("rangeKey") - local oblong, halfLen, halfWid, rotY, r - if rangeKey and TOKEN_BASE_RADIUS[rangeKey] then - r = TOKEN_BASE_RADIUS[rangeKey] + 3.0 - else - local baseSize = macGetBaseSize(fig) or "small" - local od = macOblongDims(fig, baseSize) - if od then - oblong, halfLen, halfWid, rotY = true, od.halfLen, od.halfWid, od.rotY - else - r = (FIG_BASE_RADIUS_IN[baseSize] or FIG_BASE_RADIUS_IN.small) + 3.0 - end - end - if params and params.radius then - oblong, r = false, params.radius - end - - -- Find ground level under the fig so the flat decal sits on the table, - -- not at the fig pivot (which is above the base). - local hits = Physics.cast({ - origin = {pos.x, pos.y + 10, pos.z}, - direction = {0, -1, 0}, - type = 1, - max_distance = 20, - }) - local groundY = pos.y - for _, h in ipairs(hits) do - if h.hit_object ~= fig then - groundY = h.point.y - break - end - end - - if oblong then - -- Halo decal: stretch the round halo PNG into an ellipse aligned with - -- the fig's long axis. Approximates the stadium fade (a true stadium - -- halo would need a custom PNG); good enough visually since the - -- vector stadium outline carries the precise boundary. - local longSpan = 2 * (halfLen + 3.0) - local shortSpan = 2 * (halfWid + 3.0) - return { - lines = { macBuildStadium(pos, halfLen, halfWid, rotY, 3.0, - {1, 1, 1, 0.8}, 0.06, fig) }, - decals = { - { - name = "cohesion_halo_" .. fig.getGUID(), - url = COHESION_HALO_URL, - position = {pos.x, groundY + 0.04, pos.z}, - -- 90 deg pitch lays the decal flat on the table; rotY (yaw) - -- aligns its local Y with the fig's long axis. - rotation = {90, rotY, 0}, - scale = {shortSpan, longSpan, 1}, - } - }, - } - end - - return { - lines = { macBuildRing(pos, r, {1, 1, 1, 0.8}, 0.06, fig) }, - decals = { - { - name = "cohesion_halo_" .. fig.getGUID(), - url = COHESION_HALO_URL, - position = {pos.x, groundY + 0.04, pos.z}, - rotation = {90, 0, 0}, - scale = {r * 2, r * 2, r * 2}, - } - }, - } -end - -function macRedrawAll() - local lines, decals = {}, {} - -- Preload all texture URLs (invisible decals far below the table) so TTS - -- keeps them cached and we never get the white-square flash on respawn. - local preloadURLs = { - COHESION_HALO_URL, - RANGE_DECAL_URLS.smokeToken, - RANGE_DECAL_URLS.token, RANGE_DECAL_URLS.tokenRangeTwo, - RANGE_DECAL_URLS.poi, RANGE_DECAL_URLS.bombCart, - RANGE_DECAL_URLS.fig_leader.small, RANGE_DECAL_URLS.fig_leader.medium, - RANGE_DECAL_URLS.fig_leader.large, RANGE_DECAL_URLS.fig_leader.huge, - RANGE_DECAL_URLS.fig_leader.laat, RANGE_DECAL_URLS.fig_leader.epic, - RANGE_DECAL_URLS.fig_leader.long, RANGE_DECAL_URLS.fig_leader.snail, - MAX_MOVE_CYAN_URL, - DEPLOYMENT_RED_URL, DEPLOYMENT_BLUE_URL, - } - for i, url in ipairs(preloadURLs) do - table.insert(decals, { - name = "preload_" .. i, - url = url, - position = {0, -200 - i * 0.01, 0}, - rotation = {90, 0, 0}, - scale = {0.01, 0.01, 0.01}, - }) - end - local stale = {} - for key, entry in pairs(activeOverlays) do - local b = macBuilders[entry.type] - if b then - -- Deployment entries have no fig (params-driven). Others need a - -- live Object - drop them silently if destroyed/invalid. - local needsFig = (entry.type ~= "deployment") - if needsFig and (not entry.fig - or type(entry.fig.getPosition) ~= "function") then - stale[#stale + 1] = key - else - local ok, out = pcall(b, entry.fig, entry.params) - if ok and out then - for _, l in ipairs(out.lines or {}) do - table.insert(lines, l) - end - for _, d in ipairs(out.decals or {}) do - table.insert(decals, d) - end - else - stale[#stale + 1] = key - end - end - end - end - for _, k in ipairs(stale) do activeOverlays[k] = nil end - Global.setVectorLines(lines) - Global.setDecals(decals) -end - -function gSpawnCohesion(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":cohesion"] = { - type = "cohesion", fig = fig, params = params or {} - } - macRedrawAll() -end - -function gClearCohesion(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":cohesion"] = nil - macRedrawAll() -end - -function gToggleCohesion(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local key = fig.getGUID() .. ":cohesion" - if activeOverlays[key] then - gClearCohesion(params) - else - gSpawnCohesion(params) - end -end - --- Range polling: a slow timer (5 frames = ~12 fps) that re-draws active --- range rulers so they follow figures in real-time. Stops automatically when --- no range overlay is active anymore - avoids the overnight memory leak. -macRangePollingActive = macRangePollingActive or false - -function macRangePoll() - local hasRange = false - for _, e in pairs(activeOverlays) do - if e.type == "range" then hasRange = true; break end - end - if hasRange then - macRedrawAll() - Wait.frames(macRangePoll, 5) - else - macRangePollingActive = false - end -end - -function gSpawnRange(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":range"] = { - type = "range", fig = fig, params = params or {} - } - macRedrawAll() - if not macRangePollingActive then - macRangePollingActive = true - Wait.frames(macRangePoll, 5) - end -end - -function gClearRange(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":range"] = nil - macRedrawAll() -end - -function gToggleRange(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local key = fig.getGUID() .. ":range" - if activeOverlays[key] then - gClearRange(params) - else - gSpawnRange(params) - end -end - -function gSpawnDeployment(params) - -- params: { cell = "r" | "b" | ..., pos = {x, y, z} } - if not params or not params.cell or not params.pos then return end - local key = "deploy:" .. params.cell .. ":" - .. tostring(params.pos[1]) .. ":" .. tostring(params.pos[3]) - activeOverlays[key] = {type = "deployment", fig = nil, params = params} - macRedrawAll() -end - -function gClearAllDeployment() - for k, e in pairs(activeOverlays) do - if e.type == "deployment" then activeOverlays[k] = nil end - end - macRedrawAll() -end - -function gSpawnMaxMove(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - -- Capture the spawn-time position + yaw. MaxMove is anchored to where - -- the move STARTED - it must not follow the fig as it slides toward - -- the destination. The yaw matters for oblong stadium overlays so the - -- white base ring keeps its orientation while the fig pivots. - local p = (params and params.params) or params or {} - p.anchorPos = fig.getPosition() - p.anchorRot = fig.getRotation().y - activeOverlays[fig.getGUID() .. ":maxmove"] = { - type = "maxmove", fig = fig, params = p - } - macRedrawAll() -end - -function gClearMaxMove(params) - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - activeOverlays[fig.getGUID() .. ":maxmove"] = nil - macRedrawAll() -end - -function onObjectPickUp(player_color, obj) - if not obj or not obj.getGUID then return end - local guid = obj.getGUID() - local prefix = guid .. ":" - local plen = #prefix - local changed = false - for key, entry in pairs(activeOverlays) do - if key:sub(1, plen) == prefix then - -- Cohesion: static, hide during pickup, restore on drop. - -- Range: keep visible; the polling timer redraws each tick so - -- the ruler follows the figure in real-time during the drag. - if entry.type == "cohesion" then - hiddenWhilePickedUp[key] = entry - activeOverlays[key] = nil - changed = true - end - end - end - if changed then macRedrawAll() end -end - -function onObjectDrop(player_color, obj) - if not obj or not obj.getGUID then return end - local guid = obj.getGUID() - local prefix = guid .. ":" - local plen = #prefix - - -- Defer the overlay restore until the fig has come to rest. Without this, - -- a flick-drop with residual inertia leaves the overlay anchored at the - -- release position while the fig continues to slide across the table. - local function tryRestore() - local fig = getObjectFromGUID(guid) - if not fig then return end -- destroyed during slide - if fig.held_by_color then return end -- picked up again, hold off - local v = fig.getVelocity and fig.getVelocity() - if v then - local speed = math.sqrt((v.x or 0)^2 + (v.y or 0)^2 + (v.z or 0)^2) - if speed > 0.05 then - Wait.frames(tryRestore, 3) - return - end - end - local changed = false - for key, entry in pairs(hiddenWhilePickedUp) do - if key:sub(1, plen) == prefix then - activeOverlays[key] = entry - hiddenWhilePickedUp[key] = nil - changed = true - end - end - if changed then macRedrawAll() end - end - tryRestore() -end - -function onObjectDestroy(obj) - if not obj or not obj.getGUID then return end - local guid = obj.getGUID() - local prefix = guid .. ":" - local plen = #prefix - local changed = false - for key in pairs(activeOverlays) do - if key:sub(1, plen) == prefix then - activeOverlays[key] = nil - changed = true - end - end - for key in pairs(hiddenWhilePickedUp) do - if key:sub(1, plen) == prefix then - hiddenWhilePickedUp[key] = nil - end - end - if changed then macRedrawAll() end -end - --- Texture preload is handled by macRedrawAll which always includes all PNG --- URLs as invisible decals far below the table. No separate preload needed. - --- ============================================ --- PER-SEAT MODE TOGGLE (Cohesion + Range) --- Each seated player picks their renderer: "mac" (this patch) or --- "windows" (original bundle Projector). When a player triggers an overlay, --- the router checks THEIR mode and uses that renderer. The rendered overlay --- is visible to everyone (TTS engine limitation). --- ============================================ - -playerOverlayMode = playerOverlayMode or {} -- color -> "mac" | "windows" --- Default = "windows" (majority of TTS users). Mac users toggle their seat --- via the floating panel. Same for deployment (table-wide setting). -deploymentMode = deploymentMode or "windows" - -function gGetMode(params) - return playerOverlayMode[params.color] or "windows" -end - -function gGetDeploymentMode() - return deploymentMode -end - -function gToggleDeploymentMode() - deploymentMode = (deploymentMode == "mac") and "windows" or "mac" - -- Wipe any active deployment zones so the next setup uses the new mode. - gClearAllDeployment() - macDeferRefresh() -end - --- Replicates the original showRangeOnHoveredModel toggle, calling the --- aliased Global originals so our Mac overrides don't recapture the path. -function gWindowsRangeToggle(fig) - if not fig then return end - if rangeRuler ~= nil then - pcall(clearRangeRulersOriginalGlobal) - if selectedUnitObj == fig then - selectedUnitObj = nil - return - end - end - if fig.interactable then - pcall(function() spawnRangeRulerOriginalGlobal(fig) end) - selectedUnitObj = fig - end -end - --- Replicates the original showCohesionOnHoveredModel toggle. Uses Global --- aliased originals + Global cohesionRuler state (set by the original). -function gWindowsCohesionToggle(fig) - if not fig then return end - if fig.interactable and selectedUnitObj == fig and cohesionRuler ~= nil then - pcall(clearCohesionRulerOriginalGlobal) - selectedUnitObj = nil - return - end - pcall(clearCohesionRulerOriginalGlobal) - pcall(function() spawnCohesionRulerOriginalGlobal(fig) end) - selectedUnitObj = fig -end - -function gCohesionTrigger(params) - if not params or not params.figGUID then return end - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local mode = playerOverlayMode[params.playerColor] or "windows" - if mode == "windows" then - gWindowsCohesionToggle(fig) - else - gToggleCohesion({figGUID = params.figGUID}) - end -end - -function gRangeTrigger(params) - if not params or not params.figGUID then return end - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local mode = playerOverlayMode[params.playerColor] or "windows" - if mode == "windows" then - gWindowsRangeToggle(fig) - else - gToggleRange({ - figGUID = params.figGUID, - forceFigMode = params.forceFigMode, - rangeKey = params.rangeKey, - }) - end -end - --- UI: floating panel with one toggle button per seated player -function macModeClick(player, _, id) - local color = id:sub(9) -- "macmode_Red" -> "Red" - if player.color ~= color then - broadcastToColor("Only the player at this seat can toggle their mode.", - player.color, {1, 0.5, 0.5}) - return - end - local cur = playerOverlayMode[color] or "windows" - playerOverlayMode[color] = (cur == "mac") and "windows" or "mac" - -- Wipe Mac-side overlays so stale visuals don't linger after the toggle. - -- We don't track per-color ownership, so this clears Cohesion+Range for - -- everyone - acceptable since each player can re-trigger their hotkey. - -- Windows-side overlays (fig-scoped cohesionRuler/rangeRuler) are - -- cleared by iterating objects with active state. - for k, e in pairs(activeOverlays) do - if e.type == "cohesion" or e.type == "range" then - activeOverlays[k] = nil - end - end - macRedrawAll() - for _, obj in ipairs(getAllObjects()) do - if obj.getVar and obj.getVar("cohesionRuler") then - pcall(function() obj.call("clearCohesionRulerOriginal", obj) end) - end - if obj.getVar and obj.getVar("rangeRuler") then - pcall(function() obj.call("clearRangeRulersOriginal", obj) end) - end - end - macDeferRefresh() -end - -local function macFindNodeById(tree, id) - for _, node in ipairs(tree) do - if node.attributes and node.attributes.id == id then return node, tree end - if node.children then - local found, parent = macFindNodeById(node.children, id) - if found then return found, parent end - end - end - return nil, nil -end - --- Tracks the panel's desired active state across refreshes. Persists so a --- click on the close X before the initial refresh is honored. Hidden at first --- load (default = false); the floating "Mac Patch" menu button reveals it. -macModePanelActive = (macModePanelActive == nil) and false or macModePanelActive - -function macModeToggleVisibility() - macModePanelActive = not macModePanelActive - -- Defer the XML rebuild: TTS occasionally throws a UTF-8 byte-buffer - -- encoding error if UI.setXmlTable runs synchronously inside a UI - -- click handler. Deferring one frame + pcall has been reliable. - Wait.frames(function() - pcall(function() - local tree = UI.getXmlTable() or {} - local panel = macFindNodeById(tree, "macModePanel") - if panel then - panel.attributes.active = macModePanelActive and "true" or "false" - UI.setXmlTable(tree) - else - macRefreshModeUI() - end - end) - end, 1) -end - -function macRefreshModeUI() - local seated = Player.getPlayers() - local rows = {{ - tag = "Panel", - attributes = { - color = "transparent", - preferredHeight = "24", - }, - children = { - { - tag = "Text", - attributes = { - text = "Mac TTS U6 Patch", - fontSize = "16", - color = "white", - alignment = "MiddleLeft", - rectAlignment = "MiddleLeft", - }, - }, - { - tag = "Button", - attributes = { - id = "macModePanelClose", - text = "X", - onClick = "macModeToggleVisibility", - color = "#3a1e1e", - textColor = "white", - fontSize = "12", - width = "24", - height = "20", - rectAlignment = "MiddleRight", - }, - }, - }, - }, { - tag = "Text", - attributes = { - text = "Cohesion & Range: pick your renderer", - fontSize = "12", - color = "#bbbbbb", - alignment = "MiddleCenter", - }, - }} - if #seated == 0 then - table.insert(rows, { - tag = "Text", - attributes = { - text = "(no seated players)", - fontSize = "12", - color = "#888888", - alignment = "MiddleCenter", - }, - }) - end - for _, p in ipairs(seated) do - local mode = playerOverlayMode[p.color] or "windows" - local label = p.color .. " - " .. - (mode == "mac" and "MAC FALLBACK" or "WINDOWS ORIGINAL") - local bg = (mode == "mac") and "#1e7a3a" or "#7a3a1e" - table.insert(rows, { - tag = "Button", - attributes = { - id = "macmode_" .. p.color, - text = label, - onClick = "macModeClick", - color = bg, - fontSize = "13", - textColor = "white", - preferredHeight = "28", - }, - }) - end - -- Deployment is table-wide (everyone sees the same zones); use a single - -- global toggle rather than per-seat. - table.insert(rows, { - tag = "Text", - attributes = { - text = "Deployment (table-wide)", - fontSize = "12", - color = "#bbbbbb", - alignment = "MiddleCenter", - }, - }) - table.insert(rows, { - tag = "Button", - attributes = { - id = "macmode_deployment", - text = (deploymentMode == "mac") and "DEPLOYMENT: MAC FALLBACK" - or "DEPLOYMENT: WINDOWS ORIGINAL", - onClick = "macDeploymentClick", - color = (deploymentMode == "mac") and "#1e3a7a" or "#7a3a1e", - fontSize = "13", - textColor = "white", - preferredHeight = "28", - }, - }) - -- Build my panel (will be merged into the existing UI tree below so we - -- don't clobber legionFloatingMenu / Welcome / Chess Clocks etc.). - local panel = { - tag = "Panel", - attributes = { - id = "macModePanel", - active = macModePanelActive and "true" or "false", - rectAlignment = "MiddleRight", - offsetXY = "-10 80", - width = "260", - height = "320", - color = "rgba(0.06,0.06,0.06,0.9)", - padding = "8 8 8 8", - outlineSize = "1 1", - outline = "#303030", - }, - children = {{ - tag = "VerticalLayout", - attributes = { - spacing = "4", - childForceExpandHeight = "false", - childForceExpandWidth = "true", - }, - children = rows, - }}, - } - - local tree = UI.getXmlTable() or {} - -- Remove any stale macModePanel before reinserting the fresh one. - for i = #tree, 1, -1 do - if tree[i].attributes and tree[i].attributes.id == "macModePanel" then - table.remove(tree, i) - end - end - table.insert(tree, panel) - - -- Inject a "Mac Patch" button into the bottom-right legionFloatingMenu - -- (replaces the first interactable=false placeholder button). - local menu = macFindNodeById(tree, "legionFloatingMenu") - if menu and menu.children then - local hasMine = false - for _, c in ipairs(menu.children) do - if c.attributes and c.attributes.id == "macModeMenuButton" then - hasMine = true; break - end - end - if not hasMine then - for i, c in ipairs(menu.children) do - if c.attributes and c.attributes.interactable == "false" then - menu.children[i] = { - tag = "Button", - attributes = { - id = "macModeMenuButton", - fontSize = "10", - onClick = "macModeToggleVisibility", - tooltip = "Toggle Mac TTS U6 Patch panel", - }, - value = "Mac Patch", - } - break - end - end - end - end - - UI.setXmlTable(tree) -end - -function macDeploymentClick(_, _, _) - gToggleDeploymentMode() -end - --- Defer one frame: TTS throws a UTF-8 byte-buffer encoding error if we --- rebuild the UI XML synchronously inside a click handler / seat change. --- pcall guards against any residual race. Promoted to a global function --- so macModeClick / macModeToggleVisibility / gToggleDeploymentMode (all --- defined before this point) can call it via name lookup at call time. -function macDeferRefresh() - Wait.frames(function() pcall(macRefreshModeUI) end, 1) -end -function onPlayerChangeColor(_) macDeferRefresh() end -function onPlayerConnect(_) macDeferRefresh() end -function onPlayerDisconnect(_) macDeferRefresh() end - --- Initial UI build, deferred + pcalled like the rest. -Wait.time(function() pcall(macRefreshModeUI) end, 2) - --- Override hotkey init functions to capture playerColor and route through --- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys --- here SHADOWS the originals - when the original onLoad runs init*(), our --- versions register the hotkey instead. - -function initCohesionHotkeys() - addHotkey("Show Cohesion On Hovered Model", - function(playerColor, hoverObject, _) - if not hoverObject or not hoverObject.interactable then return end - gCohesionTrigger({ - figGUID = hoverObject.getGUID(), - playerColor = playerColor, - }) - end) -end - -function initRangebandHotkeys() - addHotkey("Show Range On Hovered Model", - function(playerColor, hoverObject, _) - if not hoverObject or not hoverObject.interactable then return end - gRangeTrigger({ - figGUID = hoverObject.getGUID(), - playerColor = playerColor, - forceFigMode = true, -- hotkey always shows 6-band fig overlay - }) - end) -end - --- The vanilla showRangeOnHoveredModel calls spawnRangeRuler(fig) which spawns --- the Range AssetBundle directly. On Mac, the "Oblong Range" shader used by --- long/snail bundles isn't supported (magenta). Override the global function --- to route through gToggleRange so our stadium overlay renders instead. --- Vanilla Range bundle still works for round bases via gWindowsRangeToggle --- when the player explicitly chose Windows mode. -function showRangeOnHoveredModel(hoverObject) - if not hoverObject or not hoverObject.interactable then return end - gToggleRange({figGUID = hoverObject.getGUID(), forceFigMode = true}) -end - --- Note: we deliberately do NOT override the global spawnRangeRuler / --- clearRangeRulers / showRangeOnHoveredModel chain at the Lua scope where --- the vanilla mod defines it. Overriding spawnRangeRuler from a token --- script reproducibly SIGSEGV'd TTS on Mac in May 2026 (Mono GC stack --- corruption). Mac routing happens via the g* functions called from our --- own hotkey/button wrappers; the originals stay reachable for --- Windows-mode players via spawnRangeRulerOriginalGlobal aliases above. - --- END MAC COHESION FALLBACK -""" - - -# ------------------------------------------------------------ -# 2) Wrapper appended AFTER the original Cohesion include (per-seat router) -# ------------------------------------------------------------ -# Strategy: keep the ORIGINAL Cohesion/RangeRulers expansion intact, then -# append wrappers that alias originals + route through the Global router. -# This preserves the original Custom_AssetBundle Projector spawn for -# Windows-mode players (called back as spawnCohesionRulerOriginal). - -COHESION_WRAPPER = r""" --- MAC PATCH per-seat router (Cohesion) --- Aliases the original spawn so Windows-mode players still get their bundle. -spawnCohesionRulerOriginal = spawnCohesionRuler -clearCohesionRulerOriginal = clearCohesionRuler - -function spawnCohesionRuler(cohesionSourceObject) - if not cohesionSourceObject then return end - Global.call("gCohesionTrigger", { - figGUID = cohesionSourceObject.getGUID(), - playerColor = nil, - }) -end - -function clearCohesionRuler() - if self and self.getGUID and self.getGUID() ~= "-1" then - Global.call("gClearCohesion", { figGUID = self.getGUID() }) - end - if cohesionRuler ~= nil then - pcall(clearCohesionRulerOriginal) - end -end - --- Vanilla Order Token's clearTemplates() calls clearCohesionRulers (plural), --- but the save was built from an older mod version that ships only the --- singular clearCohesionRuler. Provide the plural as a delegating stub so --- standby/clearTemplates/etc. don't nil-error on click. -function clearCohesionRulers() - if selectedUnitObj then - pcall(function() selectedUnitObj.setVar("moveState", false) end) - pcall(function() selectedUnitObj.call("clearCohesionRuler") end) - end -end - --- Same root cause as clearCohesionRulers: newer Order Token code paths in --- the live save (e.g. moveUnit -> stopAttack) call helpers the older bundled --- script doesn't ship. Stub them so MOVE/AIM/etc don't nil-error. The bodies --- mirror the current vanilla source but pcall every dependency so we don't --- chain-error if anything else is also missing. -function clearAttackLine() - if attackLine then - for k, v in pairs(attackLine) do - pcall(destroyObject, v) - end - attackLine = nil - end -end - -function exitTargetingMode() - enemyHighlighted = false - attackModeOn = false - pcall(clearRangeRulers) - pcall(unhighlightEnemies) - pcall(clearAttackLine) -end - -function exitAttackMode() - enemyHighlighted = false - attackModeOn = false - pcall(clearRangeRulers) - pcall(unhighlightEnemies) -end --- END MAC PATCH per-seat router (Cohesion) -""" - - -COHESION_BLOCK_RE = re.compile( - r"----#include !/Cohesion\r?\n.*?----#include !/Cohesion\r?\n", - re.DOTALL -) - -RANGE_BLOCK_RE = re.compile( - r"----#include !/RangeRulers\r?\n.*?----#include !/RangeRulers\r?\n", - re.DOTALL -) - -# Maximum Move spawn block in Order_Token (a57c41), inline (not an include). -# Captures from `local maxMoveBundles = getMovementLinks()` up to and -# including the two closing ends of the original `if isDeploy / if ... ~= nil` -# scaffold. The replacement is self-contained (opens 3 ifs, closes all 3), -# so re-applying yields the same canonical block - idempotent. (Earlier -# versions only matched up to setName("Maximum Move") and re-matched their -# own output, accumulating orphan else/end copies on each re-run.) -MAXMOVE_SPAWN_RE = re.compile( - r"local maxMoveBundles = getMovementLinks\(\).*?" - r"\r?\n end\r?\n end\r?\n", - re.DOTALL -) - -MAXMOVE_SPAWN_REPLACEMENT = r"""local maxMoveBundles = getMovementLinks() - local baseSizeMoveBundles = maxMoveBundles[unitData.baseSize] - local maxMoveTemplateBundleToSpawn = baseSizeMoveBundles and baseSizeMoveBundles[unitData.selectedSpeed] - - -- MAC PATCH: permissive condition (covers nil + false) so the overlay - -- also fires when changeSpeed2/3 calls moveUnit() with no isDeploy arg. - if isDeploy ~= true then - if maxMoveTemplateBundleToSpawn ~= nil then - local _macMode = Global.call("gGetMode", {color = macActivePlayerForMove}) - if _macMode == "windows" then - -- WINDOWS ORIGINAL: spawn Custom_AssetBundle Projector - maxMoveTemplate = spawnObject({ - type = "Custom_AssetBundle", - position = {basePos.x, basePos.y + 20, basePos.z}, - rotation = {0, basePos.y, 0}, - scale = {0,0,0} - }) - maxMoveTemplate.setCustomObject({ - type = 0, - assetbundle = maxMoveTemplateBundleToSpawn - }) - maxMoveTemplate.setLock(true) - maxMoveTemplate.use_gravity = false - maxMoveTemplate.setName("Maximum Move") - else - -- MAC FALLBACK: route through the Global Overlays manager - Global.call("gSpawnMaxMove", { - figGUID = selectedUnitObj.getGUID(), - baseSize = unitData.baseSize, - speed = unitData.selectedSpeed, - }) - maxMoveTemplate = nil - end - end - end -""" - -# clearMovementTemplates function: needs to also clear our managed maxmove -# overlay. We replace the whole function body. -# Match up to the first un-indented `end` (= the function's closing end), so -# the regex is idempotent - it matches both the vanilla body and any -# previously patched body shape (which has extra lines past the inner `end`). -CLEAR_MOVEMENT_RE = re.compile( - r"function clearMovementTemplates\(\).*?\r?\nend\b", - re.DOTALL -) - -CLEAR_MOVEMENT_REPLACEMENT = r"""function clearMovementTemplates() - if templateA ~= nil then - destroyObject(templateA) - end - if templateB ~= nil then - destroyObject(templateB) - end - -- MAC PATCH per-seat: destroy bundle Object if present (Windows-mode - -- spawn) AND clear the manager entry (Mac fallback). Either may be set. - if maxMoveTemplate ~= nil then - pcall(destroyObject, maxMoveTemplate) - end - -- Clear only THIS token's MaxMove (keyed by the attached unit), not - -- everyone's - gClearAllMaxMove was killing other tokens' rings when - -- two players had MOVE open at the same time. - if selectedUnitObj then - Global.call("gClearMaxMove", { figGUID = selectedUnitObj.getGUID() }) - end - maxMoveTemplate = nil -end""" - -# Deployment Boundary: replace the Custom_AssetBundle Projector spawn inside -# spawnBoundaryCell (SETUP_CONTROLLER 1cb552) with a Global.call to the manager. -# Idempotent: matches both the vanilla form AND a previously-injected patched -# form (between the MAC PATCH dual-path marker and the closing ` end`). The -# patched alternative is listed first so re-runs prefer it. -DEPLOYMENT_SPAWN_RE = re.compile( - r"(?:" - r"-- MAC PATCH dual-path:.*?\r?\n end" - r"|" - r"local projector = spawnObject\(\{\r?\n" - r" type = \"Custom_AssetBundle\",.*?" - r"projector\.setCustomObject\(\{\r?\n" - r" assetbundle = asset,\r?\n" - r" \}\)" - r")", - re.DOTALL -) - -DEPLOYMENT_SPAWN_REPLACEMENT = r"""-- MAC PATCH dual-path: branch on the table-wide deploymentMode toggle. - local _macDeployMode = Global.call("gGetDeploymentMode") - local projector = nil - if _macDeployMode == "windows" then - projector = spawnObject({ - type = "Custom_AssetBundle", - position = pos, - scale = {0, 0, 0}, - rotation = {0, deployRotations[cell], 0} - }) - projector.setName("Deployment Boundary") - projector.setLock(true) - projector.setCustomObject({ - assetbundle = asset, - }) - else - Global.call("gSpawnDeployment", { cell = cell, pos = pos }) - end""" - -# clearDeploymentBoundary: also clear manager entries. -CLEAR_DEPLOYMENT_RE = re.compile( - r"function clearDeploymentBoundary\(\)\r?\n" - r" local battlefieldObjs = battlefieldZone\.getObjects\(\).*?\r?\n" - r" end\r?\nend", - re.DOTALL -) - -CLEAR_DEPLOYMENT_REPLACEMENT = r"""function clearDeploymentBoundary() - -- MAC PATCH: clear Global Overlays manager entries first. - Global.call("gClearAllDeployment", {}) - local battlefieldObjs = battlefieldZone.getObjects() - for _, obj in pairs(battlefieldObjs) do - if obj.getName() == "Deployment Boundary" then - destroyObject(obj) - end - end -end""" - -# Silhouette URL swap to our Unity-6-compiled bundle. -# -# The upstream BucketheadBits_Silhouette bundle uses Allen White's custom -# Silhouette shader compiled under Unity 2019.4. TTS U6 on Mac fails to -# load that shader at runtime (console: "Shader didn't load correctly for -# AssetBundle material BucketheadBits_Silhouette. Assigning Standard -# shader.") and falls back to Standard, which renders the cylinder opaque. -# -# Our fork-hosted bundle uses the SAME prefab + material + shader source -# (Assets/Sihl/), only the compile target differs (Unity 6 instead of -# Unity 2019.4). Path A from Dicewrench's analysis. -# -# We swap ALL silhouette URLs (default cylinder, snail, long) to the same -# fork URL. Snail/long visual fidelity is acceptable for now (cylinder -# placeholder); per-base meshes are a separate follow-up. -SILHOUETTE_BUNDLE_URL = ( - "https://raw.githubusercontent.com/ironsquadronfr-hub/tts/" - "mac-projector-fallback/mod/data/mac-fallback-assets/" - "silhouette_mac_fallback.unity3d" -) -SILHOUETTE_URL_RE = re.compile( - r'(silhouetteData\s*=\s*)"https?://[^"]*/ugc/[^"]+"' -) -SILHOUETTE_URL_REPLACEMENT = r'\1"' + SILHOUETTE_BUNDLE_URL + '"' - - -# Cleanup of debug prints and corrupted signatures from earlier sessions. -# Safe no-op on a clean save. Matches both the legacy `print("[SIL DEBUG] ...)` -# form AND the chat-visible `printToAll("[SIL] ...", {...})` form. -SIL_DEBUG_STRIP_RE = re.compile( - # Match leading horizontal whitespace only (NOT \s* which would also eat - # the newline before, mashing the function signature against the next - # statement - that was a real bug we hit earlier this session). - r'[ \t]*(?:print\("\[SIL DEBUG\][^\n]*\)|' - r'printToAll\("\[SIL\][^"]*",\s*\{[^}]*\}\))\r?\n' -) -# Repair function signatures that were mashed by the previous \s*-glob bug. -# Idempotent: target the specific mashed patterns; matches are zero on a -# clean save. -SIL_REPAIR_TOGGLE_RE = re.compile( - r'(function toggleSilhouettes\(\))( if silhouetteState then)' -) -SIL_REPAIR_SHOW_RE = re.compile( - r'(function showSilhouette\(\))( for k, guid in pairs\(miniGUIDs)' -) -SIL_REPAIR_SPAWN_RE = re.compile( - r'(function spawnSilhouette\(obj, pos, rot\))( local globals)' -) - - -# Silhouette state-desync fix (upstream bug). -# -# Upstream clearSilhouette assumes removeAttachments()[1] always returns an -# object, but the state variable `silhouetteState` is persisted across saves -# while the physical attachments are not - so a save made with silhouettes -# visible reloads with state=true but no attachments. First SIL click then -# calls clearSilhouette which dereferences nil and crashes. -# -# Two-part patch: -# (1) clearSilhouette: guard the nil case (skip the destruct if no -# attachment), so re-clicks don't crash. -# (2) onload: force silhouetteState=false on every load, since the load -# cannot restore the physical silhouettes anyway. -# -# Also fixes the "two silhouettes stacked" symptom: that happens when -# showSilhouette runs while state is desynced, spawning a second set on top -# of one whose handles have been forgotten. -CLEAR_SILHOUETTE_RE = re.compile( - r"function clearSilhouette\(\).*?silhouetteState = false\r?\nend", - re.DOTALL -) -CLEAR_SILHOUETTE_REPLACEMENT = r"""function clearSilhouette() - -- MAC PATCH: guard nil silToDestroy. silhouetteState can be persisted as - -- true across saves while the physical attachments are not, so a load - -- with state=true + no attachments would otherwise crash here. - for k, guid in pairs(miniGUIDs or {}) do - local obj = getObjectFromGUID(guid) - if obj then - local silToDestroy = obj.removeAttachments()[1] - if silToDestroy then silToDestroy.destruct() end - end - end - silhouetteState = false -end""" - -# Match the existing `function onload()` body (one-line empty or actual -# code). Idempotent: re-runs detect the marker `MAC PATCH: silhouette reset` -# and skip. -SIL_ONLOAD_RESET_LINE = ( - ' -- MAC PATCH: silhouette reset on load (attachments lost across saves)\n' - ' silhouetteState = false\n' -) -ONLOAD_OPEN_RE = re.compile(r"function onload\(\)\r?\n") - - -# SIL button rename (root cause: TTS engine intercepts the click_function -# name "toggleSilhouettes" silently in Mac mode; LCK/R buttons unaffected -# because their click_function names differ. Confirmed empirically 17 mai -# 2026: renaming to "macToggleSil" + forwarder unblocks Mac mode entirely). -# -# We swap the click_function name on the SIL button definition AND inject a -# forwarder function that simply calls toggleSilhouettes(). The original -# toggleSilhouettes is untouched, so Windows mode behavior is identical. -# Idempotent: re-runs detect both markers and skip. -SIL_BUTTON_CLICKFN_OLD = 'click_function = "toggleSilhouettes"' -SIL_BUTTON_CLICKFN_NEW = 'click_function = "macToggleSil"' -SIL_FORWARDER = ''' --- MAC PATCH: rebuilt SIL button click handler. The original click_function --- name "toggleSilhouettes" is silently intercepted by TTS in Mac mode (the --- SIL button plays its click sound/animation but the function is never --- called). Renaming the click_function on the button definition + adding --- this forwarder restores the routing. -function macToggleSil() - toggleSilhouettes() -end --- END MAC PATCH SIL forwarder -''' -SIL_FORWARDER_MARKER = 'function macToggleSil()' - - -RANGE_WRAPPER = r""" --- MAC PATCH per-seat router (Range) - ALIAS ONLY. --- --- HISTORY (do not re-introduce): we previously overrode spawnRangeRuler / --- clearRangeRulers / clearRangeRuler on the ~20 token scripts. That --- reproducibly SIGSEGV'd TTS Mac (Mono GC_clear_stack_inner) when a fig --- hotkey spawned the vanilla Range bundle. Overrides REMOVED - per-seat --- Range routing for fig hotkey lives in Global (gRangeTrigger via our --- initRangebandHotkeys shadow). Tokens' R buttons keep vanilla behavior --- (magenta on Mac, native on Windows) until a per-token Mac fallback --- ships - see TOKEN_BUTTON_WRAPPER for the per-token override path. --- --- The single alias below is REQUIRED: Global macModeClick calls --- obj.call("clearRangeRulersOriginal", obj) on every object holding a --- rangeRuler state, to wipe the vanilla ruler when toggling modes. -clearRangeRulersOriginal = clearRangeRulers --- END MAC PATCH per-seat router (Range) -""" - -# Order_Token-only overrides: intercept the UI button click handlers -# (toggleCohesionRuler, targetingMode, changeSpeedN, moveX) to capture -# playerColor passed by TTS for per-seat routing. -ORDER_TOKEN_BUTTON_OVERRIDES = r""" --- MAC PATCH per-seat router (Order_Token button click overrides) -function toggleCohesionRuler(_, playerColor) - if not rulerOn then - Global.call("gCohesionTrigger", { - figGUID = selectedUnitObj.getGUID(), - playerColor = playerColor, - }) - rulerOn = true - else - selectedUnitObj.call("clearCohesionRuler") - rulerOn = false - end -end - -function targetingMode(_, playerColor) - if not enemyHighlighted then - exitAttackMode() - highlightEnemies() - Global.call("gRangeTrigger", { - figGUID = selectedUnitObj.getGUID(), - playerColor = playerColor, - }) - enemyHighlighted = true - resetRangeButtons() - else - exitTargetingMode() - end -end - --- Capture playerColor for Maximum Move per-seat routing. moveUnit() doesn't --- receive the click color, so each entry-point button stashes it in a --- script-global var that the spawn block reads. Defaults to nil -> Mac mode. - --- initMove / initDeploy: redefine the body INLINE rather than wrap, because --- wrapping (capturing the original via a local + calling it) reproducibly --- broke every Order Token button click in testing (root cause unknown; --- suspected Lua chunk-level upvalue interaction with TTS engine click --- dispatch). The inline body mirrors the vanilla logic + the playerColor --- capture so the MAXMOVE_SPAWN block can route to Mac/Windows per seat. -function initMove(obj, playerColor) - if not selectedUnitObj then return end - macActivePlayerForMove = playerColor - initPos = selectedUnitObj.getPosition() - initRot = selectedUnitObj.getRotation() - selectedUnitObj.call("setStartPos") - moveUnit(false) -end -function initDeploy(obj, playerColor) - if not selectedUnitObj then return end - macActivePlayerForMove = playerColor - initPos = selectedUnitObj.getPosition() - initRot = selectedUnitObj.getRotation() - selectedUnitObj.call("setStartPos") - moveUnit(true) -end - --- INLINE the vanilla bodies (mirror Order_Token.a57c41.lua) rather than --- wrap. The List Builder copies this block to Command Token Custom_Models --- which DO NOT carry the vanilla Order_Token function bodies - so a --- wrap-then-call pattern hits `_macOrigChangeSpeed1 = nil` and crashes the --- moment a Speed/Move button is pressed. Inlining keeps the behavior --- identical on both Order Tokens (vanilla body re-defined) and Command --- Tokens (helper fns setTemplateVariables/clearTemplates/moveUnit exist --- on both because they're part of the include set the List Builder emits). -function changeSpeed1(_, playerColor) - macActivePlayerForMove = playerColor - unitData.selectedSpeed = 1 - setTemplateVariables() - clearTemplates() - moveUnit() -end -function changeSpeed2(_, playerColor) - macActivePlayerForMove = playerColor - unitData.selectedSpeed = 2 - setTemplateVariables() - clearTemplates() - moveUnit() -end -function changeSpeed3(_, playerColor) - macActivePlayerForMove = playerColor - unitData.selectedSpeed = 3 - setTemplateVariables() - clearTemplates() - moveUnit() -end -function moveForward(_, playerColor) - macActivePlayerForMove = playerColor - self.editButton({ - index = 11, click_function = "moveBackwards", - label = "B", tooltip = "Move Backwards" - }) - moveDirection = "forward" - moveUnit() -end -function moveBackwards(_, playerColor) - macActivePlayerForMove = playerColor - self.editButton({ - index = 11, click_function = "moveForward", - label = "F", tooltip = "Move Forward" - }) - moveDirection = "backwards" - moveUnit() -end -function moveLeft(_, playerColor) - macActivePlayerForMove = playerColor - moveDirection = "left" - moveUnit() -end -function moveRight(_, playerColor) - macActivePlayerForMove = playerColor - moveDirection = "right" - moveUnit() -end --- END MAC PATCH per-seat router (Order_Token button click overrides) -""" - -# Wrapper appended to objects that have `function toggleRangeRuler()` - -# the R button on tokens (POI, Smoke, Bomb Cart, Objective). Per-seat -# routing: Mac players get our vector bands (TOKEN-spec via rangeKey), -# Windows players get the vanilla token bundle (spawnTokenRangeRuler). -# The vanilla function is aliased BEFORE override so Windows-mode clicks -# can fall through to it. -TOKEN_BUTTON_WRAPPER = r""" --- MAC PATCH per-seat router (token R button) --- Aliases the vanilla toggleRangeRuler before overriding so Windows-mode --- players keep spawning the token-specific Range bundle (POI / Smoke / --- bomb_cart / objective). Mac-mode players get our vector overlay via --- gRangeTrigger which dispatches to macBuilders.range with the token's --- rangeKey var. -local _macToggleRangeRulerOriginal = toggleRangeRuler -function toggleRangeRuler(_, playerColor) - local mode = Global.call("gGetMode", { color = playerColor }) - if mode == "windows" then - if _macToggleRangeRulerOriginal then - return _macToggleRangeRulerOriginal() - end - return - end - if rangeOn then - Global.call("gClearRange", { figGUID = self.getGUID() }) - rangeOn = false - else - Global.call("gRangeTrigger", { - figGUID = self.getGUID(), - playerColor = playerColor, - }) - rangeOn = true - end -end --- END MAC PATCH per-seat router (token R button) -""" - -TOKEN_BUTTON_WRAPPER_RE = re.compile( - r"\r?\n-- MAC PATCH per-seat router \(token R button\).*?" - r"-- END MAC PATCH per-seat router \(token R button\)\r?\n", - re.DOTALL -) - -# List Builder _loadArmyFromJson nil-guard. Stock SWL never nil-checks the -# result of JSON.decode(text) inside importFromText, so any blur of the -# import InputField with empty/invalid text crashes _loadArmyFromJson at -# `data.armyFaction`. The guard is upstream-able. Idempotent via the -# negative lookahead on the marker comment. -LIST_BUILDER_GUARD_RE = re.compile( - r"(function _loadArmyFromJson\(data\)\r?\n)(?! -- MAC PATCH: guard )" -) -LIST_BUILDER_GUARD_REPLACEMENT = ( - r"\1" - " -- MAC PATCH: guard nil/invalid JSON from onEndEdit blur (upstream bug)\r\n" - " if not data then return end\r\n" -) - -# Idempotent marker - used to detect and remove an existing Mac patch in the -# Global LuaScript before applying a fresh one. -GLOBAL_PATCH_MARKER_RE = re.compile( - r"\r?\n-- ={5,}\r?\n-- MAC COHESION FALLBACK.*?-- END MAC COHESION FALLBACK\r?\n", - re.DOTALL -) - -# Idempotent markers for the per-seat wrappers added to object scripts. -# Strip these before re-applying to avoid double-wrapping. -COHESION_WRAPPER_RE = re.compile( - r"\r?\n-- MAC PATCH per-seat router \(Cohesion\).*?" - r"-- END MAC PATCH per-seat router \(Cohesion\)\r?\n", - re.DOTALL -) -RANGE_WRAPPER_RE = re.compile( - r"\r?\n-- MAC PATCH per-seat router \(Range\).*?" - r"-- END MAC PATCH per-seat router \(Range\)\r?\n", - re.DOTALL -) -ORDER_TOKEN_OVERRIDES_RE = re.compile( - r"\r?\n-- MAC PATCH per-seat router \(Order_Token button click overrides\).*?" - r"-- END MAC PATCH per-seat router \(Order_Token button click overrides\)\r?\n", - re.DOTALL -) - - -TARGET_GUIDS = {"99f1c8", "a57c41"} # Objects with the Cohesion block to replace - - -def collect_patched_scripts(data: dict) -> list: - """Return script_states for the TTS External Editor API (Save & Play). - - Includes the Global script and any object script we patched (matching - TARGET_GUIDS). Format: list of dicts with name, guid, script. - """ - states = [ - {"name": "Global", "guid": "-1", "script": data.get("LuaScript", "")} - ] - - def walk(o): - if isinstance(o, dict): - guid = o.get("GUID", "").lower() - if guid in TARGET_GUIDS and "LuaScript" in o: - states.append({ - "name": o.get("Name", guid), - "guid": guid, - "script": o["LuaScript"], - }) - for v in o.values(): - walk(v) - elif isinstance(o, list): - for x in o: - walk(x) - - walk(data) - return states - - -def send_to_tts(script_states: list) -> bool: - """Send a Save & Play (messageID 1) to TTS via the External Editor API. - - Returns True on success, False if TTS isn't listening (likely the API - is disabled in TTS Configuration). - """ - msg = {"messageID": 1, "scriptStates": script_states} - payload = json.dumps(msg).encode("utf-8") - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.settimeout(3) - s.connect(("127.0.0.1", 39999)) - s.sendall(payload) - s.close() - return True - except (ConnectionRefusedError, socket.timeout, OSError) as e: - print(f" WARN: could not connect to TTS on port 39999 ({e}).") - print(f" -> Enable Configuration -> Game Tweaks -> 'External Editor API' in TTS.") - return False - - -def patch_object_scripts(data: dict) -> tuple: - """Walk the JSON tree, replace Cohesion and Range blocks in object scripts. - - Cohesion block: only on TARGET_GUIDS (Unit_Leader, Order_Token). - Range block: on every object containing the !/RangeRulers include - (Order_Token, Tokens, POI, bomb_cart, etc. - ~20 objects). - - Returns (n_cohesion_patched, n_range_patched, n_dep_patched). - """ - n_coh, n_rng, n_dep = 0, 0, 0 - - def walk(o): - nonlocal n_coh, n_rng, n_dep - if isinstance(o, dict): - guid = o.get("GUID", "").lower() - if "LuaScript" in o: - ls = o["LuaScript"] - changed = False - - # Strip any previous per-seat wrappers so re-runs are idempotent. - for rx in (COHESION_WRAPPER_RE, RANGE_WRAPPER_RE, - ORDER_TOKEN_OVERRIDES_RE, TOKEN_BUTTON_WRAPPER_RE): - new_ls, n = rx.subn("", ls) - if n > 0: - ls = new_ls - changed = True - - # Cleanup: strip stale debug prints + repair any signatures - # mashed by an earlier (overzealous) strip pass. Both are - # no-ops on a clean save. - new_ls, n = SIL_DEBUG_STRIP_RE.subn("", ls) - if n > 0: - ls = new_ls - changed = True - for rx in (SIL_REPAIR_TOGGLE_RE, SIL_REPAIR_SHOW_RE, SIL_REPAIR_SPAWN_RE): - new_ls, n = rx.subn(r'\1\n\2', ls) - if n > 0: - ls = new_ls - changed = True - - # (Diagnostic SIL prints removed 17 mai 2026 once mute Mac - # mode confirmed. SIL_DEBUG_STRIP_RE above stays as a cleanup - # pass for older saves that still carry the prints.) - - # Silhouette URL swap to our Unity-6-compiled bundle. - # Applies to any object that defines spawnSilhouette - # (Unit_Leader 99f1c8, Bomb Cart b497e1, POI Token 761483). - # Idempotent: the regex only matches Steam UGC URLs (/ugc/), - # not our fork URL, so re-runs are no-ops. - new_ls, n = SILHOUETTE_URL_RE.subn(SILHOUETTE_URL_REPLACEMENT, ls) - if n > 0: - ls = new_ls - changed = True - - # SIL button rename + forwarder. Bypasses the TTS engine - # bug where the click_function name "toggleSilhouettes" is - # silently muted in Mac mode. Idempotent: replace() is no-op - # once swap is done, marker check prevents double-injection. - if SIL_BUTTON_CLICKFN_OLD in ls: - ls = ls.replace(SIL_BUTTON_CLICKFN_OLD, SIL_BUTTON_CLICKFN_NEW) - changed = True - if SIL_BUTTON_CLICKFN_NEW in ls and SIL_FORWARDER_MARKER not in ls: - ls = ls.rstrip() + '\n' + SIL_FORWARDER - changed = True - - # Cohesion: KEEP the original include block, append per-seat - # wrapper after it. Only Unit_Leader / Order_Token have it. - if guid in TARGET_GUIDS: - def coh_repl(m): - return m.group(0) + COHESION_WRAPPER - new_ls, n = COHESION_BLOCK_RE.subn(coh_repl, ls, count=1) - if n > 0: - ls = new_ls - n_coh += 1 - changed = True - - # Silhouette desync fix (any object with clearSilhouette). - # Guard the nil-attachment case and reset silhouetteState in - # onload. Idempotent: the regex matches both the vanilla - # body and our previously-patched body (same shape). - if "function clearSilhouette()" in ls: - new_ls, n = CLEAR_SILHOUETTE_RE.subn(CLEAR_SILHOUETTE_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - changed = True - # Inject the state reset at the top of onload. - if "MAC PATCH: silhouette reset on load" not in ls: - new_ls, n = ONLOAD_OPEN_RE.subn( - "function onload()\n" + SIL_ONLOAD_RESET_LINE, - ls, count=1, - ) - if n > 0: - ls = new_ls - changed = True - - # Range: KEEP block, append wrapper. ~21 objects. - def rng_repl(m): - return m.group(0) + RANGE_WRAPPER - new_ls, n = RANGE_BLOCK_RE.subn(rng_repl, ls, count=1) - if n > 0: - ls = new_ls - n_rng += 1 - changed = True - - # List Builder import guard. Applies to BLUE/RED LIST - # BUILDER (any object that defines _loadArmyFromJson). - # Fixes a pre-existing upstream crash where blurring the - # import InputField with empty/invalid text fires - # onEndEdit -> importFromText -> JSON.decode("") -> nil -> - # data.armyFaction crash. Idempotent. - if "function _loadArmyFromJson(data)" in ls: - new_ls, n = LIST_BUILDER_GUARD_RE.subn( - LIST_BUILDER_GUARD_REPLACEMENT, ls, count=1 - ) - if n > 0: - ls = new_ls - changed = True - - # Maximum Move spawn (only in Order_Token a57c41) - Mac only - if guid == "a57c41": - new_ls, n = MAXMOVE_SPAWN_RE.subn(MAXMOVE_SPAWN_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - changed = True - new_ls, n = CLEAR_MOVEMENT_RE.subn(CLEAR_MOVEMENT_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - changed = True - # Button click overrides (per-seat capture of playerColor) - ls = ls + ORDER_TOKEN_BUTTON_OVERRIDES - changed = True - - # Token R button: append toggleRangeRuler override on any - # object that defines that function (POI, Smoke, BombCart, - # Objective, etc.). Routes the click to the Mac fallback - # Range overlay sized via TOKEN_BASE_RADIUS. (Already-applied - # case is handled by the strip loop above.) - if "function toggleRangeRuler()" in ls: - ls = ls + TOKEN_BUTTON_WRAPPER - changed = True - - # Deployment Boundary (only in SETUP_CONTROLLER 1cb552) - Mac only - if guid == "1cb552": - new_ls, n = DEPLOYMENT_SPAWN_RE.subn(DEPLOYMENT_SPAWN_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - n_dep += 1 - changed = True - new_ls, n = CLEAR_DEPLOYMENT_RE.subn(CLEAR_DEPLOYMENT_REPLACEMENT, ls, count=1) - if n > 0: - ls = new_ls - changed = True - - if changed: - o["LuaScript"] = ls - - for v in o.values(): - walk(v) - elif isinstance(o, list): - for x in o: - walk(x) - - walk(data) - return n_coh, n_rng, n_dep - - -def main(): - args = [a for a in sys.argv[1:] if not a.startswith("--")] - flags = [a for a in sys.argv[1:] if a.startswith("--")] - if len(args) != 2: - print(f"Usage: {sys.argv[0]} [--reload]") - sys.exit(1) - src = Path(args[0]) - dst = Path(args[1]) - reload_tts = "--reload" in flags - - with src.open("r") as f: - data = json.load(f) - - # Rename so the patched save is recognizable in TTS load list. - data["SaveName"] = "SWL BETA - MAC PATCH" - - # 1. Append manager + handlers to Global LuaScript (idempotent: remove - # any previous Mac patch block first, then append fresh). - original_global_len = len(data.get("LuaScript", "")) - existing = data.get("LuaScript", "") - cleaned, n_removed = GLOBAL_PATCH_MARKER_RE.subn("", existing) - data["LuaScript"] = cleaned + GLOBAL_PATCH_LUA - new_global_len = len(data["LuaScript"]) - note = " (replaced existing patch)" if n_removed else "" - print(f"Global LuaScript: {original_global_len} -> {new_global_len} bytes{note}") - print() - - # 2. Replace Cohesion + Range + Deployment blocks in object scripts. - print("Object script patches:") - n_coh, n_rng, n_dep = patch_object_scripts(data) - print(f" Cohesion blocks replaced: {n_coh}") - print(f" Range blocks replaced: {n_rng}") - print(f" Deployment blocks replaced: {n_dep}") - print() - - dst.parent.mkdir(parents=True, exist_ok=True) - with dst.open("w") as f: - json.dump(data, f, indent=2, ensure_ascii=False) - - print(f"Written: {dst}") - print(f" Save Name: {data.get('SaveName', '?').strip()}") - print(f" Version: {data.get('VersionNumber', '?')}") - - # Mirror to TTS Saves folder so it appears in Games -> Save & Load. - # Picks the next free TS_Save_N.json slot (TTS scans these on load). - saves_dir = Path.home() / "Library" / "Tabletop Simulator" / "Saves" - if saves_dir.exists(): - existing = sorted( - int(p.stem.split("_")[-1]) - for p in saves_dir.glob("TS_Save_*.json") - if p.stem.split("_")[-1].isdigit() - ) - # Reuse the highest existing slot if its content matches the prefix - # "SWL BETA - MAC PATCH"; else pick next free number. - target = None - for n in reversed(existing): - p = saves_dir / f"TS_Save_{n}.json" - try: - with p.open("r") as f: - if '"SWL BETA - MAC PATCH"' in f.read(512): - target = p - break - except OSError: - continue - if target is None: - next_n = (existing[-1] if existing else 0) + 1 - target = saves_dir / f"TS_Save_{next_n}.json" - with target.open("w") as f: - json.dump(data, f, indent=2, ensure_ascii=False) - print(f" Mirrored to: {target}") - - if reload_tts: - print() - print("Hot-reloading into TTS via External Editor API...") - states = collect_patched_scripts(data) - print(f" Sending {len(states)} script(s): " - + ", ".join(f"{s['name']}({s['guid']})" for s in states)) - if send_to_tts(states): - print(" OK Sent. TTS should restart Lua with the new code " - "(table/objects state preserved).") - - -if __name__ == "__main__": - main() diff --git a/mac-patcher/requirements.txt b/mac-patcher/requirements.txt deleted file mode 100644 index fd6ee21e..00000000 --- a/mac-patcher/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -UnityPy>=1.10 -Pillow>=10.0 -requests>=2.31 diff --git a/mac-patcher/retro-cohesion.md b/mac-patcher/retro-cohesion.md deleted file mode 100644 index a67ea58c..00000000 --- a/mac-patcher/retro-cohesion.md +++ /dev/null @@ -1,170 +0,0 @@ -# Rétro-ingé Cohesion Ruler - -Rédigé en mode prod, base pour le refactor event-driven vector lines (15 mai 2026, post-finding session 14-15 mai). - -## 1. Définition fonctionnelle (rappel règles SWL) - -Le **Cohesion Ruler** matérialise la **distance de cohésion d'unité** : tous les minis d'une même unité doivent rester à une distance maximale du leader d'unité (sinon malus / dispersion). Le ruler aide à vérifier visuellement cette contrainte pendant le tour. - -Distance de cohésion : **0.5 inch** (1,27 cm) entre les bords des socles, règle officielle SWL. - -## 2. Visuel attendu - -3 tailles, indexées par `baseSize` du leader : - -| Clé | Base size physique | Bundle | -|---|---|---| -| `small` | 27 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305632/...` | -| `medium` | 50 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305877/...` | -| `large` | 70 mm | `https://steamusercontent-a.akamaihd.net/ugc/2482129948496305957/...` | - -Rendu côté Windows (TTS Unity 2019.4) : cercle semi-transparent vert/cyan projeté au sol via Unity Projector, centré sur la fig. **À extraire empiriquement** via UnityPy + référence Workshop pour la couleur/alpha exacts (en TODO ci-dessous). - -Comportement Projector : drape sur le relief de la table (rochers, élévations) grâce au Projector legacy. - -## 3. Déclencheurs (qui peut spawn/clear le ruler ?) - -### Spawn - -1. **Hotkey global "Show Cohesion On Hovered Model"** — `StarWarsLegion.lua:611-619` - - Bind via `addHotkey()` côté Global - - Appelle `showCohesionOnHoveredModel(hoverObject)` qui : - - Si déjà selected → clear + reset `selectedUnitObj` - - Sinon → `clearCohesionRuler() + spawnCohesionRuler(hoverObject)` - - **État global** `selectedUnitObj` tracké côté Global (variable du script `StarWarsLegion.lua` via `require('!/Cohesion')`) - -2. **Bouton "COHESION" sur Order Token** — `Order_Token.a57c41.lua:347-352, 369-378` - - Bouton créé pendant l'activation d'une unité - - `toggleCohesionRuler()` toggle via flag `rulerOn` local au Order Token - - Appelle `selectedUnitObj.call("spawnCohesionRuler", selectedUnitObj)` → s'exécute dans le script de la **fig sélectionnée** (Unit_Leader.99f1c8) - -3. **Re-spawn automatique après mouvement** — `Unit_Leader.99f1c8.lua:278-286` - - `dropCoroutine()` attend que `getVelocity().y == 0` (fig au sol) - - Si `moveState == true` (= en cours de mesure de mouvement) → `spawnCohesionRuler(self)` - -### Clear - -1. **Hotkey re-press** — toggle via `showCohesionOnHoveredModel` (idem spawn, second appel) - -2. **Bouton COHESION re-press** — toggle via `toggleCohesionRuler` (idem) - -3. **Bouton invisible "unitID" sur Unit_Leader** — `Unit_Leader.99f1c8.lua:45` - - Bouton au centre de la fig avec label = numéro d'unité, couleur alpha 0.01 (quasi-invisible) - - `click_function = "clearCohesionRuler"` - - C'est l'overlay cliquable du numéro affiché sur chaque fig - -4. **onPickedUp sur Unit_Leader** — `Unit_Leader.99f1c8.lua:274-276` - - **Event-driven déjà existant** : pickup la fig → clear automatique - -5. **`standbyTokens()` global** — `StarWarsLegion.lua:562-571` - - Boucle sur `getAllObjects()` et destroy tout objet nommé `"Cohesion Ruler"` (et "Range Ruler", "Movement Template", "Deployment Boundary") - - Déclenché par... à tracer (probablement reset de partie ou hotkey debug) - -6. **`removeLockedRulers()` GAME_CONTROLLER** — `GAME_CONTROLLER.623b03.lua:148-155` - - Boucle sur `getAllObjects()` et destroy "Cohesion Ruler" / "Range Ruler" - - Appelé par la fonction de reload (`reloadObj` voisine) - -7. **`clearCohesionRulers()` (pluriel) Order Token** — `Order_Token.a57c41.lua:788-793` - - Appelé par `clearTemplates()` lors du reset après mouvement - - `selectedUnitObj.setVar("moveState", false)` + `selectedUnitObj.call("clearCohesionRuler")` - -## 4. Lifecycle complet - -``` -[Spawn] - showCohesionOnHoveredModel(fig) [hotkey hover] - toggleCohesionRuler() [via Order Token bouton] - dropCoroutine() [auto si moveState] - ↓ - spawnCohesionRuler(fig): - 1. unitData = fig.getTable("unitData") - 2. bundleURL = getCohesionLinks()[unitData.baseSize] - 3. spawnObject(Custom_AssetBundle, pos = fig.pos + Y+20, scale = 0, rot = (0, fig.rot.y, 0)) - 4. setCustomObject({type=0, assetbundle=bundleURL}) - 5. setLock(true), use_gravity=false, setName("Cohesion Ruler") - ↓ -[Ruler affiché — STATIQUE, ne suit pas la fig] - Note: pas de onFixedUpdate. Si fig bouge sans pickup (impossible normalement - car les figs sont lockables), le ruler reste à l'ancienne position. - -[Clear] - showCohesionOnHoveredModel(same fig) [hotkey re-press toggle] - toggleCohesionRuler() [re-press toggle] - onPickedUp(fig) [pickup auto] - clearCohesionRuler() [click invisible "unitID" button] - standbyTokens() [global reset] - removeLockedRulers() [reload] - clearCohesionRulers() [via Order Token clearTemplates] - ↓ - clearCohesionRuler(): - 1. destroyObject(cohesionRuler) - 2. cohesionRuler = nil - -[Re-spawn auto post-drop] - onDropped → checkVelocity → si moveState → startLuaCoroutine(dropCoroutine) - ↓ - dropCoroutine: - while velocity.y ~= 0: yield - if moveState == true: spawnCohesionRuler(self) -``` - -## 5. État scopé : ATTENTION pattern hétérogène - -`cohesionRuler` est une variable Lua **scopée au script Lua qui fait le require**. Chaque objet qui require `!/Cohesion` a SA propre `cohesionRuler`. Trois scopes coexistent : - -| Site require | Scope `cohesionRuler` | -|---|---| -| `StarWarsLegion.lua:8` (Global) | Script Global | -| `Unit_Leader.99f1c8.lua:1` (chaque fig leader) | Par-fig | -| `Order_Token.a57c41.lua:4` (chaque Order Token) | Par-Order-Token | - -**Conséquence pratique** : -- Hotkey hover → spawn dans le scope Global → `cohesionRuler` du Global mis à jour, **pas celui de la fig** -- Bouton COHESION → `selectedUnitObj.call("spawnCohesionRuler", selectedUnitObj)` → exécute dans le scope de la fig → `cohesionRuler` de la fig mis à jour -- `onPickedUp(fig)` → clear dans le scope de la fig → ne clear PAS un ruler spawn via hotkey hover (mais le bouton invisible "unitID" est sur la fig, donc dans le scope fig...) - -**Bug latent connu (non reproduit)** : si tu spawn via hotkey hover, puis tu pickup une autre fig, le ruler du Global subsiste. À garder en tête pour le refactor (à corriger ou pas selon scope). - -## 6. Filtres de cleanup global - -Deux fonctions scannent `getAllObjects()` et destroy par nom : - -| Fonction | Fichier | Cible | -|---|---|---| -| `standbyTokens()` | StarWarsLegion.lua:562 | "Cohesion Ruler" + "Range Ruler" + "Movement Template" + "Deployment Boundary" | -| `removeLockedRulers()` | GAME_CONTROLLER.623b03.lua:148 | "Cohesion Ruler" + "Range Ruler" | - -**Impact refactor** : si on remplace l'Object Custom_AssetBundle par des vector lines (qui ne sont pas des Objects), ces deux filtres deviennent **caducs**. Il faudra leur substituer un clear de la table d'état globale + `Global.setVectorLines({})`. - -## 7. Paramètres visuels exacts — TODO - -À extraire empiriquement via UnityPy sur les bundles small/medium/large : - -- [ ] Bundle `halfcohesion_27mm.unity3d` (ou équivalent small) : material color, alpha, orthographicSize du Projector, animation éventuelle -- [ ] Bundle medium (50mm) : idem -- [ ] Bundle large (70mm) : idem -- [ ] Screenshot référence côté Workshop (rendu attendu côté Windows TTS 2019.4 / U6 non-magenta) - -Scripts UnityPy disponibles dans `Mod TTS SWL/` (`inspect_cohesion.py`, `inspect_externals.py`). À adapter pour extraire les params materials. - -## 8. Implications pour le refactor - -### Bonnes nouvelles -- API publique petite et conservable : `showCohesionOnHoveredModel`, `spawnCohesionRuler`, `clearCohesionRuler` -- 80% du pattern event-driven déjà en place (`onPickedUp`, `dropCoroutine`) -- Lifecycle bien défini, déclencheurs énumérés exhaustivement - -### Décisions à prendre dans le design doc -1. **Unifier les scopes** : faire que `cohesionRuler` soit une table globale indexée par GUID (Variante A) plutôt que par-script. Résout le bug latent (§5) et permet le pattern multi-figs simultanées. -2. **Remplacer les filtres de cleanup** (§6) par des appels à la fonction de clear globale. -3. **Reproduire le drape sur relief** via `Physics.cast()` par segment, comme le POC validé du 14-15 mai. -4. **Compatibilité Order Token bouton** : conserver la sémantique toggle `rulerOn` ou simplifier. - -(Note : esthétique du rendu vector lines hors specs — on traitera une fois le dossier refactor clos.) - -## 9. Références - -- Code : `swlegion-tts/mod/src/includes/Cohesion.ttslua`, `data/CohesionLinks.ttslua`, `StarWarsLegion.lua`, `StarWarsLegion/Unit_Leader.99f1c8.lua`, `StarWarsLegion/Order_Token.a57c41.lua`, `StarWarsLegion/GAME_CONTROLLER.623b03.lua` -- Bundles : cache TTS local, sources Unity dans `Mod TTS SWL/UnityProject-U6/Assets/` -- Session 14-15 mai validation empirique : `~/.claude/projects/-Users-martinpourrat-MARTIN-Star-Wars-Legion/memory/mod-tts-swl/session-14-mai.md` -- Plan B identifié : `~/.claude/projects/-Users-martinpourrat-MARTIN-Star-Wars-Legion/memory/mod-tts-swl/paths-forward.md` diff --git a/mac-patcher/retro-deployment.md b/mac-patcher/retro-deployment.md deleted file mode 100644 index 20d1741e..00000000 --- a/mac-patcher/retro-deployment.md +++ /dev/null @@ -1,76 +0,0 @@ -# Rétro-ingé Deployment Boundary - -## Visuel attendu - -Zones de déploiement rouge/bleu peintes au sol au début de la partie selon le scenario. Pattern matriciel avec 14 codes de cellule : - -| Code | Sens | -|---|---| -| `r` / `b` | Base red / blue | -| `rh` / `bh` | Home (zone arrière) | -| `rs` / `bs` | Side (zone latérale) | -| `rss` / `bss` | Side stretched | -| `rl` / `bl` | Long (zone allongée, spawn 2 cellules : sX + ccX) | -| `rc` / `bc` | Corner | -| `rcc` / `bcc` | Corner-corner (autre orientation) | - -URLs bundles dans `deployLinks` (`SETUP_CONTROLLER.1cb552.lua:188-202`). - -Plusieurs codes partagent la même URL (e.g. `rh = rs = rss` → même bundle, rotation différente). Distinct rotations : `r=0, rh=0, rs=90, rss=90, ...` (`deployRotations` ligne 204). - -Rendu côté Windows : grand rectangle/zone projeté au sol via Projector. Couleurs rouge/bleu. **À extraire via UnityPy** (task #13). - -## Spawn - -**1 seul déclencheur** : `spawnDeploymentBoundary(matrix)` — `SETUP_CONTROLLER.1cb552.lua:267-314` - -- Reçoit une matrice 12×N (codes par cellule) inversée puis lue ligne par ligne -- Pour chaque cellule non-vide : `spawnBoundaryCell(cell, x, z)` qui spawn 1 Custom_AssetBundle au pos calculé `{xStart + 6*(x-1), yValue, zStart - 6*(z-1)}` + `deployOffset[cell]` -- Cas spécial `bl`/`rl` : spawn 2 cellules (sX + ccX) pour matérialiser le L - -Appelé par `SETUP_CONTROLLER.1cb552.lua:369` (probablement après sélection de la carte Deployment via UI menu). - -## Clear - -**1 seul déclencheur** : `clearDeploymentBoundary()` — ligne 316-323 -- Boucle sur `battlefieldZone.getObjects()` et `destroyObject` chaque objet nommé `"Deployment Boundary"` - -Aussi via : -- **`standbyTokens()`** — `StarWarsLegion.lua:567` — destroy global par nom - -(Pas dans `removeLockedRulers` de GAME_CONTROLLER : Deployment n'est pas dans la liste.) - -## Lifecycle - -``` -[Setup partie] - Menu UI "Mount Deployment" → checkDeployment() → spawnDeploymentBoundary(matrix) - ↓ - Loop sur matrix → pour chaque cell : spawnBoundaryCell - ↓ - Custom_AssetBundle spawn statique (scale 0, locked, no gravity) - -[Clear] - Menu UI "Remove Overlay" → clearDeploymentBoundary() - ↓ - Boucle battlefieldZone → destroyObject par nom - -[Aussi : standbyTokens() global au reset] -``` - -**Pattern : 100% statique.** Pas de suivi, pas d'event. Spawn au setup, clear quand on remove. Plus simple que Cohesion et Range. - -## Différences clés vs Cohesion/Range - -1. **Pas de fig source** — c'est un overlay de zone, pas un overlay attaché à un objet mobile. Pas besoin de drape sur relief si la table est plate (à confirmer : sur certaines tables custom avec relief, est-ce que la zone de déploiement doit draper ? probablement oui pour cohérence visuelle). -2. **Multi-cellules** par scenario — peut-être 10-20 Custom_AssetBundle spawn simultanément (vs 1 pour Cohesion/Range). -3. **Pas de variable d'état scopée** — pas de `deploymentRuler = nil` ; le code itère sur `battlefieldZone.getObjects()` filtré par nom pour le cleanup. - -## Implications refactor - -- API publique à conserver : `spawnDeploymentBoundary(matrix)`, `clearDeploymentBoundary()` -- 14 bundle URLs (mais visuellement ~6 zones distinctes vu les doublons URL) → 6 sets de params visuels à extraire (task #13) -- Pas d'event-driven nécessaire — statique simple -- En vector lines : chaque cellule = un polygone fermé `setVectorLines({{points={p1,p2,p3,p4,p1}, ...}, ...})`. Pas de Physics.cast si table plate, sinon raycast par segment du contour -- Filtre cleanup `standbyTokens` à adapter pour clear la table d'état globale (ou laisser un `Global.setVectorLines({})` de la collection deployment) -- **Décision design** : Deployment partage-t-il la même collection `Global.setVectorLines()` que Cohesion/Range, ou faut-il un canal séparé ? `Global.setVectorLines` est singleton donc tout doit cohabiter dans la même collection. Implication : la table d'état globale doit indexer par type (`cohesion` / `range` / `deployment`) pour gérer les clears partiels. diff --git a/mac-patcher/retro-movement.md b/mac-patcher/retro-movement.md deleted file mode 100644 index 579aaca3..00000000 --- a/mac-patcher/retro-movement.md +++ /dev/null @@ -1,122 +0,0 @@ -# Rétro-ingé Movement Template + Maximum Move - -**Correction** : Movement implique **2 objets visuels** spawn ensemble lors de l'activation de mouvement. Un seul des deux est cassé magenta. - -## Composant 1 : Movement Template A/B (templates 3D courbes) — NON AFFECTÉ - -Code : `Order_Token.a57c41.lua:480-529` - -```lua -templateA = spawnObject({ type = "Custom_AssetBundle", scale = {1,1,1} ... }) -templateA.setCustomObject({ assetbundle = ..., material = 1 }) -templateA.setColorTint(...) -templateA.setName("Movement Template (A)") -``` - -- **scale = {1,1,1}** → mesh visible (pas un Projector caché) -- **`material = 1`** + **`setColorTint()`** → material standard avec colorTint TTS exposé -- Bundles `longBundle` / `shortBundle` / `sharedBundle` par speed (templateInfo) -- **Pas affecté par bug magenta** : c'est un mesh classique, pas un Projector receiver - -## Composant 2 : Maximum Move (cercle de portée) — CASSÉ MAGENTA ✅ - -Code : `Order_Token.a57c41.lua:552-574` - -```lua -maxMoveTemplate = spawnObject({ - type = "Custom_AssetBundle", - position = {basePos.x, basePos.y + 20, basePos.z}, - scale = {0,0,0} -- ← scale 0, pattern Projector -}) -maxMoveTemplate.setCustomObject({ - type = 0, - assetbundle = maxMoveTemplateBundleToSpawn -}) -maxMoveTemplate.setLock(true) -maxMoveTemplate.use_gravity = false -maxMoveTemplate.setName("Maximum Move") -- ← nom différent de "Movement Template" -``` - -- **scale = {0,0,0}** → Projector legacy (même pattern que Cohesion/Range/Deployment) -- **Pas de setColorTint** sur ce spawn -- **CASSÉ MAGENTA** sur Mac confirmé par Martin - -### Bundles (`includes/data/MovementLinks.ttslua`) - -`getMovementLinks()` retourne table indexée `[baseSize][selectedSpeed]` : - -| baseSize | Taille | Speeds | -|---|---|---| -| `small` | 27mm | 3 bundles (speed 1/2/3) | -| `medium` | 50mm | 3 | -| `large` | 70mm | 3 | -| `huge` | 100mm | 3 | -| `laat` | 120mm | 3 | -| `epic` | 150mm | 3 | -| `long` | 100×175mm oblong | 3 | -| `snail` | 100×200mm oblong | 3 | - -**Total : 24 bundles uniques** pour le cercle de portée max. - -Rendu visuel attendu côté Windows : cercle/anneau projeté au sol matérialisant la portée maximale de mouvement à cette vitesse. **Couleur/alpha à extraire via UnityPy** (task #13). - -## Déclencheurs - -### Spawn -**1 seul déclencheur** : flow d'activation mouvement via Order Token -- Bouton speed (1/2/3) cliqué sur Order Token → set `unitData.selectedSpeed` -- Une fonction de mouvement (autour de `Order_Token.a57c41.lua:480+`) spawn `templateA`, `templateB`, ET `maxMoveTemplate` ensemble -- Conditional : `if isDeploy == false` → pas de Maximum Move pendant la phase de déploiement (ligne 556) - -### Clear -**Fonction `clearMovementTemplates()`** — `Order_Token.a57c41.lua:776-786` -```lua -function clearMovementTemplates() - if templateA ~= nil then destroyObject(templateA) end - if templateB ~= nil then destroyObject(templateB) end - if maxMoveTemplate ~= nil then destroyObject(maxMoveTemplate) end -end -``` - -Appelé par : -- `clearTemplates()` (ligne 770-774) → appelé après le drop final du mouvement -- Aucun cleanup global (Maximum Move PAS dans `standbyTokens()` ni `removeLockedRulers()`) - -## Lifecycle - -``` -[Spawn] - Order Token bouton speed (1/2/3) → spawn flow - ↓ - templateA, templateB (mesh visible) + maxMoveTemplate (Projector) - ↓ - L'utilisateur déplace templateA jusqu'à la position cible - → contrôle visuel "ma fig peut-elle aller là" avec le cercle Maximum Move - -[Clear] - Drop final (fig placée) → clearTemplates() → destruct 3 objets ensemble -``` - -**Pattern : statique au spawn, pas de suivi de fig**. Le cercle Maximum Move est positionné une fois à `basePos + Y20` et reste là pendant que tu manipules les templates de mouvement. - -## État scopé - -`maxMoveTemplate` est une variable par Order Token (`require('!/Cohesion')` ligne 4 mais maxMoveTemplate vient du flow Order_Token directement, pas d'include). Pas de partage Global / Unit_Leader. - -## Implications refactor - -- API publique à conserver : le spawn et `clearMovementTemplates()` (mais cette dernière mélange templates A/B + maxMoveTemplate) -- 24 bundles cercle de portée → 8 sets de params visuels distincts par baseSize (les 3 speeds partagent probablement le même material avec rayon différent ; à extraire task #13) -- Pattern statique simple : spawn une fois, clear ensemble avec templates A/B -- En vector lines : 1 cercle par maxMoveTemplate, dessiné via setVectorLines + Physics.cast par segment pour drape relief -- Cleanup propre : `clearMovementTemplates` doit clear la table d'état globale spécifique au maxMove de cet Order Token -- **Templates A/B (mesh) restent inchangés** — pas dans le scope du refactor - -## Couleur attendue - -Le cercle Maximum Move devrait visuellement matcher la couleur du template de speed correspondant : -- Speed 1 : couleur 1 (vert ?) -- Speed 2 : couleur 2 (jaune ?) -- Speed 3 : couleur 3 (rouge ?) - -Note `templateInfo.moveTemplate[selectedSpeed].colorTint` — c'est le tint des templates A/B, mais le cercle Maximum Move utilise un bundle distinct sans colorTint (le bundle embarque sa propre couleur). À vérifier empiriquement / via UnityPy quelle est la couleur baked-in de chaque bundle Maximum Move par speed. diff --git a/mac-patcher/retro-range.md b/mac-patcher/retro-range.md deleted file mode 100644 index a4569069..00000000 --- a/mac-patcher/retro-range.md +++ /dev/null @@ -1,86 +0,0 @@ -# Rétro-ingé Range Ruler - -## Visuel attendu - -12 tailles dans `getRangeRulerLinks()` (`mod/src/includes/data/RangeRulerLinks.ttslua`) : - -| Clé | Cible | Range | -|---|---|---| -| `small` | base 27mm | range mini | -| `medium` | base 50mm | range mini | -| `large` | base 70mm | range mini | -| `huge` | base 100mm | range véhicule | -| `laat` | base 120mm | range LAAT | -| `epic` | base 150mm | range épique | -| `long` | base 100mm oblong | range véhicule long | -| `snail` | base 100x200mm | range escargot | -| `bombCart` | bomb cart | range spécifique | -| `smokeToken` | jeton fumée 18.8mm | range 1 | -| `token` | jeton charge/objectif/condition 25.1mm | range 1 | -| `tokenRangeTwo` | jeton graffiti 25.1mm | range 2 | -| `poi` | POI token 50.8mm | range 0.5 (3") | - -Rendu côté Windows : **4 cercles concentriques** projetés au sol via Unity Projector legacy (1 bundle = N Projectors embarqués pour les bandes de portée 1/2/3/4). Couleurs/alphas/rayons par bande **à extraire via UnityPy** (task #13). - -**Configurations par overlay** : -- Rulers de fig (small/med/large/huge/laat/epic/long/snail) : **4 cercles** (range 1/2/3/4) -- `smokeToken` : 1 cercle (range 1, effet smoke 1 pouce) -- `token` (charge/obj/cond) : 1 cercle (range 1) -- `tokenRangeTwo` (graffiti) : 2 cercles (range 1 + 2) -- `poi` : 1 cercle (range 0.5 = 3") -- `bombCart` : à confirmer empiriquement - -## Déclencheurs - -### Spawn -1. **Hotkey "Show Range On Hovered Model"** — `StarWarsLegion.lua:601-608` → `showRangeOnHoveredModel(hoverObject)` -2. **Bouton "RANGE" sur Order Token** — `Order_Token.a57c41.lua:354-365` → `targetingMode()` ligne 922 → `spawnRangeRuler(selectedUnitObj)` -3. **Bouton "RANGE" via attackMode** — `Order_Token.a57c41.lua:934` → `spawnRangeRuler(selectedUnitObj)` -4. **Bouton "R" sur POI Token** — `POI_Tokens/POI_Token.761483.lua:23,41-46` → `toggleRangeRuler()` → `spawnTokenRangeRuler()` → `spawnRangeRuler(self, tokenRulerBundle)` avec override -5. **Bouton "R" sur autres tokens** — `includes/TokenWithRangeRuler.ttslua:37-43,52-56` (smokeToken, token, tokenRangeTwo via `rangeKey` scopé par-token) - -### Clear -1. **Hotkey re-press** — toggle via `showRangeOnHoveredModel` -2. **`clearRangeRulers()` (pluriel)** — Order Token : appelé par `clearTemplates`, `exitTargetingMode`, `exitAttackMode`, `attackMenu` -3. **`clearRangeRuler()` (singulier)** — POI/Token : appelé par `toggleRangeRuler` et `onDestroy` -4. **`standbyTokens()` global** — `StarWarsLegion.lua:567` — destroy tout objet nommé `"Range Ruler"` -5. **`removeLockedRulers()`** — `GAME_CONTROLLER.623b03.lua:151` — destroy par nom - -## Différence clé vs Cohesion : suivi temps réel - -`RangeRulers.ttslua:78-79` : - -```lua -luaScript = "targetGUID = '"..rangeSourceObject.getGUID().."'\n" - .. "function onFixedUpdate()\n" - .. " if targetGUID ~= nil then\n" - .. " targetObj = getObjectFromGUID(targetGUID)\n" - .. " local targetPosition = targetObj.getPosition()\n" - .. " self.setPosition({targetPosition.x, targetPosition.y + 20, targetPosition.z})\n" - .. " self.setRotation({0,targetObj.getRotation().y,0})\n" - .. " end\n" - .. "end" -rangeRuler.setLuaScript(luaScript) -``` - -→ Le ruler **se repositionne 60 fois/sec** côté objet ruler (pas côté Lua de la fig). Différent de Cohesion qui est statique. - -**Implication refactor** : pour Range, le suivi temps réel est utile gameplay (tu mesures pendant que tu déplaces la fig pour évaluer si tu vas être en range). On ne peut pas simplement passer en pur event-driven sans perte UX. Le drape sur relief recalculé doit suivre — soit redraw à chaque tick visible (avec mémoization de position pour éviter le leak), soit accepter une dégradation UX (redraw au drop seulement). - -## État scopé (même problème que Cohesion §5) - -Variable `rangeRuler` scopée par script-objet (Global, chaque fig leader, chaque POI Token, chaque token générique). Pattern identique à Cohesion : 3+ scopes peuvent désaccorder. - -`selectedUnitObj` global aussi tracké côté Range (`showRangeOnHoveredModel` ligne 7-17 de RangeRulers.ttslua). - -## Override bundle (paramètre `projectorBundleOverride`) - -`spawnRangeRuler(rangeSourceObject, projectorBundleOverride)` — le 2e paramètre permet de fournir une URL bundle au lieu de la lookuper via `unitData.baseSize`. Utilisé par POI Token et TokenWithRangeRuler qui passent `rangeRulerTable[rangeKey]` directement. À conserver dans le refactor. - -## Implications refactor - -- API publique à conserver : `showRangeOnHoveredModel`, `spawnRangeRuler` (avec override), `clearRangeRulers` (Order Token), `clearRangeRuler` (POI/Token) -- 12 bundle URLs → 12 sets de params visuels à extraire (chacun avec **1 à 4 cercles concentriques** selon le type) et reproduire en vector lines (task #13) -- Pattern suivi temps réel → décision design (redraw par tick avec mémoization vs redraw event-driven uniquement) -- Filtres cleanup global (`standbyTokens`, `removeLockedRulers`) à adapter pour la table d'état -- Override `projectorBundleOverride` → s'inscrit naturellement dans une table d'état `{fig=obj, rangeKey="poi"}` ou similaire diff --git a/mac-patcher/scan_bundles.py b/mac-patcher/scan_bundles.py deleted file mode 100644 index eb5edfd2..00000000 --- a/mac-patcher/scan_bundles.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python3 -"""Scan TTS AssetBundles for material -> shader references.""" -import UnityPy -import os -import sys -from collections import defaultdict, Counter - -BUNDLE_DIR = os.path.expanduser("~/Library/Tabletop Simulator/Mods/Assetbundles/") -STOCK_PREFIXES = ( - "Standard", - "Hidden/", - "Mobile/", - "Particles/", - "UI/", - "Sprites/", - "Skybox/", - "Legacy Shaders/", - "Universal Render Pipeline/", - "URP/", - "Unlit/", - "GUI/", - "Lightmap", - "VertexLit", - "Reflective/", - "Self-Illumin/", - "Transparent/", - "FX/", - "Nature/", - "TextMeshPro/", - "TextMesh Pro/", -) - -def is_stock(name: str) -> bool: - return any(name.startswith(p) for p in STOCK_PREFIXES) - -shader_to_bundles = defaultdict(set) -shader_material_count = Counter() -bundle_to_shaders = defaultdict(set) -embedded_shaders_per_bundle = defaultdict(set) -errors = [] -bundle_summary = [] - -bundles = sorted(f for f in os.listdir(BUNDLE_DIR) if f.endswith(".unity3d")) -print(f"Scanning {len(bundles)} bundles in {BUNDLE_DIR}\n", file=sys.stderr) - -for fname in bundles: - path = os.path.join(BUNDLE_DIR, fname) - short_id = fname.replace("httpssteamusercontentaakamaihdnetugc", "")[:20] - fsize = os.path.getsize(path) - n_mat = 0 - n_shader = 0 - n_tex = 0 - try: - env = UnityPy.load(path) - for obj in env.objects: - t = obj.type.name - if t == "Material": - n_mat += 1 - try: - data = obj.read() - shader_name = None - try: - sref = data.m_Shader - # Try multiple ways to resolve PPtr - try: - shader_obj = sref.deref() - except Exception: - shader_obj = None - if shader_obj is None: - try: - shader_obj_read = sref.read() - if shader_obj_read is not None: - shader_name = ( - getattr(getattr(shader_obj_read, "m_ParsedForm", None), "m_Name", None) - or getattr(shader_obj_read, "m_Name", None) - ) - except Exception: - pass - else: - sd = shader_obj.read() - shader_name = ( - getattr(getattr(sd, "m_ParsedForm", None), "m_Name", None) - or getattr(sd, "m_Name", None) - ) - except Exception as e: - errors.append(f"{short_id} mat shader resolve: {e!r}") - if not shader_name: - # PPtr fields - try: - file_id = getattr(data.m_Shader, "file_id", "?") - path_id = getattr(data.m_Shader, "path_id", "?") - shader_name = f"" - except Exception: - shader_name = "" - # Material name - mat_name = getattr(data, "m_Name", "") or "" - shader_to_bundles[shader_name].add(short_id) - shader_material_count[shader_name] += 1 - bundle_to_shaders[short_id].add((shader_name, mat_name)) - except Exception as e: - errors.append(f"{short_id} mat read err: {e!r}") - elif t == "Shader": - n_shader += 1 - try: - data = obj.read() - sname = ( - getattr(getattr(data, "m_ParsedForm", None), "m_Name", None) - or getattr(data, "m_Name", None) - ) - if sname: - embedded_shaders_per_bundle[short_id].add(sname) - except Exception as e: - errors.append(f"{short_id} shader read: {e!r}") - elif t in ("Texture2D", "Texture"): - n_tex += 1 - except Exception as e: - errors.append(f"{short_id}: load failed: {e!r}") - - bundle_summary.append((short_id, fsize, n_mat, n_shader, n_tex)) - -# === REPORT === -print("=" * 90) -print("BUNDLE SUMMARY (id_prefix, size_bytes, n_materials, n_shaders_embedded, n_textures)") -print("=" * 90) -for short_id, fsize, n_mat, n_shader, n_tex in bundle_summary: - print(f" {short_id} {fsize:>10} mats={n_mat:>3} shaders={n_shader:>2} tex={n_tex:>3}") - -print() -print("=" * 90) -print("SHADER FREQUENCY ACROSS ALL MATERIALS (sorted by usage)") -print("=" * 90) -for sname, count in shader_material_count.most_common(): - flag = " [CUSTOM?]" if not is_stock(sname) and not sname.startswith("4} mats / {n_bundles:>2} bundles {sname}{flag}") - -print() -print("=" * 90) -print("EMBEDDED SHADERS (shaders bundled INSIDE a .unity3d, sorted)") -print("=" * 90) -all_embedded = set() -for shaders in embedded_shaders_per_bundle.values(): - all_embedded.update(shaders) -print(f"\nDistinct embedded shaders: {len(all_embedded)}") -for s in sorted(all_embedded): - bundles_with = [b for b, ss in embedded_shaders_per_bundle.items() if s in ss] - print(f" - {s} ({len(bundles_with)} bundle(s): {','.join(bundles_with[:3])}{'...' if len(bundles_with) > 3 else ''})") - -print() -print("=" * 90) -print("BUNDLES REFERENCING NON-STOCK SHADERS (with material names)") -print("=" * 90) -for short_id, items in sorted(bundle_to_shaders.items()): - custom = [(s, m) for (s, m) in items if not is_stock(s) and not s.startswith("A2>%liFU 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 diff --git a/mod/data/mac-fallback-assets/deployment_red.png b/mod/data/mac-fallback-assets/deployment_red.png deleted file mode 100644 index a8e05c5e28d935439f57cf4d2f709049cdbfa337..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 856 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJD{c~2L|kcv5PuNX2iDDWKE z&}jGPHlw7BugI5IyNuHtmi)D5_;H*;;mjx+4S^9C0tvh1F7Idd-q+F|3d}JKp00i_ I>zopr0J6>oo&W#< diff --git a/mod/data/mac-fallback-assets/max_move_cyan.png b/mod/data/mac-fallback-assets/max_move_cyan.png deleted file mode 100644 index dc017745ade7c63aa858f7608a3ced343b7a245f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4124 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

Bd<4L18 zPhy1_KviTWqb66G4Wh}(ygXz&9|O)f3uwdFCa!4?KlGMbAr;%khN$<(Y752wfi=7~ zWG4G*^5@_P?P@?2+hFLeI(3EmX(Km6l@0{RXC?DJIj|^xzb^^HF3sM z9~PS`6uUoN8GIH{0(P50y^o{>`Mwj7&yB=yv(@-6K(qgn8eWbySkKes&)x$Z9RE3* za02qV^R>uF*@S3(l2A;Yjo6Tj5aKKL4 zu=%7aeAiN__!24|{fT8`0nNkY9a?!V z2v_uGfr~Z0(yKMiEmatc!CA%v8h^#3B;(YCdOu0pdwj|uQwv`&QSUVCoau))5ypqA zv!Qu!vUoYgeZX8+>#Pj9vozsWiEZWlGfHL1JVgkn?@1cm$X4=nb3n3m3_fi@+k@bR zI+`%|Q{6+MP!xim!pt~U7Y=Q%(Yk-1WiTd4I^Gn3Bdu+VLTH}4D-^r-vkRISV5>kH z_so){ZNVQx)jgnCB}StZF&z|CWw3a>bf&5kaM)@1jK#ho#=CL?JWzdsqc%RefUiho z&(!|!c?lYt*PzcFW#g{)7KJFotcVp!ipMD&aeI^AG+})$1IEA4q{DHC!~98D?POGG ziYr^^Nql-nabO~oy#5I`Mb_#;?vq;tp5A1rqFQs-Xh0gm=h5H=FXm`Kh8GlDr#Yb$ zI)?Tu0v8aVDbMqfY_-$m`gwx=l6{5EabF9YCmj`u(w9C9 zQZ#cX>%Uh@9X5^i#>>R5QkyYzu{v%{TK_?U5GF2utp#f)5!r0$iXzi=g}_5*K@Fi zOA0w^<2+1nMESu5Oob^6aVXb9`9zevpj;W{T$H1^h)b1$D|H0EVLCs634DvniQJ$~8psD0W)Fkby;G_juxYBqbDUNaWx{ zaP)=HJLF^!rWA0qJxI816Nu#l~@=dv@RHBjpU2((sL j_or@ySp6?@>S;C418-OBbs(nX|BS)Y)z4*}Q$iB}(dK-T diff --git a/mod/data/mac-fallback-assets/range_fig_leader_huge.png b/mod/data/mac-fallback-assets/range_fig_leader_huge.png deleted file mode 100644 index 6ad39ebcba76a691f394fefe08f950cb75aa9228..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18811 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 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_laat.png b/mod/data/mac-fallback-assets/range_fig_leader_laat.png deleted file mode 100644 index 3a2ac1d221b1d13e9c357efadb6afdb7fda6c4dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19058 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 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_large.png b/mod/data/mac-fallback-assets/range_fig_leader_large.png deleted file mode 100644 index c1baafb4cda99568d4f54cd6317be0e17ec5320f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18753 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 diff --git a/mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png b/mod/data/mac-fallback-assets/range_fig_leader_snail_stadium.png deleted file mode 100644 index 81c68a68c4138627192149b6f1f66b0ae59d152a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19127 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 diff --git a/mod/data/mac-fallback-assets/range_poi.png b/mod/data/mac-fallback-assets/range_poi.png deleted file mode 100644 index 5a282adb3a6a5f2a524b878032b466cb29a15285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10364 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 diff --git a/mod/data/mac-fallback-assets/range_smokeToken.png b/mod/data/mac-fallback-assets/range_smokeToken.png deleted file mode 100644 index 5a282adb3a6a5f2a524b878032b466cb29a15285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10364 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 diff --git a/mod/data/mac-fallback-assets/range_token.png b/mod/data/mac-fallback-assets/range_token.png deleted file mode 100644 index 5a282adb3a6a5f2a524b878032b466cb29a15285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10364 zcmdUVd0bQ1x9>VhAb?0BIG`efC^%KY36KDB#1T`qYFmdmfYymvkb)u{!Gcx`Xs?A< zl=u@Z*j7-S5X56q(24N&3o;& zhqbmU{Cqo_^)Mr%j*}*in?WQ(Q$!}l_=)T_Fq){}*h%9?&k9X`a%c3;p1w!NUyVAE zdSrISk?WgBjvRkv?x4=>xPX|Pvq8p@B@N=?EA2Zy4WV^mLi+u5)4PW1&xa+Rz1#HZ zyFj9duU=RA2x&~=hAzDYR6CuBb+&%_a*Gk^tQn#v|J%iVEr}(PONA+)iv(n8LbQEx zRhHXYV*5j`TdvR3$JmhCiXrx>`Nq?h#*{XZNG)2D@^BnOv#p5SckPp$MZfztB5u+4 zjinPhlh#B)l;f(eu#O_yASEh2UmANH2z&by>7>ziE38Cx%9ltx)7a;QlL_f|K(r$g zhj9$MN?*JU&*y%|#Wi?$2>+MZnEiCnf^mDr3nr=-zD%Zs$) z*-{a$wOJ!awbf`+4`xVhzuzuNZcb_AVcp+q#7ENO#a@+^#9EVTH`>t^l9EIz={yBL*?Mf6Za_TOKXBup_UOH-2DY5qs4<5>^z^OjF_}#`sW% zrGWiHL`w7hcI^Hw42MZ9*;^suKHW^}VGJ$5tP?A{I8Y5{?K9UmOxgril6%twq`2CP_A8z@E#{@JMfLQmPXfF;*pf;e3DUGFcjkgQjVrfNlFHBRNzCZ z;nf{$iaWx0#jcbYRYoP>As%#ZBDv8RdU0& zxHK{BqvSbiZ#PoA*rk%(ca2a!4RU=9!X>+c%&%1$Q|C&d6(yZtO=!L@mt zM%^Y>5bh!%Q%HXt|6G8?_Fo4{ez|Wfmbh3q}>=H;Vc5Pzl#M-zh{vXf! zO-7Vy2XpwWAubMV*^M3x&qau+bgRUf*d^}E&UuF$(SjIHOD}=IRfok(6i#{eU@CTT`M5j#n=ucpQiajXAL;da5d43 zih+!3cTdt8RoaodTeaJ%X~gyn55-88jX0&g3+XwrF>y9v>jSn=t(%Fm+{@2R7f|En zSRrv!HDS6vMBbf=3WDwm&oXj%JH*GZ#=(?+8|xO6m2k-;VU~b)$X+mX)I+j!BH72q zdJ&bociUMINYZ?vEwP^i!4wElK;Y&icbkN8wl~&;C|VMcUmiCKZd^4P!)cukARkhx(i7qs-AjKO4jGbb|==+ zBXH;@l5emrAWA-${au}iKC71+lXj{mCHO%GmFyG`A^Q1s2PU}En4a*;&eG1Cj8*Y# z=tTi3w$}IGV?xI&D$GbeUXvc|KqXPJ1BiBcWc&fTVYUZ}g0;6NuM^OLuq_BUTk8kz zGNu2xUxB#gt2r{OFDZ7$A|~wdD4ucE|D_Mztyd#*{CIg`VDm3cZk*d1QpE5U7^lge zikDA>;ajGp^5hzFHaWG9Q*9DbCePG<`_bL*hno+MuD!Iw2szP3iW7+@kIxvZ!8U zO!UgbEoeByjt$#_2wa(*;;|Fkmn;|fVVbLmRa&X-X9)Ov>WBMEsdP_l7X&6v_K#;w zsrL7=*u5Jn!>{ZWQAlM4{NQbnCSZtwa&!s-v6b$DSz?Aw46_F1XUREXyNH`2+YHJw zO?mDZQVxS7lY2^#`{$;#>fSyf(PQ`ctgngtQ?`mo=~eyZ-oB*kEa^`0@Yl)9Oh{Ig zZ$$KHtzY%W0+KvaU^_mYd?Rc#ar13qXGKbo=PMHmzcd!Iyir9|55)e9Lxq%1P4dD{ z6_H$-4UJ~n!U!da^|F*sr2jS+6kk-&EA|!8SzQA5UytOcVbe+LV+(t_#0Po%m{4O% zEGQM-&n<>?*urX3F58Z=PmTrkDpgal6N%~{h>4Yj$g#`f6PVDxSw2J;{Uu8+Nf4F= zvlA{O1%ol6mDc%uMZtyrl8GFf0i; z`$smIMK{kS7L3v)a55&LUqgYpfTO$E9Js5saZMujL9QtbnpJ9t$;Bs?)AFesA(kc} z_C;SBet(*n=$FOuFZ$5%sw*(UF9C@!9BFvvG_+?eN_o+n$C4AH?z9eyr4{%+981f8 z+YM3DeqZo#G%e?LgK(N=O*&3s2C?L82k=wz^}89nNs;6xLHpZYGZRViA8w#fvde2G zwgY^Oh>f~76bCNPoP)NE+By(N9HrUYgIyW5G!WCI?BV$nwikRb{P?uRP-#|!Uq^wQ zxoeJga7gib0k`-ryh3jFcG*X5kR8$&qNj)F@?MOx(~$K<_Pe}}#qeZ!jz-r8qi4_j zU~fjdbU_t5%WwKtTasVZ1qi1b==o5FnJ~DQ-@jIXX1Hd-s8~`7h6F&ljGNO6aUcQ( zmQ*fXI>6$t4SyaHvBI!tK$8{5jf&%kc}~%3v(YFkomnU)3+)9MOxGi4MPwWGkb^To zy*5AZRw7PbL;OH}F*|z7^d-sS; zAA{jxH#d~G`K}@w9R)eJTBPW+MM48`0++|E)Hm@qAWEFEnMz~Ty}B*h{$(w^Q5k33SvRR`=rJ*a}7+v<{@z87u$vy2Xpi z3nRaA3#2*o8!KU(kIpG;5egRue1c;pH2?5Sv=ScPQl2~kuQ6DqWZ%E|#&LbbN0{Ii+1BIe zOYC6J6Ty5BH8}8pN8TktH_GLh3!PP=dtDNd?EXk*HwOH z&y#inu4xspw0yyavcFR#gW`f(OA2r1RgTLltox8eL&U8TRh~J0ww;cl{abac4)c%w zfKaU*R#{L4@#fN;To?ZVFm@Wlz2Z%x-1AY+?GK13>(wga^$j5@A0W`mN7jWJ$sT;J zJuCA2>I1fwv}v;5_Rixq03?KE{-^yENQ}sb_(@skb+oLV!Yy;o@+jD4_`fD9H`4R# znH1$`-qrox@+{xm3N}&FYJxZV6@~w-fx#0JY^c$&sA{UO@BNS(Ee`f~S<)FUg&+@` z=Uw*lpB4K%qATgGcnM3&3pYOfC$U?X*x!wm(3QNKzLf)0o$<=E`~$Pq&Yl0xl4nOW ziP64!RhF`{xj+8l?ncyZY`4%_tZL6gesFUBSAxvo@}}aKPfz;HX&wtKwh~uPe690i z&$s_wO8*n)oZXGH{l1ZrKOl%bKPYDt1+1NbkI(hFNAuemA=HEPxDMik`}D7O?KW(J@s$pq|s4mi9EuuBt-a@rpQ{ae{j4F2?3Y%9_f zA^SPcxli_>+vnP?fk6_=>hisJIWiuITJO8^t(5tu2On;xmY?ZsSj$pN8gPxb zbkgasE*mx$t$WKN@7IM6&1*R%d!960ooI|eeqaTJn9i88!_Bvkd@r8cN|0U*@h_$%E@5>j1zG%C3*!Q8}^a1EwdTRKs^j{4s zfX1l5@G3?RP7iOp#pq-PQi5aVbm#D=7<0LNpknaqmb*j(l^i*NzLN3-4{cYs2#vnu zr}@4=54^ulp)cET#VS1Tx@i@EDkTTB^gB-`^h{80F$$A-^^>ao>h7SMhL| z6Ykmyn2d8B9Q{>QjiGO|h`sa@KES@47xT36S(}Of9LPtBjBL^S8}4F(e9RS|U|3?p z8S9oi{-?8OV)`fm${tuAv)XU4Wa>8^Xp-R~T{d{pTbIE+7x}*~Qo`&owDh0cdw&TZ zNqw&Km!!LkUny77#HJy=i0uFB8!Db)2oDoG+0jn^(j?zG<^646PKr%HDaa{peG!mS z(*a|MRkZZ!MrVwWoEUAdPvAbvuAWbw8Le;#G-12f<}5VCc5H_F&6DB1CA;Up@-(Q6 zv$7kXOKW|+$kUF8jY_ul`f4{%52qg1^O_@Q*w&uRXrmFzeghhc-FHG#NM}F93By0u z9xrV1S~-PA9pZ<#?P+jtV=;Y3B8aWXEZ*x#tF+@`@|L$5o&_Z6nLZ7Tm8DC{-qYCh z2sBiKJy&@$+VKm*G2HU}g9emTiU+{wxg)iQZ;;9>6*SJf${P{I6D_+x=25HA* zI#RQ?Y1$@B+39!(yw~_kes!QhRpT+=sYjx(8y1B0o9$n6&}P z&Tn)9eEaBEE81=iiY)_R)2U0UuQ?!J#Cz)0MdjCeQ|BuFsvo@U20w=e-PHY#Bka`t zzBhdm^()#9teS$j^eNO&vm1_A^IL>rDu^Y4T*$v{D)!u&kwPV#-Qe_!U1$D?rjjjg zu#V!}J%7aVGZxHbau%j|7xH%9FcI27(TA}7_3QTGqT6N<9ShE!)p?HY)+qFog- zaUmo!oUMPHSq{%>d8`$NTA|xF%kzyD#pnVspK*%=i|on#-ZXe!?G;VZSRM;vaV~~; z;BIdhcV(3Jfipn#laLEC0jIe83npTA!}+NKPI>nj+F7+*vPEn~ZWz<{^b8VM119Qw zu+Xd0j`gPGkoyBhKd=4TTp*VQt^hShPls5N79hz)`g7hFdz0w76HHSZB@@W|1%|Y} zh=UZSNILbUu=Ua0KH1Hu?JZsFfMF&X51e`*2^Lh+<*<}M5j9orLw~({b$v8(Lu7bI zQ(9DSxNF6~;X~!tH~f_6nb0&Zx%%~X;-(p1`s0ISC)?ahJLM6mzo=-`%n?!Db1B4s zlU$bVOIim*1|hM2RJ@czlVZ(qb*@a->=)7Adlj(mI`@=%bB6sX>>_NlUUOeIg_Kqj zs5!SVEcqi5jjS&Lt$L5xEJKNgT*MTUbCPtWpmX z0Rh7fm33w)>7qyCfWbdEGrAuhmj^~3Zo(0e8)zPR2zk1lGA#d^qOk7~`hI_2f@Hg6 z_1r_2)bC0@a`}GA3)82PS|<6JXq0Nr0p$O>%BCW%SroR*LPWQ83L{eYuAYU2{qhU> zU5K5WJpJJ~QumTzYaO5(d|>zGN0F3!kH1R)+>GPRQrE_Zzjs8kPiNGU>~lgfK&%VV zGF4rwC&PA~XHHzFw<0|QTJS@DOiR)&_>E3HJKKa@V ziQqZ#-$z3mE08JIj`p5~z2r;3XzVyTksUVKG&kHB#6+_!PDwpAC8z_Xa|tvhGcp zKHzKb4aaTEf;+3QaJ1g}cKllba_8M!bLU8;pXU?lhO9ag=^gZwAyS288BEeuw@6H> zLAFu#&~{rkrn!%6iF8)XH$-Iyn7! z9wj(HKh}#c5%5ON^KkMKwUZmEnUh$#7Uq#@$j2?`V*4X(*PC6dHb!a-5m7N8QK;5K6aY?sD;&I}n#u{?)aHg|2(P-B7(5iJnzhP?Vg*E6qP3IpctR3=UvF8T1w;*UN?568?nu&py9CYO?+L4t|!7=fJ@`1 zS11^9LVNwt#!ba0NR)!Z3sik9eCEh<^DV59G%a4KwFUZ5Jo<6#CJr%AX$6$)Kv{;$ zl_{GMvJxvKt1$05P*yuP#z%*QPw6f&e+wXlFU|o#q2E?WYNw&R1=TEEka?uGpMX?m zpp+M&7y4qQ&qRVzhk6&jNNSLSh!Qk9vBrsL3i?xB8#f`Rtrj^D`_Md6IRa%k<4us- z%ePB|%#&fi8h{@{mCPUDbsFF}DXEPB{>VI1JqkrW09OON2H>|Wg*5jPSE$7oA*oOzGu4@B?RjK+T;6O#eSvg} z3DUWG;Z_lmD=HVRN>Z__-|Zxz2PlxqLwk)>NN>=Wdsdu?Ut?XWitpO&y0hcYV-%B@ncaR^Wy<-Old zI~QT#66&&2P@l!uW_8%g$a|xDsw2v}Ok63cs75>&_oRLk&B+OcQY&x{`o>Dg`T_hj z=m&@xwPm!O8mBz9t1Hb#aaPSFBI|Kdic{)@MTqR?eT^vts1JZT#zer)F^^!eHL!{( zS~^V6!1ZVgBSqQRMJT1~|1qr}*1+f|VkJ97(X{}bP!$hPc8a23{B=SdB0hU(i$G<* z#E$g>s?7u`yV^X0;=oBY;A4vLuNCq|Qmo}>=HRdw%v?B*deF!0;q4n~0QSY@zSe59 zv39H<>^-5AxODdp3D;Qw+n;CFxISklf(&<76qRir&cCN?`}ZF0Rp3Rk2?!X`Ta3!O eza-s>oMz^npIP1k8?TQD-=y(=;|`BeM*Syc!aU*t diff --git a/mod/data/mac-fallback-assets/range_tokenRangeTwo.png b/mod/data/mac-fallback-assets/range_tokenRangeTwo.png deleted file mode 100644 index 4a8a2f488e72d64ace0344269757f910d16f74ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12344 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 diff --git a/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d b/mod/data/mac-fallback-assets/silhouette_mac_fallback.unity3d deleted file mode 100644 index 15bb9258a1c225572de9b686b6595455abfe184f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10922 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 Mcz6b}|N5b`;!*7d "mac" | "windows" -deploymentMode = deploymentMode or "mac" -- table-wide setting - -function gGetMode(params) - return playerOverlayMode[params.color] or "mac" -end - -function gGetDeploymentMode() - return deploymentMode -end - -function gToggleDeploymentMode() - deploymentMode = (deploymentMode == "mac") and "windows" or "mac" - macRefreshModeUI() -end - -function gCohesionTrigger(params) - if not params or not params.figGUID then return end - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local mode = playerOverlayMode[params.playerColor] or "mac" - if mode == "windows" then - -- Object may not have the original alias (e.g., hovered non-fig object). - -- pcall guards against "no such function" errors that would otherwise - -- propagate out of the hotkey/button callback. - local ok = pcall(function() fig.call("spawnCohesionRulerOriginal", fig) end) - if not ok then gToggleCohesion({figGUID = params.figGUID}) end - else - gToggleCohesion({figGUID = params.figGUID}) - end -end - -function gRangeTrigger(params) - if not params or not params.figGUID then return end - local fig = getObjectFromGUID(params.figGUID) - if not fig then return end - local mode = playerOverlayMode[params.playerColor] or "mac" - if mode == "windows" then - local ok = pcall(function() fig.call("spawnRangeRulerOriginal", fig) end) - if not ok then gToggleRange({figGUID = params.figGUID}) end - else - gToggleRange({figGUID = params.figGUID}) - end -end - --- UI: floating panel with one toggle button per seated player -function macModeClick(player, _, id) - local color = id:sub(9) -- "macmode_Red" -> "Red" - if player.color ~= color then - broadcastToColor("Only the player at this seat can toggle their mode.", - player.color, {1, 0.5, 0.5}) - return - end - local cur = playerOverlayMode[color] or "mac" - playerOverlayMode[color] = (cur == "mac") and "windows" or "mac" - -- Wipe any active Cohesion/Range overlays for this player so the next - -- trigger redraws in the chosen mode without stale visuals. - macRefreshModeUI() -end - -local function macFindNodeById(tree, id) - for _, node in ipairs(tree) do - if node.attributes and node.attributes.id == id then return node, tree end - if node.children then - local found, parent = macFindNodeById(node.children, id) - if found then return found, parent end - end - end - return nil, nil -end - --- Tracks the panel's desired active state across refreshes. Persists so a --- click on the close X before the initial refresh is honored. -macModePanelActive = (macModePanelActive == nil) and true or macModePanelActive - -function macModeToggleVisibility() - macModePanelActive = not macModePanelActive - local tree = UI.getXmlTable() or {} - local panel = macFindNodeById(tree, "macModePanel") - if panel then - panel.attributes.active = macModePanelActive and "true" or "false" - UI.setXmlTable(tree) - else - macRefreshModeUI() - end -end - -function macRefreshModeUI() - local seated = Player.getPlayers() - local rows = {{ - tag = "Panel", - attributes = { - color = "transparent", - preferredHeight = "24", - }, - children = { - { - tag = "Text", - attributes = { - text = "Mac TTS U6 Patch", - fontSize = "16", - color = "white", - alignment = "MiddleLeft", - rectAlignment = "MiddleLeft", - }, - }, - { - tag = "Button", - attributes = { - id = "macModePanelClose", - text = "X", - onClick = "macModeToggleVisibility", - color = "#3a1e1e", - textColor = "white", - fontSize = "12", - width = "24", - height = "20", - rectAlignment = "MiddleRight", - }, - }, - }, - }, { - tag = "Text", - attributes = { - text = "Cohesion & Range: pick your renderer", - fontSize = "12", - color = "#bbbbbb", - alignment = "MiddleCenter", - }, - }} - if #seated == 0 then - table.insert(rows, { - tag = "Text", - attributes = { - text = "(no seated players)", - fontSize = "12", - color = "#888888", - alignment = "MiddleCenter", - }, - }) - end - for _, p in ipairs(seated) do - local mode = playerOverlayMode[p.color] or "mac" - local label = p.color .. " - " .. - (mode == "mac" and "MAC FALLBACK" or "WINDOWS ORIGINAL") - local bg = (mode == "mac") and "#1e7a3a" or "#7a3a1e" - table.insert(rows, { - tag = "Button", - attributes = { - id = "macmode_" .. p.color, - text = label, - onClick = "macModeClick", - color = bg, - fontSize = "13", - textColor = "white", - preferredHeight = "28", - }, - }) - end - -- Deployment is table-wide (everyone sees the same zones); use a single - -- global toggle rather than per-seat. - table.insert(rows, { - tag = "Text", - attributes = { - text = "Deployment (table-wide)", - fontSize = "12", - color = "#bbbbbb", - alignment = "MiddleCenter", - }, - }) - table.insert(rows, { - tag = "Button", - attributes = { - id = "macmode_deployment", - text = (deploymentMode == "mac") and "DEPLOYMENT: MAC FALLBACK" - or "DEPLOYMENT: WINDOWS ORIGINAL", - onClick = "macDeploymentClick", - color = (deploymentMode == "mac") and "#1e3a7a" or "#7a3a1e", - fontSize = "13", - textColor = "white", - preferredHeight = "28", - }, - }) - -- Build my panel (will be merged into the existing UI tree below so we - -- don't clobber legionFloatingMenu / Welcome / Chess Clocks etc.). - local panel = { - tag = "Panel", - attributes = { - id = "macModePanel", - active = macModePanelActive and "true" or "false", - rectAlignment = "MiddleRight", - offsetXY = "-10 80", - width = "260", - height = "320", - color = "rgba(0.06,0.06,0.06,0.9)", - padding = "8 8 8 8", - outlineSize = "1 1", - outline = "#303030", - }, - children = {{ - tag = "VerticalLayout", - attributes = { - spacing = "4", - childForceExpandHeight = "false", - childForceExpandWidth = "true", - }, - children = rows, - }}, - } - - local tree = UI.getXmlTable() or {} - -- Remove any stale macModePanel before reinserting the fresh one. - for i = #tree, 1, -1 do - if tree[i].attributes and tree[i].attributes.id == "macModePanel" then - table.remove(tree, i) - end - end - table.insert(tree, panel) - - -- Inject a "Mac Patch" button into the bottom-right legionFloatingMenu - -- (replaces the first interactable=false placeholder button). - local menu = macFindNodeById(tree, "legionFloatingMenu") - if menu and menu.children then - local hasMine = false - for _, c in ipairs(menu.children) do - if c.attributes and c.attributes.id == "macModeMenuButton" then - hasMine = true; break - end - end - if not hasMine then - for i, c in ipairs(menu.children) do - if c.attributes and c.attributes.interactable == "false" then - menu.children[i] = { - tag = "Button", - attributes = { - id = "macModeMenuButton", - fontSize = "10", - onClick = "macModeToggleVisibility", - tooltip = "Toggle Mac TTS U6 Patch panel", - }, - value = "Mac Patch", - } - break - end - end - end - end - - UI.setXmlTable(tree) -end - -function macDeploymentClick(_, _, _) - gToggleDeploymentMode() -end - -function onPlayerChangeColor(_) macRefreshModeUI() end -function onPlayerConnect(_) macRefreshModeUI() end -function onPlayerDisconnect(_) macRefreshModeUI() end - -Wait.time(macRefreshModeUI, 2) - --- Override hotkey init functions to capture playerColor and route through --- the per-seat trigger. Defining initCohesionHotkeys/initRangebandHotkeys --- here SHADOWS the originals - when the original onLoad runs init*(), our --- versions register the hotkey instead. - -function initCohesionHotkeys() - addHotkey("Show Cohesion On Hovered Model", - function(playerColor, hoverObject, _) - if not hoverObject or not hoverObject.interactable then return end - gCohesionTrigger({ - figGUID = hoverObject.getGUID(), - playerColor = playerColor, - }) - end) -end - -function initRangebandHotkeys() - addHotkey("Show Range On Hovered Model", - function(playerColor, hoverObject, _) - if not hoverObject or not hoverObject.interactable then return end - gRangeTrigger({ - figGUID = hoverObject.getGUID(), - playerColor = playerColor, - }) - end) -end - --- Legacy global-scope wrappers (for any code that still calls these directly). --- Default to mac mode when no color context is available. -function showCohesionOnHoveredModel(hoverObject) - if not hoverObject or not hoverObject.interactable then return end - gToggleCohesion({ figGUID = hoverObject.getGUID() }) -end - -function spawnCohesionRuler(cohesionSourceObject) - if not cohesionSourceObject then return end - gSpawnCohesion({ figGUID = cohesionSourceObject.getGUID() }) -end - -function clearCohesionRuler() - for key, entry in pairs(activeOverlays) do - if entry.type == "cohesion" then activeOverlays[key] = nil end - end - macRedrawAll() -end - -function showRangeOnHoveredModel(hoverObject) - if not hoverObject or not hoverObject.interactable then return end - gToggleRange({ figGUID = hoverObject.getGUID() }) -end - -function spawnRangeRuler(rangeSourceObject, _) - if not rangeSourceObject then return end - gSpawnRange({ figGUID = rangeSourceObject.getGUID() }) -end - -function clearRangeRulers() - gClearAllRange() -end - diff --git a/mod/src/includes/RangeRulers.ttslua b/mod/src/includes/RangeRulers.ttslua index c51676e0..6df6e15a 100644 --- a/mod/src/includes/RangeRulers.ttslua +++ b/mod/src/includes/RangeRulers.ttslua @@ -80,33 +80,3 @@ function spawnRangeRuler(rangeSourceObject, projectorBundleOverride) noRulers = false end - --- ============================================ --- Mac fallback per-seat router (appended). --- Mirrors the Cohesion pattern. See !/Overlays for the manager. --- ============================================ -spawnRangeRulerOriginal = spawnRangeRuler -clearRangeRulersOriginal = clearRangeRulers - -function spawnRangeRuler(rangeSourceObject, _) - if not rangeSourceObject then return end - Global.call("gRangeTrigger", { - figGUID = rangeSourceObject.getGUID(), - playerColor = nil, - }) -end - -function clearRangeRulers() - Global.call("gClearAllRange", {}) - -- Also run the original (destroys the bundle Object spawned in Windows - -- mode). Skip if rangeRuler was never set (pure-Mac session). - if rangeRuler ~= nil then - pcall(clearRangeRulersOriginal) - end -end - -function clearRangeRuler() - if self and self.getGUID and self.getGUID() ~= "-1" then - Global.call("gClearRange", { figGUID = self.getGUID() }) - end -end diff --git a/src/index.ts b/src/index.ts index ba63a1e4..45d99efd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -103,31 +103,25 @@ export async function compileSaveFile( } } -function defaultTTSHomeDir(): string { - const platform = os.platform(); - if (platform === 'win32') { - return steam.homeDir.win32(process.env); - } - if (platform === 'darwin') { - return path.join(os.homedir(), 'Library', 'Tabletop Simulator'); - } - if (platform === 'linux') { - return path.join(os.homedir(), '.local', 'share', 'Tabletop Simulator'); - } - throw new Error(`Unsupported platform: ${platform}`); -} - export async function destroySymlink(homeDir?: string): Promise { + // TODO: Add non-win32 support. if (!homeDir) { - homeDir = defaultTTSHomeDir(); + if (os.platform() !== 'win32') { + throw new Error(`Unsupported platform: ${os.platform()}`); + } + homeDir = steam.homeDir.win32(process.env); } const from = path.join(homeDir, 'Saves', 'TTSDevLink'); return fs.remove(from); } export async function createSymlink(homeDir?: string): Promise { + // TODO: Add non-win32 support. if (!homeDir) { - homeDir = defaultTTSHomeDir(); + if (os.platform() !== 'win32') { + throw new Error(`Unsupported platform: ${os.platform()}`); + } + homeDir = steam.homeDir.win32(process.env); } await destroySymlink(homeDir); const from = path.join(homeDir, 'Saves', 'TTSDevLink'); From 91b6c3815ebc0e8ab9306180aa0aaba679b14631 Mon Sep 17 00:00:00 2001 From: halestom15 Date: Wed, 20 May 2026 01:29:15 -0400 Subject: [PATCH 92/93] Update Welcome --- contrib/maps/competitive.json | 23 +---------------------- mod/src/includes/ui/Welcome.xml | 7 +++++-- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/contrib/maps/competitive.json b/contrib/maps/competitive.json index d88af8bd..fe51488c 100644 --- a/contrib/maps/competitive.json +++ b/contrib/maps/competitive.json @@ -1,22 +1 @@ -[ - { - "name": "Bespin Gardens", - "url": "http://cloud-3.steamusercontent.com/ugc/1646592601245349867/4E682DF85F3DB485E9826FCCF7E85318DE20E599/" - }, - { - "name": "Geonosis: \"Badlands of Ngzi\"", - "url": "http://cloud-3.steamusercontent.com/ugc/1646592601245348737/80E59C4755458DD3853136B69E179BF9653DF331/" - }, - { - "name": "Jedha City Center", - "url": "http://cloud-3.steamusercontent.com/ugc/1646592601245349257/B5BB45EB4995E1429D2EBE24D25F9622C0BF4319/" - }, - { - "name": "Tatooine: \"Scum and Villainy\"", - "url": "http://cloud-3.steamusercontent.com/ugc/1646592601245347987/AC056D4619E1FD0C94DFDFC7602C5336BB1FCBD3/" - }, - { - "name": "Scarif Landing", - "url": "http://cloud-3.steamusercontent.com/ugc/1646592601245350323/DD4FEA2C9A15220358F9E78D21E3D5B79D244CF8/" - } -] +[] diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index 47e68d21..d83e35e9 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -37,7 +37,7 @@ - Welcome to Star Wars: Legion for TTS, v8.0.17-beta. + Welcome to Star Wars: Legion for TTS, v8.0.18-beta. This is a beta release . @@ -46,7 +46,10 @@ CHANGELOG (Since last Beta) - - Merc Test 1 + - Add: Ohnaka Gang including New CCs + - Add: Droideka, Stap, Trusted Agent, Insatiable Curiousity Errata + - Add: Geonosian Engineers + - Remove: Buggy Maps in module From 9d16eaa4d2042db83a017b22956fa7d9c758fdaa Mon Sep 17 00:00:00 2001 From: halestom15 Date: Thu, 21 May 2026 22:56:42 -0400 Subject: [PATCH 93/93] Update Welcome.xml --- mod/src/includes/ui/Welcome.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/mod/src/includes/ui/Welcome.xml b/mod/src/includes/ui/Welcome.xml index d83e35e9..cef3057d 100644 --- a/mod/src/includes/ui/Welcome.xml +++ b/mod/src/includes/ui/Welcome.xml @@ -49,7 +49,6 @@ - Add: Ohnaka Gang including New CCs - Add: Droideka, Stap, Trusted Agent, Insatiable Curiousity Errata - Add: Geonosian Engineers - - Remove: Buggy Maps in module