]> git.pond.sub.org Git - empserver/blob - src/lib/update/ship.c
Import of Empire 4.2.12
[empserver] / src / lib / update / ship.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  ship.c: Do production for ships
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Steve McClure, 1996
33  */
34
35 #include "misc.h"
36 #include "var.h"
37 #include "sect.h"
38 #include "nat.h"
39 #include "ship.h"
40 #include "var.h"
41 #include "news.h"
42 #include "file.h"
43 #include "product.h"
44 #include "land.h"
45 #include "xy.h"
46 #include "nsc.h"
47 #include "optlist.h"
48 #include "player.h"
49 #include "update.h"
50 #include "common.h"
51 #include "subs.h"
52 #include "gen.h"
53 #include "lost.h"
54
55 #ifndef MIN
56 #define MIN(x,y)        ((x) > (y) ? (y) : (x))
57 #endif
58
59 int
60 prod_ship(int etus, int natnum, int *bp, int build)
61                 /* build = 1, maintain = 0 */
62 {
63         register struct shpstr *sp;
64         struct  natstr *np;
65         int     n, k=0;
66         extern  long sea_money[MAXNOC];
67         int     start_money;
68         int     lastx = 9999, lasty = 9999;
69
70         bp_enable_cachepath();
71         for (n=0; NULL != (sp = getshipp(n)); n++) {
72                 if (sp->shp_own == 0)
73                         continue;
74                 if (sp->shp_own != natnum)
75                         continue;
76                 np = getnatp(sp->shp_own);
77                 start_money = np->nat_money;
78                 if (lastx == 9999 || lasty == 9999) {
79                   lastx = sp->shp_x;
80                   lasty = sp->shp_y;
81                 }
82                 if (lastx != sp->shp_x || lasty != sp->shp_y) {
83                   /* Reset the cache */
84                   bp_disable_cachepath();
85                   bp_clear_cachepath();
86                   bp_enable_cachepath();
87                 }
88                 upd_ship(sp, n, etus, np, bp, build);
89                 if (build && !player->simulation) /* make sure to only autonav once */
90                         nav_ship(sp);           /* autonav the ship */
91                 sea_money[sp->shp_own] += np->nat_money - start_money;
92                 if ((build && (np->nat_money != start_money)) || (!build))
93                   k++;
94                 if (player->simulation)
95                   np->nat_money = start_money;     
96         }
97         bp_disable_cachepath();
98         bp_clear_cachepath();
99
100         if (opt_SAIL) {
101             if (build && !player->simulation) /* make sure to only sail once */
102                 sail_ship(natnum);
103         }
104         return k;
105 }
106
107 void
108 upd_ship(register struct shpstr *sp, int shipno, register int etus, struct natstr *np, int *bp, int build)
109                /* build = 1, maintain = 0 */
110 {
111         extern  long pops[];
112         struct  sctstr *sectp;
113         struct  mchrstr *mp;
114         int     vec[I_MAX+1];
115         int     cvec[I_MAX+1];
116         int     oil_gained;
117         int     max_oil;
118         int     max_food;
119         struct  pchrstr *product;
120         s_char  *resource;
121         int     n;
122         int     mult;
123         extern  double money_ship;
124         int     needed;
125         int     cost;
126         int     eff;
127
128         mp = &mchr[(int)sp->shp_type];
129         getvec(VT_ITEM, vec, (s_char *)sp, EF_SHIP);
130         if (build == 1){
131                 if (np->nat_priorities[PRI_SBUILD] == 0 ||
132                     np->nat_money < 0)
133                         return;
134                 if (sp->shp_effic < SHIP_MINEFF ||
135                         !shiprepair(sp, vec, np, bp, etus)) {
136                         makelost(EF_SHIP, sp->shp_own, sp->shp_uid, sp->shp_x, sp->shp_y);
137                         sp->shp_own = 0;
138                         return;
139                 }
140         }else{
141                 mult = 1;
142                 if (np->nat_level[NAT_TLEV] < sp->shp_tech * 0.85)
143                         mult = 2;
144                 cost = -(mult * etus * dmin(0.0, money_ship * mp->m_cost));
145                 if ((np->nat_priorities[PRI_SMAINT] == 0 ||
146                     np->nat_money < cost) && !player->simulation){
147                         if ((eff = sp->shp_effic - etus/5) < SHIP_MINEFF) {
148                                 wu(0, sp->shp_own,
149                                    "%s lost to lack of maintenance\n",
150                                    prship(sp));
151                                 makelost(EF_SHIP, sp->shp_own, sp->shp_uid, sp->shp_x, sp->shp_y);
152                                 sp->shp_own = 0;
153                                 return;
154                         }
155                         wu(0, sp->shp_own,
156                            "%s lost %d%% to lack of maintenance\n",
157                            prship(sp), sp->shp_effic - eff);
158                         sp->shp_effic = eff;
159                 } else {
160                         np->nat_money -= cost;
161                 }
162
163                 sectp = getsectp(sp->shp_x, sp->shp_y);
164                 if (((mp->m_flags & M_OIL) && (sectp->sct_type == SCT_WATER))
165                          && !player->simulation){
166                         /*
167                          * take care of oil production
168                          */
169                         oil_gained = roundavg((vec[I_CIVIL] * etus / 10000.0)
170                                 * sectp->sct_oil);
171                         vec[I_OIL] += oil_gained;
172                         max_oil = vl_find(V_OIL,mp->m_vtype,mp->m_vamt,mp->m_nv);
173                         if (vec[I_OIL] > max_oil)
174                                 vec[I_OIL] = max_oil;
175                         product = &pchr[P_OIL];
176                         if (product->p_nrdep != 0 && oil_gained > 0) {
177                                 resource = ((s_char *)sectp) + product->p_nrndx;
178                                 *resource -= roundavg(oil_gained *
179                                         product->p_nrdep / 100.0);
180                         }
181                 } else if (((mp->m_flags&M_FOOD)&&(sectp->sct_type==SCT_WATER)) && !player->simulation){
182                         sectp = getsectp(sp->shp_x, sp->shp_y);
183                         vec[I_FOOD] += ((vec[I_CIVIL] * etus) / 1000.0)
184                                 * sectp->sct_fertil;
185                 }
186 /* Military costs are now part of regular military costs, not ship costs */
187 /*              np->nat_money += (int) (etus * vec[I_MILIT] * money_mil);*/
188                 if (!player->simulation){
189                         if ((n = feed_ship(sp,vec,etus, &needed, 1)) > 0) {
190                                 wu(0, sp->shp_own, "%d starved on %s\n",
191                                    n, prship(sp));
192                                 if (n > 10)
193                                         nreport(sp->shp_own, N_DIE_FAMINE, 0, 1);
194                         }
195                         max_food = vl_find(V_FOOD, mp->m_vtype, mp->m_vamt, mp->m_nv);
196                         if (vec[I_FOOD] > max_food)
197                                 vec[I_FOOD] = max_food;
198                         /*
199                          * do plague stuff.  plague can't break out on ships,
200                          * but it can still kill people.
201                          */
202                         getvec(VT_COND, cvec, (s_char *)sp, EF_SHIP);
203                         if (cvec[C_PSTAGE] > 0) {
204                             n = plague_people(np, vec, cvec, etus);
205                             switch (n) {
206                             case PLG_DYING:
207                                 wu(0, sp->shp_own,
208                                    "PLAGUE deaths reported on %s\n",
209                                    prship(sp));
210                                 nreport(sp->shp_own, N_DIE_PLAGUE, 0, 1);
211                                 break;
212                             case PLG_INFECT:
213                                 wu(0, sp->shp_own, "%s battling PLAGUE\n",
214                                    prship(sp));
215                                 break;
216                             case PLG_INCUBATE:
217                                 /* Are we still incubating? */
218                                 if (n == cvec[C_PSTAGE]) {
219                                     /* Yes. Will it turn "infectious" next time? */
220                                     if (cvec[C_PTIME] <= etus) {
221                                         /* Yes.  Report an outbreak. */
222                                         wu(0, sp->shp_own,
223                                            "Outbreak of PLAGUE on %s!\n",
224                                            prship(sp));
225                                         nreport(sp->shp_own, N_OUT_PLAGUE, 0, 1);
226                                     }
227                                 } else {
228                                     /* It has already moved on to "infectious" */
229                                     wu(0, sp->shp_own,
230                                        "%s battling PLAGUE\n", prship(sp));
231                                 }
232                                 break;
233                             case PLG_EXPOSED:
234                                 /* Has the plague moved to "incubation" yet? */
235                                 if (n != cvec[C_PSTAGE]) {
236                                     /* Yes. Will it turn "infectious" next time? */
237                                     if (cvec[C_PTIME] <= etus) {
238                                         /* Yes.  Report an outbreak. */
239                                         wu(0, sp->shp_own,
240                                            "Outbreak of PLAGUE on %s!\n",
241                                            prship(sp));
242                                         nreport(sp->shp_own, N_OUT_PLAGUE, 0, 1);
243                                     }
244                                 }
245                                 break;
246                             default:
247                                 break;
248                             }
249                             
250                             putvec(VT_COND, cvec, (s_char *)sp, EF_SHIP);
251                         }
252                         putvec(VT_ITEM, vec, (s_char *)sp, EF_SHIP);
253                         pops[sp->shp_own] += vec[I_CIVIL];
254                 }
255         }
256 }
257
258 /*
259  * idea is: a sector full of workers can fix up eight
260  * battleships +8 % eff each etu.  This will cost around
261  * 8 * 8 * $40 = $2560!
262  */
263 int
264 shiprepair(register struct shpstr *ship, int *vec, struct natstr *np, int *bp, int etus)
265 {
266         extern  int ship_grow_scale;
267         register int delta;
268         struct  sctstr *sp;
269         struct  mchrstr *mp;
270         float   leftp, buildp;
271         int     left, build;
272         int     lcm_needed, hcm_needed;
273         int     wf;
274         int     avail;
275         int     w_p_eff;
276         int     mult;
277         int     svec[I_MAX+1];
278         int     mvec[I_MAX+1];
279         int     rel;
280
281         mp = &mchr[(int)ship->shp_type];
282         sp = getsectp(ship->shp_x, ship->shp_y);
283
284         if ((sp->sct_own != ship->shp_own) && (sp->sct_own != 0)) {
285                 rel=getrel(getnatp(sp->sct_own),ship->shp_own);
286
287                 if (rel < FRIENDLY)
288                         return 1;
289         }
290
291         wf = 0;
292         /* only military can work on a military boat */
293         if (ship->shp_glim > 0)
294                 wf = etus * vec[I_MILIT]/2;
295         else
296                 wf = etus * (vec[I_CIVIL]/2 + vec[I_MILIT]/5);
297             
298         if (sp->sct_type != SCT_HARBR){
299                 wf /= 3;
300                 avail = wf;
301         }else{
302                 if (!player->simulation)
303                   avail = wf + sp->sct_avail * 100;
304                 else
305                   avail = wf + gt_bg_nmbr(bp, sp, I_MAX+1) * 100;
306         }
307
308         w_p_eff = 20 + (mp->m_lcm + 2 * mp->m_hcm);
309
310         if (sp->sct_type != SCT_HARBR){
311                 int     abs_max, amt;
312
313                 if (ship->shp_glim > 0){
314                         abs_max = vl_find(V_MILIT, mp->m_vtype,
315                                 mp->m_vamt, (int) mp->m_nv);
316                         amt = vec[I_MILIT];
317                 }else{
318                         abs_max = vl_find(V_CIVIL, mp->m_vtype,
319                                 mp->m_vamt, (int) mp->m_nv);
320                         amt = vec[I_CIVIL];
321                         if (abs_max==0) {
322                           abs_max = vl_find(V_MILIT, mp->m_vtype, mp->m_vamt, 
323                                             (int) mp->m_nv);
324                           amt = vec[I_MILIT];
325                         }
326                 }
327                 
328                 if (abs_max == 0){
329                         logerror("Abs max of 0 for ship %d\n",ship->shp_uid);
330                         abs_max = 1;
331                 }
332                 avail -= (etus * (100-((amt*100)/abs_max)))/7;
333                 /* think of it as entropy in action */
334         }
335
336         if (avail <= 0){
337           if (!player->simulation) {
338             if (opt_SHIP_DECAY)  {
339               ship->shp_effic += avail/w_p_eff;
340             }
341             return 1;
342           }
343         }
344
345         if ((sp->sct_off) && (sp->sct_own == ship->shp_own))
346                 return 1;
347
348         getvec(VT_ITEM, svec, (s_char *)sp, EF_SECTOR);
349
350         mult = 1;
351         if (np->nat_level[NAT_TLEV] < ship->shp_tech * 0.85)
352                 mult = 2;
353
354         if (ship->shp_effic == 100) {
355                 /* ship is ok; no repairs needed */
356                 return 1;
357         }
358
359         left = 100 - ship->shp_effic;
360         delta = roundavg((double)avail/w_p_eff);
361         if (delta <= 0)
362                 return 1;
363         if (delta > etus*ship_grow_scale)
364                 delta = etus*ship_grow_scale;
365         if (delta > left)
366                 delta = left;
367
368         /* delta is the max amount we can grow */
369
370         left = 100 - ship->shp_effic;
371         if (left > delta)
372                 left = delta;
373
374         leftp = ((float)left/100.0);
375         bzero((s_char *)mvec, sizeof(mvec));
376         mvec[I_LCM] = lcm_needed = ldround((double)(mp->m_lcm * leftp),1);
377         mvec[I_HCM] = hcm_needed = ldround((double)(mp->m_hcm * leftp),1);
378
379         get_materials(sp, bp, mvec, 0);
380
381         if (mvec[I_LCM]>=lcm_needed)
382                 buildp=leftp;
383         else
384                 buildp=((float)mvec[I_LCM]/(float)mp->m_lcm);
385         if (mvec[I_HCM] < hcm_needed)
386                 buildp = MIN(buildp,((float)mvec[I_HCM]/(float)mp->m_hcm));
387
388         build=ldround((double)(buildp*100.0),1);
389         bzero((s_char *)mvec, sizeof(mvec));
390         mvec[I_LCM] = lcm_needed = roundavg((double)(mp->m_lcm * buildp));
391         mvec[I_HCM] = hcm_needed = roundavg((double)(mp->m_hcm * buildp));
392
393         get_materials(sp, bp, mvec, 1);
394
395         if (sp->sct_type != SCT_HARBR)
396                 build = delta;
397         wf -= build * w_p_eff;
398         if (wf < 0) {
399                 /*
400                  * I didn't use roundavg here, because I want to penalize
401                  * the player with a large number of ships.
402                  */
403                 if (!player->simulation)
404                   avail = (sp->sct_avail * 100 + wf) / 100;
405                 else
406                   avail = (gt_bg_nmbr(bp,sp,I_MAX+1) * 100 + wf) / 100;
407                 if (avail < 0)
408                         avail = 0;
409                 if (!player->simulation)
410                   sp->sct_avail = avail;
411                 else
412                   pt_bg_nmbr(bp, sp, I_MAX+1, avail);
413         }
414         if (sp->sct_type != SCT_HARBR)
415                 if ((build+ship->shp_effic)>80){
416                         build = 80 - ship->shp_effic;
417                         if (build < 0)
418                                 build = 0;
419                 }
420    
421         np->nat_money -= mult * mp->m_cost * build / 100.0;
422         if (!player->simulation)
423           ship->shp_effic += (s_char)build;
424         return 1;
425 }
426
427 /*
428  * returns the number who starved, if any.
429  */
430 int
431 feed_ship(struct shpstr *sp, register int *vec, int etus, int *needed, int doit)
432 {
433         extern  double eatrate;
434         double  food_eaten, land_eaten;
435         double  people_left;
436         int     ifood_eaten;
437         int     can_eat, need;
438         int     total_people;
439         int     to_starve;
440         int     starved, lvec[I_MAX+1];
441         struct  nstr_item ni;
442         struct  lndstr *lp;
443         s_char  *nxtitemp(struct nstr_item *np, int owner);
444
445         if (opt_NOFOOD) return 0; /* no food no work to do */
446
447         food_eaten = (etus * eatrate) * (vec[I_CIVIL]+vec[I_MILIT]+vec[I_UW]);
448         ifood_eaten = (int)food_eaten;
449         if ((food_eaten-ifood_eaten) > 0)
450                 ifood_eaten++;
451         starved = 0;
452         *needed = 0;
453         if (!player->simulation &&
454             food_eaten > vec[I_FOOD])
455                 vec[I_FOOD] += supply_commod(sp->shp_own,sp->shp_x,sp->shp_y,
456                                 I_FOOD,(ifood_eaten-vec[I_FOOD]));
457
458         if (food_eaten > vec[I_FOOD]) {
459 /* doit - only steal food from land units during the update */
460                 if (sp->shp_nland > 0 && doit) {
461                         snxtitem_all(&ni,EF_LAND);
462                         while((lp=(struct lndstr *)nxtitemp(&ni, 0)) &&
463                                 (food_eaten > vec[I_FOOD])){
464                                 if (lp->lnd_ship != sp->shp_uid)
465                                         continue;
466                                 need = ifood_eaten - vec[I_FOOD];
467                                 getvec(VT_ITEM, lvec, (s_char *)lp, EF_LAND);
468                                 land_eaten = (etus * eatrate) *
469                                         (double)lnd_getmil(lp);
470                                 if (lvec[I_FOOD]-need > land_eaten){
471                                         vec[I_FOOD] += need;
472                                         lvec[I_FOOD] -= need;
473                                 }else if ((lvec[I_FOOD]-land_eaten) > 0){
474                                         vec[I_FOOD]+= (lvec[I_FOOD]-land_eaten);
475                                         lvec[I_FOOD]-=(lvec[I_FOOD]-land_eaten);
476                                 }
477                                 putvec(VT_ITEM, lvec, (s_char *)lp, EF_LAND);
478                         }
479                 }
480         }
481
482         if (food_eaten > vec[I_FOOD]){
483                 *needed = food_eaten - vec[I_FOOD];
484                 if (*needed < (food_eaten - vec[I_FOOD]))
485                         (*needed)++;
486                 if (opt_NEW_STARVE) {
487                         can_eat = (vec[I_FOOD] / (etus * eatrate));
488                         total_people = vec[I_CIVIL] + vec[I_MILIT] + vec[I_UW];
489
490                         /* only want to starve off at most 1/2 the populace. */
491                         if (can_eat < (total_people/2))
492                                 can_eat = total_people/2;
493
494                         to_starve = total_people - can_eat;     
495                         while(to_starve && vec[I_UW]){
496                                 to_starve--;
497                                 starved++;
498                                 vec[I_UW]--;
499                         }
500                         while(to_starve && vec[I_CIVIL]){
501                                 to_starve--;
502                                 starved++;
503                                 vec[I_CIVIL]--;
504                         }
505                         while(to_starve && vec[I_MILIT]){
506                                 to_starve--;
507                                 starved++;
508                                 vec[I_MILIT]--;
509                         }
510                 
511                         vec[I_FOOD] = 0;
512                 }
513                 else {          /* ! opt_NEW_STARVE */
514                         people_left = (vec[I_FOOD] + 0.01) / (food_eaten + 0.01);
515                         starved = vec[I_CIVIL] + vec[I_MILIT] + vec[I_UW];
516                         /* only want to starve off at most 1/2 the populace. */
517                         if (people_left < 0.5)
518                                 people_left = 0.5;
519                         vec[I_CIVIL] = (int) (vec[I_CIVIL] * people_left);
520                         vec[I_MILIT] = (int) (vec[I_MILIT] * people_left);
521                         vec[I_UW] = (int) (vec[I_UW] * people_left);
522                         starved -= vec[I_CIVIL] + vec[I_MILIT] + vec[I_UW];
523                         vec[I_FOOD] = 0;
524                 }
525         } else {
526                 vec[I_FOOD] -= (int)food_eaten;
527         }
528         return starved;
529 }