1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
15 #include <fc_config.h>
30 #include "spaceship.h"
37 #include "spacerace.h"
40 /**********************************************************************
41 Calculate and fill in the derived quantities about the spaceship.
42 Data reverse engineered from Civ1. --dwp
43 This could be in common, but its better for the client to take
44 the values the server calculates, in case things change.
45 ***********************************************************************/
46 void spaceship_calc_derived(struct player_spaceship
*ship
)
49 /* these are how many are connected: */
56 fc_assert_ret(ship
->structurals
<= NUM_SS_STRUCTURALS
);
57 fc_assert_ret(ship
->components
<= NUM_SS_COMPONENTS
);
58 fc_assert_ret(ship
->modules
<= NUM_SS_MODULES
);
61 ship
->support_rate
= ship
->energy_rate
=
62 ship
->success_rate
= ship
->travel_time
= 0.0;
64 for (i
= 0; i
< NUM_SS_STRUCTURALS
; i
++) {
65 if (BV_ISSET(ship
->structure
, i
)) {
66 ship
->mass
+= (i
< 6) ? 200 : 100;
67 /* s0 to s3 are heavier; actually in Civ1 its a bit stranger
68 than this, but not worth figuring out --dwp */
71 for (i
= 0; i
< ship
->fuel
; i
++) {
72 if (BV_ISSET(ship
->structure
, components_info
[i
* 2].required
)) {
76 for (i
= 0; i
< ship
->propulsion
; i
++) {
77 if (BV_ISSET(ship
->structure
, components_info
[i
* 2 + 1].required
)) {
81 for (i
= 0; i
< ship
->habitation
; i
++) {
82 if (BV_ISSET(ship
->structure
, modules_info
[i
* 3].required
)) {
86 for (i
= 0; i
< ship
->life_support
; i
++) {
87 if (BV_ISSET(ship
->structure
, modules_info
[i
* 3 + 1].required
)) {
91 for (i
= 0; i
< ship
->solar_panels
; i
++) {
92 if (BV_ISSET(ship
->structure
, modules_info
[i
* 3 + 2].required
)) {
97 ship
->mass
+= 1600 * (habitation
+ life_support
)
98 + 400 * (solar_panels
+ propulsion
+ fuel
);
100 ship
->population
= habitation
* 10000;
102 if (habitation
> 0) {
103 ship
->support_rate
= life_support
/ (double) habitation
;
105 if (life_support
+ habitation
> 0) {
106 ship
->energy_rate
= 2.0 * solar_panels
/ (double)(life_support
+habitation
);
108 if (fuel
>0 && propulsion
>0) {
110 MIN(ship
->support_rate
, 1.0) * MIN(ship
->energy_rate
, 1.0);
113 /* The Success% can be less by up to a few % in some cases
114 (I think if P != F or if P and/or F too small (eg <= 2?) ?)
115 but probably not worth worrying about.
116 Actually, the Civ1 manual suggests travel time is relevant. --dwp
119 ship
->travel_time
= ship
->mass
120 / (200.0 * MIN(propulsion
,fuel
) + 20.0);
124 /**************************************************************************
125 Send details of src's spaceship (or spaceships of all players
126 if src is NULL) to specified destinations. If dest is NULL then
127 game.est_connections is used.
128 **************************************************************************/
129 void send_spaceship_info(struct player
*src
, struct conn_list
*dest
)
132 dest
= game
.est_connections
;
135 players_iterate(pplayer
) {
136 if (!src
|| pplayer
== src
) {
137 struct packet_spaceship_info info
;
138 struct player_spaceship
*ship
= &pplayer
->spaceship
;
140 info
.player_num
= player_number(pplayer
);
141 info
.sship_state
= ship
->state
;
142 info
.structurals
= ship
->structurals
;
143 info
.components
= ship
->components
;
144 info
.modules
= ship
->modules
;
145 info
.fuel
= ship
->fuel
;
146 info
.propulsion
= ship
->propulsion
;
147 info
.habitation
= ship
->habitation
;
148 info
.life_support
= ship
->life_support
;
149 info
.solar_panels
= ship
->solar_panels
;
150 info
.launch_year
= ship
->launch_year
;
151 info
.population
= ship
->population
;
152 info
.mass
= ship
->mass
;
153 info
.support_rate
= ship
->support_rate
;
154 info
.energy_rate
= ship
->energy_rate
;
155 info
.success_rate
= ship
->success_rate
;
156 info
.travel_time
= ship
->travel_time
;
157 info
.structure
= ship
->structure
;
159 lsend_packet_spaceship_info(dest
, &info
);
161 } players_iterate_end
;
164 /**************************************************************************
165 Handle spaceship launch request.
166 **************************************************************************/
167 void handle_spaceship_launch(struct player
*pplayer
)
169 struct player_spaceship
*ship
= &pplayer
->spaceship
;
172 if (!player_capital(pplayer
)) {
173 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
174 _("You need to have a capital in order to launch "
178 if (ship
->state
>= SSHIP_LAUNCHED
) {
179 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
180 _("Your spaceship is already launched!"));
183 if (ship
->state
!= SSHIP_STARTED
184 || ship
->success_rate
== 0.0) {
185 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
186 _("Your spaceship can't be launched yet!"));
190 ship
->state
= SSHIP_LAUNCHED
;
191 ship
->launch_year
= game
.info
.year
;
192 arrival
= ship
->launch_year
+ (int) ship
->travel_time
;
194 notify_player(NULL
, NULL
, E_SPACESHIP
, ftc_server
,
195 _("The %s have launched a spaceship! "
196 "It is estimated to arrive at Alpha Centauri in %s."),
197 nation_plural_for_player(pplayer
),
200 send_spaceship_info(pplayer
, NULL
);
203 /**************************************************************************
204 Handle spaceship part placement request
205 **************************************************************************/
206 void handle_spaceship_place(struct player
*pplayer
,
207 enum spaceship_place_type type
, int num
)
209 (void) do_spaceship_place(pplayer
, TRUE
, type
, num
);
212 /**************************************************************************
213 Place a spaceship part
214 **************************************************************************/
215 bool do_spaceship_place(struct player
*pplayer
, bool user_initiated
,
216 enum spaceship_place_type type
, int num
)
218 struct player_spaceship
*ship
= &pplayer
->spaceship
;
220 if (ship
->state
== SSHIP_NONE
) {
221 if (user_initiated
) {
222 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
223 _("Spaceship action received,"
224 " but you don't have a spaceship!"));
230 if (ship
->state
>= SSHIP_LAUNCHED
) {
231 if (user_initiated
) {
232 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
233 _("You can't modify your spaceship after launch!"));
239 if (type
== SSHIP_PLACE_STRUCTURAL
) {
240 if (num
< 0 || num
>= NUM_SS_STRUCTURALS
241 || BV_ISSET(ship
->structure
, num
)) {
244 if (num_spaceship_structurals_placed(ship
) >= ship
->structurals
) {
245 if (user_initiated
) {
246 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
247 _("You don't have any unplaced Space Structurals!"));
253 && !BV_ISSET(ship
->structure
, structurals_info
[num
].required
)) {
254 if (user_initiated
) {
255 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
256 _("That Space Structural would not be connected!"));
262 BV_SET(ship
->structure
, num
);
263 spaceship_calc_derived(ship
);
264 send_spaceship_info(pplayer
, NULL
);
268 if (type
== SSHIP_PLACE_FUEL
) {
269 if (ship
->fuel
!= num
- 1) {
272 if (ship
->fuel
+ ship
->propulsion
>= ship
->components
) {
273 if (user_initiated
) {
274 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
275 _("You don't have any unplaced Space Components!"));
280 if (num
> NUM_SS_COMPONENTS
/2) {
281 if (user_initiated
) {
282 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
283 _("Your spaceship already has"
284 " the maximum number of Fuel Components!"));
291 spaceship_calc_derived(ship
);
292 send_spaceship_info(pplayer
, NULL
);
296 if (type
== SSHIP_PLACE_PROPULSION
) {
297 if (ship
->propulsion
!= num
- 1) {
300 if (ship
->fuel
+ ship
->propulsion
>= ship
->components
) {
301 if (user_initiated
) {
302 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
303 _("You don't have any unplaced"
304 " Space Components!"));
309 if (num
> NUM_SS_COMPONENTS
/2) {
310 if (user_initiated
) {
311 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
312 _("Your spaceship already has the"
313 " maximum number of Propulsion Components!"));
320 spaceship_calc_derived(ship
);
321 send_spaceship_info(pplayer
, NULL
);
325 if (type
== SSHIP_PLACE_HABITATION
) {
326 if (ship
->habitation
!= num
- 1) {
329 if (ship
->habitation
+ ship
->life_support
+ ship
->solar_panels
331 if (user_initiated
) {
332 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
333 _("You don't have any unplaced Space Modules!"));
338 if (num
> NUM_SS_MODULES
/ 3) {
339 if (user_initiated
) {
340 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
341 _("Your spaceship already has the"
342 " maximum number of Habitation Modules!"));
349 spaceship_calc_derived(ship
);
350 send_spaceship_info(pplayer
, NULL
);
354 if (type
== SSHIP_PLACE_LIFE_SUPPORT
) {
355 if (ship
->life_support
!= num
- 1) {
358 if (ship
->habitation
+ ship
->life_support
+ ship
->solar_panels
360 if (user_initiated
) {
361 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
362 _("You don't have any unplaced Space Modules!"));
367 if (num
> NUM_SS_MODULES
/ 3) {
368 if (user_initiated
) {
369 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
370 _("Your spaceship already has the"
371 " maximum number of Life Support Modules!"));
377 ship
->life_support
++;
378 spaceship_calc_derived(ship
);
379 send_spaceship_info(pplayer
, NULL
);
383 if (type
== SSHIP_PLACE_SOLAR_PANELS
) {
384 if (ship
->solar_panels
!= num
- 1) {
387 if (ship
->habitation
+ ship
->life_support
+ ship
->solar_panels
389 if (user_initiated
) {
390 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
391 _("You don't have any unplaced Space Modules!"));
396 if (num
> NUM_SS_MODULES
/ 3) {
397 if (user_initiated
) {
398 notify_player(pplayer
, NULL
, E_SPACESHIP
, ftc_server
,
399 _("Your spaceship already has the"
400 " maximum number of Solar Panel Modules!"));
406 ship
->solar_panels
++;
407 spaceship_calc_derived(ship
);
408 send_spaceship_info(pplayer
, NULL
);
412 log_error("Received unknown spaceship place type %d from %s",
413 type
, player_name(pplayer
));
417 /**************************************************************************
418 Handle spaceship loss.
419 **************************************************************************/
420 void spaceship_lost(struct player
*pplayer
)
422 notify_player(NULL
, NULL
, E_SPACESHIP
, ftc_server
,
423 _("Without guidance from the capital, the %s "
424 "spaceship is lost!"),
425 nation_adjective_for_player(pplayer
));
426 spaceship_init(&pplayer
->spaceship
);
427 send_spaceship_info(pplayer
, NULL
);
430 /**************************************************************************
431 Use shuffled order to randomly resolve ties.
432 **************************************************************************/
433 struct player
*check_spaceship_arrival(void)
435 double arrival
, best_arrival
= 0.0;
436 struct player
*best_pplayer
= NULL
;
438 shuffled_players_iterate(pplayer
) {
439 struct player_spaceship
*ship
= &pplayer
->spaceship
;
441 if (ship
->state
== SSHIP_LAUNCHED
) {
442 arrival
= ship
->launch_year
+ ship
->travel_time
;
443 if (game
.info
.year
>= (int)arrival
444 && (!best_pplayer
|| arrival
< best_arrival
)) {
445 best_arrival
= arrival
;
446 best_pplayer
= pplayer
;
449 } shuffled_players_iterate_end
;
452 best_pplayer
->spaceship
.state
= SSHIP_ARRIVED
;