@@ -4,22 +4,26 @@ use crate::action_buttons::{
4
4
use crate :: client_state:: { ActiveDialog , StateUpdate } ;
5
5
use crate :: collect_ui:: CollectResources ;
6
6
use crate :: construct_ui:: { new_building_positions, ConstructionPayment , ConstructionProject } ;
7
+ use crate :: custom_phase_ui:: { highlight_structures, StructureHighlight } ;
7
8
use crate :: happiness_ui:: {
8
9
add_increase_happiness, can_play_increase_happiness, open_increase_happiness_dialog,
9
10
} ;
10
11
use crate :: hex_ui;
11
- use crate :: hex_ui:: Point ;
12
- use crate :: layout_ui:: draw_scaled_icon;
12
+ use crate :: layout_ui:: { draw_scaled_icon, is_in_circle} ;
13
13
use crate :: map_ui:: { move_units_buttons, show_map_action_buttons} ;
14
14
use crate :: recruit_unit_ui:: RecruitAmount ;
15
15
use crate :: render_context:: RenderContext ;
16
+ use crate :: select_ui:: HighlightType ;
17
+ use itertools:: Itertools ;
16
18
use macroquad:: prelude:: * ;
17
19
use server:: city:: { City , MoodState } ;
18
20
use server:: city_pieces:: Building ;
19
21
use server:: collect:: possible_resource_collections;
20
22
use server:: content:: custom_actions:: CustomActionType ;
23
+ use server:: content:: custom_phase_actions:: Structure ;
21
24
use server:: game:: Game ;
22
25
use server:: playing_actions:: PlayingActionType ;
26
+ use server:: position:: Position ;
23
27
use server:: resource:: ResourceType ;
24
28
use server:: unit:: { UnitType , Units } ;
25
29
use std:: collections:: HashMap ;
@@ -241,11 +245,66 @@ pub fn city_labels(game: &Game, city: &City) -> Vec<String> {
241
245
242
246
pub const BUILDING_SIZE : f32 = 12.0 ;
243
247
244
- pub fn draw_city ( rc : & RenderContext , city : & City ) {
248
+ fn structure_selected (
249
+ rc : & RenderContext ,
250
+ position : Position ,
251
+ center : Vec2 ,
252
+ size : f32 ,
253
+ h : & StructureHighlight ,
254
+ ) -> Option < StateUpdate > {
255
+ draw_circle_lines ( center. x , center. y , size, 3. , h. highlight_type . color ( ) ) ;
256
+ if is_mouse_button_pressed ( MouseButton :: Left ) && is_in_circle ( rc. mouse_pos ( ) , center, size) {
257
+ let ActiveDialog :: StructuresRequest ( r) = & rc. state . active_dialog else {
258
+ panic ! ( "invalid state" ) ;
259
+ } ;
260
+ if let Some ( i) = r
261
+ . structures
262
+ . iter ( )
263
+ . position ( |( pos, s) | s == & h. structure && pos == & position)
264
+ {
265
+ let mut new = r. clone ( ) ;
266
+ new. structures . remove ( i) ;
267
+ return Some ( StateUpdate :: OpenDialog ( ActiveDialog :: StructuresRequest (
268
+ new,
269
+ ) ) ) ;
270
+ }
271
+ if r. request . choices . contains ( & ( position, h. structure . clone ( ) ) ) {
272
+ let mut new = r. clone ( ) ;
273
+ new. structures . push ( ( position, h. structure . clone ( ) ) ) ;
274
+ return Some ( StateUpdate :: OpenDialog ( ActiveDialog :: StructuresRequest (
275
+ new,
276
+ ) ) ) ;
277
+ } ;
278
+ }
279
+ None
280
+ }
281
+
282
+ pub fn draw_city ( rc : & RenderContext , city : & City ) -> Option < StateUpdate > {
245
283
let c = hex_ui:: center ( city. position ) ;
246
284
let owner = city. player_index ;
247
285
248
- if city. is_activated ( ) {
286
+ let highlighted = match rc. state . active_dialog {
287
+ ActiveDialog :: StructuresRequest ( ref s) => highlight_structures (
288
+ & position_structures ( city, & s. structures ) ,
289
+ HighlightType :: Primary ,
290
+ )
291
+ . into_iter ( )
292
+ . chain ( highlight_structures (
293
+ & position_structures ( city, & s. request . choices ) ,
294
+ HighlightType :: Secondary ,
295
+ ) )
296
+ . collect_vec ( ) ,
297
+ _ => vec ! [ ] ,
298
+ } ;
299
+
300
+ if let Some ( h) = highlighted
301
+ . iter ( )
302
+ . find ( |s| matches ! ( s. structure, Structure :: CityCenter ) )
303
+ {
304
+ if let Some ( u) = structure_selected ( rc, city. position , c, 15. , h) {
305
+ return Some ( u) ;
306
+ }
307
+ } else if city. is_activated ( ) {
249
308
draw_circle ( c. x , c. y , 18.0 , WHITE ) ;
250
309
}
251
310
draw_circle ( c. x , c. y , 15.0 , rc. player_color ( owner) ) ;
@@ -272,29 +331,38 @@ pub fn draw_city(rc: &RenderContext, city: &City) {
272
331
rc,
273
332
t,
274
333
& format ! ( "Happiness: {:?}" , city. mood_state) ,
275
- c. to_vec2 ( ) + vec2 ( -size / 2. , -size / 2. ) ,
334
+ c + vec2 ( -size / 2. , -size / 2. ) ,
276
335
size,
277
336
) ;
278
337
}
279
338
280
- let mut i = 0 ;
281
- city. pieces . wonders . iter ( ) . for_each ( |w| {
282
- let p = hex_ui:: rotate_around ( c, 20.0 , 90 * i) ;
283
- draw_circle ( p. x , p. y , 18.0 , rc. player_color ( owner) ) ;
284
- let size = 20. ;
285
- draw_scaled_icon (
286
- rc,
287
- & rc. assets ( ) . wonders [ & w. name ] ,
288
- & w. name ,
289
- p. to_vec2 ( ) + vec2 ( -size / 2. , -size / 2. ) ,
290
- size,
291
- ) ;
292
- i += 1 ;
293
- } ) ;
339
+ let i = match draw_wonders ( rc, city, c, owner, & highlighted) {
340
+ Ok ( value) => value,
341
+ Err ( value) => return Some ( value) ,
342
+ } ;
294
343
344
+ draw_buildings ( rc, city, c, & highlighted, i)
345
+ }
346
+
347
+ fn draw_buildings (
348
+ rc : & RenderContext ,
349
+ city : & City ,
350
+ center : Vec2 ,
351
+ highlighted : & [ StructureHighlight ] ,
352
+ mut i : usize ,
353
+ ) -> Option < StateUpdate > {
354
+ let state = & rc. state ;
295
355
for player_index in 0 ..4 {
296
356
for b in & city. pieces . buildings ( Some ( player_index) ) {
297
- let p = building_position ( city, c, i, * b) ;
357
+ let p = building_position ( city, center, i, * b) ;
358
+ if let Some ( h) = highlighted
359
+ . iter ( )
360
+ . find ( |s| matches ! ( s. structure, Structure :: Building ( bb) if bb == * b) )
361
+ {
362
+ if let Some ( u) = structure_selected ( rc, city. position , p, BUILDING_SIZE , h) {
363
+ return Some ( u) ;
364
+ }
365
+ }
298
366
draw_circle ( p. x , p. y , BUILDING_SIZE , rc. player_color ( player_index) ) ;
299
367
let tooltip = if matches ! ( state. active_dialog, ActiveDialog :: CulturalInfluence ( _) ) {
300
368
""
@@ -305,15 +373,61 @@ pub fn draw_city(rc: &RenderContext, city: &City) {
305
373
rc,
306
374
& rc. assets ( ) . buildings [ b] ,
307
375
tooltip,
308
- p. to_vec2 ( ) + vec2 ( -8. , -8. ) ,
376
+ p + vec2 ( -8. , -8. ) ,
309
377
16. ,
310
378
) ;
311
379
i += 1 ;
312
380
}
313
381
}
382
+ None
383
+ }
384
+
385
+ #[ allow( clippy:: result_large_err) ]
386
+ fn draw_wonders (
387
+ rc : & RenderContext ,
388
+ city : & City ,
389
+ c : Vec2 ,
390
+ owner : usize ,
391
+ highlighted : & [ StructureHighlight ] ,
392
+ ) -> Result < usize , StateUpdate > {
393
+ let mut i = 0 ;
394
+ for w in & city. pieces . wonders {
395
+ let p = hex_ui:: rotate_around ( c, 20.0 , 90 * i) ;
396
+ draw_circle ( p. x , p. y , 18.0 , rc. player_color ( owner) ) ;
397
+ let size = 20. ;
398
+ if let Some ( h) = highlighted
399
+ . iter ( )
400
+ . find ( |s| matches ! ( & s. structure, Structure :: Wonder ( n) if n == & w. name) )
401
+ {
402
+ if let Some ( u) = structure_selected ( rc, city. position , p, 18. , h) {
403
+ return Err ( u) ;
404
+ }
405
+ }
406
+ draw_scaled_icon (
407
+ rc,
408
+ & rc. assets ( ) . wonders [ & w. name ] ,
409
+ & w. name ,
410
+ p + vec2 ( -size / 2. , -size / 2. ) ,
411
+ size,
412
+ ) ;
413
+ i += 1 ;
414
+ }
415
+ Ok ( i)
416
+ }
417
+
418
+ fn position_structures ( city : & City , list : & [ ( Position , Structure ) ] ) -> Vec < Structure > {
419
+ list. iter ( )
420
+ . filter_map ( |( pos, st) | {
421
+ if pos == & city. position {
422
+ Some ( st. clone ( ) )
423
+ } else {
424
+ None
425
+ }
426
+ } )
427
+ . collect_vec ( )
314
428
}
315
429
316
- pub fn building_position ( city : & City , center : Point , i : usize , building : Building ) -> Point {
430
+ pub fn building_position ( city : & City , center : Vec2 , i : usize , building : Building ) -> Vec2 {
317
431
if matches ! ( building, Building :: Port ) {
318
432
let r: f32 = city
319
433
. position
0 commit comments