]> git.pond.sub.org Git - empserver/blob - src/lib/commands/tend.c
Update copyright notice.
[empserver] / src / lib / commands / tend.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2004, 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  *  tend.c: Transfer goodies from one ship to another.
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Thomas Ruschak, 1992
33  *     Steve McClure, 2000
34  */
35
36 #include <string.h>
37 #include "misc.h"
38 #include "player.h"
39 #include "var.h"
40 #include "xy.h"
41 #include "file.h"
42 #include "ship.h"
43 #include "item.h"
44 #include "nsc.h"
45 #include "nat.h"
46 #include "land.h"
47 #include "plane.h"
48 #include "nuke.h"
49 #include "genitem.h"
50 #include "commands.h"
51
52 static void expose_ship(struct shpstr *s1, struct shpstr *s2);
53 static int tend_land(struct shpstr *tenderp, s_char *units);
54
55 int
56 tend(void)
57 {
58     struct nstr_item targets;
59     struct nstr_item tenders;
60     struct shpstr tender;
61     struct shpstr target;
62     struct ichrstr *ip;
63     struct mchrstr *vbase;
64     int amt;
65     int retval;
66     int ontender;
67     int ontarget;
68     int maxtender;
69     int maxtarget;
70     int transfer;
71     int total;
72     int type;
73     s_char *p;
74     s_char prompt[512];
75     s_char buf[1024];
76
77     if (!(p = getstarg(player->argp[1],
78                        "Tend what commodity (or 'land')? ", buf)) || !*p)
79         return RET_SYN;
80
81     if (!strncmp(p, "land", 4))
82         type = EF_LAND;
83     else if (NULL != (ip = item_by_name(p)))
84         type = EF_SECTOR;
85     else {
86         pr("Can't tend '%s'\n", p);
87         return RET_SYN;
88     }
89
90     if (!snxtitem(&tenders, EF_SHIP,
91                   getstarg(player->argp[2], "Tender(s)? ", buf)))
92         return RET_SYN;
93
94     while (nxtitem(&tenders, (s_char *)&tender)) {
95         if (!player->owner)
96             continue;
97         if (type == EF_LAND) {
98             sprintf(prompt, "Land unit(s) to tend from %s? ",
99                     prship(&tender));
100             if (!(p = getstarg(player->argp[3], prompt, buf)) || !*p)
101                 continue;
102             if (!check_ship_ok(&tender))
103                 return RET_SYN;
104             if (0 != (retval = tend_land(&tender, p)))
105                 return retval;
106             continue;
107         }
108         sprintf(prompt, "Number of %s to tend from %s? ",
109                 ip->i_name, prship(&tender));
110         if (!(p = getstarg(player->argp[3], prompt, buf)) || !*p)
111             continue;
112         if (!check_ship_ok(&tender))
113             return RET_SYN;
114         if (!(amt = atoi(p))) {
115             pr("Amount must be non-zero!\n");
116             return RET_SYN;
117         }
118         ontender = tender.shp_item[ip->i_vtype];
119         if (ontender == 0 && amt > 0) {
120             pr("No %s on %s\n", ip->i_name, prship(&tender));
121             return RET_FAIL;
122         }
123         vbase = &mchr[(int)tender.shp_type];
124         maxtender = vbase->m_item[ip->i_vtype];
125         if (maxtender == 0) {
126             pr("A %s cannot hold any %s\n",
127                mchr[(int)tender.shp_type].m_name, ip->i_name);
128             break;
129         }
130         if (!snxtitem(&targets, EF_SHIP,
131                       getstarg(player->argp[4], "Ships to be tended? ",
132                                buf)))
133             break;
134         if (!check_ship_ok(&tender))
135             return RET_SYN;
136         total = 0;
137         while (tend_nxtitem(&targets, (s_char *)&target)) {
138             if (!player->owner &&
139                 (getrel(getnatp(target.shp_own), player->cnum) < FRIENDLY))
140                 continue;
141             if (target.shp_uid == tender.shp_uid)
142                 continue;
143             if (tender.shp_x != target.shp_x ||
144                 tender.shp_y != target.shp_y)
145                 continue;
146             ontarget = target.shp_item[ip->i_vtype];
147             if (ontarget == 0 && amt < 0) {
148                 pr("No %s on %s\n", ip->i_name, prship(&target));
149                 continue;
150             }
151             vbase = &mchr[(int)target.shp_type];
152             maxtarget = vbase->m_item[ip->i_vtype];
153             if (amt < 0) {
154                 if (!player->owner)
155                     amt = 0;
156
157                 /* take from target and give to tender */
158                 transfer = min(ontarget, -amt);
159                 transfer = min(maxtender - ontender, transfer);
160                 if (transfer == 0)
161                     continue;
162                 target.shp_item[ip->i_vtype] = ontarget - transfer;
163                 ontender += transfer;
164                 total += transfer;
165             } else {
166                 /* give to target from tender */
167                 transfer = min(ontender, amt);
168                 transfer = min(transfer, maxtarget - ontarget);
169                 if (transfer == 0)
170                     continue;
171                 target.shp_item[ip->i_vtype] = ontarget + transfer;
172                 ontender -= transfer;
173                 total += transfer;
174             }
175             expose_ship(&tender, &target);
176             putship(target.shp_uid, &target);
177             if (amt > 0 && ontender == 0) {
178                 pr("%s out of %s\n", prship(&tender), ip->i_name);
179                 break;
180             }
181         }
182         pr("%d total %s transferred %s %s\n",
183            total, ip->i_name, (amt > 0) ? "off of" : "to",
184            prship(&tender));
185         tender.shp_item[ip->i_vtype] = ontender;
186         tender.shp_mission = 0;
187         putship(tender.shp_uid, &tender);
188     }
189     return RET_OK;
190 }
191
192 static void
193 expose_ship(struct shpstr *s1, struct shpstr *s2)
194 {
195     if (s1->shp_pstage == PLG_INFECT && s2->shp_pstage == PLG_HEALTHY)
196         s2->shp_pstage = PLG_EXPOSED;
197     if (s2->shp_pstage == PLG_INFECT && s1->shp_pstage == PLG_HEALTHY)
198         s1->shp_pstage = PLG_EXPOSED;
199 }
200
201 /*
202  * tend_nxtitem.c
203  *
204  * get next item from list. Stolen from nxtitem to make 1 itsy-bitsy change
205  *
206  * Dave Pare, 1989
207  */
208
209 int
210 tend_nxtitem(struct nstr_item *np, void *ptr)
211 {
212     struct genitem *gp;
213     int selected;
214
215     if (np->sel == NS_UNDEF)
216         return 0;
217     gp = (struct genitem *)ptr;
218     do {
219         if (np->sel == NS_LIST) {
220             np->index++;
221             if (np->index >= np->size)
222                 return 0;
223             np->cur = np->list[np->index];
224         } else {
225             np->cur++;
226         }
227         if (!np->read(np->type, np->cur, ptr)) {
228             /* if read fails, fatal */
229             return 0;
230         }
231         selected = 1;
232         switch (np->sel) {
233         case NS_LIST:
234             /* The change is to take the player->owner check out here */
235             break;
236         case NS_ALL:
237             /* XXX maybe combine NS_LIST and NS_ALL later */
238             break;
239         case NS_DIST:
240             if (!xyinrange(gp->x, gp->y, &np->range)) {
241                 selected = 0;
242                 break;
243             }
244             np->curdist = mapdist((int)gp->x, (int)gp->y,
245                                   (int)np->cx, (int)np->cy);
246             if (np->curdist > np->dist)
247                 selected = 0;
248             break;
249         case NS_AREA:
250             if (!xyinrange(gp->x, gp->y, &np->range))
251                 selected = 0;
252             if (gp->x == np->range.hx || gp->y == np->range.hy)
253                 selected = 0;
254             break;
255         case NS_XY:
256             if (gp->x != np->cx || gp->y != np->cy)
257                 selected = 0;
258             break;
259         case NS_GROUP:
260             if (np->group != gp->group)
261                 selected = 0;
262             break;
263         default:
264             CANT_HAPPEN("bad np->sel");
265             return 0;
266         }
267         if (selected && np->ncond) {
268             /* nstr_exec is expensive, so we do it last */
269             if (!nstr_exec(np->cond, np->ncond, ptr))
270                 selected = 0;
271         }
272     } while (!selected);
273     return 1;
274 }
275
276 static int
277 tend_land(struct shpstr *tenderp, s_char *units)
278 {
279     struct nstr_item lni;
280     struct nstr_item targets;
281     struct shpstr target;
282     struct lndstr land;
283     struct plnstr plane;
284     struct nstr_item pni;
285     s_char buf[1024];
286
287     if (!snxtitem(&lni, EF_LAND, units))
288         return RET_SYN;
289
290     while (nxtitem(&lni, (s_char *)&land)) {
291         if (!player->owner)
292             continue;
293         if (land.lnd_ship != tenderp->shp_uid) {
294             pr("%s is not on %s!\n", prland(&land), prship(tenderp));
295             continue;
296         }
297         if (!(lchr[(int)land.lnd_type].l_flags & L_ASSAULT)) {
298             pr("%s does not have \"assault\" capability and can't be tended\n", prland(&land));
299             continue;
300         }
301         if (!snxtitem(&targets, EF_SHIP,
302                       getstarg(player->argp[4], "Ship to be tended? ",
303                                buf)))
304             break;
305         if (!check_land_ok(&land))
306             return RET_SYN;
307         while (tend_nxtitem(&targets, (s_char *)&target)) {
308             if (!player->owner &&
309                 (getrel(getnatp(target.shp_own), player->cnum) < FRIENDLY))
310                 continue;
311             if (target.shp_uid == tenderp->shp_uid)
312                 continue;
313             if (tenderp->shp_x != target.shp_x ||
314                 tenderp->shp_y != target.shp_y)
315                 continue;
316
317             /* Fit unit on ship */
318             count_units(&target);
319             getship(target.shp_uid, &target);
320
321             if (target.shp_nland >= mchr[(int)target.shp_type].m_nland) {
322                 if (mchr[(int)target.shp_type].m_nland)
323                     pr("%s doesn't have room for any more land units!\n",
324                        prship(&target));
325                 else
326                     pr("%s doesn't carry land units!\n", prship(&target));
327                 continue;
328             }
329             pr("%s transferred from %s to %s\n",
330                prland(&land), prship(tenderp), prship(&target));
331             sprintf(buf, "loaded on your %s at %s",
332                     prship(&target), xyas(target.shp_x, target.shp_y,
333                                           target.shp_own));
334             gift(target.shp_own, player->cnum, (s_char *)&land,
335                  EF_LAND, buf);
336             makelost(EF_LAND, land.lnd_own, land.lnd_uid, land.lnd_x,
337                      land.lnd_y);
338             land.lnd_own = target.shp_own;
339             makenotlost(EF_LAND, land.lnd_own, land.lnd_uid, land.lnd_x,
340                         land.lnd_y);
341             land.lnd_ship = target.shp_uid;
342             land.lnd_harden = 0;
343             land.lnd_mission = 0;
344             target.shp_nland++;
345             putland(land.lnd_uid, &land);
346             expose_ship(tenderp, &target);
347             putship(target.shp_uid, &target);
348             count_units(tenderp);
349             putship(tenderp->shp_uid, tenderp);
350             snxtitem_xy(&pni, EF_PLANE, land.lnd_x, land.lnd_y);
351             while (nxtitem(&pni, (s_char *)&plane)) {
352                 if (plane.pln_flags & PLN_LAUNCHED)
353                     continue;
354                 if (plane.pln_land != land.lnd_uid)
355                     continue;
356                 sprintf(buf, "loaded on %s", prship(&target));
357                 gift(target.shp_own, player->cnum, (s_char *)&plane,
358                      EF_PLANE, buf);
359                 makelost(EF_PLANE, plane.pln_own, plane.pln_uid,
360                          plane.pln_x, plane.pln_y);
361                 plane.pln_own = target.shp_own;
362                 makenotlost(EF_PLANE, plane.pln_own, plane.pln_uid,
363                             plane.pln_x, plane.pln_y);
364                 plane.pln_mission = 0;
365                 putplane(plane.pln_uid, &plane);
366             }
367         }
368     }
369     return 0;
370 }