Skip to content

Commit 6171fe0

Browse files
CoroniaZivDero
andauthored
Insignia.Passengers & InsigniaTypes (#1365)
1 - added insignia based on current passenger size: Normal insignia can be overridden when its current passenger size reaches a certain amount by setting `Insignia(.Frame/.Frames).PassengersN` where `N` stands for the current passenger size amount. If not set, defaults to non-passenger specific insignia settings. Will be overridden by weapon mode insignia settings, if set. In `rulesmd.ini`: ```ini Insignia.PassengersN= ; filename - excluding the .shp extension Insignia.PassengersN.Rookie= ; filename - excluding the .shp extension Insignia.PassengersN.Veteran= ; filename - excluding the .shp extension Insignia.PassengersN.Elite= ; filename - excluding the .shp extension InsigniaFrame.PassengersN=-1 ; int, frame of insignia shp (zero-based) or -1 for default InsigniaFrame.PassengersN.Rookie=-1 ; int, frame of insignia shp (zero-based) or -1 for default InsigniaFrame.PassengersN.Veteran=-1 ; int, frame of insignia shp (zero-based) or -1 for default InsigniaFrame.PassengersN.Elite=-1 ; int, frame of insignia shp (zero-based) or -1 for default InsigniaFrames.PassengersN=-1,-1,-1 ; int, frames of insignia shp (zero-based) or -1 for default ``` 2 - This isn't a real logic, but more like an interface to make ini edit easier. Previously we have to define insignia by the techno's veterancy, which requires a lot of tags especially if it's for Gunner or using `InsigniaFrame(s)`. This pull request allows you to define an `InsigniaType` that stores all these data together, so that you can reuse them in different places with one line of code. In `rulesmd.ini`: ```ini [InsigniaTypes] 0=SOMEINSIGNIATYPE [SOMEINSIGNIATYPE] ; InsigniaType Insignia= ; filename - excluding the .shp extension Insignia.Rookie= ; filename - excluding the .shp extension Insignia.Veteran= ; filename - excluding the .shp extension Insignia.Elite= ; filename - excluding the .shp extension InsigniaFrame=-1 ; int, frame of insignia shp (zero-based) or -1 for default InsigniaFrame.Rookie=-1 ; int, frame of insignia shp (zero-based) or -1 for default InsigniaFrame.Veteran=-1 ; int, frame of insignia shp (zero-based) or -1 for default InsigniaFrame.Elite=-1 ; int, frame of insignia shp (zero-based) or -1 for default [SOMETECHNO] ; TechnoType InsigniaType= ; InsigniaType InsigniaType.WeaponN= ; InsigniaType InsigniaType.PassengersN= ; InsigniaType ``` An example: ```ini [InsigniaTypes] 0=InsigniaHero [InsigniaHero] Insignia.Rookie=herostcr Insignia.Veteran=herostbr Insignia.Elite=herostar [TANY] InsigniaType=InsigniaHero IFVMode=15 [VOLKOV] InsigniaType=InsigniaHero IFVMode=35 [FV] InsigniaType.Weapon15=InsigniaHero InsigniaType.Weapon35=InsigniaHero ``` --------- Co-authored-by: ZivDero <[email protected]>
1 parent c00e260 commit 6171fe0

File tree

11 files changed

+194
-14
lines changed

11 files changed

+194
-14
lines changed

CREDITS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,8 @@ This page lists all the individual contributions to the project by their author.
470470
- Fire weapon when Warhead kills something
471471
- Promotion animation deglobalization
472472
- Forcing specific weapon by range
473+
- Passenger-based insignias
474+
- Use `InsigniaType` to set the properties of insignia in a batch
473475
- **NaotoYuuki** - Vertical & meteor trajectory projectile prototypes
474476
- **handama** - AI script action to `16005 Jump Back To Previous Script`
475477
- **TaranDahl (航味麻酱)**:

Phobos.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
<ClCompile Include="src\Misc\RetryDialog.cpp" />
8585
<ClCompile Include="src\New\Type\Affiliated\DroppodTypeClass.cpp" />
8686
<ClCompile Include="src\New\Type\DigitalDisplayTypeClass.cpp" />
87+
<ClCompile Include="src\New\Type\InsigniaTypeClass.cpp" />
8788
<ClCompile Include="src\New\Type\RadTypeClass.cpp" />
8889
<ClCompile Include="src\New\Type\SelectBoxTypeClass.cpp" />
8990
<ClCompile Include="src\New\Type\ShieldTypeClass.cpp" />
@@ -230,6 +231,7 @@
230231
<ClInclude Include="src\New\Type\Affiliated\TypeConvertGroup.h" />
231232
<ClInclude Include="src\New\Type\Affiliated\DroppodTypeClass.h" />
232233
<ClInclude Include="src\New\Type\DigitalDisplayTypeClass.h" />
234+
<ClInclude Include="src\New\Type\InsigniaTypeClass.h" />
233235
<ClInclude Include="src\New\Type\RadTypeClass.h" />
234236
<ClInclude Include="src\New\Type\SelectBoxTypeClass.h" />
235237
<ClInclude Include="src\New\Type\ShieldTypeClass.h" />

docs/Fixed-or-Improved-Logics.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -858,9 +858,13 @@ Image= ; name of the file that will be used as image, without exten
858858

859859
- You can now customize veterancy insignia of TechnoTypes.
860860
- `Insignia.(Rookie|Veteran|Elite)` can be used to set a custom insignia file, optionally for each veterancy stage. Like the original / default file, `pips.shp`, they are drawn using `palette.pal` as palette.
861-
- `InsigniaFrame.(Rookie|Veteran|Elite)` can be used to set (zero-based) frame index of the insignia to display, optionally for each veterancy stage. Using -1 uses the default setting. Default settings are -1 (none) for rookie, 14 for veteran and 15 for elite.
862-
- A shorthand `InsigniaFrames` can be used to list them in order from rookie, veteran and elite instead as well. `InsigniaFrame.(Rookie|Veteran|Elite)` takes priority over this.
863-
- Normal insignia can be overridden for specific weapon modes of `Gunner=true` units by setting `Insignia.(Frame/Frames).WeaponN` where `N` stands for 1-based weapon mode index. If not set, defaults to non-mode specific insignia settings.
861+
- `InsigniaFrame(.Rookie|Veteran|Elite)` can be used to set (zero-based) frame index of the insignia to display, optionally for each veterancy stage. Using -1 uses the default setting. Default settings are -1 (none) for rookie, 14 for veteran and 15 for elite.
862+
- A shorthand `InsigniaFrames` can be used to list them in order from rookie, veteran and elite instead as well. `InsigniaFrame(.Rookie|Veteran|Elite)` takes priority over this.
863+
- These settings will be overriden by the properties set in [InsigniaType](Miscellanous.md#insignia-type), if `InsigniaType` is set.
864+
- Normal insignia can be overridden for specific weapon modes of `Gunner=true` units by setting `Insignia(.Frame/.Frames).WeaponN` where `N` stands for 1-based weapon mode index. If not set, defaults to non-mode specific insignia settings.
865+
- These settings will be overriden by the properties set in [InsigniaType](Miscellanous.md#insignia-type), if `InsigniaType.WeaponN` is set.
866+
- Normal insignia can be overridden when its current passenger size reaches a certain amount by setting `Insignia(.Frame/.Frames).PassengersN` where `N` stands for the current passenger size amount (from 0 to `Passengers` of the transport). If not set, defaults to non-passenger specific insignia settings. Will be overridden by weapon mode insignia settings, if set.
867+
- These settings will be overriden by the properties set in [InsigniaType](Miscellanous.md#insignia-type), if `InsigniaType.PassengersN` is set.
864868
- `Insignia.ShowEnemy` controls whether or not the insignia is shown to enemy players. Defaults to `[General] -> EnemyInsignia`, which in turn defaults to true.
865869
- You can make insignias appear only on selected units using `DrawInsignia.OnlyOnSelected`.
866870
- Position for insignias can be adjusted by setting `DrawInsignia.AdjustPos.Infantry` for infantry, `DrawInsignia.AdjustPos.Buildings` for buildings, and `DrawInsignia.AdjustPos.Units` for others.
@@ -897,6 +901,15 @@ InsigniaFrame.WeaponN.Rookie=-1 ; int, frame of insignia shp (zero-base
897901
InsigniaFrame.WeaponN.Veteran=-1 ; int, frame of insignia shp (zero-based) or -1 for default
898902
InsigniaFrame.WeaponN.Elite=-1 ; int, frame of insignia shp (zero-based) or -1 for default
899903
InsigniaFrames.WeaponN=-1,-1,-1 ; int, frames of insignia shp (zero-based) or -1 for default
904+
Insignia.PassengersN= ; filename - excluding the .shp extension
905+
Insignia.PassengersN.Rookie= ; filename - excluding the .shp extension
906+
Insignia.PassengersN.Veteran= ; filename - excluding the .shp extension
907+
Insignia.PassengersN.Elite= ; filename - excluding the .shp extension
908+
InsigniaFrame.PassengersN=-1 ; int, frame of insignia shp (zero-based) or -1 for default
909+
InsigniaFrame.PassengersN.Rookie=-1 ; int, frame of insignia shp (zero-based) or -1 for default
910+
InsigniaFrame.PassengersN.Veteran=-1 ; int, frame of insignia shp (zero-based) or -1 for default
911+
InsigniaFrame.PassengersN.Elite=-1 ; int, frame of insignia shp (zero-based) or -1 for default
912+
InsigniaFrames.PassengersN=-1,-1,-1 ; int, frames of insignia shp (zero-based) or -1 for default
900913
Insignia.ShowEnemy= ; boolean
901914
```
902915

docs/Miscellanous.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,31 @@ SaveVariablesOnScenarioEnd=false ; boolean
8585
Correspondingly, if such a writing method causes any errors, it is also not within the scope of responsibility of this function.
8686
```
8787

88+
### Insignia Type
89+
90+
- It is now possible to define the properties of insignia in an entity, so that all properties in it will be used once it's applied to a techno.
91+
92+
In `rulesmd.ini`:
93+
```ini
94+
[InsigniaTypes]
95+
0=SOMEINSIGNIATYPE
96+
97+
[SOMEINSIGNIATYPE] ; InsigniaType
98+
Insignia= ; filename - excluding the .shp extension
99+
Insignia.Rookie= ; filename - excluding the .shp extension
100+
Insignia.Veteran= ; filename - excluding the .shp extension
101+
Insignia.Elite= ; filename - excluding the .shp extension
102+
InsigniaFrame=-1 ; int, frame of insignia shp (zero-based) or -1 for default
103+
InsigniaFrame.Rookie=-1 ; int, frame of insignia shp (zero-based) or -1 for default
104+
InsigniaFrame.Veteran=-1 ; int, frame of insignia shp (zero-based) or -1 for default
105+
InsigniaFrame.Elite=-1 ; int, frame of insignia shp (zero-based) or -1 for default
106+
107+
[SOMETECHNO] ; TechnoType
108+
InsigniaType= ; InsigniaType
109+
InsigniaType.WeaponN= ; InsigniaType
110+
InsigniaType.PassengersN= ; InsigniaType
111+
```
112+
88113
## Game Speed
89114

90115
### Single player game speed

docs/Whats-New.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ New:
372372
- [Amphibious access vehicle](New-or-Enhanced-Logics.md#amphibious-access-vehicle) (by CrimRecya)
373373
- [Allow miners do area guard](Fixed-or-Improved-Logics.md#allow-miners-do-area-guard) (by TaranDahl)
374374
- [Make harvesters do addtional scan after unload](Fixed-or-Improved-Logics.md#make-harvesters-do-addtional-scan-after-unload) (by TaranDahl)
375+
- [Passenger-based insignias](Fixed-or-Improved-Logics.md#customizable-veterancy-insignias) (by Ollerus)
376+
- [Use `InsigniaType` to set the properties of insignia in a batch](Miscellanous.md#insignia-type) (by Ollerus)
375377
376378
Vanilla fixes:
377379
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)

src/Ext/Rules/Body.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <New/Type/LaserTrailTypeClass.h>
1010
#include <New/Type/DigitalDisplayTypeClass.h>
1111
#include <New/Type/AttachEffectTypeClass.h>
12+
#include <New/Type/InsigniaTypeClass.h>
1213
#include <New/Type/SelectBoxTypeClass.h>
1314
#include <Utilities/Patch.h>
1415

@@ -37,6 +38,7 @@ void RulesExt::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)
3738
ShieldTypeClass::LoadFromINIList(pINI);
3839
LaserTrailTypeClass::LoadFromINIList(&CCINIClass::INI_Art);
3940
AttachEffectTypeClass::LoadFromINIList(pINI);
41+
InsigniaTypeClass::LoadFromINIList(pINI);
4042

4143
Data->LoadBeforeTypeData(pThis, pINI);
4244
}

src/Ext/Techno/Body.Visuals.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,28 @@ void TechnoExt::DrawInsignia(TechnoClass* pThis, Point2D* pLocation, RectangleSt
134134
int insigniaFrame = insigniaFrames.X;
135135
int frameIndex = pTechnoTypeExt->InsigniaFrame.Get(pThis);
136136

137+
if (pTechnoType->Passengers > 0)
138+
{
139+
int passengersIndex = pThis->Passengers.GetTotalSize();
140+
141+
if (auto const pCustomShapeFile = pTechnoTypeExt->Insignia_Passengers[passengersIndex].Get(pThis))
142+
{
143+
pShapeFile = pCustomShapeFile;
144+
defaultFrameIndex = 0;
145+
isCustomInsignia = true;
146+
}
147+
148+
int frame = pTechnoTypeExt->InsigniaFrame_Passengers[passengersIndex].Get(pThis);
149+
150+
if (frame != -1)
151+
frameIndex = frame;
152+
153+
auto const& frames = pTechnoTypeExt->InsigniaFrames_Passengers[passengersIndex];
154+
155+
if (frames != Vector3D<int>(-1, -1, -1))
156+
insigniaFrames = frames.Get();
157+
}
158+
137159
if (pTechnoType->Gunner)
138160
{
139161
int weaponIndex = pThis->CurrentWeaponNumber;
@@ -150,10 +172,10 @@ void TechnoExt::DrawInsignia(TechnoClass* pThis, Point2D* pLocation, RectangleSt
150172
if (frame != -1)
151173
frameIndex = frame;
152174

153-
auto& frames = pTechnoTypeExt->InsigniaFrames_Weapon[weaponIndex];
175+
auto const& frames = pTechnoTypeExt->InsigniaFrames_Weapon[weaponIndex];
154176

155177
if (frames != Vector3D<int>(-1, -1, -1))
156-
insigniaFrames = frames;
178+
insigniaFrames = frames.Get();
157179
}
158180

159181
if (pVeterancy->IsVeteran())

src/Ext/TechnoType/Body.cpp

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <Ext/BuildingType/Body.h>
1212
#include <Ext/BulletType/Body.h>
1313
#include <Ext/Techno/Body.h>
14+
#include <New/Type/InsigniaTypeClass.h>
1415

1516
#include <Utilities/GeneralUtils.h>
1617

@@ -419,9 +420,23 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
419420
this->DeployFireWeapon.Read(exINI, pSection, "DeployFireWeapon");
420421
this->TargetZoneScanType.Read(exINI, pSection, "TargetZoneScanType");
421422

422-
this->Insignia.Read(exINI, pSection, "Insignia.%s");
423-
this->InsigniaFrames.Read(exINI, pSection, "InsigniaFrames");
424-
this->InsigniaFrame.Read(exINI, pSection, "InsigniaFrame.%s");
423+
// insignia type
424+
Nullable<InsigniaTypeClass*> InsigniaType;
425+
InsigniaType.Read(exINI, pSection, "InsigniaType");
426+
427+
if (InsigniaType.isset())
428+
{
429+
this->Insignia = InsigniaType.Get()->Insignia;
430+
this->InsigniaFrame = InsigniaType.Get()->InsigniaFrame;
431+
this->InsigniaFrames = Vector3D<int>(-1, -1, -1); // override it so only InsigniaFrame will be used
432+
}
433+
else
434+
{
435+
this->Insignia.Read(exINI, pSection, "Insignia.%s");
436+
this->InsigniaFrames.Read(exINI, pSection, "InsigniaFrames");
437+
this->InsigniaFrame.Read(exINI, pSection, "InsigniaFrame.%s");
438+
}
439+
425440
this->Insignia_ShowEnemy.Read(exINI, pSection, "Insignia.ShowEnemy");
426441

427442
this->JumpjetTilt.Read(exINI, pSection, "JumpjetTilt");
@@ -586,14 +601,66 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
586601

587602
for (size_t i = 0; i < weaponCount; i++)
588603
{
589-
_snprintf_s(tempBuffer, sizeof(tempBuffer), "Insignia.Weapon%d.%s", i + 1, "%s");
590-
this->Insignia_Weapon[i].Read(exINI, pSection, tempBuffer);
604+
Nullable<InsigniaTypeClass*> InsigniaType_Weapon;
605+
_snprintf_s(tempBuffer, sizeof(tempBuffer), "InsigniaType.Weapon%d", i + 1);
606+
InsigniaType_Weapon.Read(exINI, pSection, tempBuffer);
607+
608+
if (InsigniaType_Weapon.isset())
609+
{
610+
this->Insignia_Weapon[i] = InsigniaType_Weapon.Get()->Insignia;
611+
this->InsigniaFrame_Weapon[i] = InsigniaType_Weapon.Get()->InsigniaFrame;
612+
this->InsigniaFrames_Weapon[i] = Vector3D<int>(-1, -1, -1); // override it so only InsigniaFrame will be used
613+
}
614+
else
615+
{
616+
_snprintf_s(tempBuffer, sizeof(tempBuffer), "Insignia.Weapon%d.%s", i + 1, "%s");
617+
this->Insignia_Weapon[i].Read(exINI, pSection, tempBuffer);
618+
619+
_snprintf_s(tempBuffer, sizeof(tempBuffer), "InsigniaFrame.Weapon%d.%s", i + 1, "%s");
620+
this->InsigniaFrame_Weapon[i].Read(exINI, pSection, tempBuffer);
621+
622+
_snprintf_s(tempBuffer, sizeof(tempBuffer), "InsigniaFrames.Weapon%d", i + 1);
623+
this->InsigniaFrames_Weapon[i].Read(exINI, pSection, tempBuffer);
624+
}
625+
}
626+
}
627+
628+
if (this->OwnerObject()->Passengers > 0)
629+
{
630+
size_t passengers = this->OwnerObject()->Passengers + 1;
631+
632+
if (this->Insignia_Passengers.empty() || this->Insignia_Passengers.size() != passengers)
633+
{
634+
this->Insignia_Passengers.resize(passengers);
635+
this->InsigniaFrame_Passengers.resize(passengers, Promotable<int>(-1));
636+
Valueable<Vector3D<int>> frames;
637+
frames = Vector3D<int>(-1, -1, -1);
638+
this->InsigniaFrames_Passengers.resize(passengers, frames);
639+
}
640+
641+
for (size_t i = 0; i < passengers; i++)
642+
{
643+
Nullable<InsigniaTypeClass*> InsigniaType_Passengers;
644+
_snprintf_s(tempBuffer, sizeof(tempBuffer), "InsigniaType.Passengers%d", i);
645+
InsigniaType_Passengers.Read(exINI, pSection, tempBuffer);
646+
647+
if (InsigniaType_Passengers.isset())
648+
{
649+
this->Insignia_Passengers[i] = InsigniaType_Passengers.Get()->Insignia;
650+
this->InsigniaFrame_Passengers[i] = InsigniaType_Passengers.Get()->InsigniaFrame;
651+
this->InsigniaFrames_Passengers[i] = Vector3D<int>(-1, -1, -1); // override it so only InsigniaFrame will be used
652+
}
653+
else
654+
{
655+
_snprintf_s(tempBuffer, sizeof(tempBuffer), "Insignia.Passengers%d.%s", i, "%s");
656+
this->Insignia_Passengers[i].Read(exINI, pSection, tempBuffer);
591657

592-
_snprintf_s(tempBuffer, sizeof(tempBuffer), "InsigniaFrame.Weapon%d.%s", i + 1, "%s");
593-
this->InsigniaFrame_Weapon[i].Read(exINI, pSection, tempBuffer);
658+
_snprintf_s(tempBuffer, sizeof(tempBuffer), "InsigniaFrame.Passengers%d.%s", i, "%s");
659+
this->InsigniaFrame_Passengers[i].Read(exINI, pSection, tempBuffer);
594660

595-
_snprintf_s(tempBuffer, sizeof(tempBuffer), "InsigniaFrames.Weapon%d", i + 1);
596-
this->InsigniaFrames_Weapon[i].Read(exINI, pSection, tempBuffer);
661+
_snprintf_s(tempBuffer, sizeof(tempBuffer), "InsigniaFrames.Passengers%d", i);
662+
this->InsigniaFrames_Passengers[i].Read(exINI, pSection, tempBuffer);
663+
}
597664
}
598665
}
599666

@@ -890,6 +957,9 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
890957
.Process(this->Insignia_Weapon)
891958
.Process(this->InsigniaFrame_Weapon)
892959
.Process(this->InsigniaFrames_Weapon)
960+
.Process(this->Insignia_Passengers)
961+
.Process(this->InsigniaFrame_Passengers)
962+
.Process(this->InsigniaFrames_Passengers)
893963

894964
.Process(this->JumpjetTilt)
895965
.Process(this->JumpjetTilt_ForwardAccelFactor)

src/Ext/TechnoType/Body.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ class TechnoTypeExt
196196
std::vector<Promotable<SHPStruct*>> Insignia_Weapon;
197197
std::vector<Promotable<int>> InsigniaFrame_Weapon;
198198
std::vector<Valueable<Vector3D<int>>> InsigniaFrames_Weapon;
199+
std::vector<Promotable<SHPStruct*>> Insignia_Passengers;
200+
std::vector<Promotable<int>> InsigniaFrame_Passengers;
201+
std::vector<Valueable<Vector3D<int>>> InsigniaFrames_Passengers;
199202

200203
Valueable<bool> JumpjetTilt;
201204
Valueable<double> JumpjetTilt_ForwardAccelFactor;
@@ -515,6 +518,9 @@ class TechnoTypeExt
515518
, Insignia_Weapon {}
516519
, InsigniaFrame_Weapon {}
517520
, InsigniaFrames_Weapon {}
521+
, Insignia_Passengers {}
522+
, InsigniaFrame_Passengers {}
523+
, InsigniaFrames_Passengers {}
518524

519525
, JumpjetTilt { false }
520526
, JumpjetTilt_ForwardAccelFactor { 1.0 }

src/New/Type/InsigniaTypeClass.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "InsigniaTypeClass.h"
2+
3+
template<>
4+
const char* Enumerable<InsigniaTypeClass>::GetMainSection()
5+
{
6+
return "InsigniaTypes";
7+
}
8+
9+
void InsigniaTypeClass::LoadFromINI(CCINIClass* pINI)
10+
{
11+
const char* section = this->Name;
12+
13+
INI_EX exINI(pINI);
14+
15+
this->Insignia.Read(exINI, section, "Insignia.%s");
16+
this->InsigniaFrame.Read(exINI, section, "InsigniaFrame.%s");
17+
}

0 commit comments

Comments
 (0)