1 /**********************************************************************
2 Freeciv - Copyright (C) 2005-2007 - The Freeciv Project
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
15 #include <fc_config.h>
25 #include "government.h"
26 #include "improvement.h"
31 #include "client_main.h"
40 * Hierarchical directed draph drawing for Freeciv's technology tree
43 * \ Layer 0 / \ Layer 1 / \ Layer 2 /
44 * vvvvvvvvvvvvvvvv vvvvvvvvvvvvvvv vvvvvvvvvvv
46 * +-----------------+ +-------------+ +----------+
47 * | Alphabeth |----------| Code of Laws|----| Monarchy |
48 * +-----------------+ +-------------+ /+----------+
50 * +-----------------+ Dummy node /
51 * |Ceremonial Burial|-----------=============/
63 /****************************************************************************
64 This structure desribes a node in a technology tree diagram.
65 A node can by dummy or real. Real node describes a technology.
66 ****************************************************************************/
73 struct tree_node
**require
;
77 struct tree_node
**provide
;
79 /* logical position on the diagram */
82 /* Coordinates of the rectangle on the diagram in pixels */
83 int node_x
, node_y
, node_width
, node_height
;
85 /* for general purpose */
89 /****************************************************************************
90 Structure which describes abstract technology diagram.
91 Nodes are ordered inside layers[] table.
92 ****************************************************************************/
95 struct tree_node
**nodes
;
98 /* size of each layer */
100 struct tree_node
***layers
;
103 int diagram_width
, diagram_height
;
107 /****************************************************************************
108 Edge types for coloring the edges by type in the tree
109 ****************************************************************************/
110 enum reqtree_edge_type
{
111 REQTREE_EDGE
= 0, /* Normal, "unvisited" */
113 REQTREE_KNOWN_EDGE
, /* Both nodes known, "visited" */
115 REQTREE_GOAL_EDGE
/* Dest node is part of goal "future visited" */
118 /*************************************************************************
119 Add requirement edge to node and provide edge to req
120 *************************************************************************/
121 static void add_requirement(struct tree_node
*node
, struct tree_node
*req
)
123 fc_assert_ret(node
!= NULL
);
124 fc_assert_ret(req
!= NULL
);
127 fc_realloc(node
->require
,
128 sizeof(*node
->require
) * (node
->nrequire
+ 1));
129 node
->require
[node
->nrequire
] = req
;
133 fc_realloc(req
->provide
,
134 sizeof(*req
->provide
) * (req
->nprovide
+ 1));
135 req
->provide
[req
->nprovide
] = node
;
139 /*************************************************************************
140 Allocate and initialize new tree node
141 *************************************************************************/
142 static struct tree_node
*new_tree_node(void)
144 struct tree_node
*node
= fc_malloc(sizeof(*node
));
148 node
->require
= NULL
;
149 node
->provide
= NULL
;
155 /****************************************************************************
156 Return minimum size of the rectangle in pixels on the diagram which
157 corresponds to the given node
158 ****************************************************************************/
159 static void node_rectangle_minimum_size(struct tree_node
*node
,
160 int *width
, int *height
)
162 int max_icon_height
; /* maximal height of icons below the text */
163 int icons_width_sum
; /* sum of icons width plus space between them */
164 struct sprite
* sprite
;
167 if (node
->is_dummy
) {
168 /* Dummy node is a straight line */
169 *width
= *height
= 1;
171 get_text_size(width
, height
, FONT_REQTREE_TEXT
,
172 research_advance_name_translation
173 (research_get(client_player()), node
->tech
));
180 if (gui_options
.reqtree_show_icons
) {
182 unit_type_iterate(unit
) {
183 if (advance_number(unit
->require_advance
) != node
->tech
) {
186 sprite
= get_unittype_sprite(tileset
, unit
, direction8_invalid(),
188 get_sprite_dimensions(sprite
, &swidth
, &sheight
);
189 max_icon_height
= MAX(max_icon_height
, sheight
);
190 icons_width_sum
+= swidth
+ 2;
191 } unit_type_iterate_end
;
194 improvement_iterate(pimprove
) {
195 requirement_vector_iterate(&(pimprove
->reqs
), preq
) {
196 if (VUT_ADVANCE
== preq
->source
.kind
197 && advance_number(preq
->source
.value
.advance
) == node
->tech
) {
198 sprite
= get_building_sprite(tileset
, pimprove
);
199 /* Improvement icons are not guaranteed to exist */
201 get_sprite_dimensions(sprite
, &swidth
, &sheight
);
202 max_icon_height
= MAX(max_icon_height
, sheight
);
203 icons_width_sum
+= swidth
+ 2;
206 } requirement_vector_iterate_end
;
207 } improvement_iterate_end
;
210 governments_iterate(gov
) {
211 requirement_vector_iterate(&(gov
->reqs
), preq
) {
212 if (VUT_ADVANCE
== preq
->source
.kind
213 && advance_number(preq
->source
.value
.advance
) == node
->tech
) {
214 sprite
= get_government_sprite(tileset
, gov
);
215 get_sprite_dimensions(sprite
, &swidth
, &sheight
);
216 max_icon_height
= MAX(max_icon_height
, sheight
);
217 icons_width_sum
+= swidth
+ 2;
219 } requirement_vector_iterate_end
;
220 } governments_iterate_end
;
223 *height
+= max_icon_height
;
224 if (*width
< icons_width_sum
) {
225 *width
= icons_width_sum
;
230 /****************************************************************************
231 Move nodes up and down without changing order but making it more
232 symetrical. Gravitate towards parents average position.
233 ****************************************************************************/
234 static void symmetrize(struct reqtree
* tree
)
239 for (layer
= 0; layer
< tree
->num_layers
; layer
++) {
240 for (i
= 0; i
< tree
->layer_size
[layer
]; i
++) {
241 struct tree_node
*node
= tree
->layers
[layer
][i
];
242 int v
, node_y
, node_height
;
244 if (node
->nrequire
== 0) {
248 for (j
= 0; j
< node
->nrequire
; j
++) {
249 struct tree_node
*node_require
= node
->require
[j
];
251 v
+= node_require
->node_y
+ node_require
->node_height
/ 2;
254 node_y
= node
->node_y
;
255 node_height
= node
->node_height
;
256 if (v
< node_y
+ node_height
/ 2) {
261 struct tree_node
*node_above
= tree
->layers
[layer
][i
- 1];
263 if (node_above
->node_y
264 + node_above
->node_height
>= node_y
- 11) {
269 } else if (v
> node_y
+ node_height
/ 2) {
270 if (node_y
+ node_height
>= tree
->diagram_height
- 1) {
273 if (i
< tree
->layer_size
[layer
] - 1) {
274 struct tree_node
* node_below
= tree
->layers
[layer
][i
+ 1];
276 if (node_y
+ node_height
>= node_below
->node_y
- 11) {
282 node
->node_y
= node_y
;
287 /****************************************************************************
288 Calculate rectangles position and size from the tree.
289 Logical order should already be calculated.
290 ****************************************************************************/
291 static void calculate_diagram_layout(struct reqtree
*tree
)
293 int i
, layer
, layer_offs
;
295 /* calculate minimum size of rectangle for each node */
296 for (i
= 0; i
< tree
->num_nodes
; i
++) {
297 struct tree_node
*node
= tree
->nodes
[i
];
299 node_rectangle_minimum_size(tree
->nodes
[i
],
300 &node
->node_width
, &node
->node_height
);
304 /* calculate height of the diagram. There should be at least 10 pixels
305 * beetween any two nodes */
306 tree
->diagram_height
= 0;
307 for (layer
= 0; layer
< tree
->num_layers
; layer
++) {
310 for (i
= 0; i
< tree
->layer_size
[layer
]; i
++) {
311 struct tree_node
*node
= tree
->layers
[layer
][i
];
313 h_sum
+= node
->node_height
;
314 if (i
< tree
->layer_size
[layer
] - 1) {
318 tree
->diagram_height
= MAX(tree
->diagram_height
, h_sum
);
321 /* calculate maximum width of node for each layer and enlarge other nodes
322 * to match maximum width
323 * calculate x offsets
326 for (layer
= 0; layer
< tree
->num_layers
; layer
++) {
329 for (i
= 0; i
< tree
->layer_size
[layer
]; i
++) {
330 struct tree_node
*node
= tree
->layers
[layer
][i
];
332 max_width
= MAX(max_width
, node
->node_width
);
335 for (i
= 0; i
< tree
->layer_size
[layer
]; i
++) {
336 struct tree_node
*node
= tree
->layers
[layer
][i
];
338 node
->node_width
= max_width
;
339 node
->node_x
= layer_offs
;
342 /* space between layers should be proportional to their size */
343 if (layer
!= tree
->num_layers
- 1) {
344 layer_offs
+= max_width
* 5 / 4 + 80;
346 layer_offs
+= max_width
+ 10;
349 tree
->diagram_width
= layer_offs
;
351 /* Once we have x positions calculated we can
352 * calculate y-position of nodes on the diagram
353 * Distribute nodes steadily.
355 for (layer
= 0; layer
< tree
->num_layers
; layer
++) {
359 for (i
= 0; i
< tree
->layer_size
[layer
]; i
++) {
360 struct tree_node
*node
= tree
->layers
[layer
][i
];
362 h_sum
+= node
->node_height
;
364 for (i
= 0; i
< tree
->layer_size
[layer
]; i
++) {
365 struct tree_node
*node
= tree
->layers
[layer
][i
];
368 y
+= node
->node_height
;
369 if (tree
->layer_size
[layer
] > 1) {
370 y
+= (tree
->diagram_height
- h_sum
)
371 / (tree
->layer_size
[layer
] - 1) - 1;
376 /* The symetrize() function moves node by one pixel per call */
377 for (i
= 0; i
< tree
->diagram_height
; i
++) {
383 /*************************************************************************
384 Create a "dummy" tech tree from current ruleset. This tree is then
385 fleshed out further (see create_reqtree). This tree doesn't include
386 dummy edges. Layering and ordering isn't done also.
388 If pplayer is given, add only techs reachable by that player to tree.
389 *************************************************************************/
390 static struct reqtree
*create_dummy_reqtree(struct player
*pplayer
,
393 const struct research
*presearch
= research_get(pplayer
);
394 struct reqtree
*tree
= fc_malloc(sizeof(*tree
));
396 struct tree_node
*nodes
[advance_count()];
398 nodes
[A_NONE
] = NULL
;
399 advance_index_iterate(A_FIRST
, tech
) {
400 if (!valid_advance_by_number(tech
)) {
404 if (pplayer
&& !show_all
405 && !research_invention_reachable(presearch
, tech
)) {
406 /* Reqtree requested for particular player and this tech is
407 * unreachable to him/her. */
411 nodes
[tech
] = new_tree_node();
412 nodes
[tech
]->is_dummy
= FALSE
;
413 nodes
[tech
]->tech
= tech
;
414 } advance_index_iterate_end
;
416 advance_index_iterate(A_FIRST
, tech
) {
417 struct advance
*padvance
= valid_advance_by_number(tech
);
418 Tech_type_id tech_one
, tech_two
;
423 if (nodes
[tech
] == NULL
) {
427 tech_one
= advance_required(tech
, AR_ONE
);
428 tech_two
= advance_required(tech
, AR_TWO
);
430 if (!show_all
&& A_NONE
!= tech_one
431 && A_LAST
!= tech_two
&& A_NONE
!= tech_two
432 && (nodes
[tech_one
] == NULL
|| nodes
[tech_two
] == NULL
)) {
433 /* Print only reachable techs. */
437 /* Formerly, we used to remove the redundant requirement nodes (the
438 * technologies already included in the requirements of the other
439 * requirement). However, it doesn't look like a good idea, because
440 * a player can steal any technology independently of the technology
442 if (A_NONE
!= tech_one
&& A_LAST
!= tech_two
) {
443 add_requirement(nodes
[tech
], nodes
[tech_one
]);
444 if (A_NONE
!= tech_two
) {
445 add_requirement(nodes
[tech
], nodes
[tech_two
]);
448 } advance_index_iterate_end
;
450 /* Copy nodes from local array to dynamically allocated one.
451 * Skip non-existing entries */
452 tree
->nodes
= fc_calloc(advance_count(), sizeof(*tree
->nodes
));
454 advance_index_iterate(A_FIRST
, tech
) {
456 fc_assert_action(valid_advance_by_number(nodes
[tech
]->tech
), continue);
457 tree
->nodes
[j
++] = nodes
[tech
];
459 } advance_index_iterate_end
;
466 /*************************************************************************
467 Free all memory used by tech_tree struct
468 *************************************************************************/
469 void destroy_reqtree(struct reqtree
*tree
)
473 for (i
= 0; i
< tree
->num_nodes
; i
++) {
474 free(tree
->nodes
[i
]->require
);
475 free(tree
->nodes
[i
]->provide
);
476 free(tree
->nodes
[i
]);
480 for (i
= 0; i
< tree
->num_layers
; i
++) {
481 free(tree
->layers
[i
]);
483 if (tree
->layer_size
) {
484 free(tree
->layer_size
);
490 /*************************************************************************
491 Compute the longest path from this tree_node to the node with
492 no requirements. Store the result in node->layer.
493 *************************************************************************/
494 static int longest_path(struct tree_node
*node
)
498 if (node
->layer
!= -1) {
502 for (i
= 0; i
< node
->nrequire
; i
++) {
503 max
= MAX(max
, longest_path(node
->require
[i
]));
505 node
->layer
= max
+ 1;
509 /*************************************************************************
510 Compute longest_path for all nodes, thus prepare longest path layering
511 *************************************************************************/
512 static void longest_path_layering(struct reqtree
*tree
)
516 for (i
= 0; i
< tree
->num_nodes
; i
++) {
517 if (tree
->nodes
[i
]) {
518 longest_path(tree
->nodes
[i
]);
523 /*************************************************************************
524 Find the largest value of layer amongst children of the given node
525 *************************************************************************/
526 static int max_provide_layer(struct tree_node
*node
)
529 int max
= node
->layer
;
531 for (i
= 0; i
< node
->nprovide
; i
++) {
532 if (node
->provide
[i
]->layer
> max
) {
533 max
= node
->provide
[i
]->layer
;
539 /*************************************************************************
540 Create new tree which has dummy nodes added. The source tree is
541 completely copied, you can freely deallocate it.
542 *************************************************************************/
543 static struct reqtree
*add_dummy_nodes(struct reqtree
*tree
)
545 struct reqtree
*new_tree
;
546 int num_dummy_nodes
= 0;
549 /* Count dummy nodes to be added */
550 for (i
= 0; i
< tree
->num_nodes
; i
++) {
553 if (tree
->nodes
[i
] == NULL
) {
556 mpl
= max_provide_layer(tree
->nodes
[i
]);
557 if (mpl
> tree
->nodes
[i
]->layer
+ 1) {
558 num_dummy_nodes
+= mpl
- tree
->nodes
[i
]->layer
- 1;
562 /* create new tree */
563 new_tree
= fc_malloc(sizeof(*new_tree
));
565 fc_malloc(sizeof(new_tree
->nodes
) *
566 (tree
->num_nodes
+ num_dummy_nodes
));
567 new_tree
->num_nodes
= tree
->num_nodes
+ num_dummy_nodes
;
569 /* copy normal nodes */
570 for (i
= 0; i
< tree
->num_nodes
; i
++) {
571 new_tree
->nodes
[i
] = new_tree_node();
572 new_tree
->nodes
[i
]->is_dummy
= FALSE
;
573 new_tree
->nodes
[i
]->tech
= tree
->nodes
[i
]->tech
;
574 new_tree
->nodes
[i
]->layer
= tree
->nodes
[i
]->layer
;
575 tree
->nodes
[i
]->number
= i
;
578 /* allocate dummy nodes */
579 for (i
= 0; i
< num_dummy_nodes
; i
++) {
580 new_tree
->nodes
[i
+ tree
->num_nodes
] = new_tree_node();
581 new_tree
->nodes
[i
+ tree
->num_nodes
]->is_dummy
= TRUE
;
583 /* k points to the first unused dummy node */
586 for (i
= 0; i
< tree
->num_nodes
; i
++) {
587 struct tree_node
*node
= tree
->nodes
[i
];
590 fc_assert_action(!node
->is_dummy
, continue);
592 mpl
= max_provide_layer(node
);
594 /* if this node will have dummy as ancestors, connect them in a chain */
595 if (mpl
> node
->layer
+ 1) {
596 add_requirement(new_tree
->nodes
[k
], new_tree
->nodes
[i
]);
597 for (j
= node
->layer
+ 2; j
< mpl
; j
++) {
598 add_requirement(new_tree
->nodes
[k
+ j
- node
->layer
- 1],
599 new_tree
->nodes
[k
+ j
- node
->layer
- 2]);
601 for (j
= node
->layer
+ 1; j
< mpl
; j
++) {
602 new_tree
->nodes
[k
+ j
- node
->layer
- 1]->layer
= j
;
606 /* copy all edges and create edges with dummy nodes */
607 for (j
= 0; j
< node
->nprovide
; j
++) {
608 int provide_y
= node
->provide
[j
]->layer
;
610 if (provide_y
== node
->layer
+ 1) {
611 /* direct connection */
612 add_requirement(new_tree
->nodes
[node
->provide
[j
]->number
],
615 /* connection through dummy node */
616 add_requirement(new_tree
->nodes
[node
->provide
[j
]->number
],
617 new_tree
->nodes
[k
+ provide_y
- node
->layer
- 2]);
621 if (mpl
> node
->layer
+ 1) {
622 k
+= mpl
- node
->layer
- 1;
623 fc_assert(k
<= new_tree
->num_nodes
);
626 new_tree
->layers
= NULL
;
631 /*************************************************************************
632 Calculate layers[] and layer_size[] fields of tree.
633 There should be layer value calculated for each node.
634 Nodes will be put into layers in no particular order.
635 *************************************************************************/
636 static void set_layers(struct reqtree
*tree
)
641 /* count total number of layers */
642 for (i
= 0; i
< tree
->num_nodes
; i
++) {
643 num_layers
= MAX(num_layers
, tree
->nodes
[i
]->layer
);
646 tree
->num_layers
= num_layers
;
649 /* Counters for order - order number for the next node in the layer */
652 tree
->layers
= fc_malloc(sizeof(*tree
->layers
) * num_layers
);
653 tree
->layer_size
= fc_malloc(sizeof(*tree
->layer_size
) * num_layers
);
654 for (i
= 0; i
< num_layers
; i
++) {
656 tree
->layer_size
[i
] = 0;
658 for (i
= 0; i
< tree
->num_nodes
; i
++) {
659 tree
->layer_size
[tree
->nodes
[i
]->layer
]++;
662 for (i
= 0; i
< num_layers
; i
++) {
664 fc_malloc(sizeof(*tree
->layers
[i
]) * tree
->layer_size
[i
]);
666 for (i
= 0; i
< tree
->num_nodes
; i
++) {
667 struct tree_node
*node
= tree
->nodes
[i
];
669 tree
->layers
[node
->layer
][T
[node
->layer
]] = node
;
670 node
->order
= T
[node
->layer
];
676 struct node_and_float
{
677 struct tree_node
*node
;
681 /*************************************************************************
682 Comparison function used by barycentric_sort.
683 *************************************************************************/
684 static int cmp_func(const void *_a
, const void *_b
)
686 const struct node_and_float
*a
= _a
, *b
= _b
;
688 if (a
->value
> b
->value
) {
691 if (a
->value
< b
->value
) {
697 /*************************************************************************
698 Simple heuristic: Sort nodes on the given layer by the average x-value
700 *************************************************************************/
701 static void barycentric_sort(struct reqtree
*tree
, int layer
)
703 struct node_and_float T
[tree
->layer_size
[layer
]];
707 for (i
= 0; i
< tree
->layer_size
[layer
]; i
++) {
708 struct tree_node
*node
= tree
->layers
[layer
][i
];
712 for (j
= 0; j
< node
->nrequire
; j
++) {
713 v
+= node
->require
[j
]->order
;
715 if (node
->nrequire
> 0) {
716 v
/= (float) node
->nrequire
;
720 qsort(T
, tree
->layer_size
[layer
], sizeof(*T
),
723 for (i
= 0; i
< tree
->layer_size
[layer
]; i
++) {
724 tree
->layers
[layer
][i
] = T
[i
].node
;
725 T
[i
].node
->order
= i
;
729 /*************************************************************************
730 Calculate number of edge crossings beetwen layer and layer+1
731 *************************************************************************/
732 static int count_crossings(struct reqtree
*tree
, int layer
)
734 int layer1_size
= tree
->layer_size
[layer
];
735 int layer2_size
= tree
->layer_size
[layer
+ 1];
740 for (i
= 0; i
< layer2_size
; i
++) {
744 for (i
= 0; i
< layer1_size
; i
++) {
745 struct tree_node
*node
= tree
->layers
[layer
][i
];
747 for (j
= 0; j
< node
->nprovide
; j
++) {
748 sum
+= X
[node
->provide
[j
]->order
];
750 for (j
= 0; j
< node
->nprovide
; j
++) {
751 for (k
= 0; k
< node
->provide
[j
]->order
; k
++) {
760 /*************************************************************************
761 Swap positions of two nodes on the same layer
762 *************************************************************************/
763 static void swap(struct reqtree
*tree
, int layer
, int order1
, int order2
)
765 struct tree_node
*node1
= tree
->layers
[layer
][order1
];
766 struct tree_node
*node2
= tree
->layers
[layer
][order2
];
768 tree
->layers
[layer
][order1
] = node2
;
769 tree
->layers
[layer
][order2
] = node1
;
770 node1
->order
= order2
;
771 node2
->order
= order1
;
774 /*************************************************************************
775 Try to reduce the number of crossings by swapping two nodes and checking
776 if it improves the situation.
777 *************************************************************************/
778 static void improve(struct reqtree
*tree
)
780 int crossings
[tree
->num_layers
- 1];
781 int i
, x1
, x2
, layer
;
783 for (i
= 0; i
< tree
->num_layers
- 1; i
++) {
784 crossings
[i
] = count_crossings(tree
, i
);
787 for (layer
= 0; layer
< tree
->num_layers
; layer
++) {
788 int layer_size
= tree
->layer_size
[layer
];
792 layer_sum
+= crossings
[layer
- 1];
794 if (layer
< tree
->num_layers
- 1) {
795 layer_sum
+= crossings
[layer
];
798 for (x1
= 0; x1
< layer_size
; x1
++) {
799 for (x2
= x1
+ 1; x2
< layer_size
; x2
++) {
800 int new_crossings
= 0;
801 int new_crossings_before
= 0;
803 swap(tree
, layer
, x1
, x2
);
805 new_crossings_before
+= count_crossings(tree
, layer
- 1);
807 if (layer
< tree
->num_layers
- 1) {
808 new_crossings
+= count_crossings(tree
, layer
);
810 if (new_crossings
+ new_crossings_before
> layer_sum
) {
811 swap(tree
, layer
, x1
, x2
);
813 layer_sum
= new_crossings
+ new_crossings_before
;
815 crossings
[layer
- 1] = new_crossings_before
;
817 if (layer
< tree
->num_layers
- 1) {
818 crossings
[layer
] = new_crossings
;
826 /*************************************************************************
827 Generate optimized tech_tree from current ruleset.
828 You should free it by destroy_reqtree.
830 If pplayer is not NULL, techs unreachable to that player are not shown.
831 *************************************************************************/
832 struct reqtree
*create_reqtree(struct player
*pplayer
, bool show_all
)
834 struct reqtree
*tree1
, *tree2
;
837 tree1
= create_dummy_reqtree(pplayer
, show_all
);
838 longest_path_layering(tree1
);
839 tree2
= add_dummy_nodes(tree1
);
840 destroy_reqtree(tree1
);
843 /* It's good heuristics for beginning */
844 for (j
= 0; j
< 20; j
++) {
845 for (i
= 0; i
< tree2
->num_layers
; i
++) {
846 barycentric_sort(tree2
, i
);
850 /* Now burn some CPU */
851 for (j
= 0; j
< 20; j
++) {
855 calculate_diagram_layout(tree2
);
860 /****************************************************************************
861 Give the dimensions of the reqtree.
862 ****************************************************************************/
863 void get_reqtree_dimensions(struct reqtree
*reqtree
,
864 int *width
, int *height
)
867 *width
= reqtree
->diagram_width
;
870 *height
= reqtree
->diagram_height
;
874 /****************************************************************************
875 Return a background color of node's rectangle
876 ****************************************************************************/
877 static enum color_std
node_color(struct tree_node
*node
)
879 if (!node
->is_dummy
) {
880 struct research
*research
= research_get(client_player());
883 return COLOR_REQTREE_KNOWN
;
886 if (!research_invention_reachable(research
, node
->tech
)) {
887 return COLOR_REQTREE_UNREACHABLE
;
890 if (!research_invention_gettable(research
, node
->tech
, TRUE
)) {
891 if (research_goal_tech_req(research
, research
->tech_goal
, node
->tech
)
892 || node
->tech
== research
->tech_goal
) {
893 return COLOR_REQTREE_GOAL_NOT_GETTABLE
;
895 return COLOR_REQTREE_NOT_GETTABLE
;
899 if (research
->researching
== node
->tech
) {
900 return COLOR_REQTREE_RESEARCHING
;
903 if (TECH_KNOWN
== research_invention_state(research
, node
->tech
)) {
904 return COLOR_REQTREE_KNOWN
;
907 if (research_goal_tech_req(research
, research
->tech_goal
, node
->tech
)
908 || node
->tech
== research
->tech_goal
) {
909 if (TECH_PREREQS_KNOWN
== research_invention_state(research
,
911 return COLOR_REQTREE_GOAL_PREREQS_KNOWN
;
913 return COLOR_REQTREE_GOAL_UNKNOWN
;
917 if (TECH_PREREQS_KNOWN
== research_invention_state(research
,
919 return COLOR_REQTREE_PREREQS_KNOWN
;
922 return COLOR_REQTREE_UNKNOWN
;
924 return COLOR_REQTREE_BACKGROUND
;
930 /****************************************************************************
931 Return the type for an edge between two nodes
932 if node is a dummy, dest_node can be NULL
933 ****************************************************************************/
934 static enum reqtree_edge_type
get_edge_type(struct tree_node
*node
,
935 struct tree_node
*dest_node
)
937 struct research
*research
= research_get(client_player());
939 if (dest_node
== NULL
) {
940 /* assume node is a dummy */
944 /* find the required tech */
945 while (node
->is_dummy
) {
946 fc_assert(node
->nrequire
== 1);
947 node
= node
->require
[0];
950 /* find destination advance by recursing in dest_node->provide[]
951 * watch out: recursion */
952 if (dest_node
->is_dummy
) {
953 enum reqtree_edge_type sum_type
= REQTREE_EDGE
;
956 fc_assert(dest_node
->nprovide
> 0);
957 for (i
= 0; i
< dest_node
->nprovide
; ++i
) {
958 enum reqtree_edge_type type
= get_edge_type(node
, dest_node
->provide
[i
]);
960 case REQTREE_ACTIVE_EDGE
:
961 case REQTREE_GOAL_EDGE
:
963 case REQTREE_KNOWN_EDGE
:
964 case REQTREE_READY_EDGE
:
976 /* Global observer case */
977 return REQTREE_KNOWN_EDGE
;
980 if (research
->researching
== dest_node
->tech
) {
981 return REQTREE_ACTIVE_EDGE
;
984 if (research_goal_tech_req(research
, research
->tech_goal
, dest_node
->tech
)
985 || dest_node
->tech
== research
->tech_goal
) {
986 return REQTREE_GOAL_EDGE
;
989 if (TECH_KNOWN
== research_invention_state(research
, node
->tech
)) {
990 if (TECH_KNOWN
== research_invention_state(research
, dest_node
->tech
)) {
991 return REQTREE_KNOWN_EDGE
;
993 return REQTREE_READY_EDGE
;
1000 /****************************************************************************
1001 Return a stroke color for an edge between two nodes
1002 if node is a dummy, dest_node can be NULL
1003 ****************************************************************************/
1004 static enum color_std
edge_color(struct tree_node
*node
,
1005 struct tree_node
*dest_node
)
1007 enum reqtree_edge_type type
= get_edge_type(node
, dest_node
);
1009 case REQTREE_ACTIVE_EDGE
:
1010 return COLOR_REQTREE_RESEARCHING
;
1011 case REQTREE_GOAL_EDGE
:
1012 return COLOR_REQTREE_GOAL_UNKNOWN
;
1013 case REQTREE_KNOWN_EDGE
:
1014 /* using "text" black instead of "known" white/ground/green */
1015 return COLOR_REQTREE_TEXT
;
1016 case REQTREE_READY_EDGE
:
1017 return COLOR_REQTREE_PREREQS_KNOWN
;
1019 return COLOR_REQTREE_EDGE
;
1023 /****************************************************************************
1024 Draw the reqtree diagram!
1026 This draws the given portion of the reqtree diagram (given by
1027 (tt_x,tt_y) and (w,h) onto the canvas at position (canvas_x, canvas_y).
1028 ****************************************************************************/
1029 void draw_reqtree(struct reqtree
*tree
, struct canvas
*pcanvas
,
1030 int canvas_x
, int canvas_y
,
1031 int tt_x
, int tt_y
, int w
, int h
)
1034 int swidth
, sheight
;
1035 struct sprite
* sprite
;
1036 struct color
*color
;
1038 /* draw the diagram */
1039 for (i
= 0; i
< tree
->num_layers
; i
++) {
1040 for (j
= 0; j
< tree
->layer_size
[i
]; j
++) {
1041 struct tree_node
*node
= tree
->layers
[i
][j
];
1042 int startx
, starty
, endx
, endy
, width
, height
;
1044 startx
= node
->node_x
;
1045 starty
= node
->node_y
;
1046 width
= node
->node_width
;
1047 height
= node
->node_height
;
1049 if (node
->is_dummy
) {
1050 /* Use the same layout as lines for dummy nodes */
1051 canvas_put_line(pcanvas
,
1052 get_color(tileset
, edge_color(node
, NULL
)),
1054 startx
, starty
, width
, 0);
1056 const char *text
= research_advance_name_translation
1057 (research_get(client_player()), node
->tech
);
1061 canvas_put_rectangle(pcanvas
,
1062 get_color(tileset
, COLOR_REQTREE_BACKGROUND
),
1063 startx
, starty
, width
, height
);
1065 /* Print color rectangle with text inside. */
1066 canvas_put_rectangle(pcanvas
, get_color(tileset
, node_color(node
)),
1067 startx
+ 1, starty
+ 1,
1068 width
- 2, height
- 2);
1069 /* The following code is similar to the one in
1070 * node_rectangle_minimum_size(). If you change something here,
1071 * change also node_rectangle_minimum_size().
1074 get_text_size(&text_w
, &text_h
, FONT_REQTREE_TEXT
, text
);
1076 canvas_put_text(pcanvas
,
1077 startx
+ (width
- text_w
) / 2,
1080 get_color(tileset
, COLOR_REQTREE_TEXT
),
1082 icon_startx
= startx
+ 5;
1084 if (gui_options
.reqtree_show_icons
) {
1085 unit_type_iterate(unit
) {
1086 if (advance_number(unit
->require_advance
) != node
->tech
) {
1089 sprite
= get_unittype_sprite(tileset
, unit
,
1090 direction8_invalid(), TRUE
);
1091 get_sprite_dimensions(sprite
, &swidth
, &sheight
);
1092 canvas_put_sprite_full(pcanvas
,
1095 + (height
- text_h
- 4 - sheight
) / 2,
1097 icon_startx
+= swidth
+ 2;
1098 } unit_type_iterate_end
;
1100 improvement_iterate(pimprove
) {
1101 requirement_vector_iterate(&(pimprove
->reqs
), preq
) {
1102 if (VUT_ADVANCE
== preq
->source
.kind
1103 && advance_number(preq
->source
.value
.advance
) == node
->tech
) {
1104 sprite
= get_building_sprite(tileset
, pimprove
);
1105 /* Improvement icons are not guaranteed to exist */
1107 get_sprite_dimensions(sprite
, &swidth
, &sheight
);
1108 canvas_put_sprite_full(pcanvas
,
1111 + (height
- text_h
- 4 - sheight
) / 2,
1113 icon_startx
+= swidth
+ 2;
1116 } requirement_vector_iterate_end
;
1117 } improvement_iterate_end
;
1119 governments_iterate(gov
) {
1120 requirement_vector_iterate(&(gov
->reqs
), preq
) {
1121 if (VUT_ADVANCE
== preq
->source
.kind
1122 && advance_number(preq
->source
.value
.advance
) == node
->tech
) {
1123 sprite
= get_government_sprite(tileset
, gov
);
1124 get_sprite_dimensions(sprite
, &swidth
, &sheight
);
1125 canvas_put_sprite_full(pcanvas
,
1128 + (height
- text_h
- 4 - sheight
) / 2,
1130 icon_startx
+= swidth
+ 2;
1132 } requirement_vector_iterate_end
;
1133 } governments_iterate_end
;
1137 /* Draw all outgoing edges */
1138 startx
= node
->node_x
+ node
->node_width
;
1139 starty
= node
->node_y
+ node
->node_height
/ 2;
1140 for (k
= 0; k
< node
->nprovide
; k
++) {
1141 struct tree_node
*dest_node
= node
->provide
[k
];
1142 color
= get_color(tileset
, edge_color(node
, dest_node
));
1144 endx
= dest_node
->node_x
;
1145 endy
= dest_node
->node_y
+ dest_node
->node_height
/ 2;
1147 if (gui_options
.reqtree_curved_lines
) {
1148 canvas_put_curved_line(pcanvas
, color
, LINE_GOTO
,
1149 startx
, starty
, endx
- startx
,
1152 canvas_put_line(pcanvas
, color
, LINE_GOTO
,
1153 startx
, starty
, endx
- startx
,
1161 /****************************************************************************
1162 Return the tech ID at the given position of the reqtree (or A_NONE).
1163 ****************************************************************************/
1164 Tech_type_id
get_tech_on_reqtree(struct reqtree
*tree
, int x
, int y
)
1168 for (i
= 0; i
< tree
->num_nodes
; i
++) {
1169 struct tree_node
*node
= tree
->nodes
[i
];
1171 if (node
->is_dummy
) {
1174 if (node
->node_x
<= x
&& node
->node_y
<= y
1175 && node
->node_x
+ node
->node_width
> x
1176 && node
->node_y
+ node
->node_height
> y
) {
1183 /****************************************************************************
1184 Return the position of the given tech on the reqtree. Return TRUE iff
1186 ****************************************************************************/
1187 bool find_tech_on_reqtree(struct reqtree
*tree
, Tech_type_id tech
,
1188 int *x
, int *y
, int *w
, int *h
)
1192 for (i
= 0; i
< tree
->num_nodes
; i
++) {
1193 struct tree_node
*node
= tree
->nodes
[i
];
1195 if (!node
->is_dummy
&& node
->tech
== tech
) {
1203 *w
= node
->node_width
;
1206 *h
= node
->node_height
;