1 /***********************************************************************
2 Freeciv - Copyright (C) 2005 - 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>
18 #include <limits.h> /* USHRT_MAX */
21 #include "bitvector.h"
30 #include "government.h"
39 #include "utilities.h"
43 #include "citytools.h"
45 #include "connecthand.h"
51 #include "sanitycheck.h"
53 #include "stdinhand.h"
54 #include "techtools.h"
55 #include "unittools.h"
59 /* Set if anything in a sequence of edits triggers the expensive
60 * assign_continent_numbers() check, which will be done once when the
61 * sequence is complete. */
62 static bool need_continents_reassigned
= FALSE
;
63 /* Hold pointers to tiles which were changed during the edit sequence,
64 * so that they can be sanity-checked when the sequence is complete
65 * and final global fix-ups have been done. */
66 static struct tile_hash
*modified_tile_table
= NULL
;
68 /* Array of size player_slot_count() indexed by player
69 * number to tell whether a given player has fog of war
70 * disabled in edit mode. */
71 static bool *unfogged_players
;
73 /****************************************************************************
74 Initialize data structures required for edit mode.
75 ****************************************************************************/
76 void edithand_init(void)
78 if (NULL
!= modified_tile_table
) {
79 tile_hash_destroy(modified_tile_table
);
81 modified_tile_table
= tile_hash_new();
83 need_continents_reassigned
= FALSE
;
85 if (unfogged_players
!= NULL
) {
86 free(unfogged_players
);
88 unfogged_players
= fc_calloc(player_slot_count(), sizeof(bool));
91 /****************************************************************************
92 Free all memory used by data structures required for edit mode.
93 ****************************************************************************/
94 void edithand_free(void)
96 if (NULL
!= modified_tile_table
) {
97 tile_hash_destroy(modified_tile_table
);
98 modified_tile_table
= NULL
;
101 if (unfogged_players
!= NULL
) {
102 free(unfogged_players
);
103 unfogged_players
= NULL
;
107 /****************************************************************************
108 Send the needed packets for connections entering in the editing mode.
109 ****************************************************************************/
110 void edithand_send_initial_packets(struct conn_list
*dest
)
112 struct packet_edit_startpos startpos
;
113 struct packet_edit_startpos_full startpos_full
;
116 dest
= game
.est_connections
;
119 /* Send map start positions. */
120 map_startpos_iterate(psp
) {
121 startpos
.id
= tile_index(startpos_tile(psp
));
122 startpos
.removal
= FALSE
;
125 startpos_pack(psp
, &startpos_full
);
127 conn_list_iterate(dest
, pconn
) {
128 if (can_conn_edit(pconn
)) {
129 send_packet_edit_startpos(pconn
, &startpos
);
130 send_packet_edit_startpos_full(pconn
, &startpos_full
);
132 } conn_list_iterate_end
;
133 } map_startpos_iterate_end
;
136 /****************************************************************************
137 Do the potentially slow checks required after one or several tiles'
139 ****************************************************************************/
140 static void check_edited_tile_terrains(void)
142 if (need_continents_reassigned
) {
143 assign_continent_numbers();
144 send_all_known_tiles(NULL
);
145 need_continents_reassigned
= FALSE
;
148 #ifdef SANITY_CHECKING
149 tile_hash_iterate(modified_tile_table
, ptile
) {
150 sanity_check_tile(ptile
);
151 } tile_hash_iterate_end
;
152 #endif /* SANITY_CHECKING */
153 tile_hash_clear(modified_tile_table
);
156 /****************************************************************************
157 Do any necessary checks after leaving edit mode to ensure that the game
158 is in a consistent state.
159 ****************************************************************************/
160 static void check_leaving_edit_mode(void)
164 conn_list_do_buffer(game
.est_connections
);
165 players_iterate(pplayer
) {
166 unfogged
= unfogged_players
[player_number(pplayer
)];
167 if (unfogged
&& game
.info
.fogofwar
) {
168 enable_fog_of_war_player(pplayer
);
169 } else if (!unfogged
&& !game
.info
.fogofwar
) {
170 disable_fog_of_war_player(pplayer
);
172 } players_iterate_end
;
174 /* Clear the whole array. */
175 memset(unfogged_players
, 0, player_slot_count() * sizeof(bool));
177 check_edited_tile_terrains();
178 conn_list_do_unbuffer(game
.est_connections
);
181 /****************************************************************************
182 Handles a request by the client to enter edit mode.
183 ****************************************************************************/
184 void handle_edit_mode(struct connection
*pc
, bool is_edit_mode
)
186 if (!can_conn_enable_editing(pc
)) {
190 if (!game
.info
.is_edit_mode
&& is_edit_mode
) {
191 /* Someone could be cheating! Warn people. */
192 notify_conn(NULL
, NULL
, E_SETTING
, ftc_editor
,
193 _(" *** Server set to edit mode by %s! *** "),
194 conn_description(pc
));
197 if (game
.info
.is_edit_mode
&& !is_edit_mode
) {
198 notify_conn(NULL
, NULL
, E_SETTING
, ftc_editor
,
199 _(" *** Edit mode canceled by %s. *** "),
200 conn_description(pc
));
202 check_leaving_edit_mode();
205 if (game
.info
.is_edit_mode
!= is_edit_mode
) {
206 game
.info
.is_edit_mode
= is_edit_mode
;
208 send_game_info(NULL
);
209 edithand_send_initial_packets(NULL
);
213 /****************************************************************************
214 Base function to edit the terrain property of a tile. Returns TRUE if
215 the terrain has changed.
216 ****************************************************************************/
217 static bool edit_tile_terrain_handling(struct tile
*ptile
,
218 struct terrain
*pterrain
,
221 struct terrain
*old_terrain
= tile_terrain(ptile
);
223 if (old_terrain
== pterrain
224 || (terrain_has_flag(pterrain
, TER_NO_CITIES
)
225 && NULL
!= tile_city(ptile
))) {
229 tile_change_terrain(ptile
, pterrain
);
230 fix_tile_on_terrain_change(ptile
, old_terrain
, FALSE
);
231 tile_hash_insert(modified_tile_table
, ptile
, NULL
);
232 if (need_to_reassign_continents(old_terrain
, pterrain
)) {
233 need_continents_reassigned
= TRUE
;
237 update_tile_knowledge(ptile
);
243 /****************************************************************************
244 Base function to edit the resource property of a tile. Returns TRUE if
245 the resource has changed.
246 ****************************************************************************/
247 static bool edit_tile_resource_handling(struct tile
*ptile
,
248 struct resource
*presource
,
251 if (presource
== tile_resource(ptile
)) {
255 if (NULL
!= presource
256 && !terrain_has_resource(tile_terrain(ptile
), presource
)) {
260 tile_set_resource(ptile
, presource
);
263 update_tile_knowledge(ptile
);
269 /****************************************************************************
270 Base function to edit the extras property of a tile. Returns TRUE if
271 the extra state has changed.
272 ****************************************************************************/
273 static bool edit_tile_extra_handling(struct tile
*ptile
,
274 struct extra_type
*pextra
,
275 bool remove_mode
, bool send_info
)
278 if (!tile_has_extra(ptile
, pextra
)) {
282 if (!tile_extra_rm_apply(ptile
, pextra
)) {
286 terrain_changed(ptile
);
289 if (tile_has_extra(ptile
, pextra
)) {
293 if (!tile_extra_apply(ptile
, pextra
)) {
299 update_tile_knowledge(ptile
);
305 /****************************************************************************
306 Handles a client request to change the terrain of the tile at the given
307 x, y coordinates. The 'size' parameter indicates that all tiles in a
308 square of "radius" 'size' should be affected. So size=1 corresponds to
309 the single tile case.
310 ****************************************************************************/
311 void handle_edit_tile_terrain(struct connection
*pc
, int tile
,
312 Terrain_type_id terrain
, int size
)
314 struct terrain
*pterrain
;
315 struct tile
*ptile_center
;
317 ptile_center
= index_to_tile(tile
);
319 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
320 _("Cannot edit the tile because %d is not a valid "
321 "tile index on this map!"), tile
);
325 pterrain
= terrain_by_number(terrain
);
327 notify_conn(pc
->self
, ptile_center
, E_BAD_COMMAND
, ftc_editor
,
328 /* TRANS: ..." the tile <tile-coordinates> because"... */
329 _("Cannot modify terrain for the tile %s because "
330 "%d is not a valid terrain id."),
331 tile_link(ptile_center
), terrain
);
335 conn_list_do_buffer(game
.est_connections
);
336 /* This iterates outward, which gives any units that can't survive on
337 * changed terrain the best chance of survival. */
338 square_iterate(ptile_center
, size
- 1, ptile
) {
339 edit_tile_terrain_handling(ptile
, pterrain
, TRUE
);
340 } square_iterate_end
;
341 conn_list_do_unbuffer(game
.est_connections
);
344 /****************************************************************************
345 Handle a request to change one or more tiles' resources.
346 ****************************************************************************/
347 void handle_edit_tile_resource(struct connection
*pc
, int tile
,
348 Resource_type_id resource
, int size
)
350 struct resource
*presource
;
351 struct tile
*ptile_center
;
353 ptile_center
= index_to_tile(tile
);
355 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
356 _("Cannot edit the tile because %d is not a valid "
357 "tile index on this map!"), tile
);
360 presource
= resource_by_number(resource
); /* May be NULL. */
362 conn_list_do_buffer(game
.est_connections
);
363 square_iterate(ptile_center
, size
- 1, ptile
) {
364 edit_tile_resource_handling(ptile
, presource
, TRUE
);
365 } square_iterate_end
;
366 conn_list_do_unbuffer(game
.est_connections
);
369 /****************************************************************************
370 Handle a request to change one or more tiles' extras. The 'remove'
371 argument controls whether to remove or add the given extra from the tile.
372 ****************************************************************************/
373 void handle_edit_tile_extra(struct connection
*pc
, int tile
,
374 int id
, bool removal
, int size
)
376 struct tile
*ptile_center
;
378 ptile_center
= index_to_tile(tile
);
380 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
381 _("Cannot edit the tile because %d is not a valid "
382 "tile index on this map!"), tile
);
386 if (id
< 0 || id
>= game
.control
.num_extra_types
) {
387 notify_conn(pc
->self
, ptile_center
, E_BAD_COMMAND
, ftc_editor
,
388 /* TRANS: ..." the tile <tile-coordinates> because"... */
389 _("Cannot modify extras for the tile %s because "
390 "%d is not a valid extra id."),
391 tile_link(ptile_center
), id
);
395 conn_list_do_buffer(game
.est_connections
);
396 square_iterate(ptile_center
, size
- 1, ptile
) {
397 edit_tile_extra_handling(ptile
, extra_by_number(id
), removal
, TRUE
);
398 } square_iterate_end
;
399 conn_list_do_unbuffer(game
.est_connections
);
402 /****************************************************************************
403 Handles tile information from the client, to make edits to tiles.
404 ****************************************************************************/
405 void handle_edit_tile(struct connection
*pc
,
406 const struct packet_edit_tile
*packet
)
409 bool changed
= FALSE
;
411 ptile
= index_to_tile(packet
->tile
);
413 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
414 _("Cannot edit the tile because %d is not a valid "
415 "tile index on this map!"), packet
->tile
);
419 /* Handle changes in extras. */
420 if (!BV_ARE_EQUAL(packet
->extras
, ptile
->extras
)) {
421 extra_type_iterate(pextra
) {
422 if (edit_tile_extra_handling(ptile
, pextra
,
423 !BV_ISSET(packet
->extras
, extra_number(pextra
)),
427 } extra_type_iterate_end
;
430 /* Handle changes in label */
431 if (tile_set_label(ptile
, packet
->label
)) {
435 /* TODO: Handle more property edits. */
438 /* Send the new state to all affected. */
440 update_tile_knowledge(ptile
);
441 send_tile_info(NULL
, ptile
, FALSE
);
445 /****************************************************************************
446 Handle a request to create 'count' units of type 'utid' at the tile given
447 by the x, y coordinates and owned by player with number 'owner'.
448 ****************************************************************************/
449 void handle_edit_unit_create(struct connection
*pc
, int owner
, int tile
,
450 Unit_type_id utid
, int count
, int tag
)
453 struct unit_type
*punittype
;
454 struct player
*pplayer
;
455 struct city
*homecity
;
459 ptile
= index_to_tile(tile
);
461 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
462 _("Cannot create units because %d is not a valid "
463 "tile index on this map!"), tile
);
467 punittype
= utype_by_number(utid
);
469 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
470 /* TRANS: ..." at <tile-coordinates> because"... */
471 _("Cannot create a unit at %s because the "
472 "given unit type id %d is invalid."),
473 tile_link(ptile
), utid
);
477 pplayer
= player_by_number(owner
);
479 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
480 /* TRANS: ..." type <unit-type> at <tile-coordinates>"... */
481 _("Cannot create a unit of type %s at %s "
482 "because the given owner's player id %d is "
483 "invalid."), utype_name_translation(punittype
),
484 tile_link(ptile
), owner
);
488 if (is_non_allied_unit_tile(ptile
, pplayer
)
490 && !pplayers_allied(pplayer
, city_owner(tile_city(ptile
))))) {
491 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
492 /* TRANS: ..." type <unit-type> on enemy tile
493 * <tile-coordinates>"... */
494 _("Cannot create unit of type %s on enemy tile "
495 "%s."), utype_name_translation(punittype
),
500 if (!can_exist_at_tile(punittype
, ptile
)) {
501 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
502 /* TRANS: ..." type <unit-type> on the terrain at
503 * <tile-coordinates>"... */
504 _("Cannot create a unit of type %s on the terrain "
506 utype_name_translation(punittype
), tile_link(ptile
));
510 if (count
> 0 && !pplayer
->is_alive
) {
511 pplayer
->is_alive
= TRUE
;
512 send_player_info_c(pplayer
, NULL
);
515 homecity
= find_closest_city(ptile
, NULL
, pplayer
, FALSE
, FALSE
, FALSE
,
516 TRUE
, FALSE
, utype_class(punittype
));
517 id
= homecity
? homecity
->id
: 0;
519 conn_list_do_buffer(game
.est_connections
);
520 map_show_circle(pplayer
, ptile
, punittype
->vision_radius_sq
);
521 for (i
= 0; i
< count
; i
++) {
522 /* As far as I can see create_unit is guaranteed to
523 * never return NULL. */
524 punit
= create_unit(pplayer
, ptile
, punittype
, 0, id
, -1);
526 dsend_packet_edit_object_created(pc
, tag
, punit
->id
);
529 conn_list_do_unbuffer(game
.est_connections
);
532 /****************************************************************************
533 Remove 'count' units of type 'utid' owned by player number 'owner' at
535 ****************************************************************************/
536 void handle_edit_unit_remove(struct connection
*pc
, int owner
,
537 int tile
, Unit_type_id utid
, int count
)
540 struct unit_type
*punittype
;
541 struct player
*pplayer
;
544 ptile
= index_to_tile(tile
);
546 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
547 _("Cannot remove units because %d is not a valid "
548 "tile index on this map!"), tile
);
552 punittype
= utype_by_number(utid
);
554 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
555 /* TRANS: ..." at <tile-coordinates> because"... */
556 _("Cannot remove a unit at %s because the "
557 "given unit type id %d is invalid."),
558 tile_link(ptile
), utid
);
562 pplayer
= player_by_number(owner
);
564 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
565 /* TRANS: ..." type <unit-type> at <tile-coordinates>
567 _("Cannot remove a unit of type %s at %s "
568 "because the given owner's player id %d is "
569 "invalid."), utype_name_translation(punittype
),
570 tile_link(ptile
), owner
);
575 unit_list_iterate_safe(ptile
->units
, punit
) {
579 if (unit_type_get(punit
) != punittype
580 || unit_owner(punit
) != pplayer
) {
583 wipe_unit(punit
, ULR_EDITOR
, NULL
);
585 } unit_list_iterate_safe_end
;
588 /****************************************************************************
589 Handle a request to remove a unit given by its id.
590 ****************************************************************************/
591 void handle_edit_unit_remove_by_id(struct connection
*pc
, Unit_type_id id
)
595 punit
= game_unit_by_number(id
);
597 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
598 _("No such unit (ID %d)."), id
);
602 wipe_unit(punit
, ULR_EDITOR
, NULL
);
605 /****************************************************************************
606 Handles unit information from the client, to make edits to units.
607 ****************************************************************************/
608 void handle_edit_unit(struct connection
*pc
,
609 const struct packet_edit_unit
*packet
)
611 struct unit_type
*putype
;
614 bool changed
= FALSE
;
618 punit
= game_unit_by_number(id
);
620 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
621 _("No such unit (ID %d)."), id
);
625 putype
= unit_type_get(punit
);
627 if (packet
->moves_left
!= punit
->moves_left
) {
628 punit
->moves_left
= packet
->moves_left
;
632 fuel
= CLIP(0, packet
->fuel
, utype_fuel(putype
));
633 if (fuel
!= punit
->fuel
) {
638 if (packet
->moved
!= punit
->moved
) {
639 punit
->moved
= packet
->moved
;
643 if (packet
->done_moving
!= punit
->done_moving
) {
644 punit
->done_moving
= packet
->done_moving
;
648 hp
= CLIP(1, packet
->hp
, putype
->hp
);
649 if (hp
!= punit
->hp
) {
654 if (packet
->veteran
!= punit
->veteran
) {
655 int v
= packet
->veteran
;
656 if (!utype_veteran_level(putype
, v
)) {
657 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
658 _("Invalid veteran level %d for unit %d (%s)."),
659 v
, id
, unit_link(punit
));
666 /* TODO: Handle more property edits. */
669 /* Send the new state to all affected. */
671 send_unit_info(NULL
, punit
);
675 /****************************************************************************
676 Allows the editing client to create a city at the given position and
678 ****************************************************************************/
679 void handle_edit_city_create(struct connection
*pc
, int owner
, int tile
,
684 struct player
*pplayer
;
686 ptile
= index_to_tile(tile
);
688 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
689 _("Cannot create a city because %d is not a valid "
690 "tile index on this map!"), tile
);
694 pplayer
= player_by_number(owner
);
696 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
697 /* TRANS: ..." at <tile-coordinates> because"... */
698 _("Cannot create a city at %s because the "
699 "given owner's player id %d is invalid"),
700 tile_link(ptile
), owner
);
706 if (is_enemy_unit_tile(ptile
, pplayer
) != NULL
707 || !city_can_be_built_here(ptile
, NULL
)) {
708 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
709 /* TRANS: ..." at <tile-coordinates>." */
710 _("A city may not be built at %s."), tile_link(ptile
));
714 if (!pplayer
->is_alive
) {
715 pplayer
->is_alive
= TRUE
;
716 send_player_info_c(pplayer
, NULL
);
719 conn_list_do_buffer(game
.est_connections
);
721 map_show_tile(pplayer
, ptile
);
722 create_city(pplayer
, ptile
, city_name_suggestion(pplayer
, ptile
),
724 pcity
= tile_city(ptile
);
727 /* FIXME: Slow and inefficient for large size changes. */
728 city_change_size(pcity
, CLIP(1, size
, MAX_CITY_SIZE
), pplayer
, NULL
);
729 send_city_info(NULL
, pcity
);
733 dsend_packet_edit_object_created(pc
, tag
, pcity
->id
);
736 conn_list_do_unbuffer(game
.est_connections
);
740 /****************************************************************************
741 Handle a request to change the internal state of a city.
742 ****************************************************************************/
743 void handle_edit_city(struct connection
*pc
,
744 const struct packet_edit_city
*packet
)
747 struct city
*pcity
, *oldcity
;
748 struct player
*pplayer
;
751 bool changed
= FALSE
;
752 bool need_game_info
= FALSE
;
753 bv_player need_player_info
;
755 pcity
= game_city_by_number(packet
->id
);
757 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
758 _("Cannot edit city with invalid city ID %d."),
763 pplayer
= city_owner(pcity
);
764 ptile
= city_tile(pcity
);
765 BV_CLR_ALL(need_player_info
);
767 /* Handle name change. */
768 if (0 != strcmp(pcity
->name
, packet
->name
)) {
769 if (!is_allowed_city_name(pplayer
, packet
->name
, buf
, sizeof(buf
))) {
770 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
771 _("Cannot edit city name: %s"), buf
);
773 sz_strlcpy(pcity
->name
, packet
->name
);
778 /* Handle size change. */
779 if (packet
->size
!= city_size_get(pcity
)) {
780 if (!(0 < packet
->size
&& packet
->size
<= MAX_CITY_SIZE
)) {
781 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
782 _("Invalid city size %d for city %s."),
783 packet
->size
, city_link(pcity
));
785 /* FIXME: Slow and inefficient for large size changes. */
786 city_change_size(pcity
, packet
->size
, NULL
, NULL
);
791 if (packet
->history
!= pcity
->history
) {
792 pcity
->history
= packet
->history
;
796 /* Handle city improvement changes. */
797 improvement_iterate(pimprove
) {
799 id
= improvement_number(pimprove
);
801 if (is_special_improvement(pimprove
)) {
802 if (packet
->built
[id
] >= 0) {
803 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
804 _("It is impossible for a city to have %s!"),
805 improvement_name_translation(pimprove
));
810 /* FIXME: game.info.great_wonder_owners and pplayer->wonders
811 * logic duplication with city_build_building. */
813 if (city_has_building(pcity
, pimprove
) && packet
->built
[id
] < 0) {
815 city_remove_improvement(pcity
, pimprove
);
818 } else if (!city_has_building(pcity
, pimprove
)
819 && packet
->built
[id
] >= 0) {
821 if (is_great_wonder(pimprove
)) {
822 oldcity
= city_from_great_wonder(pimprove
);
823 if (oldcity
!= pcity
) {
824 BV_SET(need_player_info
, player_index(pplayer
));
826 if (NULL
!= oldcity
&& city_owner(oldcity
) != pplayer
) {
827 /* Great wonders make more changes. */
828 need_game_info
= TRUE
;
829 BV_SET(need_player_info
, player_index(city_owner(oldcity
)));
831 } else if (is_small_wonder(pimprove
)) {
832 oldcity
= city_from_small_wonder(pplayer
, pimprove
);
833 if (oldcity
!= pcity
) {
834 BV_SET(need_player_info
, player_index(pplayer
));
839 city_remove_improvement(oldcity
, pimprove
);
840 city_refresh_queue_add(oldcity
);
843 city_add_improvement(pcity
, pimprove
);
846 } improvement_iterate_end
;
848 /* Handle food stock change. */
849 if (packet
->food_stock
!= pcity
->food_stock
) {
850 int max
= city_granary_size(city_size_get(pcity
));
851 if (!(0 <= packet
->food_stock
&& packet
->food_stock
<= max
)) {
852 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
853 _("Invalid city food stock amount %d for city %s "
854 "(allowed range is %d to %d)."),
855 packet
->food_stock
, city_link(pcity
), 0, max
);
857 pcity
->food_stock
= packet
->food_stock
;
862 /* Handle shield stock change. */
863 if (packet
->shield_stock
!= pcity
->shield_stock
) {
864 int max
= USHRT_MAX
; /* Limited to uint16 by city info packet. */
865 if (!(0 <= packet
->shield_stock
&& packet
->shield_stock
<= max
)) {
866 notify_conn(pc
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
867 _("Invalid city shield stock amount %d for city %s "
868 "(allowed range is %d to %d)."),
869 packet
->shield_stock
, city_link(pcity
), 0, max
);
871 pcity
->shield_stock
= packet
->shield_stock
;
876 /* TODO: Handle more property edits. */
880 city_refresh_queue_add(pcity
);
881 conn_list_do_buffer(game
.est_connections
);
882 city_refresh_queue_processing();
884 /* FIXME: city_refresh_queue_processing only sends to city owner? */
885 send_city_info(NULL
, pcity
);
887 conn_list_do_unbuffer(game
.est_connections
);
890 /* Update wonder infos. */
891 if (need_game_info
) {
892 send_game_info(NULL
);
894 if (BV_ISSET_ANY(need_player_info
)) {
895 players_iterate(aplayer
) {
896 if (BV_ISSET(need_player_info
, player_index(aplayer
))) {
897 /* No need to send to detached connections. */
898 send_player_info_c(aplayer
, NULL
);
900 } players_iterate_end
;
904 /****************************************************************************
905 Handle a request to create a new player.
906 ****************************************************************************/
907 void handle_edit_player_create(struct connection
*pc
, int tag
)
909 struct player
*pplayer
;
910 struct nation_type
*pnation
;
911 struct research
*presearch
;
913 if (player_count() >= player_slot_count()) {
914 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
915 _("No more players can be added because the maximum "
916 "number of players (%d) has been reached."),
917 player_slot_count());
921 if (player_count() >= nation_count() ) {
922 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
923 _("No more players can be added because there are "
924 "no available nations (%d used)."),
929 pnation
= pick_a_nation(NULL
, TRUE
, TRUE
, NOT_A_BARBARIAN
);
931 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
932 _("Player cannot be created because random nation "
933 "selection failed."));
937 pplayer
= server_create_player(-1, default_ai_type_name(),
940 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
941 _("Player creation failed."));
944 server_player_init(pplayer
, TRUE
, TRUE
);
946 player_nation_defaults(pplayer
, pnation
, TRUE
);
947 if (game_was_started()) {
948 /* Find a color for the new player. */
949 assign_player_colors();
951 sz_strlcpy(pplayer
->username
, _(ANON_USER_NAME
));
952 pplayer
->unassigned_user
= TRUE
;
953 pplayer
->is_connected
= FALSE
;
954 pplayer
->government
= init_government_of_nation(pnation
);
955 pplayer
->server
.got_first_city
= FALSE
;
957 pplayer
->economic
.gold
= 0;
958 pplayer
->economic
= player_limit_to_max_rates(pplayer
);
960 presearch
= research_get(pplayer
);
961 init_tech(presearch
, TRUE
);
962 give_initial_techs(presearch
, 0);
964 send_player_all_c(pplayer
, NULL
);
965 /* Send research info after player info, else the client will complain
966 * about invalid team. */
967 send_research_info(presearch
, NULL
);
969 dsend_packet_edit_object_created(pc
, tag
, player_number(pplayer
));
973 /****************************************************************************
974 Handle a request to remove a player.
975 ****************************************************************************/
976 void handle_edit_player_remove(struct connection
*pc
, int id
)
978 struct player
*pplayer
;
980 pplayer
= player_by_number(id
);
981 if (pplayer
== NULL
) {
982 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
983 _("No such player (ID %d)."), id
);
987 /* Don't use conn_list_iterate here because connection_detach() can be
988 * recursive and free the next connection pointer. */
989 while (conn_list_size(pplayer
->connections
) > 0) {
990 connection_detach(conn_list_get(pplayer
->connections
, 0), FALSE
);
993 kill_player(pplayer
);
994 server_remove_player(pplayer
);
997 /**************************************************************************
998 Handle editing of any or all player properties.
999 ***************************************************************************/
1000 void handle_edit_player(struct connection
*pc
,
1001 const struct packet_edit_player
*packet
)
1003 struct player
*pplayer
;
1004 bool changed
= FALSE
, update_research
= FALSE
;
1005 struct nation_type
*pnation
;
1006 struct research
*research
;
1007 enum tech_state known
;
1008 struct government
*gov
;
1010 pplayer
= player_by_number(packet
->id
);
1012 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1013 _("Cannot edit player with invalid player ID %d."),
1018 research
= research_get(pplayer
);
1021 /* Handle player name change. */
1022 if (0 != strcmp(packet
->name
, player_name(pplayer
))) {
1023 char error_buf
[256];
1025 if (server_player_set_name_full(pc
, pplayer
, NULL
, packet
->name
,
1026 error_buf
, sizeof(error_buf
))) {
1029 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1030 _("Cannot change name of player (%d) '%s' to '%s': %s"),
1031 player_number(pplayer
), player_name(pplayer
),
1032 packet
->name
, error_buf
);
1036 /* Handle nation change. */
1037 pnation
= nation_by_number(packet
->nation
);
1038 if (nation_of_player(pplayer
) != pnation
) {
1039 if (pnation
== NULL
) {
1040 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1041 _("Cannot change nation for player %d (%s) "
1042 "because the given nation ID %d is invalid."),
1043 player_number(pplayer
), player_name(pplayer
),
1045 } else if (pnation
->player
!= NULL
) {
1046 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1047 _("Cannot change nation for player %d (%s) "
1048 "to nation %d (%s) because that nation is "
1049 "already assigned to player %d (%s)."),
1050 player_number(pplayer
), player_name(pplayer
),
1051 packet
->nation
, nation_plural_translation(pnation
),
1052 player_number(pnation
->player
),
1053 player_name(pnation
->player
));
1054 } else if (!nation_is_in_current_set(pnation
)) {
1055 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1056 _("Cannot change nation for player %d (%s) "
1057 "to nation %d (%s) because that nation is "
1058 "not in the current nation set."),
1059 player_number(pplayer
), player_name(pplayer
),
1060 packet
->nation
, nation_plural_translation(pnation
));
1061 } else if (pplayer
->ai_common
.barbarian_type
1062 != nation_barbarian_type(pnation
)
1063 || (!is_barbarian(pplayer
) && !is_nation_playable(pnation
))) {
1064 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1065 _("Cannot change nation for player %d (%s) "
1066 "to nation %d (%s) because that nation is "
1067 "unsuitable for this player."),
1068 player_number(pplayer
), player_name(pplayer
),
1069 packet
->nation
, nation_plural_translation(pnation
));
1071 changed
= player_set_nation(pplayer
, pnation
);
1075 /* Handle a change in research progress. */
1076 if (packet
->bulbs_researched
!= research
->bulbs_researched
) {
1077 research
->bulbs_researched
= packet
->bulbs_researched
;
1079 update_research
= TRUE
;
1082 /* Handle a change in known inventions. */
1083 advance_index_iterate(A_FIRST
, tech
) {
1084 known
= research_invention_state(research
, tech
);
1085 if ((packet
->inventions
[tech
] && known
== TECH_KNOWN
)
1086 || (!packet
->inventions
[tech
] && known
!= TECH_KNOWN
)) {
1089 if (packet
->inventions
[tech
]) {
1090 /* FIXME: Side-effect modifies game.info.global_advances. */
1091 research_invention_set(research
, tech
, TECH_KNOWN
);
1092 research
->techs_researched
++;
1094 research_invention_set(research
, tech
, TECH_UNKNOWN
);
1095 research
->techs_researched
--;
1098 update_research
= TRUE
;
1099 } advance_index_iterate_end
;
1101 /* Handle a change in the player's gold. */
1102 if (packet
->gold
!= pplayer
->economic
.gold
) {
1103 if (!(0 <= packet
->gold
&& packet
->gold
<= 1000000)) {
1104 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1105 _("Cannot set gold for player %d (%s) because "
1106 "the value %d is outside the allowed range."),
1107 player_number(pplayer
), player_name(pplayer
),
1110 pplayer
->economic
.gold
= packet
->gold
;
1115 /* Handle player government change */
1116 gov
= government_by_number(packet
->government
);
1117 if (gov
!= pplayer
->government
) {
1118 if (gov
!= game
.government_during_revolution
) {
1119 government_change(pplayer
, gov
, FALSE
);
1121 int turns
= revolution_length(gov
, pplayer
);
1124 pplayer
->government
= gov
;
1125 pplayer
->revolution_finishes
= game
.info
.turn
+ turns
;
1132 /* TODO: Handle more property edits. */
1134 if (update_research
) {
1135 Tech_type_id current
, goal
;
1137 research_update(research
);
1139 /* FIXME: Modifies struct research directly. */
1141 current
= research
->researching
;
1142 goal
= research
->tech_goal
;
1144 if (current
!= A_UNSET
) {
1145 known
= research_invention_state(research
, current
);
1146 if (known
!= TECH_PREREQS_KNOWN
) {
1147 research
->researching
= A_UNSET
;
1150 if (goal
!= A_UNSET
) {
1151 known
= research_invention_state(research
, goal
);
1152 if (known
== TECH_KNOWN
) {
1153 research
->tech_goal
= A_UNSET
;
1158 /* Inform everybody about global advances */
1159 send_game_info(NULL
);
1160 send_research_info(research
, NULL
);
1164 send_player_all_c(pplayer
, NULL
);
1168 /****************************************************************************
1169 Handles vision editing requests from client.
1170 ****************************************************************************/
1171 void handle_edit_player_vision(struct connection
*pc
, int plr_no
,
1172 int tile
, bool known
, int size
)
1174 struct player
*pplayer
;
1175 struct tile
*ptile_center
;
1177 ptile_center
= index_to_tile(tile
);
1178 if (!ptile_center
) {
1179 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1180 _("Cannot edit vision because %d is not a valid "
1181 "tile index on this map!"), tile
);
1185 pplayer
= player_by_number(plr_no
);
1187 notify_conn(pc
->self
, ptile_center
, E_BAD_COMMAND
, ftc_editor
,
1188 /* TRANS: ..." at <tile-coordinates> because"... */
1189 _("Cannot edit vision for the tile at %s because "
1190 "given player id %d is invalid."),
1191 tile_link(ptile_center
), plr_no
);
1195 conn_list_do_buffer(game
.est_connections
);
1196 square_iterate(ptile_center
, size
- 1, ptile
) {
1199 struct city
*pcity
= tile_city(ptile
);
1200 bool cannot_make_unknown
= FALSE
;
1202 if (pcity
&& city_owner(pcity
) == pplayer
) {
1206 unit_list_iterate(ptile
->units
, punit
) {
1207 if (unit_owner(punit
) == pplayer
1208 || really_gives_vision(pplayer
, unit_owner(punit
))) {
1209 cannot_make_unknown
= TRUE
;
1212 } unit_list_iterate_end
;
1214 if (cannot_make_unknown
) {
1218 /* The client expects tiles which become unseen to
1219 * contain no units (client/packhand.c +2368).
1220 * So here we tell it to remove units that do
1221 * not give it vision. */
1222 unit_list_iterate(ptile
->units
, punit
) {
1223 conn_list_iterate(pplayer
->connections
, pconn
) {
1224 dsend_packet_unit_remove(pconn
, punit
->id
);
1225 } conn_list_iterate_end
;
1226 } unit_list_iterate_end
;
1230 map_show_tile(pplayer
, ptile
);
1232 map_hide_tile(pplayer
, ptile
);
1234 } square_iterate_end
;
1235 conn_list_do_unbuffer(game
.est_connections
);
1238 /****************************************************************************
1239 Client editor requests us to recalculate borders. Note that this does
1240 not necessarily extend borders to their maximum due to the way the
1241 borders code is written. This may be considered a feature or limitation.
1242 ****************************************************************************/
1243 void handle_edit_recalculate_borders(struct connection
*pc
)
1245 map_calculate_borders();
1248 /****************************************************************************
1249 Remove any city at the given location.
1250 ****************************************************************************/
1251 void handle_edit_city_remove(struct connection
*pc
, int id
)
1255 pcity
= game_city_by_number(id
);
1256 if (pcity
== NULL
) {
1257 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1258 _("No such city (ID %d)."), id
);
1265 /****************************************************************************
1266 Run any pending tile checks.
1267 ****************************************************************************/
1268 void handle_edit_check_tiles(struct connection
*pc
)
1270 check_edited_tile_terrains();
1273 /****************************************************************************
1274 Temporarily remove fog-of-war for the player with player number 'plr_no'.
1275 This will only stay in effect while the server is in edit mode and the
1276 connection is editing. Has no effect if fog-of-war is disabled globally.
1277 ****************************************************************************/
1278 void handle_edit_toggle_fogofwar(struct connection
*pc
, int plr_no
)
1280 struct player
*pplayer
;
1282 if (!game
.info
.fogofwar
) {
1283 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1284 _("Cannot toggle fog-of-war when it is already "
1289 pplayer
= player_by_number(plr_no
);
1291 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1292 _("Cannot toggle fog-of-war for invalid player ID %d."),
1297 conn_list_do_buffer(game
.est_connections
);
1298 if (unfogged_players
[player_number(pplayer
)]) {
1299 enable_fog_of_war_player(pplayer
);
1300 unfogged_players
[player_number(pplayer
)] = FALSE
;
1302 disable_fog_of_war_player(pplayer
);
1303 unfogged_players
[player_number(pplayer
)] = TRUE
;
1305 conn_list_do_unbuffer(game
.est_connections
);
1308 /****************************************************************************
1309 Create or remove a start position at a tile.
1310 ****************************************************************************/
1311 void handle_edit_startpos(struct connection
*pconn
,
1312 const struct packet_edit_startpos
*packet
)
1314 struct tile
*ptile
= index_to_tile(packet
->id
);
1318 if (NULL
== ptile
) {
1319 notify_conn(pconn
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1320 _("Invalid tile index %d for start position."), packet
->id
);
1325 if (packet
->removal
) {
1326 changed
= map_startpos_remove(ptile
);
1328 if (NULL
!= map_startpos_get(ptile
)) {
1331 map_startpos_new(ptile
);
1338 conn_list_iterate(game
.est_connections
, aconn
) {
1339 if (can_conn_edit(aconn
)) {
1340 send_packet_edit_startpos(aconn
, packet
);
1342 } conn_list_iterate_end
;
1346 /****************************************************************************
1347 Setup which nations can start at a start position.
1348 ****************************************************************************/
1349 void handle_edit_startpos_full(struct connection
*pconn
,
1350 const struct packet_edit_startpos_full
*
1353 struct tile
*ptile
= index_to_tile(packet
->id
);
1354 struct startpos
*psp
;
1357 if (NULL
== ptile
) {
1358 notify_conn(pconn
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1359 _("Invalid tile index %d for start position."),
1364 psp
= map_startpos_get(ptile
);
1366 notify_conn(pconn
->self
, ptile
, E_BAD_COMMAND
, ftc_editor
,
1367 _("Cannot edit start position nations at (%d, %d) "
1368 "because there is no start position there."),
1374 if (startpos_unpack(psp
, packet
)) {
1376 conn_list_iterate(game
.est_connections
, aconn
) {
1377 if (can_conn_edit(aconn
)) {
1378 send_packet_edit_startpos_full(aconn
, packet
);
1380 } conn_list_iterate_end
;
1384 /****************************************************************************
1385 Handle edit requests to the main game data structure.
1386 ****************************************************************************/
1387 void handle_edit_game(struct connection
*pc
,
1388 const struct packet_edit_game
*packet
)
1390 bool changed
= FALSE
;
1392 if (packet
->year
!= game
.info
.year
) {
1394 /* 'year' is stored in a signed short. */
1395 const short min_year
= -30000, max_year
= 30000;
1397 if (!(min_year
<= packet
->year
&& packet
->year
<= max_year
)) {
1398 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1399 _("Cannot set invalid game year %d. Valid year range "
1400 "is from %d to %d."),
1401 packet
->year
, min_year
, max_year
);
1403 game
.info
.year
= packet
->year
;
1408 if (packet
->scenario
!= game
.scenario
.is_scenario
) {
1409 game
.scenario
.is_scenario
= packet
->scenario
;
1413 if (0 != strncmp(packet
->scenario_name
, game
.scenario
.name
, 256)) {
1414 sz_strlcpy(game
.scenario
.name
, packet
->scenario_name
);
1418 if (0 != strncmp(packet
->scenario_authors
, game
.scenario
.authors
,
1420 sz_strlcpy(game
.scenario
.authors
, packet
->scenario_authors
);
1424 if (packet
->scenario_random
!= game
.scenario
.save_random
) {
1425 game
.scenario
.save_random
= packet
->scenario_random
;
1429 if (packet
->scenario_players
!= game
.scenario
.players
) {
1430 game
.scenario
.players
= packet
->scenario_players
;
1434 if (packet
->startpos_nations
!= game
.scenario
.startpos_nations
) {
1435 game
.scenario
.startpos_nations
= packet
->startpos_nations
;
1439 if (packet
->prevent_new_cities
!= game
.scenario
.prevent_new_cities
) {
1440 game
.scenario
.prevent_new_cities
= packet
->prevent_new_cities
;
1444 if (packet
->lake_flooding
!= game
.scenario
.lake_flooding
) {
1445 game
.scenario
.lake_flooding
= packet
->lake_flooding
;
1450 send_scenario_info(NULL
);
1451 send_game_info(NULL
);
1455 /****************************************************************************
1456 Handle edit requests to scenario description
1457 ****************************************************************************/
1458 void handle_edit_scenario_desc(struct connection
*pc
, const char *scenario_desc
)
1460 if (0 != strncmp(scenario_desc
, game
.scenario_desc
.description
,
1462 sz_strlcpy(game
.scenario_desc
.description
, scenario_desc
);
1463 send_scenario_description(NULL
);
1467 /****************************************************************************
1468 Make scenario file out of current game.
1469 ****************************************************************************/
1470 void handle_save_scenario(struct connection
*pc
, const char *name
)
1472 if (pc
->access_level
!= ALLOW_HACK
) {
1473 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1474 _("No permissions to remotely save scenario."));
1478 if (!game
.scenario
.is_scenario
) {
1479 /* Scenario information not available */
1480 notify_conn(pc
->self
, NULL
, E_BAD_COMMAND
, ftc_editor
,
1481 _("Scenario information not set. Cannot save scenario."));
1485 /* Client initiated scenario saving is not handmade */
1486 game
.scenario
.handmade
= FALSE
;
1488 save_game(name
, "Scenario", TRUE
);