webperimental: killstack decides stack protects.
[freeciv.git] / server / techtools.c
blob389d7f87a9bb62eeb8ff77b3f40d42527208b2ca
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 FREECIV_DEBUG
257 log_verbose("Research nb %d inventions: %s",
258 research_number(presearch),
259 packet->inventions);
260 #endif /* FREECIV_DEBUG */
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 if (!is_future_tech(tech_found)) {
325 #ifndef FREECIV_NDEBUG
326 fc_assert(NULL != vap);
327 fc_assert(TECH_KNOWN != research_invention_state(presearch, tech_found));
328 #endif /* FREECIV_NDEBUG */
330 was_first = (!game.info.global_advances[tech_found]);
333 /* Assign 'advance_name' before we increase the future tech counter. */
334 advance_name = research_advance_name_translation(presearch, tech_found);
336 if (was_first && vap) {
337 /* Alert the owners of any wonders that have been made obsolete */
338 improvement_iterate(pimprove) {
339 requirement_vector_iterate(&pimprove->obsolete_by, pobs) {
340 if (pobs->source.kind == VUT_ADVANCE
341 && pobs->source.value.advance == vap
342 && pobs->range >= REQ_RANGE_WORLD
343 && pobs->survives
344 && is_great_wonder(pimprove)
345 && (pcity = city_from_great_wonder(pimprove))) {
346 notify_player(city_owner(pcity), NULL, E_WONDER_OBSOLETE, ftc_server,
347 _("Discovery of %s OBSOLETES %s in %s!"),
348 research_advance_name_translation
349 (research_get(city_owner(pcity)), tech_found),
350 improvement_name_translation(pimprove),
351 city_link(pcity));
353 } requirement_vector_iterate_end;
354 } improvement_iterate_end;
357 if (was_first
358 && !is_future_tech(tech_found)
359 && advance_has_flag(tech_found, TF_BONUS_TECH)) {
360 bonus_tech_hack = TRUE;
363 /* Memorize some values before the tech is marked as researched.
364 * We will check what has changed later. */
365 players_iterate(aplayer) {
366 i = player_index(aplayer);
368 /* Count EFT_HAVE_EMBASSIES effect for each player. */
369 had_embassies[i] = get_player_bonus(aplayer, EFT_HAVE_EMBASSIES);
371 if (presearch != research_get(aplayer)) {
372 continue;
375 /* Memorize for the players sharing the research what government
376 * they could switch on. */
377 governments_iterate(pgov) {
378 could_switch[i][government_index(pgov)]
379 = can_change_to_government(aplayer, pgov);
380 } governments_iterate_end;
381 } players_iterate_end;
383 /* got_tech allows us to change research without applying techpenalty
384 * (without losing bulbs) */
385 if (tech_found == presearch->researching) {
386 presearch->got_tech = TRUE;
388 presearch->researching_saved = A_UNKNOWN;
389 presearch->techs_researched++;
391 /* Mark the tech as known in the research struct and update
392 * global_advances array. */
393 if (is_future_tech(tech_found)) {
394 presearch->future_tech++;
395 } else {
396 research_invention_set(presearch, tech_found, TECH_KNOWN);
397 research_update(presearch);
400 /* Inform players about their new tech. */
401 send_research_info(presearch, NULL);
403 if (was_first) {
404 /* Inform all players about new global advances to give them a
405 * chance for obsolete buildings. */
406 send_game_info(NULL);
409 /* Make proper changes for all players. Use shuffled order, in case
410 * a script would detect a signal. */
411 shuffled_players_iterate(aplayer) {
412 i = player_index(aplayer);
414 if (presearch == research_get(aplayer)) {
415 /* Only for players sharing the research. */
416 remove_obsolete_buildings(aplayer);
418 /* Give free infrastructure in every city */
419 if (tech_found != A_FUTURE) {
420 upgrade_all_city_extras(aplayer, was_discovery);
422 /* Revealing of extras with visibility_req */
423 whole_map_iterate(&(wld.map),ptile) {
424 if (map_is_known_and_seen(ptile, aplayer, V_MAIN)) {
425 if (update_player_tile_knowledge(aplayer, ptile)) {
426 send_tile_info(aplayer->connections, ptile, FALSE);
429 } whole_map_iterate_end;
432 /* Enhance vision of units if a player-ranged effect has changed. Note
433 * that world-ranged effects will not be updated immediately. */
434 unit_list_refresh_vision(aplayer->units);
436 /* Notify a player about new governments available */
437 governments_iterate(pgov) {
438 if (!could_switch[i][government_index(pgov)]
439 && can_change_to_government(aplayer, pgov)) {
440 notify_player(aplayer, NULL, E_NEW_GOVERNMENT, ftc_server,
441 _("Discovery of %s makes the government form %s"
442 " available. You may want to start a revolution."),
443 advance_name,
444 government_name_translation(pgov));
446 } governments_iterate_end;
449 /* For any player. */
450 /* Update all cities in case the tech changed some effects. This is
451 * inefficient; it could be optimized if it's found to be a problem.
452 * But techs aren't researched that often. */
453 city_list_iterate(aplayer->cities, apcity) {
454 /* Refresh the city data; this also updates the squared city radius. */
455 city_refresh(apcity);
456 city_refresh_vision(apcity);
457 send_city_info(aplayer, apcity);
458 } city_list_iterate_end;
460 /* Send all player an updated info of the owner of the Marco Polo
461 * Wonder if this wonder has become obsolete. */
462 if (0 < had_embassies[i]
463 && 0 <= get_player_bonus(aplayer, EFT_HAVE_EMBASSIES)) {
464 send_player_all_c(aplayer, aplayer->connections);
465 players_iterate(pother_player) {
466 if (aplayer != pother_player) {
467 send_player_all_c(aplayer, pother_player->connections);
468 send_player_all_c(pother_player, aplayer->connections);
470 } players_iterate_end;
472 } shuffled_players_iterate_end;
474 if (tech_found == presearch->tech_goal) {
475 presearch->tech_goal = A_UNSET;
478 if (tech_found == presearch->researching) {
479 /* Try to pick new tech to research. */
480 Tech_type_id next_tech = research_goal_step(presearch,
481 presearch->tech_goal);
483 /* As this function can be recursive, we need to print the messages
484 * before really picking the new technology. */
485 if (A_UNSET != next_tech) {
486 notify_research(presearch, NULL, E_TECH_LEARNED, ftc_server,
487 _("Learned %s. Our scientists focus on %s; "
488 "goal is %s."),
489 advance_name,
490 research_advance_name_translation(presearch,
491 next_tech),
492 research_advance_name_translation
493 (presearch, presearch->tech_goal));
494 } else {
495 if (is_future_tech(tech_found)) {
496 /* Continue researching future tech. */
497 next_tech = A_FUTURE;
498 } else {
499 /* If there is at least one AI player still alive, then pick
500 * a random tech, else keep A_UNSET. */
501 research_players_iterate(presearch, aplayer) {
502 if (is_ai(aplayer)) {
503 next_tech = pick_random_tech(presearch);
504 break;
506 } research_players_iterate_end;
509 if (A_UNSET == next_tech) {
510 notify_research(presearch, NULL, E_TECH_LEARNED, ftc_server,
511 _("Learned %s. Scientists "
512 "do not know what to research next."),
513 advance_name);
514 } else {
515 notify_research(presearch, NULL, E_TECH_LEARNED, ftc_server,
516 _("Learned %s. Scientists choose to research %s."),
517 advance_name,
518 research_advance_name_translation(presearch,
519 next_tech));
523 if (A_UNSET != next_tech) {
524 choose_tech(presearch, next_tech);
525 } else {
526 presearch->researching = A_UNSET;
530 if (!saving_bulbs && presearch->bulbs_researched > 0) {
531 presearch->bulbs_researched = 0;
534 if (bonus_tech_hack) {
535 Tech_type_id additional_tech;
536 char research_name[MAX_LEN_NAME * 2];
537 const char *radv_name;
539 research_pretty_name(presearch, research_name, sizeof(research_name));
541 additional_tech = give_immediate_free_tech(presearch);
543 radv_name = research_advance_name_translation(presearch, additional_tech);
545 if (advance_by_number(tech_found)->bonus_message != NULL
546 && additional_tech != A_UNSET) {
547 notify_research(presearch, NULL, E_TECH_GAIN, ftc_server,
548 _(advance_by_number(tech_found)->bonus_message),
549 radv_name);
550 } else if (additional_tech != A_UNSET) {
551 /* FIXME: "your" when it was just civilization of one of the players
552 * sharing the reseach. */
553 notify_research(presearch, NULL, E_TECH_GAIN, ftc_server,
554 _("Great scientists from all the "
555 "world join your civilization: you learn "
556 "%s immediately."), radv_name);
558 /* TODO: Ruleset should be able to customize this message too */
559 notify_research_embassies(presearch, NULL, E_TECH_EMBASSY, ftc_server,
560 _("%s acquire %s as a result of learning %s."),
561 research_name, radv_name, advance_name);
565 /****************************************************************************
566 Is player about to lose tech?
567 ****************************************************************************/
568 static bool lose_tech(struct research *research)
570 if (game.server.techloss_forgiveness < 0) {
571 /* Tech loss disabled */
572 return FALSE;
575 if (research->techs_researched == 0) {
576 /* No tech to lose */
577 fc_assert(research->future_tech == 0);
578 return FALSE;
581 if (research->bulbs_researched <
582 (-research_total_bulbs_required(research, research->researching, FALSE)
583 * game.server.techloss_forgiveness / 100)) {
584 return TRUE;
587 return FALSE;
590 /****************************************************************************
591 Adds the given number of bulbs into the player's tech and (if necessary and
592 'check_tech' is TRUE) completes the research. If the total number of bulbs
593 is negative due to tech upkeep, one (randomly chosen) tech is lost.
595 The caller is responsible for sending updated player information.
597 This is called from each city every turn, from caravan revenue, and at the
598 end of the phase.
599 ****************************************************************************/
600 void update_bulbs(struct player *pplayer, int bulbs, bool check_tech)
602 struct research *research = research_get(pplayer);
604 if (!pplayer->is_alive) {
605 /* Dead players do not produce research */
606 return;
609 /* count our research contribution this turn */
610 pplayer->server.bulbs_last_turn += bulbs;
611 research->bulbs_researched += bulbs;
613 do {
614 /* If we have a negative number of bulbs we do try to:
615 * - reduce the number of future techs;
616 * - or lose one random tech.
617 * After that the number of bulbs available is incresed based on the
618 * value of the lost tech. */
619 if (lose_tech(research)) {
620 Tech_type_id tech = (research->future_tech > 0
621 ? A_FUTURE : pick_random_tech_to_lose(research));
623 if (tech != A_NONE) {
624 if (game.server.techloss_restore >= 0) {
625 research->bulbs_researched +=
626 (research_total_bulbs_required(research, tech, TRUE)
627 * game.server.techloss_restore / 100);
628 } else {
629 research->bulbs_researched = 0;
631 research->researching_saved = A_UNKNOWN;
633 log_debug("%s: tech loss (%s)",
634 research_rule_name(research),
635 (is_future_tech(tech) ? "Future Tech"
636 : research_advance_rule_name(research, tech)));
637 research_tech_lost(research, tech);
638 /* Make notification after losing the research, in case it is
639 * a future tech (for getting the right tech number). */
640 notify_research(research, NULL, E_TECH_LOST, ftc_server,
641 _("Insufficient science output. We lost %s."),
642 research_advance_name_translation(research, tech));
646 /* Check for finished research. */
647 if (!check_tech
648 || research->researching == A_UNSET
649 || (research->bulbs_researched
650 < research_total_bulbs_required(research, research->researching,
651 FALSE))) {
652 break;
655 tech_researched(research);
656 } while (research->researching != A_UNSET);
659 /****************************************************************************
660 Choose a random tech for player to lose.
661 ****************************************************************************/
662 static Tech_type_id
663 pick_random_tech_to_lose(const struct research *presearch)
665 bv_techs eligible_techs;
666 /* A_NONE included in advance_count(). */
667 int eligible = advance_count() - 1;
668 int chosen;
670 BV_SET_ALL(eligible_techs);
672 advance_index_iterate(A_FIRST, i) {
673 if (research_invention_state(presearch, i) != TECH_KNOWN) {
674 if (BV_ISSET(eligible_techs, i)) {
675 eligible--;
676 BV_CLR(eligible_techs, i);
678 } else {
679 /* Knowing this tech may make others ineligible */
680 Tech_type_id root = advance_required(i, AR_ROOT);
681 /* Never lose techs that are root_req for a currently known tech
682 * (including self root_req) */
683 if (root != A_NONE && BV_ISSET(eligible_techs, root)) {
684 eligible--;
685 BV_CLR(eligible_techs, root);
687 if (!game.info.tech_loss_allow_holes) {
688 /* Ruleset can prevent this kind of tech loss from opening up
689 * holes in the tech tree */
690 Tech_type_id prereq;
691 prereq = advance_required(i, AR_ONE);
692 if (prereq != A_NONE && BV_ISSET(eligible_techs, prereq)) {
693 eligible--;
694 BV_CLR(eligible_techs, prereq);
696 prereq = advance_required(i, AR_TWO);
697 if (prereq != A_NONE && BV_ISSET(eligible_techs, prereq)) {
698 eligible--;
699 BV_CLR(eligible_techs, prereq);
703 } advance_index_iterate_end;
705 if (eligible == 0) {
706 /* no researched technology at all */
707 return A_NONE;
710 chosen = fc_rand(eligible) + 1;
712 advance_index_iterate(A_FIRST, i) {
713 if (BV_ISSET(eligible_techs, i)) {
714 chosen--;
715 if (chosen == 0) {
716 return i;
719 } advance_index_iterate_end;
721 /* should never be reached */
722 fc_assert_msg(chosen == 0, "internal error (eligible=%d, chosen=%d)",
723 eligible, chosen);
724 return A_NONE;
727 /****************************************************************************
728 Helper for research_tech_lost().
729 ****************************************************************************/
730 static inline struct government *
731 pick_random_government(struct player *pplayer)
733 struct government *picked = NULL;
734 int gov_nb = 0;
736 governments_iterate(pgov) {
737 if (can_change_to_government(pplayer, pgov) && 0 == fc_rand(++gov_nb)) {
738 picked = pgov;
740 } governments_iterate_end;
741 fc_assert(NULL != picked);
742 return picked;
745 /****************************************************************************
746 Remove one tech from the research.
747 ****************************************************************************/
748 static void research_tech_lost(struct research *presearch, Tech_type_id tech)
750 char research_name[MAX_LEN_NAME * 2];
751 /* Research members will be notified when new tech is chosen. */
753 research_pretty_name(presearch, research_name, sizeof(research_name));
755 presearch->techs_researched--;
756 if (is_future_tech(tech)) {
757 presearch->future_tech--;
758 research_update(presearch);
759 /* Notify after decreasing the future tech counter, to get the right
760 * tech number in the message. */
761 notify_research_embassies(presearch, NULL, E_TECH_EMBASSY, ftc_server,
762 _("The %s have lost %s."),
763 research_name,
764 research_advance_name_translation(presearch,
765 tech));
766 /* Inform players about their technology loss. */
767 send_research_info(presearch, NULL);
768 return;
771 fc_assert_ret(valid_advance_by_number(tech));
772 notify_research_embassies(presearch, NULL, E_TECH_EMBASSY, ftc_server,
773 /* TRANS: technology loss */
774 _("The %s have lost %s."),
775 research_name,
776 research_advance_name_translation(presearch,
777 tech));
779 /* Remove technology. */
780 research_invention_set(presearch, tech, TECH_UNKNOWN);
781 research_update(presearch);
782 log_debug("%s lost tech id %d (%s)", research_rule_name(presearch), tech,
783 advance_rule_name(advance_by_number(tech)));
785 /* Inform players about their technology loss. */
786 send_research_info(presearch, NULL);
788 research_players_iterate(presearch, pplayer) {
789 /* Check government. */
790 if (!can_change_to_government(pplayer, government_of_player(pplayer))) {
791 /* Lost the technology for the government; switch to random
792 * available government. */
793 struct government *pgov = pick_random_government(pplayer);
795 notify_player(pplayer, NULL, E_NEW_GOVERNMENT, ftc_server,
796 _("The required technology for our government '%s' "
797 "was lost. The citizens have started a "
798 "revolution into '%s'."),
799 government_name_translation(government_of_player
800 (pplayer)),
801 government_name_translation(pgov));
802 handle_player_change_government(pplayer, government_number(pgov));
803 send_player_info_c(pplayer, NULL);
804 } else if (NULL != pplayer->target_government
805 && !can_change_to_government(pplayer,
806 pplayer->target_government)) {
807 /* Lost the technology for the target government; use a random
808 * available government as new target government. */
809 struct government *pgov = pick_random_government(pplayer);
811 notify_player(pplayer, NULL, E_NEW_GOVERNMENT, ftc_server,
812 _("The required technology for our new government "
813 "'%s' was lost. The citizens chose '%s' as new "
814 "target government."),
815 government_name_translation(pplayer->target_government),
816 government_name_translation(pgov));
817 pplayer->target_government = pgov;
818 send_player_info_c(pplayer, pplayer->connections);
821 /* Check all units for valid activities. */
822 unit_list_iterate(pplayer->units, punit) {
823 if (!can_unit_continue_current_activity(punit)) {
824 log_debug("lost technology for activity of unit %s of %s (%d, %d)",
825 unit_name_translation(punit), player_name(pplayer),
826 TILE_XY(unit_tile(punit)));
827 set_unit_activity(punit, ACTIVITY_IDLE);
828 send_unit_info(NULL, punit);
830 } unit_list_iterate_end;
832 /* Check city production */
833 city_list_iterate(pplayer->cities, pcity) {
834 bool update = FALSE;
836 if (pcity->production.kind == VUT_UTYPE
837 && !can_city_build_unit_now(pcity, pcity->production.value.utype)) {
838 notify_player(pplayer, city_tile(pcity),
839 E_CITY_CANTBUILD, ftc_server,
840 _("%s can't build %s. The required technology was "
841 "lost."),
842 city_link(pcity),
843 utype_name_translation(pcity->production.value.utype));
844 choose_build_target(pplayer, pcity);
845 update = TRUE;
848 if (pcity->production.kind == VUT_IMPROVEMENT
849 && !can_city_build_improvement_now(pcity,
850 pcity->production.value.building)) {
851 notify_player(pplayer, city_tile(pcity),
852 E_CITY_CANTBUILD, ftc_server,
853 _("%s can't build %s. The required technology was "
854 "lost."),
855 city_link(pcity),
856 improvement_name_translation
857 (pcity->production.value.building));
858 choose_build_target(pplayer, pcity);
859 update = TRUE;
862 if (update) {
863 city_refresh(pcity);
864 send_city_info(pplayer, pcity);
866 } city_list_iterate_end;
867 } research_players_iterate_end;
870 /****************************************************************************
871 Returns random researchable tech or A_FUTURE. No side effects.
872 ****************************************************************************/
873 Tech_type_id pick_random_tech(const struct research *presearch)
875 Tech_type_id tech = A_FUTURE;
876 int num_techs = 0;
878 advance_index_iterate(A_FIRST, i) {
879 if (research_invention_state(presearch, i) == TECH_PREREQS_KNOWN) {
880 if (fc_rand(++num_techs) == 0) {
881 tech = i;
884 } advance_index_iterate_end;
885 return tech;
888 /****************************************************************************
889 Returns cheapest researchable tech, random among equal cost ones.
890 ****************************************************************************/
891 Tech_type_id pick_cheapest_tech(const struct research *presearch)
893 int cheapest_cost = -1;
894 int cheapest_amount = 0;
895 Tech_type_id cheapest = A_FUTURE; /* If no real tech is found to be missing */
897 advance_index_iterate(A_FIRST, i) {
898 if (research_invention_state(presearch, i) == TECH_PREREQS_KNOWN) {
899 int cost = research_total_bulbs_required(presearch, i, FALSE);
901 if (cost < cheapest_cost || cheapest_cost == -1) {
902 cheapest_cost = cost;
903 cheapest_amount = 1;
904 cheapest = i;
905 } else if (cost == cheapest_cost && fc_rand(++cheapest_amount) == 0) {
906 cheapest = i;
909 } advance_index_iterate_end;
911 return cheapest;
914 /****************************************************************************
915 Finds and chooses (sets) a random research target from among all those
916 available until presearch->researching != A_UNSET.
917 Players may research more than one tech in this function.
918 Possible reasons:
919 - techpenalty < 100;
920 - research.got_tech = TRUE and enough bulbs was saved;
921 - research.researching = A_UNSET and enough bulbs was saved.
922 ****************************************************************************/
923 void choose_random_tech(struct research *research)
925 do {
926 choose_tech(research, pick_random_tech(research));
927 } while (research->researching == A_UNSET);
930 /****************************************************************************
931 Called when a player chooses the tech he wants to research (or when
932 the server chooses it for him automatically).
934 This takes care of all side effects so the research target probably
935 shouldn't be changed outside of this function (doing so has been the
936 cause of several bugs).
937 ****************************************************************************/
938 void choose_tech(struct research *research, Tech_type_id tech)
940 if (is_future_tech(tech)) {
941 if (is_future_tech(research->researching)
942 && (research->bulbs_researched
943 >= research_total_bulbs_required(research, tech, FALSE))) {
944 tech_researched(research);
946 } else {
947 if (research->researching == tech) {
948 return;
950 if (research_invention_state(research, tech) != TECH_PREREQS_KNOWN) {
951 /* Can't research this. */
952 return;
955 if (!research->got_tech && research->researching_saved == A_UNKNOWN) {
956 research->bulbs_researching_saved = research->bulbs_researched;
957 research->researching_saved = research->researching;
958 /* Subtract a penalty because we changed subject. */
959 if (research->bulbs_researched > 0) {
960 research->bulbs_researched
961 -= ((research->bulbs_researched * game.server.techpenalty) / 100);
962 fc_assert(research->bulbs_researched >= 0);
964 } else if (tech == research->researching_saved) {
965 research->bulbs_researched = research->bulbs_researching_saved;
966 research->researching_saved = A_UNKNOWN;
968 research->researching = tech;
969 if (research->bulbs_researched
970 >= research_total_bulbs_required(research, tech, FALSE)) {
971 tech_researched(research);
975 /****************************************************************************
976 Called when a player chooses the tech goal he wants to research (or when
977 the server chooses it for him automatically).
978 ****************************************************************************/
979 void choose_tech_goal(struct research *presearch, Tech_type_id tech)
981 fc_assert_ret(presearch != NULL);
983 if (tech == presearch->tech_goal) {
984 return;
987 /* It's been suggested that if the research target is empty then
988 * choose_random_tech() should be called here. */
989 presearch->tech_goal = tech;
990 notify_research(presearch, NULL, E_TECH_GOAL, ftc_server,
991 _("Technology goal is %s."),
992 research_advance_name_translation(presearch, tech));
995 /****************************************************************************
996 Initializes tech data for the research.
997 ****************************************************************************/
998 void init_tech(struct research *research, bool update)
1000 research_invention_set(research, A_NONE, TECH_KNOWN);
1002 advance_index_iterate(A_FIRST, i) {
1003 research_invention_set(research, i, TECH_UNKNOWN);
1004 } advance_index_iterate_end;
1006 #ifdef TECH_UPKEEP_DEBUGGING
1007 /* Print a list of the needed upkeep if 'i' techs are researched.
1008 * If the ruleset contains self-rooted techs this can not work! */
1010 bool global_state[A_LAST];
1011 Tech_type_id tech = A_LAST;
1013 /* Save the game research state. */
1014 advance_index_iterate(A_FIRST, i) {
1015 global_state[i] = game.info.global_advances[i];
1016 } advance_index_iterate_end;
1018 research->techs_researched = 1;
1019 research_update(presearch);
1021 /* Show research costs. */
1022 advance_index_iterate(A_NONE, i) {
1023 log_debug("[research %d] %-25s (ID: %3d) cost: %6d - reachable: %-3s "
1024 "(now) / %-3s (ever)", research_number(research),
1025 advance_rule_name(advance_by_number(i)), i,
1026 research_total_bulbs_required(research, i, FALSE),
1027 research_invention_gettable(research, i, FALSE)
1028 ? "yes" : "no",
1029 research_invention_reachable(research, i) ? "yes" : "no");
1030 } advance_index_iterate_end;
1032 /* Update step for step each tech as known and print the upkeep. */
1033 while (tech != A_NONE) {
1034 tech = A_NONE;
1035 advance_index_iterate(A_FIRST, i) {
1036 if (research_invention_state(research, i) == TECH_PREREQS_KNOWN) {
1037 /* Found a tech which can be researched. */
1038 tech = i;
1039 break;
1041 } advance_index_iterate_end;
1043 if (tech != A_NONE) {
1044 research->inventions[tech].state = TECH_KNOWN;
1045 research->techs_researched++;
1047 /* This will change the game state! */
1048 research_update(research);
1050 research_players_iterate(research, pplayer) {
1051 log_debug("[player %d] researched: %-25s (ID: %4d) techs: %3d "
1052 "upkeep: %4d", research_number(research),
1053 advance_rule_name(advance_by_number(tech)), tech,
1054 research->techs_researched, player_tech_upkeep(pplayer));
1055 } research_players_iterate_end;
1059 /* Reset the changes done. */
1060 advance_index_iterate(A_FIRST, i) {
1061 research_invention_set(research, i, TECH_UNKNOWN);
1062 game.info.global_advances[i] = global_state[i];
1063 } advance_index_iterate_end;
1065 #endif /* TECH_UPKEEP_DEBUGGING */
1067 research->techs_researched = 1;
1069 if (update) {
1070 Tech_type_id next_tech;
1072 /* Mark the reachable techs */
1073 research_update(research);
1075 next_tech = research_goal_step(research, research->tech_goal);
1076 if (A_UNSET != next_tech) {
1077 choose_tech(research, next_tech);
1078 } else {
1079 choose_random_tech(research);
1084 /****************************************************************************
1085 Gives global (read from the game ruleset file) and nation (read from the
1086 nation ruleset files) initial techs as specified in the ruleset, and
1087 random free technologies thanks to the techlevel setting.
1088 ****************************************************************************/
1089 void give_initial_techs(struct research *presearch, int num_random_techs)
1091 int i;
1093 /* Global techs. */
1094 for (i = 0; i < MAX_NUM_TECH_LIST; i++) {
1095 if (game.rgame.global_init_techs[i] == A_LAST) {
1096 break;
1098 /* Maybe the player already got this tech by an other way (e.g. team). */
1099 if (research_invention_state(presearch, game.rgame.global_init_techs[i])
1100 != TECH_KNOWN) {
1101 found_new_tech(presearch, game.rgame.global_init_techs[i],
1102 FALSE, TRUE);
1106 /* Nation techs. */
1107 research_players_iterate(presearch, pplayer) {
1108 const struct nation_type *pnation = nation_of_player(pplayer);
1110 for (i = 0; i < MAX_NUM_TECH_LIST; i++) {
1111 if (pnation->init_techs[i] == A_LAST) {
1112 break;
1114 /* Maybe the player already got this tech by an other way. */
1115 if (research_invention_state(presearch, pnation->init_techs[i])
1116 != TECH_KNOWN) {
1117 found_new_tech(presearch, pnation->init_techs[i], FALSE, TRUE);
1120 } research_players_iterate_end;
1122 /* Random free techs (N.B.: freecost penalty not applied). */
1123 for (i = 0; i < num_random_techs; i++) {
1124 found_new_tech(presearch, pick_random_tech(presearch), FALSE, TRUE);
1128 /****************************************************************************
1129 If victim has a tech which pplayer doesn't have, pplayer will get it.
1130 The clients will both be notified and the conquer cost
1131 penalty applied. Used for diplomats and city conquest.
1132 If preferred is A_UNSET one random tech will be chosen.
1133 Returns the stolen tech or A_NONE if no tech was found.
1134 ****************************************************************************/
1135 Tech_type_id steal_a_tech(struct player *pplayer, struct player *victim,
1136 Tech_type_id preferred)
1138 struct research *presearch, *vresearch;
1139 Tech_type_id stolen_tech = A_NONE;
1140 const char *advance_name;
1141 char research_name[MAX_LEN_NAME * 2];
1143 if (get_player_bonus(victim, EFT_NOT_TECH_SOURCE) > 0) {
1144 return A_NONE;
1147 presearch = research_get(pplayer);
1148 vresearch = research_get(victim);
1150 if (preferred == A_UNSET) {
1151 int j = 0;
1152 advance_index_iterate(A_FIRST, i) {
1153 if (research_invention_gettable(presearch, i,
1154 game.info.tech_steal_allow_holes)
1155 && research_invention_state(presearch, i) != TECH_KNOWN
1156 && research_invention_state(vresearch, i) == TECH_KNOWN) {
1157 j++;
1159 } advance_index_iterate_end;
1161 if (j == 0) {
1162 /* we've moved on to future tech */
1163 if (vresearch->future_tech > presearch->future_tech) {
1164 found_new_tech(presearch, A_FUTURE, FALSE, TRUE);
1165 stolen_tech = A_FUTURE;
1166 } else {
1167 return A_NONE;
1169 } else {
1170 /* pick random tech */
1171 j = fc_rand(j) + 1;
1172 stolen_tech = A_NONE; /* avoid compiler warning */
1173 advance_index_iterate(A_FIRST, i) {
1174 if (research_invention_gettable(presearch, i,
1175 game.info.tech_steal_allow_holes)
1176 && research_invention_state(presearch, i) != TECH_KNOWN
1177 && research_invention_state(vresearch, i) == TECH_KNOWN) {
1178 j--;
1180 if (j == 0) {
1181 stolen_tech = i;
1182 break;
1184 } advance_index_iterate_end;
1185 fc_assert(stolen_tech != A_NONE);
1187 } else { /* preferred != A_UNSET */
1188 #ifndef FREECIV_NDEBUG
1189 if (!is_future_tech(preferred)) {
1190 fc_assert(NULL != valid_advance_by_number(preferred));
1191 fc_assert(TECH_KNOWN == research_invention_state(vresearch,
1192 preferred));
1194 #endif /* FREECIV_NDEBUG */
1195 stolen_tech = preferred;
1198 advance_name = research_advance_name_translation(presearch, stolen_tech);
1199 research_pretty_name(presearch, research_name, sizeof(research_name));
1200 notify_player(pplayer, NULL, E_MY_DIPLOMAT_THEFT, ftc_server,
1201 _("You steal %s from the %s."),
1202 advance_name,
1203 nation_plural_for_player(victim));
1204 notify_research(presearch, pplayer, E_TECH_GAIN, ftc_server,
1205 _("The %s stole %s from the %s and shared it with you."),
1206 nation_plural_for_player(pplayer),
1207 advance_name,
1208 nation_plural_for_player(victim));
1210 notify_player(victim, NULL, E_ENEMY_DIPLOMAT_THEFT, ftc_server,
1211 _("The %s stole %s from you!"),
1212 nation_plural_for_player(pplayer),
1213 advance_name);
1215 notify_research_embassies(presearch, victim, E_TECH_EMBASSY, ftc_server,
1216 _("The %s have stolen %s from the %s."),
1217 research_name,
1218 advance_name,
1219 nation_plural_for_player(victim));
1221 if (tech_transfer(pplayer, victim, stolen_tech)) {
1222 research_apply_penalty(presearch, stolen_tech, game.server.conquercost);
1223 found_new_tech(presearch, stolen_tech, FALSE, TRUE);
1224 script_tech_learned(presearch, pplayer, advance_by_number(stolen_tech),
1225 "stolen");
1226 return stolen_tech;
1229 return A_NONE;
1232 /****************************************************************************
1233 Handle incoming research packet. Need to check correctness
1234 Set the player to be researching the given tech.
1236 If there are enough accumulated research points, the tech may be
1237 acquired immediately.
1238 ****************************************************************************/
1239 void handle_player_research(struct player *pplayer, int tech)
1241 struct research *research = research_get(pplayer);
1243 if (tech != A_FUTURE && !valid_advance_by_number(tech)) {
1244 return;
1247 if (tech != A_FUTURE
1248 && research_invention_state(research, tech) != TECH_PREREQS_KNOWN) {
1249 return;
1252 choose_tech(research, tech);
1254 /* Notify players sharing the same research. */
1255 send_research_info(research, NULL);
1258 /****************************************************************************
1259 Handle incoming player_tech_goal packet
1260 Called from the network or AI code to set the player's tech goal.
1261 ****************************************************************************/
1262 void handle_player_tech_goal(struct player *pplayer, int tech_goal)
1264 struct research *research = research_get(pplayer);
1266 /* Set the tech goal to a defined state if it is
1267 * - not a future tech and not a valid goal
1268 * - not a future tech and not a valid advance
1269 * - not defined
1270 * - known (i.e. due to EFT_GIVE_IMM_TECH). */
1271 if ((tech_goal != A_FUTURE
1272 && (!valid_advance_by_number(tech_goal)
1273 || !research_invention_reachable(research, tech_goal)))
1274 || (tech_goal == A_NONE)
1275 || (TECH_KNOWN == research_invention_state(research, tech_goal))) {
1276 tech_goal = A_UNSET;
1279 choose_tech_goal(research, tech_goal);
1281 /* Notify players sharing the same research. */
1282 send_research_info(research, NULL);
1285 /****************************************************************************
1286 Gives an immediate free tech. Applies freecost. Returns the tech.
1287 ****************************************************************************/
1288 Tech_type_id give_immediate_free_tech(struct research *presearch)
1290 Tech_type_id tech;
1292 if (game.info.free_tech_method == FTM_CHEAPEST) {
1293 tech = pick_cheapest_tech(presearch);
1294 } else if (presearch->researching == A_UNSET
1295 || game.info.free_tech_method == FTM_RANDOM) {
1296 tech = pick_random_tech(presearch);
1297 } else {
1298 tech = presearch->researching;
1300 research_apply_penalty(presearch, tech, game.server.freecost);
1301 found_new_tech(presearch, tech, FALSE, TRUE);
1302 return tech;
1305 /****************************************************************************
1306 Let the player forget one tech.
1307 ****************************************************************************/
1308 static void forget_tech_transfered(struct player *pplayer, Tech_type_id tech)
1310 struct research *presearch = research_get(pplayer);
1312 research_tech_lost(presearch, tech);
1313 /* Make notification after losing the research, in case it is a future
1314 * tech (for getting the right tech number). */
1315 notify_player(pplayer, NULL, E_TECH_LOST, ftc_server,
1316 _("Too bad! You made a mistake transferring the tech %s and "
1317 "lost it."),
1318 research_advance_name_translation(presearch, tech));
1319 notify_research(presearch, pplayer, E_TECH_LOST, ftc_server,
1320 _("Too bad! The %s made a mistake transferring the tech "
1321 "%s and lost it."),
1322 nation_plural_for_player(pplayer),
1323 research_advance_name_translation(presearch, tech));
1326 /****************************************************************************
1327 Check if the tech is lost by the donor or receiver. Returns if the
1328 receiver gets a new tech.
1329 ****************************************************************************/
1330 bool tech_transfer(struct player *plr_recv, struct player *plr_donor,
1331 Tech_type_id tech)
1333 if (game.server.techlost_donor > 0) {
1334 struct research *donor_research = research_get(plr_donor);
1335 bool donor_can_lose = TRUE;
1337 advance_index_iterate(A_FIRST, i) {
1338 /* Never let donor lose tech if it's root_req for some other known
1339 * tech */
1340 if (research_invention_state(donor_research, i) == TECH_KNOWN
1341 && (advance_required(i, AR_ROOT) == tech
1342 || (!game.info.tech_trade_loss_allow_holes
1343 && (advance_required(i, AR_ONE) == tech
1344 || advance_required(i, AR_TWO) == tech)))) {
1345 donor_can_lose = FALSE;
1346 break;
1348 } advance_index_iterate_end;
1349 if (donor_can_lose && fc_rand(100) < game.server.techlost_donor) {
1350 forget_tech_transfered(plr_donor, tech);
1354 if (fc_rand(100) < game.server.techlost_recv) {
1355 forget_tech_transfered(plr_recv, tech);
1356 return FALSE;
1359 return TRUE;