Freeciv21
Develop your civilization from humble roots to a global empire
spacerace.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 // utility
15 #include "fcintl.h"
16 #include "shared.h"
17 
18 // common
19 #include "calendar.h"
20 #include "events.h"
21 #include "game.h"
22 #include "nation.h"
23 #include "packets.h"
24 #include "spaceship.h"
25 
26 // server
27 #include "notify.h"
28 #include "plrhand.h"
29 #include "spacerace.h"
30 
38 {
39  int i;
40  // these are how many are connected:
41  int fuel = 0;
42  int propulsion = 0;
43  int habitation = 0;
44  int life_support = 0;
45  int solar_panels = 0;
46 
50 
51  ship->mass = 0;
52  ship->support_rate = ship->energy_rate = ship->success_rate =
53  ship->travel_time = 0.0;
54 
55  for (i = 0; i < NUM_SS_STRUCTURALS; i++) {
56  if (BV_ISSET(ship->structure, i)) {
57  ship->mass += (i < 6) ? 200 : 100;
58  /* s0 to s3 are heavier; actually in Civ1 its a bit stranger
59  than this, but not worth figuring out --dwp */
60  }
61  }
62  for (i = 0; i < ship->fuel; i++) {
63  if (BV_ISSET(ship->structure, components_info[i * 2].required)) {
64  fuel++;
65  }
66  }
67  for (i = 0; i < ship->propulsion; i++) {
68  if (BV_ISSET(ship->structure, components_info[i * 2 + 1].required)) {
69  propulsion++;
70  }
71  }
72  for (i = 0; i < ship->habitation; i++) {
73  if (BV_ISSET(ship->structure, modules_info[i * 3].required)) {
74  habitation++;
75  }
76  }
77  for (i = 0; i < ship->life_support; i++) {
78  if (BV_ISSET(ship->structure, modules_info[i * 3 + 1].required)) {
79  life_support++;
80  }
81  }
82  for (i = 0; i < ship->solar_panels; i++) {
83  if (BV_ISSET(ship->structure, modules_info[i * 3 + 2].required)) {
84  solar_panels++;
85  }
86  }
87 
88  ship->mass += 1600 * (habitation + life_support)
89  + 400 * (solar_panels + propulsion + fuel);
90 
91  ship->population = habitation * 10000;
92 
93  if (habitation > 0) {
94  ship->support_rate = life_support / static_cast<double>(habitation);
95  }
96  if (life_support + habitation > 0) {
97  ship->energy_rate =
98  2.0 * solar_panels / static_cast<double>(life_support + habitation);
99  }
100  if (fuel > 0 && propulsion > 0) {
101  ship->success_rate =
102  MIN(ship->support_rate, 1.0) * MIN(ship->energy_rate, 1.0);
103  }
104 
105  /* The Success% can be less by up to a few % in some cases
106  (I think if P != F or if P and/or F too small (eg <= 2?) ?)
107  but probably not worth worrying about.
108  Actually, the Civ1 manual suggests travel time is relevant. --dwp
109  */
110 
111  ship->travel_time = ship->mass * game.server.spaceship_travel_time / 100
112  / (200.0 * MIN(propulsion, fuel) + 20.0);
113 }
114 
120 void send_spaceship_info(struct player *src, struct conn_list *dest)
121 {
122  if (!dest) {
123  dest = game.est_connections;
124  }
125 
126  players_iterate(pplayer)
127  {
128  if (!src || pplayer == src) {
129  struct packet_spaceship_info info;
130  struct player_spaceship *ship = &pplayer->spaceship;
131 
132  info.player_num = player_number(pplayer);
133  info.sship_state = ship->state;
134  info.structurals = ship->structurals;
135  info.components = ship->components;
136  info.modules = ship->modules;
137  info.fuel = ship->fuel;
138  info.propulsion = ship->propulsion;
139  info.habitation = ship->habitation;
140  info.life_support = ship->life_support;
141  info.solar_panels = ship->solar_panels;
142  info.launch_year = ship->launch_year;
143  info.population = ship->population;
144  info.mass = ship->mass;
145  info.support_rate = ship->support_rate;
146  info.energy_rate = ship->energy_rate;
147  info.success_rate = ship->success_rate;
148  info.travel_time = ship->travel_time;
149  info.structure = ship->structure;
150 
151  lsend_packet_spaceship_info(dest, &info);
152  }
153  }
155 }
156 
160 void handle_spaceship_launch(struct player *pplayer)
161 {
162  struct player_spaceship *ship = &pplayer->spaceship;
163  int arrival;
164 
165  if (!player_primary_capital(pplayer)) {
166  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
167  _("You need to have a capital in order to launch "
168  "your spaceship."));
169  return;
170  }
171  if (ship->state >= SSHIP_LAUNCHED) {
172  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
173  _("Your spaceship is already launched!"));
174  return;
175  }
176  if (ship->state != SSHIP_STARTED || ship->success_rate == 0.0) {
177  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
178  _("Your spaceship can't be launched yet!"));
179  return;
180  }
181 
182  ship->state = SSHIP_LAUNCHED;
183  ship->launch_year = game.info.year;
184  arrival = ship->launch_year + static_cast<int>(ship->travel_time);
185 
186  notify_player(nullptr, nullptr, E_SPACESHIP, ftc_server,
187  _("The %s have launched a spaceship! "
188  "It is estimated to arrive at Alpha Centauri in %s."),
189  nation_plural_for_player(pplayer), textyear(arrival));
190 
191  send_spaceship_info(pplayer, nullptr);
192 }
193 
197 void handle_spaceship_place(struct player *pplayer,
198  enum spaceship_place_type type, int num)
199 {
200  (void) do_spaceship_place(pplayer, ACT_REQ_PLAYER, type, num);
201 }
202 
206 bool do_spaceship_place(struct player *pplayer, enum action_requester from,
207  enum spaceship_place_type type, int num)
208 {
209  struct player_spaceship *ship = &pplayer->spaceship;
210 
211  if (ship->state == SSHIP_NONE) {
212  if (from == ACT_REQ_PLAYER) {
213  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
214  _("Spaceship action received,"
215  " but you don't have a spaceship!"));
216  }
217 
218  return false;
219  }
220 
221  if (ship->state >= SSHIP_LAUNCHED) {
222  if (from == ACT_REQ_PLAYER) {
223  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
224  _("You can't modify your spaceship after launch!"));
225  }
226 
227  return false;
228  }
229 
230  if (type == SSHIP_PLACE_STRUCTURAL) {
231  if (num < 0 || num >= NUM_SS_STRUCTURALS
232  || BV_ISSET(ship->structure, num)) {
233  return false;
234  }
235  if (num_spaceship_structurals_placed(ship) >= ship->structurals) {
236  if (from == ACT_REQ_PLAYER) {
237  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
238  _("You don't have any unplaced Space Structurals!"));
239  }
240 
241  return false;
242  }
243  if (num != 0
244  && !BV_ISSET(ship->structure, structurals_info[num].required)) {
245  if (from == ACT_REQ_PLAYER) {
246  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
247  _("That Space Structural would not be connected!"));
248  }
249 
250  return false;
251  }
252 
253  BV_SET(ship->structure, num);
255  send_spaceship_info(pplayer, nullptr);
256  return true;
257  }
258 
259  if (type == SSHIP_PLACE_FUEL) {
260  if (ship->fuel != num - 1) {
261  return false;
262  }
263  if (ship->fuel + ship->propulsion >= ship->components) {
264  if (from == ACT_REQ_PLAYER) {
265  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
266  _("You don't have any unplaced Space Components!"));
267  }
268 
269  return false;
270  }
271  if (num > NUM_SS_COMPONENTS / 2) {
272  if (from == ACT_REQ_PLAYER) {
273  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
274  _("Your spaceship already has"
275  " the maximum number of Fuel Components!"));
276  }
277 
278  return false;
279  }
280 
281  ship->fuel++;
283  send_spaceship_info(pplayer, nullptr);
284  return true;
285  }
286 
287  if (type == SSHIP_PLACE_PROPULSION) {
288  if (ship->propulsion != num - 1) {
289  return false;
290  }
291  if (ship->fuel + ship->propulsion >= ship->components) {
292  if (from == ACT_REQ_PLAYER) {
293  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
294  _("You don't have any unplaced"
295  " Space Components!"));
296  }
297 
298  return false;
299  }
300  if (num > NUM_SS_COMPONENTS / 2) {
301  if (from == ACT_REQ_PLAYER) {
302  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
303  _("Your spaceship already has the"
304  " maximum number of Propulsion Components!"));
305  }
306 
307  return false;
308  }
309 
310  ship->propulsion++;
312  send_spaceship_info(pplayer, nullptr);
313  return true;
314  }
315 
316  if (type == SSHIP_PLACE_HABITATION) {
317  if (ship->habitation != num - 1) {
318  return false;
319  }
320  if (ship->habitation + ship->life_support + ship->solar_panels
321  >= ship->modules) {
322  if (from == ACT_REQ_PLAYER) {
323  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
324  _("You don't have any unplaced Space Modules!"));
325  }
326 
327  return false;
328  }
329  if (num > NUM_SS_MODULES / 3) {
330  if (from == ACT_REQ_PLAYER) {
331  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
332  _("Your spaceship already has the"
333  " maximum number of Habitation Modules!"));
334  }
335 
336  return false;
337  }
338 
339  ship->habitation++;
341  send_spaceship_info(pplayer, nullptr);
342  return true;
343  }
344 
345  if (type == SSHIP_PLACE_LIFE_SUPPORT) {
346  if (ship->life_support != num - 1) {
347  return false;
348  }
349  if (ship->habitation + ship->life_support + ship->solar_panels
350  >= ship->modules) {
351  if (from == ACT_REQ_PLAYER) {
352  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
353  _("You don't have any unplaced Space Modules!"));
354  }
355 
356  return false;
357  }
358  if (num > NUM_SS_MODULES / 3) {
359  if (from == ACT_REQ_PLAYER) {
360  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
361  _("Your spaceship already has the"
362  " maximum number of Life Support Modules!"));
363  }
364 
365  return false;
366  }
367 
368  ship->life_support++;
370  send_spaceship_info(pplayer, nullptr);
371  return true;
372  }
373 
374  if (type == SSHIP_PLACE_SOLAR_PANELS) {
375  if (ship->solar_panels != num - 1) {
376  return false;
377  }
378  if (ship->habitation + ship->life_support + ship->solar_panels
379  >= ship->modules) {
380  if (from == ACT_REQ_PLAYER) {
381  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
382  _("You don't have any unplaced Space Modules!"));
383  }
384 
385  return false;
386  }
387  if (num > NUM_SS_MODULES / 3) {
388  if (from == ACT_REQ_PLAYER) {
389  notify_player(pplayer, nullptr, E_SPACESHIP, ftc_server,
390  _("Your spaceship already has the"
391  " maximum number of Solar Panel Modules!"));
392  }
393 
394  return false;
395  }
396 
397  ship->solar_panels++;
399  send_spaceship_info(pplayer, nullptr);
400  return true;
401  }
402 
403  qCritical("Received unknown spaceship place type %d from %s", type,
404  player_name(pplayer));
405  return false;
406 }
407 
411 void spaceship_arrived(struct player *pplayer)
412 {
413  notify_player(nullptr, nullptr, E_SPACESHIP, ftc_server,
414  _("The %s spaceship has arrived at Alpha Centauri."),
415  nation_adjective_for_player(pplayer));
416  pplayer->spaceship.state = SSHIP_ARRIVED;
417 }
418 
422 void spaceship_lost(struct player *pplayer)
423 {
424  notify_player(nullptr, nullptr, E_SPACESHIP, ftc_server,
425  _("Without guidance from the capital, the %s "
426  "spaceship is lost!"),
427  nation_adjective_for_player(pplayer));
428  spaceship_init(&pplayer->spaceship);
429  send_spaceship_info(pplayer, nullptr);
430 }
431 
437 double spaceship_arrival(const struct player *pplayer)
438 {
439  const struct player_spaceship *ship = &pplayer->spaceship;
440 
441  return ship->launch_year + ship->travel_time;
442 }
443 
451 int rank_spaceship_arrival(struct player **result)
452 {
453  int n = 0, i;
454 
455  shuffled_players_iterate(pplayer)
456  {
457  struct player_spaceship *ship = &pplayer->spaceship;
458 
459  if (ship->state == SSHIP_LAUNCHED) {
460  result[n++] = pplayer;
461  }
462  }
464 
465  /* An insertion sort will do; n is probably small, and we need a
466  * stable sort to preserve the shuffled order for tie-breaking, so can't
467  * use qsort() */
468  for (i = 1; i < n; i++) {
469  int j;
470  for (j = i;
471  j > 0
472  && spaceship_arrival(result[j - 1]) > spaceship_arrival(result[j]);
473  j--) {
474  struct player *tmp = result[j];
475  result[j] = result[j - 1];
476  result[j - 1] = tmp;
477  }
478  }
479 
480  return n;
481 }
#define BV_SET(bv, bit)
Definition: bitvector.h:44
bool BV_ISSET(const BV &bv, int bit)
Definition: bitvector.h:37
const char * textyear(int year)
Produce a statically allocated textual representation of the given year.
Definition: calendar.cpp:116
spaceship_place_type
Definition: fc_types.h:1097
@ SSHIP_PLACE_PROPULSION
Definition: fc_types.h:1100
@ SSHIP_PLACE_STRUCTURAL
Definition: fc_types.h:1098
@ SSHIP_PLACE_LIFE_SUPPORT
Definition: fc_types.h:1102
@ SSHIP_PLACE_SOLAR_PANELS
Definition: fc_types.h:1103
@ SSHIP_PLACE_FUEL
Definition: fc_types.h:1099
@ SSHIP_PLACE_HABITATION
Definition: fc_types.h:1101
#define _(String)
Definition: fcintl.h:50
const struct ft_color ftc_server
struct civ_game game
Definition: game.cpp:47
#define fc_assert_ret(condition)
Definition: log.h:112
const char * nation_plural_for_player(const struct player *pplayer)
Return the (translated) plural noun of the given nation of a player.
Definition: nation.cpp:155
const char * nation_adjective_for_player(const struct player *pplayer)
Return the (translated) adjective for the given nation of a player.
Definition: nation.cpp:146
void notify_player(const struct player *pplayer, const struct tile *ptile, enum event_type event, const struct ft_color color, const char *format,...)
Similar to notify_conn_packet (see also), but takes player as "destination".
Definition: notify.cpp:284
int player_number(const struct player *pplayer)
Return the player index/number/id.
Definition: player.cpp:756
struct city * player_primary_capital(const struct player *pplayer)
Locate the player's primary capital city, (nullptr Otherwise)
Definition: player.cpp:1247
const char * player_name(const struct player *pplayer)
Return the leader name of the player.
Definition: player.cpp:816
#define players_iterate_end
Definition: player.h:520
#define players_iterate(_pplayer)
Definition: player.h:514
#define shuffled_players_iterate_end
Definition: plrhand.h:93
#define shuffled_players_iterate(NAME_pplayer)
Definition: plrhand.h:83
#define MIN(x, y)
Definition: shared.h:49
int rank_spaceship_arrival(struct player **result)
Rank launched player spaceships in order of arrival.
Definition: spacerace.cpp:451
void spaceship_lost(struct player *pplayer)
Handle spaceship loss.
Definition: spacerace.cpp:422
bool do_spaceship_place(struct player *pplayer, enum action_requester from, enum spaceship_place_type type, int num)
Place a spaceship part.
Definition: spacerace.cpp:206
double spaceship_arrival(const struct player *pplayer)
Return arrival year of player's spaceship (fractional, as one spaceship may arrive before another in ...
Definition: spacerace.cpp:437
void spaceship_calc_derived(struct player_spaceship *ship)
Calculate and fill in the derived quantities about the spaceship.
Definition: spacerace.cpp:37
void send_spaceship_info(struct player *src, struct conn_list *dest)
Send details of src's spaceship (or spaceships of all players if src is nullptr) to specified destina...
Definition: spacerace.cpp:120
void handle_spaceship_place(struct player *pplayer, enum spaceship_place_type type, int num)
Handle spaceship part placement request.
Definition: spacerace.cpp:197
void handle_spaceship_launch(struct player *pplayer)
Handle spaceship launch request.
Definition: spacerace.cpp:160
void spaceship_arrived(struct player *pplayer)
Handle spaceship arrival.
Definition: spacerace.cpp:411
int num_spaceship_structurals_placed(const struct player_spaceship *ship)
Count the number of structurals placed; that is, in ship->structure[].
Definition: spaceship.cpp:62
void spaceship_init(struct player_spaceship *ship)
Initialize spaceship struct; could also be used to "cancel" a spaceship (eg, if/when capital-capture ...
Definition: spaceship.cpp:45
const struct sship_part_info structurals_info[NUM_SS_STRUCTURALS]
Definition: spaceship.cpp:18
const struct sship_part_info modules_info[NUM_SS_MODULES]
Definition: spaceship.cpp:36
const struct sship_part_info components_info[NUM_SS_COMPONENTS]
Definition: spaceship.cpp:28
#define NUM_SS_MODULES
Definition: spaceship.h:86
#define NUM_SS_COMPONENTS
Definition: spaceship.h:85
#define NUM_SS_STRUCTURALS
Definition: spaceship.h:84
@ SSHIP_ARRIVED
Definition: spaceship.h:81
@ SSHIP_STARTED
Definition: spaceship.h:79
@ SSHIP_LAUNCHED
Definition: spaceship.h:80
@ SSHIP_NONE
Definition: spaceship.h:78
struct civ_game::@28::@32 server
struct conn_list * est_connections
Definition: game.h:88
struct packet_game_info info
Definition: game.h:80
double energy_rate
Definition: spaceship.h:111
bv_spaceship_structure structure
Definition: spaceship.h:97
double success_rate
Definition: spaceship.h:112
double support_rate
Definition: spaceship.h:110
double travel_time
Definition: spaceship.h:113
enum spaceship_state state
Definition: spaceship.h:105
Definition: player.h:231
struct player_spaceship spaceship
Definition: player.h:268