Do not build gtk3.22-client by default
[freeciv.git] / server / techtools.c
blob1ed4349d8d7609133b556c8bb8a8ab7b531aac89
1 /**********************************************************************
2 Freeciv - Copyright (C) 2005 The Freeciv Team
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)
6 any later version.
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 ***********************************************************************/
13 #ifdef HAVE_CONFIG_H
14 #include <fc_config.h>
15 #endif
17 /* utility */
18 #include "astring.h"
19 #include "fcintl.h"
20 #include "log.h"
21 #include "mem.h"
22 #include "rand.h"
23 #include "shared.h"
24 #include "support.h"
26 /* common */
27 #include "game.h"
28 #include "government.h"
29 #include "movement.h"
30 #include "player.h"
31 #include "research.h"
32 #include "tech.h"
33 #include "unit.h"
35 /* common/scriptcore */
36 #include "luascript_types.h"
38 /* server */
39 #include "citytools.h"
40 #include "cityturn.h"
41 #include "connecthand.h"
42 #include "gamehand.h"
43 #include "maphand.h"
44 #include "notify.h"
45 #include "plrhand.h"
46 #include "unittools.h"
48 /* server/scripting */
49 #include "script_server.h"
51 #include "techtools.h"
53 /* Define this to add information about tech upkeep. */
54 #undef TECH_UPKEEP_DEBUGGING
56 static Tech_type_id
57 pick_random_tech_to_lose(const struct research *presearch);
58 static void research_tech_lost(struct research *presearch,
59 Tech_type_id tech);
60 static void forget_tech_transfered(struct player *pplayer, Tech_type_id tech);
62 /****************************************************************************
63 Apply a penalty to the research.
64 ****************************************************************************/
65 void research_apply_penalty(struct research *presearch, Tech_type_id tech,
66 int penalty_percent)
68 presearch->bulbs_researched -=
69 (research_total_bulbs_required(presearch, tech, FALSE)
70 * penalty_percent) / 100;
71 presearch->researching_saved = A_UNKNOWN;
74 /****************************************************************************
75 Emit script signal(s) for player/team learning new tech.
76 originating_plr is the player whose action caused this; may be NULL, and
77 is only used to order the emission of the signals.
78 ****************************************************************************/
79 void script_tech_learned(struct research *presearch,
80 struct player *originating_plr, struct advance *tech,
81 const char *reason)
83 /* Emit signal for individual player whose action triggered the
84 * tech first */
85 if (originating_plr) {
86 fc_assert(research_get(originating_plr) == presearch);
87 script_server_signal_emit("tech_researched", 3,
88 API_TYPE_TECH_TYPE, tech,
89 API_TYPE_PLAYER, originating_plr,
90 API_TYPE_STRING, reason);
93 /* Emit signal to remaining research teammates, if any */
94 research_players_iterate(presearch, member) {
95 if (member != originating_plr) {
96 script_server_signal_emit("tech_researched", 3,
97 API_TYPE_TECH_TYPE, tech,
98 API_TYPE_PLAYER, member,
99 API_TYPE_STRING, reason);
101 } research_players_iterate_end;
104 /****************************************************************************
105 Players have researched a new technology.
106 ****************************************************************************/
107 static void tech_researched(struct research *research)
109 char research_name[MAX_LEN_NAME * 2];
110 /* Cache researched technology for event signal, because found_new_tech()
111 * changes the research target. */
112 Tech_type_id tech = research->researching;
114 research_pretty_name(research, research_name, sizeof(research_name));
115 /* Players will be notified when new tech is chosen. */
116 notify_research_embassies
117 (research, NULL, E_TECH_EMBASSY, ftc_server,
118 _("The %s have researched %s."),
119 research_name,
120 research_advance_name_translation(research, tech));
122 /* Deduct tech cost. */
123 research->bulbs_researched -= research_total_bulbs_required(research, tech,
124 FALSE);
126 /* Do all the updates needed after finding new tech. */
127 found_new_tech(research, tech, TRUE, TRUE);
129 script_tech_learned(research, NULL, advance_by_number(tech), "researched");
132 /****************************************************************************
133 Give technologies to players with EFT_TECH_PARASITE (traditionally from
134 the Great Library).
135 ****************************************************************************/
136 void do_tech_parasite_effect(struct player *pplayer)
138 struct effect_list *plist = effect_list_new();
139 struct astring effects;
140 struct research *presearch;
141 char research_name[MAX_LEN_NAME * 2];
142 const char *advance_name;
143 Tech_type_id tech;
144 /* Note that two EFT_TECH_PARASITE effects will combine into a single,
145 * much worse effect. */
146 int mod = get_player_bonus_effects(plist, pplayer, EFT_TECH_PARASITE);
147 int num_players;
148 int num_techs;
150 if (mod <= 0) {
151 /* No effect. */
152 effect_list_destroy(plist);
153 return;
156 /* Pick a random technology. */
157 tech = A_UNSET;
158 num_techs = 0;
159 presearch = research_get(pplayer);
160 advance_index_iterate(A_FIRST, i) {
161 if (!research_invention_gettable(presearch, i,
162 game.info.tech_parasite_allow_holes)
163 || TECH_KNOWN == research_invention_state(presearch, i)) {
164 continue;
167 num_players = 0;
168 players_iterate(aplayer) {
169 if (TECH_KNOWN == research_invention_state(research_get(aplayer), i)) {
170 if (mod <= ++num_players) {
171 if (0 == fc_rand(++num_techs)) {
172 tech = i;
174 break;
177 } players_iterate_end;
178 } advance_index_iterate_end;
180 if (A_UNSET == tech) {
181 /* No tech found. */
182 effect_list_destroy(plist);
183 return;
186 /* Notify. */
187 research_pretty_name(presearch, research_name, sizeof(research_name));
188 advance_name = research_advance_name_translation(presearch, tech);
189 astr_init(&effects);
190 get_effect_list_req_text(plist, &effects);
192 notify_player(pplayer, NULL, E_TECH_GAIN, ftc_server,
193 /* TRANS: Tech from source of an effect
194 * (Great Library) */
195 Q_("?fromeffect:%s acquired from %s!"),
196 advance_name,
197 astr_str(&effects));
198 notify_research(presearch, pplayer, E_TECH_GAIN, ftc_server,
199 /* TRANS: Tech from source of an effect
200 * (Great Library) */
201 Q_("?fromeffect:%s acquired from %s's %s!"),
202 advance_name,
203 player_name(pplayer),
204 astr_str(&effects));
205 notify_research_embassies(presearch, NULL, E_TECH_EMBASSY, ftc_server,
206 /* TRANS: Tech from source of an effect
207 * (Great Library) */
208 Q_("?fromeffect:The %s have acquired %s from %s."),
209 research_name,
210 advance_name,
211 astr_str(&effects));
213 effect_list_destroy(plist);
214 astr_free(&effects);
216 /* Really get tech. */
217 research_apply_penalty(presearch, tech, game.server.freecost);
218 found_new_tech(presearch, tech, FALSE, TRUE);
220 research_players_iterate(presearch, member) {
221 script_server_signal_emit("tech_researched", 3,
222 API_TYPE_TECH_TYPE, advance_by_number(tech),
223 API_TYPE_PLAYER, member,
224 API_TYPE_STRING, "stolen");
225 } research_players_iterate_end;
228 /****************************************************************************
229 Fill packet fields. Helper for following functions.
230 ****************************************************************************/
231 static inline void
232 package_research_info(struct packet_research_info *packet,
233 const struct research *presearch)
235 packet->id = research_number(presearch);
236 packet->techs_researched = presearch->techs_researched;
237 packet->future_tech = presearch->future_tech;
238 packet->researching = presearch->researching;
239 packet->researching_cost =
240 (packet->researching != A_UNSET
241 ? research_total_bulbs_required(presearch, presearch->researching,
242 FALSE) : 0);
243 packet->bulbs_researched = presearch->bulbs_researched;
244 packet->tech_goal = presearch->tech_goal;
245 packet->total_bulbs_prod = 0;
246 research_players_iterate(presearch, pplayer) {
247 city_list_iterate(pplayer->cities, pcity) {
248 packet->total_bulbs_prod += pcity->surplus[O_SCIENCE];
249 } city_list_iterate_end;
250 } research_players_iterate_end;
251 advance_index_iterate(A_NONE, i) {
252 packet->inventions[i] = presearch->inventions[i].state + '0';
253 } advance_index_iterate_end;
254 packet->inventions[advance_count()] = '\0';
255 packet->tech_goal = presearch->tech_goal;
256 #ifdef DEBUG
257 log_verbose("Research nb %d inventions: %s",
258 research_number(presearch),
259 packet->inventions);
260 #endif
263 /****************************************************************************
264 Send research info for 'presearch' to 'dest'. 'dest' can be NULL to send
265 to all established connections.
266 ****************************************************************************/
267 void send_research_info(const struct research *presearch,
268 const struct conn_list *dest)
270 struct packet_research_info full_info, restricted_info;
271 const struct player *pplayer;
273 fc_assert_ret(NULL != presearch);
274 if (NULL == dest) {
275 dest = game.est_connections;
278 /* Packaging */
279 package_research_info(&full_info, presearch);
280 restricted_info = full_info;
281 restricted_info.tech_goal = A_UNSET;
282 restricted_info.total_bulbs_prod = 0;
284 conn_list_iterate(dest, pconn) {
285 pplayer = conn_get_player(pconn);
286 if (NULL != pplayer) {
287 if (presearch == research_get(pplayer)) {
288 /* Case research owner. */
289 send_packet_research_info(pconn, &full_info);
290 } else {
291 /* 'pplayer' may have an embassy for looking to 'presearch'. */
292 research_players_iterate(presearch, powner) {
293 if (player_has_embassy(pplayer, powner)) {
294 send_packet_research_info(pconn, &restricted_info);
295 break;
297 } research_players_iterate_end;
299 } else if (pconn->observer) {
300 /* Case global observer. */
301 send_packet_research_info(pconn, &full_info);
303 } conn_list_iterate_end;
306 /****************************************************************************
307 Players sharing the research have got a new technology (from somewhere).
308 'was_discovery' is passed on to upgrade_city_extras. Logging and
309 notification is not done here as it depends on how the tech came.
310 ****************************************************************************/
311 void found_new_tech(struct research *presearch, Tech_type_id tech_found,
312 bool was_discovery, bool saving_bulbs)
314 int had_embassies[player_slot_count()];
315 bool could_switch[player_slot_count()][government_count()];
316 bool was_first = FALSE;
317 bool bonus_tech_hack = FALSE;
318 int i;
319 const char *advance_name;
320 struct advance *vap = valid_advance_by_number(tech_found);
321 struct city *pcity;
323 #ifndef FREECIV_NDEBUG
324 if (!is_future_tech(tech_found)) {
325 fc_assert(NULL != vap);
326 fc_assert(TECH_KNOWN != research_invention_state(presearch, tech_found));
328 #endif /* FREECIV_NDEBUG */
330 was_first = (!game.info.global_advances[tech_found]);
331 /* Assign 'advance_name' before we increase the future tech counter. */
332 advance_name = research_advance_name_translation(presearch, tech_found);
334 if (was_first && vap) {
335 /* Alert the owners of any wonders that have been made obsolete */
336 improvement_iterate(pimprove) {
337 requirement_vector_iterate(&pimprove->obsolete_by, pobs) {
338 if (pobs->source.kind == VUT_ADVANCE
339 && pobs->source.value.advance == vap
340 && pobs->range >= REQ_RANGE_WORLD
341 && pobs->survives
342 && is_great_wonder(pimprove)
343 && (pcity = city_from_great_wonder(pimprove))) {
344 notify_player(city_owner(pcity), NULL, E_WONDER_OBSOLETE, ftc_server,
345 _("Discovery of %s OBSOLETES %s in %s!"),
346 research_advance_name_translation
347 (research_get(city_owner(pcity)), tech_found),
348 improvement_name_translation(pimprove),
349 city_link(pcity));
351 } requirement_vector_iterate_end;
352 } improvement_iterate_end;
355 if (was_first
356 && !is_future_tech(tech_found)
357 && advance_has_flag(tech_found, TF_BONUS_TECH)) {
358 bonus_tech_hack = TRUE;
361 /* Memorize some values before the tech is marked as researched.
362 * We will check what has changed later. */
363 players_iterate(aplayer) {
364 i = player_index(aplayer);
366 /* Count EFT_HAVE_EMBASSIES effect for each player. */
367 had_embassies[i] = get_player_bonus(aplayer, EFT_HAVE_EMBASSIES);
369 if (presearch != research_get(aplayer)) {
370 continue;
373 /* Memorize for the players sharing the research what government
374 * they could switch on. */
375 governments_iterate(pgov) {
376 could_switch[i][government_index(pgov)]
377 = can_change_to_government(aplayer, pgov);
378 } governments_iterate_end;
379 } players_iterate_end;
381 /* got_tech allows us to change research without applying techpenalty
382 * (without losing bulbs) */
383 if (tech_found == presearch->researching) {
384 presearch->got_tech = TRUE;
386 presearch->researching_saved = A_UNKNOWN;
387 presearch->techs_researched++;
389 /* Mark the tech as known in the research struct and update
390 * global_advances array. */
391 if (is_future_tech(tech_found)) {
392 presearch->future_tech++;
393 } else {
394 research_invention_set(presearch, tech_found, TECH_KNOWN);
395 research_update(presearch);
398 /* Inform players about their new tech. */
399 send_research_info(presearch, NULL);
401 if (was_first) {
402 /* Inform all players about new global advances to give them a
403 * chance for obsolete buildings. */
404 send_game_info(NULL);
407 /* Make proper changes for all players. Use shuffled order, in case
408 * a script would detect a signal. */
409 shuffled_players_iterate(aplayer) {
410 i = player_index(aplayer);
412 if (presearch == research_get(aplayer)) {
413 /* Only for players sharing the research. */
414 remove_obsolete_buildings(aplayer);
416 /* Give free infrastructure in every city */
417 if (tech_found != A_FUTURE) {
418 upgrade_all_city_extras(aplayer, was_discovery);
421 /* Enhance vision of units if a player-ranged effect has changed. Note
422 * that world-ranged effects will not be updated immediately. */
423 unit_list_refresh_vision(aplayer->units);
425 /* Notify a player about new governments available */
426 governments_iterate(pgov) {
427 if (!could_switch[i][government_index(pgov)]
428 && can_change_to_government(aplayer, pgov)) {
429 notify_player(aplayer, NULL, E_NEW_GOVERNMENT, ftc_server,
430 _("Discovery of %s makes the government form %s"
431 " available. You may want to start a revolution."),
432 advance_name,
433 government_name_translation(pgov));
435 } governments_iterate_end;
438 /* For any player. */
439 /* Update all cities in case the tech changed some effects. This is
440 * inefficient; it could be optimized if it's found to be a problem.
441 * But techs aren't researched that often. */
442 city_list_iterate(aplayer->cities, apcity) {
443 /* Refresh the city data; this also updates the squared city radius. */
444 city_refresh(apcity);
445 city_refresh_vision(apcity);
446 send_city_info(aplayer, apcity);
447 } city_list_iterate_end;
449 /* Send all player an updated info of the owner of the Marco Polo
450 * Wonder if this wonder has become obsolete. */
451 if (0 < had_embassies[i]
452 && 0 <= get_player_bonus(aplayer, EFT_HAVE_EMBASSIES)) {
453 send_player_all_c(aplayer, aplayer->connections);
454 players_iterate(pother_player) {
455 if (aplayer != pother_player) {
456 send_player_all_c(aplayer, pother_player->connections);
457 send_player_all_c(pother_player, aplayer->connections);
459 } players_iterate_end;
461 } shuffled_players_iterate_end;
463 if (tech_found == presearch->tech_goal) {
464 presearch->tech_goal = A_UNSET;
467 if (tech_found == presearch->researching) {
468 /* Try to pick new tech to research. */
469 Tech_type_id next_tech = research_goal_step(presearch,
470 presearch->tech_goal);
472 /* As this function can be recursive, we need to print the messages
473 * before really picking the new technology. */
474 if (A_UNSET != next_tech) {
475 notify_research(presearch, NULL, E_TECH_LEARNED, ftc_server,
476 _("Learned %s. Our scientists focus on %s; "
477 "goal is %s."),
478 advance_name,
479 research_advance_name_translation(presearch,
480 next_tech),
481 research_advance_name_translation
482 (presearch, presearch->tech_goal));
483 } else {
484 if (is_future_tech(tech_found)) {
485 /* Continue researching future tech. */
486 next_tech = A_FUTURE;
487 } else {
488 /* If there is at least one AI player still alive, then pick
489 * a random tech, else keep A_UNSET. */
490 research_players_iterate(presearch, aplayer) {
491 if (aplayer->ai_controlled) {
492 next_tech = pick_random_tech(presearch);
493 break;
495 } research_players_iterate_end;
498 if (A_UNSET == next_tech) {
499 notify_research(presearch, NULL, E_TECH_LEARNED, ftc_server,
500 _("Learned %s. Scientists "
501 "do not know what to research next."),
502 advance_name);
503 } else {
504 notify_research(presearch, NULL, E_TECH_LEARNED, ftc_server,
505 _("Learned %s. Scientists choose to research %s."),
506 advance_name,
507 research_advance_name_translation(presearch,
508 next_tech));
512 if (A_UNSET != next_tech) {
513 choose_tech(presearch, next_tech);
514 } else {
515 presearch->researching = A_UNSET;
519 if (!saving_bulbs && presearch->bulbs_researched > 0) {
520 presearch->bulbs_researched = 0;
523 if (bonus_tech_hack) {
524 Tech_type_id additional_tech;
525 char research_name[MAX_LEN_NAME * 2];
526 const char *radv_name;
528 research_pretty_name(presearch, research_name, sizeof(research_name));
530 additional_tech = give_immediate_free_tech(presearch);
532 radv_name = research_advance_name_translation(presearch, additional_tech);
534 if (advance_by_number(tech_found)->bonus_message != NULL
535 && additional_tech != A_UNSET) {
536 notify_research(presearch, NULL, E_TECH_GAIN, ftc_server,
537 _(advance_by_number(tech_found)->bonus_message),
538 radv_name);
539 } else if (additional_tech != A_UNSET) {
540 /* FIXME: "your" when it was just civilization of one of the players
541 * sharing the reseach. */
542 notify_research(presearch, NULL, E_TECH_GAIN, ftc_server,
543 _("Great scientists from all the "
544 "world join your civilization: you learn "
545 "%s immediately."), radv_name);
547 /* TODO: Ruleset should be able to customize this message too */
548 notify_research_embassies(presearch, NULL, E_TECH_EMBASSY, ftc_server,
549 _("%s acquire %s as a result of learning %s."),
550 research_name, radv_name, advance_name);
554 /****************************************************************************
555 Is player about to lose tech?
556 ****************************************************************************/
557 static bool lose_tech(struct research *research)
559 if (game.server.techloss_forgiveness < 0) {
560 /* Tech loss disabled */
561 return FALSE;
564 if (research->techs_researched == 0) {
565 /* No tech to lose */
566 fc_assert(research->future_tech == 0);
567 return FALSE;
570 if (research->bulbs_researched <
571 (-research_total_bulbs_required(research, research->researching, FALSE)
572 * game.server.techloss_forgiveness / 100)) {
573 return TRUE;
576 return FALSE;
579 /****************************************************************************
580 Adds the given number of bulbs into the player's tech and (if necessary and
581 'check_tech' is TRUE) completes the research. If the total number of bulbs
582 is negative due to tech upkeep, one (randomly chosen) tech is lost.
584 The caller is responsible for sending updated player information.
586 This is called from each city every turn, from caravan revenue, and at the
587 end of the phase.
588 ****************************************************************************/
589 void update_bulbs(struct player *pplayer, int bulbs, bool check_tech)
591 struct research *research = research_get(pplayer);
593 if (!pplayer->is_alive) {
594 /* Dead players do not produce research */
595 return;
598 /* count our research contribution this turn */
599 pplayer->server.bulbs_last_turn += bulbs;
600 research->bulbs_researched += bulbs;
602 do {
603 /* If we have a negative number of bulbs we do try to:
604 * - reduce the number of future techs;
605 * - or lose one random tech.
606 * After that the number of bulbs available is incresed based on the
607 * value of the lost tech. */
608 if (lose_tech(research)) {
609 Tech_type_id tech = (research->future_tech > 0
610 ? A_FUTURE : pick_random_tech_to_lose(research));
612 if (tech != A_NONE) {
613 if (game.server.techloss_restore >= 0) {
614 research->bulbs_researched +=
615 (research_total_bulbs_required(research, tech, TRUE)
616 * game.server.techloss_restore / 100);
617 } else {
618 research->bulbs_researched = 0;
620 research->researching_saved = A_UNKNOWN;
622 log_debug("%s: tech loss (%s)",
623 research_rule_name(research),
624 (is_future_tech(tech) ? "Future Tech"
625 : research_advance_rule_name(research, tech)));
626 research_tech_lost(research, tech);
627 /* Make notification after losing the research, in case it is
628 * a future tech (for getting the right tech number). */
629 notify_research(research, NULL, E_TECH_LOST, ftc_server,
630 _("Insufficient science output. We lost %s."),
631 research_advance_name_translation(research, tech));
635 /* Check for finished research. */
636 if (!check_tech
637 || research->researching == A_UNSET
638 || (research->bulbs_researched
639 < research_total_bulbs_required(research, research->researching,
640 FALSE))) {
641 break;
644 tech_researched(research);
645 } while (research->researching != A_UNSET);
648 /****************************************************************************
649 Choose a random tech for player to lose.
650 ****************************************************************************/
651 static Tech_type_id
652 pick_random_tech_to_lose(const struct research *presearch)
654 bv_techs eligible_techs;
655 /* A_NONE included in advance_count(). */
656 int eligible = advance_count() - 1;
657 int chosen;
659 BV_SET_ALL(eligible_techs);
661 advance_index_iterate(A_FIRST, i) {
662 if (research_invention_state(presearch, i) != TECH_KNOWN) {
663 if (BV_ISSET(eligible_techs, i)) {
664 eligible--;
665 BV_CLR(eligible_techs, i);
667 } else {
668 /* Knowing this tech may make others ineligible */
669 Tech_type_id root = advance_required(i, AR_ROOT);
670 /* Never lose techs that are root_req for a currently known tech
671 * (including self root_req) */
672 if (root != A_NONE && BV_ISSET(eligible_techs, root)) {
673 eligible--;
674 BV_CLR(eligible_techs, root);
676 if (!game.info.tech_loss_allow_holes) {
677 /* Ruleset can prevent this kind of tech loss from opening up
678 * holes in the tech tree */
679 Tech_type_id prereq;
680 prereq = advance_required(i, AR_ONE);
681 if (prereq != A_NONE && BV_ISSET(eligible_techs, prereq)) {
682 eligible--;
683 BV_CLR(eligible_techs, prereq);
685 prereq = advance_required(i, AR_TWO);
686 if (prereq != A_NONE && BV_ISSET(eligible_techs, prereq)) {
687 eligible--;
688 BV_CLR(eligible_techs, prereq);
692 } advance_index_iterate_end;
694 if (eligible == 0) {
695 /* no researched technology at all */
696 return A_NONE;
699 chosen = fc_rand(eligible) + 1;
701 advance_index_iterate(A_FIRST, i) {
702 if (BV_ISSET(eligible_techs, i)) {
703 chosen--;
704 if (chosen == 0) {
705 return i;
708 } advance_index_iterate_end;
710 /* should never be reached */
711 fc_assert_msg(chosen == 0, "internal error (eligible=%d, chosen=%d)",
712 eligible, chosen);
713 return A_NONE;
716 /****************************************************************************
717 Helper for research_tech_lost().
718 ****************************************************************************/
719 static inline struct government *
720 pick_random_government(struct player *pplayer)
722 struct government *picked = NULL;
723 int gov_nb = 0;
725 governments_iterate(pgov) {
726 if (can_change_to_government(pplayer, pgov) && 0 == fc_rand(++gov_nb)) {
727 picked = pgov;
729 } governments_iterate_end;
730 fc_assert(NULL != picked);
731 return picked;
734 /****************************************************************************
735 Remove one tech from the research.
736 ****************************************************************************/
737 static void research_tech_lost(struct research *presearch, Tech_type_id tech)
739 char research_name[MAX_LEN_NAME * 2];
740 /* Research members will be notified when new tech is chosen. */
742 research_pretty_name(presearch, research_name, sizeof(research_name));
744 presearch->techs_researched--;
745 if (is_future_tech(tech)) {
746 presearch->future_tech--;
747 research_update(presearch);
748 /* Notify after decreasing the future tech counter, to get the right
749 * tech number in the message. */
750 notify_research_embassies(presearch, NULL, E_TECH_EMBASSY, ftc_server,
751 _("The %s have lost %s."),
752 research_name,
753 research_advance_name_translation(presearch,
754 tech));
755 /* Inform players about their technology loss. */
756 send_research_info(presearch, NULL);
757 return;
760 fc_assert_ret(valid_advance_by_number(tech));
761 notify_research_embassies(presearch, NULL, E_TECH_EMBASSY, ftc_server,
762 /* TRANS: technology loss */
763 _("The %s have lost %s."),
764 research_name,
765 research_advance_name_translation(presearch,
766 tech));
768 /* Remove technology. */
769 research_invention_set(presearch, tech, TECH_UNKNOWN);
770 research_update(presearch);
771 log_debug("%s lost tech id %d (%s)", research_rule_name(presearch), tech,
772 advance_rule_name(advance_by_number(tech)));
774 /* Inform players about their technology loss. */
775 send_research_info(presearch, NULL);
777 research_players_iterate(presearch, pplayer) {
778 /* Check government. */
779 if (!can_change_to_government(pplayer, government_of_player(pplayer))) {
780 /* Lost the technology for the government; switch to random
781 * available government. */
782 struct government *pgov = pick_random_government(pplayer);
784 notify_player(pplayer, NULL, E_NEW_GOVERNMENT, ftc_server,
785 _("The required technology for our government '%s' "
786 "was lost. The citizens have started a "
787 "revolution into '%s'."),
788 government_name_translation(government_of_player
789 (pplayer)),
790 government_name_translation(pgov));
791 handle_player_change_government(pplayer, government_number(pgov));
792 send_player_info_c(pplayer, NULL);
793 } else if (NULL != pplayer->target_government
794 && !can_change_to_government(pplayer,
795 pplayer->target_government)) {
796 /* Lost the technology for the target government; use a random
797 * available government as new target government. */
798 struct government *pgov = pick_random_government(pplayer);
800 notify_player(pplayer, NULL, E_NEW_GOVERNMENT, ftc_server,
801 _("The required technology for our new government "
802 "'%s' was lost. The citizens chose '%s' as new "
803 "target government."),
804 government_name_translation(pplayer->target_government),
805 government_name_translation(pgov));
806 pplayer->target_government = pgov;
807 send_player_info_c(pplayer, pplayer->connections);
810 /* Check all units for valid activities. */
811 unit_list_iterate(pplayer->units, punit) {
812 if (!can_unit_continue_current_activity(punit)) {
813 log_debug("lost technology for activity of unit %s of %s (%d, %d)",
814 unit_name_translation(punit), player_name(pplayer),
815 TILE_XY(unit_tile(punit)));
816 set_unit_activity(punit, ACTIVITY_IDLE);
817 send_unit_info(NULL, punit);
819 } unit_list_iterate_end;
821 /* Check city production */
822 city_list_iterate(pplayer->cities, pcity) {
823 bool update = FALSE;
825 if (pcity->production.kind == VUT_UTYPE
826 && !can_city_build_unit_now(pcity, pcity->production.value.utype)) {
827 notify_player(pplayer, city_tile(pcity),
828 E_CITY_CANTBUILD, ftc_server,
829 _("%s can't build %s. The required technology was "
830 "lost."),
831 city_link(pcity),
832 utype_name_translation(pcity->production.value.utype));
833 choose_build_target(pplayer, pcity);
834 update = TRUE;
837 if (pcity->production.kind == VUT_IMPROVEMENT
838 && !can_city_build_improvement_now(pcity,
839 pcity->production.value.building)) {
840 notify_player(pplayer, city_tile(pcity),
841 E_CITY_CANTBUILD, ftc_server,
842 _("%s can't build %s. The required technology was "
843 "lost."),
844 city_link(pcity),
845 improvement_name_translation
846 (pcity->production.value.building));
847 choose_build_target(pplayer, pcity);
848 update = TRUE;
851 if (update) {
852 city_refresh(pcity);
853 send_city_info(pplayer, pcity);
855 } city_list_iterate_end;
856 } research_players_iterate_end;
859 /****************************************************************************
860 Returns random researchable tech or A_FUTURE. No side effects.
861 ****************************************************************************/
862 Tech_type_id pick_random_tech(const struct research *presearch)
864 Tech_type_id tech = A_FUTURE;
865 int num_techs = 0;
867 advance_index_iterate(A_FIRST, i) {
868 if (research_invention_state(presearch, i) == TECH_PREREQS_KNOWN) {
869 if (fc_rand(++num_techs) == 0) {
870 tech = i;
873 } advance_index_iterate_end;
874 return tech;
877 /****************************************************************************
878 Returns cheapest researchable tech, random among equal cost ones.
879 ****************************************************************************/
880 Tech_type_id pick_cheapest_tech(const struct research *presearch)
882 int cheapest_cost = -1;
883 int cheapest_amount = 0;
884 Tech_type_id cheapest = A_FUTURE; /* If no real tech is found to be missing */
886 advance_index_iterate(A_FIRST, i) {
887 if (research_invention_state(presearch, i) == TECH_PREREQS_KNOWN) {
888 int cost = research_total_bulbs_required(presearch, i, FALSE);
890 if (cost < cheapest_cost || cheapest_cost == -1) {
891 cheapest_cost = cost;
892 cheapest_amount = 1;
893 cheapest = i;
894 } else if (cost == cheapest_cost && fc_rand(++cheapest_amount) == 0) {
895 cheapest = i;
898 } advance_index_iterate_end;
900 return cheapest;
903 /****************************************************************************
904 Finds and chooses (sets) a random research target from among all those
905 available until presearch->researching != A_UNSET.
906 Players may research more than one tech in this function.
907 Possible reasons:
908 - techpenalty < 100;
909 - research.got_tech = TRUE and enough bulbs was saved;
910 - research.researching = A_UNSET and enough bulbs was saved.
911 ****************************************************************************/
912 void choose_random_tech(struct research *research)
914 do {
915 choose_tech(research, pick_random_tech(research));
916 } while (research->researching == A_UNSET);
919 /****************************************************************************
920 Called when a player chooses the tech he wants to research (or when
921 the server chooses it for him automatically).
923 This takes care of all side effects so the research target probably
924 shouldn't be changed outside of this function (doing so has been the
925 cause of several bugs).
926 ****************************************************************************/
927 void choose_tech(struct research *research, Tech_type_id tech)
929 if (is_future_tech(tech)) {
930 if (is_future_tech(research->researching)
931 && (research->bulbs_researched
932 >= research_total_bulbs_required(research, tech, FALSE))) {
933 tech_researched(research);
935 } else {
936 if (research->researching == tech) {
937 return;
939 if (research_invention_state(research, tech) != TECH_PREREQS_KNOWN) {
940 /* Can't research this. */
941 return;
944 if (!research->got_tech && research->researching_saved == A_UNKNOWN) {
945 research->bulbs_researching_saved = research->bulbs_researched;
946 research->researching_saved = research->researching;
947 /* Subtract a penalty because we changed subject. */
948 if (research->bulbs_researched > 0) {
949 research->bulbs_researched
950 -= ((research->bulbs_researched * game.server.techpenalty) / 100);
951 fc_assert(research->bulbs_researched >= 0);
953 } else if (tech == research->researching_saved) {
954 research->bulbs_researched = research->bulbs_researching_saved;
955 research->researching_saved = A_UNKNOWN;
957 research->researching = tech;
958 if (research->bulbs_researched
959 >= research_total_bulbs_required(research, tech, FALSE)) {
960 tech_researched(research);
964 /****************************************************************************
965 Called when a player chooses the tech goal he wants to research (or when
966 the server chooses it for him automatically).
967 ****************************************************************************/
968 void choose_tech_goal(struct research *presearch, Tech_type_id tech)
970 fc_assert_ret(presearch != NULL);
972 if (tech == presearch->tech_goal) {
973 return;
976 /* It's been suggested that if the research target is empty then
977 * choose_random_tech() should be called here. */
978 presearch->tech_goal = tech;
979 notify_research(presearch, NULL, E_TECH_GOAL, ftc_server,
980 _("Technology goal is %s."),
981 research_advance_name_translation(presearch, tech));
984 /****************************************************************************
985 Initializes tech data for the research.
986 ****************************************************************************/
987 void init_tech(struct research *research, bool update)
989 research_invention_set(research, A_NONE, TECH_KNOWN);
991 advance_index_iterate(A_FIRST, i) {
992 research_invention_set(research, i, TECH_UNKNOWN);
993 } advance_index_iterate_end;
995 #ifdef TECH_UPKEEP_DEBUGGING
996 /* Print a list of the needed upkeep if 'i' techs are researched.
997 * If the ruleset contains self-rooted techs this can not work! */
999 bool global_state[A_LAST];
1000 Tech_type_id tech = A_LAST;
1002 /* Save the game research state. */
1003 advance_index_iterate(A_FIRST, i) {
1004 global_state[i] = game.info.global_advances[i];
1005 } advance_index_iterate_end;
1007 research->techs_researched = 1;
1008 research_update(presearch);
1010 /* Show research costs. */
1011 advance_index_iterate(A_NONE, i) {
1012 log_debug("[research %d] %-25s (ID: %3d) cost: %6d - reachable: %-3s "
1013 "(now) / %-3s (ever)", research_number(research),
1014 advance_rule_name(advance_by_number(i)), i,
1015 research_total_bulbs_required(research, i, FALSE),
1016 research_invention_gettable(research, i, FALSE)
1017 ? "yes" : "no",
1018 research_invention_reachable(research, i) ? "yes" : "no");
1019 } advance_index_iterate_end;
1021 /* Update step for step each tech as known and print the upkeep. */
1022 while (tech != A_NONE) {
1023 tech = A_NONE;
1024 advance_index_iterate(A_FIRST, i) {
1025 if (research_invention_state(research, i) == TECH_PREREQS_KNOWN) {
1026 /* Found a tech which can be researched. */
1027 tech = i;
1028 break;
1030 } advance_index_iterate_end;
1032 if (tech != A_NONE) {
1033 research->inventions[tech].state = TECH_KNOWN;
1034 research->techs_researched++;
1036 /* This will change the game state! */
1037 research_update(research);
1039 research_players_iterate(research, pplayer) {
1040 log_debug("[player %d] researched: %-25s (ID: %4d) techs: %3d "
1041 "upkeep: %4d", research_number(research),
1042 advance_rule_name(advance_by_number(tech)), tech,
1043 research->techs_researched, player_tech_upkeep(pplayer));
1044 } research_players_iterate_end;
1048 /* Reset the changes done. */
1049 advance_index_iterate(A_FIRST, i) {
1050 research_invention_set(research, i, TECH_UNKNOWN);
1051 game.info.global_advances[i] = global_state[i];
1052 } advance_index_iterate_end;
1054 #endif /* TECH_UPKEEP_DEBUGGING */
1056 research->techs_researched = 1;
1058 if (update) {
1059 Tech_type_id next_tech;
1061 /* Mark the reachable techs */
1062 research_update(research);
1064 next_tech = research_goal_step(research, research->tech_goal);
1065 if (A_UNSET != next_tech) {
1066 choose_tech(research, next_tech);
1067 } else {
1068 choose_random_tech(research);
1073 /****************************************************************************
1074 Gives global (read from the game ruleset file) and nation (read from the
1075 nation ruleset files) initial techs as specified in the ruleset, and
1076 random free technologies thanks to the techlevel setting.
1077 ****************************************************************************/
1078 void give_initial_techs(struct research *presearch, int num_random_techs)
1080 int i;
1082 /* Global techs. */
1083 for (i = 0; i < MAX_NUM_TECH_LIST; i++) {
1084 if (game.rgame.global_init_techs[i] == A_LAST) {
1085 break;
1087 /* Maybe the player already got this tech by an other way (e.g. team). */
1088 if (research_invention_state(presearch, game.rgame.global_init_techs[i])
1089 != TECH_KNOWN) {
1090 found_new_tech(presearch, game.rgame.global_init_techs[i],
1091 FALSE, TRUE);
1095 /* Nation techs. */
1096 research_players_iterate(presearch, pplayer) {
1097 const struct nation_type *pnation = nation_of_player(pplayer);
1099 for (i = 0; i < MAX_NUM_TECH_LIST; i++) {
1100 if (pnation->init_techs[i] == A_LAST) {
1101 break;
1103 /* Maybe the player already got this tech by an other way. */
1104 if (research_invention_state(presearch, pnation->init_techs[i])
1105 != TECH_KNOWN) {
1106 found_new_tech(presearch, pnation->init_techs[i], FALSE, TRUE);
1109 } research_players_iterate_end;
1111 /* Random free techs (N.B.: freecost penalty not applied). */
1112 for (i = 0; i < num_random_techs; i++) {
1113 found_new_tech(presearch, pick_random_tech(presearch), FALSE, TRUE);
1117 /****************************************************************************
1118 If victim has a tech which pplayer doesn't have, pplayer will get it.
1119 The clients will both be notified and the conquer cost
1120 penalty applied. Used for diplomats and city conquest.
1121 If preferred is A_UNSET one random tech will be chosen.
1122 Returns the stolen tech or A_NONE if no tech was found.
1123 ****************************************************************************/
1124 Tech_type_id steal_a_tech(struct player *pplayer, struct player *victim,
1125 Tech_type_id preferred)
1127 struct research *presearch, *vresearch;
1128 Tech_type_id stolen_tech = A_NONE;
1129 const char *advance_name;
1130 char research_name[MAX_LEN_NAME * 2];
1132 if (get_player_bonus(victim, EFT_NOT_TECH_SOURCE) > 0) {
1133 return A_NONE;
1136 presearch = research_get(pplayer);
1137 vresearch = research_get(victim);
1139 if (preferred == A_UNSET) {
1140 int j = 0;
1141 advance_index_iterate(A_FIRST, i) {
1142 if (research_invention_gettable(presearch, i,
1143 game.info.tech_steal_allow_holes)
1144 && research_invention_state(presearch, i) != TECH_KNOWN
1145 && research_invention_state(vresearch, i) == TECH_KNOWN) {
1146 j++;
1148 } advance_index_iterate_end;
1150 if (j == 0) {
1151 /* we've moved on to future tech */
1152 if (vresearch->future_tech > presearch->future_tech) {
1153 found_new_tech(presearch, A_FUTURE, FALSE, TRUE);
1154 stolen_tech = A_FUTURE;
1155 } else {
1156 return A_NONE;
1158 } else {
1159 /* pick random tech */
1160 j = fc_rand(j) + 1;
1161 stolen_tech = A_NONE; /* avoid compiler warning */
1162 advance_index_iterate(A_FIRST, i) {
1163 if (research_invention_gettable(presearch, i,
1164 game.info.tech_steal_allow_holes)
1165 && research_invention_state(presearch, i) != TECH_KNOWN
1166 && research_invention_state(vresearch, i) == TECH_KNOWN) {
1167 j--;
1169 if (j == 0) {
1170 stolen_tech = i;
1171 break;
1173 } advance_index_iterate_end;
1174 fc_assert(stolen_tech != A_NONE);
1176 } else { /* preferred != A_UNSET */
1177 #ifndef FREECIV_NDEBUG
1178 if (!is_future_tech(preferred)) {
1179 fc_assert(NULL != valid_advance_by_number(preferred));
1180 fc_assert(TECH_KNOWN == research_invention_state(vresearch,
1181 preferred));
1183 #endif /* FREECIV_NDEBUG */
1184 stolen_tech = preferred;
1187 advance_name = research_advance_name_translation(presearch, stolen_tech);
1188 research_pretty_name(presearch, research_name, sizeof(research_name));
1189 notify_player(pplayer, NULL, E_MY_DIPLOMAT_THEFT, ftc_server,
1190 _("You steal %s from the %s."),
1191 advance_name,
1192 nation_plural_for_player(victim));
1193 notify_research(presearch, pplayer, E_TECH_GAIN, ftc_server,
1194 _("The %s stole %s from the %s and shared it with you."),
1195 nation_plural_for_player(pplayer),
1196 advance_name,
1197 nation_plural_for_player(victim));
1199 notify_player(victim, NULL, E_ENEMY_DIPLOMAT_THEFT, ftc_server,
1200 _("The %s stole %s from you!"),
1201 nation_plural_for_player(pplayer),
1202 advance_name);
1204 notify_research_embassies(presearch, victim, E_TECH_EMBASSY, ftc_server,
1205 _("The %s have stolen %s from the %s."),
1206 research_name,
1207 advance_name,
1208 nation_plural_for_player(victim));
1210 if (tech_transfer(pplayer, victim, stolen_tech)) {
1211 research_apply_penalty(presearch, stolen_tech, game.server.conquercost);
1212 found_new_tech(presearch, stolen_tech, FALSE, TRUE);
1213 script_tech_learned(presearch, pplayer, advance_by_number(stolen_tech),
1214 "stolen");
1215 return stolen_tech;
1218 return A_NONE;
1221 /****************************************************************************
1222 Handle incoming research packet. Need to check correctness
1223 Set the player to be researching the given tech.
1225 If there are enough accumulated research points, the tech may be
1226 acquired immediately.
1227 ****************************************************************************/
1228 void handle_player_research(struct player *pplayer, int tech)
1230 struct research *research = research_get(pplayer);
1232 if (tech != A_FUTURE && !valid_advance_by_number(tech)) {
1233 return;
1236 if (tech != A_FUTURE
1237 && research_invention_state(research, tech) != TECH_PREREQS_KNOWN) {
1238 return;
1241 choose_tech(research, tech);
1243 /* Notify players sharing the same research. */
1244 send_research_info(research, NULL);
1247 /****************************************************************************
1248 Handle incoming player_tech_goal packet
1249 Called from the network or AI code to set the player's tech goal.
1250 ****************************************************************************/
1251 void handle_player_tech_goal(struct player *pplayer, int tech_goal)
1253 struct research *research = research_get(pplayer);
1255 /* Set the tech goal to a defined state if it is
1256 * - not a future tech and not a valid goal
1257 * - not a future tech and not a valid advance
1258 * - not defined
1259 * - known (i.e. due to EFT_GIVE_IMM_TECH). */
1260 if ((tech_goal != A_FUTURE
1261 && (!valid_advance_by_number(tech_goal)
1262 || !research_invention_reachable(research, tech_goal)))
1263 || (tech_goal == A_NONE)
1264 || (TECH_KNOWN == research_invention_state(research, tech_goal))) {
1265 tech_goal = A_UNSET;
1268 choose_tech_goal(research, tech_goal);
1270 /* Notify players sharing the same research. */
1271 send_research_info(research, NULL);
1274 /****************************************************************************
1275 Gives an immediate free tech. Applies freecost. Returns the tech.
1276 ****************************************************************************/
1277 Tech_type_id give_immediate_free_tech(struct research *presearch)
1279 Tech_type_id tech;
1281 if (game.info.free_tech_method == FTM_CHEAPEST) {
1282 tech = pick_cheapest_tech(presearch);
1283 } else if (presearch->researching == A_UNSET
1284 || game.info.free_tech_method == FTM_RANDOM) {
1285 tech = pick_random_tech(presearch);
1286 } else {
1287 tech = presearch->researching;
1289 research_apply_penalty(presearch, tech, game.server.freecost);
1290 found_new_tech(presearch, tech, FALSE, TRUE);
1291 return tech;
1294 /****************************************************************************
1295 Let the player forget one tech.
1296 ****************************************************************************/
1297 static void forget_tech_transfered(struct player *pplayer, Tech_type_id tech)
1299 struct research *presearch = research_get(pplayer);
1301 research_tech_lost(presearch, tech);
1302 /* Make notification after losing the research, in case it is a future
1303 * tech (for getting the right tech number). */
1304 notify_player(pplayer, NULL, E_TECH_LOST, ftc_server,
1305 _("Too bad! You made a mistake transferring the tech %s and "
1306 "lost it."),
1307 research_advance_name_translation(presearch, tech));
1308 notify_research(presearch, pplayer, E_TECH_LOST, ftc_server,
1309 _("Too bad! The %s made a mistake transferring the tech "
1310 "%s and lost it."),
1311 nation_plural_for_player(pplayer),
1312 research_advance_name_translation(presearch, tech));
1315 /****************************************************************************
1316 Check if the tech is lost by the donor or receiver. Returns if the
1317 receiver gets a new tech.
1318 ****************************************************************************/
1319 bool tech_transfer(struct player *plr_recv, struct player *plr_donor,
1320 Tech_type_id tech)
1322 if (game.server.techlost_donor > 0) {
1323 struct research *donor_research = research_get(plr_donor);
1324 bool donor_can_lose = TRUE;
1326 advance_index_iterate(A_FIRST, i) {
1327 /* Never let donor lose tech if it's root_req for some other known
1328 * tech */
1329 if (research_invention_state(donor_research, i) == TECH_KNOWN
1330 && (advance_required(i, AR_ROOT) == tech
1331 || (!game.info.tech_trade_loss_allow_holes
1332 && (advance_required(i, AR_ONE) == tech
1333 || advance_required(i, AR_TWO) == tech)))) {
1334 donor_can_lose = FALSE;
1335 break;
1337 } advance_index_iterate_end;
1338 if (donor_can_lose && fc_rand(100) < game.server.techlost_donor) {
1339 forget_tech_transfered(plr_donor, tech);
1343 if (fc_rand(100) < game.server.techlost_recv) {
1344 forget_tech_transfered(plr_recv, tech);
1345 return FALSE;
1348 return TRUE;