1+ using dsstats . shared . DsFen ;
2+
13namespace dsstats . builder ;
24
35/// <summary>
@@ -50,6 +52,7 @@ public List<InputEvent> GetBuildEvents(ScreenArea screenArea, CmdrBuild build)
5052 List < BuildUnit > topUnits = [ ] ;
5153 List < BuildUnit > centerUnits = [ ] ;
5254 List < BuildUnit > bottomUnits = [ ] ;
55+ WorkerMenu workerMenu = new ( ) ;
5356
5457 foreach ( var unit in allUnits )
5558 {
@@ -71,15 +74,15 @@ public List<InputEvent> GetBuildEvents(ScreenArea screenArea, CmdrBuild build)
7174
7275 foreach ( var centerUnit in centerUnits )
7376 {
74- events . AddRange ( build . GetBuildEvents ( centerUnit . UnitName , centerUnit . Pos , screenArea ) ) ;
77+ events . AddRange ( build . GetBuildEvents ( centerUnit . UnitName , centerUnit . Pos , screenArea , workerMenu ) ) ;
7578 }
7679 if ( topUnits . Count > 0 )
7780 {
7881 events . AddRange ( DsBuilder . ScrollY ( Convert . ToInt32 ( 250 * screenArea . _scaleY ) , screenArea . GetCenter ( ) ) ) ;
7982 foreach ( var topUnit in topUnits )
8083 {
8184 events . AddRange ( build . GetBuildEvents ( topUnit . UnitName ,
82- topUnit . Pos with { Y = topUnit . Pos . Y + Convert . ToInt32 ( 125 * screenArea . _scaleY ) } , screenArea ) ) ;
85+ topUnit . Pos with { Y = topUnit . Pos . Y + Convert . ToInt32 ( 125 * screenArea . _scaleY ) } , screenArea , workerMenu ) ) ;
8386 }
8487 }
8588 if ( bottomUnits . Count > 0 )
@@ -91,7 +94,7 @@ public List<InputEvent> GetBuildEvents(ScreenArea screenArea, CmdrBuild build)
9194 foreach ( var bottomUnit in bottomUnits )
9295 {
9396 events . AddRange ( build . GetBuildEvents ( bottomUnit . UnitName ,
94- bottomUnit . Pos with { Y = bottomUnit . Pos . Y - Convert . ToInt32 ( 300 * screenArea . _scaleY ) } , screenArea ) ) ;
97+ bottomUnit . Pos with { Y = bottomUnit . Pos . Y - Convert . ToInt32 ( 300 * screenArea . _scaleY ) } , screenArea , workerMenu ) ) ;
9598 }
9699 }
97100
@@ -136,7 +139,10 @@ private List<RlPoint> GetUnitPositions(string unitString, int team)
136139 for ( int i = 0 ; i < stringPoints . Length ; i += 2 )
137140 {
138141 RlPoint mapPoint = new ( int . Parse ( stringPoints [ i ] ) , int . Parse ( stringPoints [ i + 1 ] ) ) ;
139- mapPoints . Add ( NormalizeToTop ( mapPoint ) ) ;
142+ if ( IsPointInPolygon ( mapPoint , polygon ) )
143+ {
144+ mapPoints . Add ( NormalizeToTop ( mapPoint ) ) ;
145+ }
140146 }
141147 return mapPoints ;
142148 }
@@ -157,6 +163,134 @@ public RlPoint GetCenter()
157163 return new ( x1 + ( ( x2 - x1 ) / 2 ) , y1 + ( ( y2 - y1 ) / 2 ) ) ;
158164 }
159165
166+ public string ToFenString ( CmdrBuild build )
167+ {
168+ var groundUnits = new Dictionary < RlPoint , char > ( ) ;
169+ var airUnits = new Dictionary < RlPoint , char > ( ) ;
170+
171+ foreach ( var kv in units )
172+ {
173+ string unitName = kv . Key ;
174+ var buildOption = build . GetUnitBuildOption ( unitName ) ;
175+ if ( buildOption == null ) continue ;
176+
177+ char key = buildOption . RequiresToggle && ! buildOption . IsActive
178+ ? char . ToUpper ( buildOption . Key )
179+ : buildOption . Key ;
180+
181+ foreach ( var pos in kv . Value )
182+ {
183+ if ( buildOption . IsAir )
184+ airUnits [ pos ] = key ;
185+ else
186+ groundUnits [ pos ] = key ;
187+ }
188+ }
189+
190+ if ( groundUnits . Count == 0 && airUnits . Count == 0 )
191+ return "" ;
192+
193+ // Determine grid bounds (combined for both layers)
194+ var allPoints = groundUnits . Keys . Concat ( airUnits . Keys ) . ToList ( ) ;
195+ int minX = allPoints . Min ( p => p . X ) ;
196+ int maxX = allPoints . Max ( p => p . X ) ;
197+ int minY = allPoints . Min ( p => p . Y ) ;
198+ int maxY = allPoints . Max ( p => p . Y ) ;
199+
200+ string EncodeLayer ( Dictionary < RlPoint , char > layer )
201+ {
202+ var rows = new List < string > ( ) ;
203+ for ( int y = minY ; y <= maxY ; y ++ )
204+ {
205+ string row = "" ;
206+ int emptyCount = 0 ;
207+
208+ for ( int x = minX ; x <= maxX ; x ++ )
209+ {
210+ var pt = new RlPoint ( x , y ) ;
211+ if ( layer . TryGetValue ( pt , out char key ) )
212+ {
213+ if ( emptyCount > 0 )
214+ {
215+ row += emptyCount . ToString ( ) ;
216+ emptyCount = 0 ;
217+ }
218+ row += key ;
219+ }
220+ else
221+ {
222+ emptyCount ++ ;
223+ }
224+ }
225+
226+ if ( emptyCount > 0 )
227+ row += emptyCount . ToString ( ) ;
228+
229+ rows . Add ( row ) ;
230+ }
231+
232+ return string . Join ( "/" , rows ) ;
233+ }
234+
235+ var groundFen = EncodeLayer ( groundUnits ) ;
236+ var airFen = EncodeLayer ( airUnits ) ;
237+
238+ return $ "{ groundFen } |{ airFen } ";
239+ }
240+
241+ public void FromFenString ( string fen , CmdrBuild build )
242+ {
243+ if ( string . IsNullOrWhiteSpace ( fen ) )
244+ return ;
245+
246+ // Split into ground and air layers
247+ var layers = fen . Split ( '|' ) ;
248+ string groundLayer = layers . Length > 0 ? layers [ 0 ] : "" ;
249+ string airLayer = layers . Length > 1 ? layers [ 1 ] : "" ;
250+
251+ ParseLayer ( groundLayer , build , isAir : false ) ;
252+ if ( ! string . IsNullOrWhiteSpace ( airLayer ) )
253+ ParseLayer ( airLayer , build , isAir : true ) ;
254+ }
255+
256+ private void ParseLayer ( string fenLayer , CmdrBuild build , bool isAir )
257+ {
258+ if ( string . IsNullOrWhiteSpace ( fenLayer ) )
259+ return ;
260+
261+ int y = 0 ;
262+ var rows = fenLayer . Split ( '/' ) ;
263+ foreach ( var row in rows )
264+ {
265+ int x = 0 ;
266+ foreach ( char ch in row )
267+ {
268+ if ( char . IsDigit ( ch ) )
269+ {
270+ x += ch - '0' ;
271+ }
272+ else
273+ {
274+ bool isUpper = char . IsUpper ( ch ) ;
275+ char key = char . ToLower ( ch ) ;
276+
277+ var unitName = build . GetUnitNameFromKey ( key , isAir , isUpper ) ;
278+ if ( unitName != null )
279+ {
280+ if ( ! units . TryGetValue ( unitName , out var unitPositions ) )
281+ unitPositions = units [ unitName ] = [ ] ;
282+
283+ unitPositions . Add ( new RlPoint ( x , y ) ) ; // normalized pos
284+ }
285+
286+ x ++ ;
287+ }
288+ }
289+
290+ y ++ ;
291+ }
292+ }
293+
160294 private bool IsPointInsideOrOnEdge ( RlPoint p )
161295 {
162296 if ( IsPointInPolygon ( p , polygon ) )
0 commit comments