]> git.pond.sub.org Git - empserver/blob - src/lib/commands/tend.c
Sectors need space for items, deliveries and distribution thresholds.
[empserver] / src / lib / commands / tend.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  *  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 = whatitem(p, (s_char *)0)))
84         type = EF_SECTOR;
85     else {
86         pr("Bad commodity.\n");
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 = vl_find(ip->i_vtype, vbase->m_vtype,
125                             vbase->m_vamt, (int)vbase->m_nv);
126         if (maxtender == 0) {
127             pr("A %s cannot hold any %s\n",
128                mchr[(int)tender.shp_type].m_name, ip->i_name);
129             break;
130         }
131         if (!snxtitem(&targets, EF_SHIP,
132                       getstarg(player->argp[4], "Ships to be tended? ",
133                                buf)))
134             break;
135         if (!check_ship_ok(&tender))
136             return RET_SYN;
137         total = 0;
138         while (tend_nxtitem(&targets, (s_char *)&target)) {
139             if (!player->owner &&
140                 (getrel(getnatp(target.shp_own), player->cnum) < FRIENDLY))
141                 continue;
142             if (target.shp_uid == tender.shp_uid)
143                 continue;
144             if (tender.shp_x != target.shp_x ||
145                 tender.shp_y != target.shp_y)
146                 continue;
147             ontarget = target.shp_item[ip->i_vtype];
148             if (ontarget == 0 && amt < 0) {
149                 pr("No %s on %s\n", ip->i_name, prship(&target));
150                 continue;
151             }
152             vbase = &mchr[(int)target.shp_type];
153             maxtarget = vl_find(ip->i_vtype, vbase->m_vtype,
154                                 vbase->m_vamt, (int)vbase->m_nv);
155             if (amt < 0) {
156                 if (!player->owner)
157                     amt = 0;
158
159                 /* take from target and give to tender */
160                 transfer = min(ontarget, -amt);
161                 transfer = min(maxtender - ontender, transfer);
162                 if (transfer == 0)
163                     continue;
164                 target.shp_item[ip->i_vtype] = ontarget - transfer;
165                 ontender += transfer;
166                 total += transfer;
167             } else {
168                 /* give to target from tender */
169                 transfer = min(ontender, amt);
170                 transfer = min(transfer, maxtarget - ontarget);
171                 if (transfer == 0)
172                     continue;
173                 target.shp_item[ip->i_vtype] = ontarget + transfer;
174                 ontender -= transfer;
175                 total += transfer;
176             }
177             expose_ship(&tender, &target);
178             putship(target.shp_uid, &target);
179             if (amt > 0 && ontender == 0) {
180                 pr("%s out of %s\n", prship(&tender), ip->i_name);
181                 break;
182             }
183         }
184         pr("%d total %s transferred %s %s\n",
185            total, ip->i_name, (amt > 0) ? "off of" : "to",
186            prship(&tender));
187         tender.shp_item[ip->i_vtype] = ontender;
188         tender.shp_mission = 0;
189         putship(tender.shp_uid, &tender);
190     }
191     return RET_OK;
192 }
193
194 static void
195 expose_ship(struct shpstr *s1, struct shpstr *s2)
196 {
197     if (s1->shp_pstage == PLG_INFECT && s2->shp_pstage == PLG_HEALTHY)
198         s2->shp_pstage = PLG_EXPOSED;
199     if (s2->shp_pstage == PLG_INFECT && s1->shp_pstage == PLG_HEALTHY)
200         s1->shp_pstage = PLG_EXPOSED;
201 }
202
203 /*
204  * tend_nxtitem.c
205  *
206  * get next item from list. Stolen from nxtitem to make 1 itsy-bitsy change
207  *
208  * Dave Pare, 1989
209  */
210
211 int
212 tend_nxtitem(struct nstr_item *np, caddr_t ptr)
213 {
214     struct genitem *gp;
215     int selected;
216
217     if (np->sel == NS_UNDEF)
218         return 0;
219     gp = (struct genitem *)ptr;
220     do {
221         if (np->sel == NS_LIST) {
222             np->index++;
223             if (np->index >= np->size)
224                 return 0;
225             np->cur = np->list[np->index];
226         } else {
227             np->cur++;
228         }
229         if (!np->read(np->type, np->cur, ptr)) {
230             /* if read fails, fatal */
231             return 0;
232         }
233         selected = 1;
234         switch (np->sel) {
235         case NS_LIST:
236             /* The change is to take the player->owner check out here */
237             break;
238         case NS_ALL:
239             /* XXX maybe combine NS_LIST and NS_ALL later */
240             break;
241         case NS_DIST:
242             if (!xyinrange(gp->x, gp->y, &np->range)) {
243                 selected = 0;
244                 break;
245             }
246             np->curdist = mapdist((int)gp->x, (int)gp->y,
247                                   (int)np->cx, (int)np->cy);
248             if (np->curdist > np->dist)
249                 selected = 0;
250             break;
251         case NS_AREA:
252             if (!xyinrange(gp->x, gp->y, &np->range))
253                 selected = 0;
254             if (gp->x == np->range.hx || gp->y == np->range.hy)
255                 selected = 0;
256             break;
257         case NS_XY:
258             if (gp->x != np->cx || gp->y != np->cy)
259                 selected = 0;
260             break;
261         case NS_GROUP:
262             if (np->group != gp->group)
263                 selected = 0;
264             break;
265         default:
266             logerror("nxtitem: bad selector %d\n", np->sel);
267             return 0;
268         }
269         if (selected && np->ncond) {
270             /* nstr_exec is expensive, so we do it last */
271             if (!nstr_exec(np->cond, np->ncond, ptr, np->type))
272                 selected = 0;
273         }
274     } while (!selected);
275     return 1;
276 }
277
278 static int
279 tend_land(struct shpstr *tenderp, s_char *units)
280 {
281     struct nstr_item lni;
282     struct nstr_item targets;
283     struct shpstr target;
284     struct lndstr land;
285     struct plnstr plane;
286     struct nstr_item pni;
287     s_char buf[1024];
288
289     if (!snxtitem(&lni, EF_LAND, units))
290         return RET_SYN;
291
292     while (nxtitem(&lni, (s_char *)&land)) {
293         if (!player->owner)
294             continue;
295         if (land.lnd_ship != tenderp->shp_uid) {
296             pr("%s is not on %s!\n", prland(&land), prship(tenderp));
297             continue;
298         }
299         if (!(lchr[(int)land.lnd_type].l_flags & L_ASSAULT)) {
300             pr("%s does not have \"assault\" capability and can't be tended\n", prland(&land));
301             continue;
302         }
303         if (!snxtitem(&targets, EF_SHIP,
304                       getstarg(player->argp[4], "Ship to be tended? ",
305                                buf)))
306             break;
307         if (!check_land_ok(&land))
308             return RET_SYN;
309         while (tend_nxtitem(&targets, (s_char *)&target)) {
310             if (!player->owner &&
311                 (getrel(getnatp(target.shp_own), player->cnum) < FRIENDLY))
312                 continue;
313             if (target.shp_uid == tenderp->shp_uid)
314                 continue;
315             if (tenderp->shp_x != target.shp_x ||
316                 tenderp->shp_y != target.shp_y)
317                 continue;
318
319             /* Fit unit on ship */
320             count_units(&target);
321             getship(target.shp_uid, &target);
322
323             if (target.shp_nland >= mchr[(int)target.shp_type].m_nland) {
324                 if (mchr[(int)target.shp_type].m_nland)
325                     pr("%s doesn't have room for any more land units!\n",
326                        prship(&target));
327                 else
328                     pr("%s doesn't carry land units!\n", prship(&target));
329                 continue;
330             }
331             pr("%s transferred from %s to %s\n",
332                prland(&land), prship(tenderp), prship(&target));
333             sprintf(buf, "loaded on your %s at %s",
334                     prship(&target), xyas(target.shp_x, target.shp_y,
335                                           target.shp_own));
336             gift(target.shp_own, player->cnum, (s_char *)&land,
337                  EF_LAND, buf);
338             makelost(EF_LAND, land.lnd_own, land.lnd_uid, land.lnd_x,
339                      land.lnd_y);
340             land.lnd_own = target.shp_own;
341             makenotlost(EF_LAND, land.lnd_own, land.lnd_uid, land.lnd_x,
342                         land.lnd_y);
343             land.lnd_ship = target.shp_uid;
344             land.lnd_harden = 0;
345             land.lnd_mission = 0;
346             target.shp_nland++;
347             putland(land.lnd_uid, &land);
348             expose_ship(tenderp, &target);
349             putship(target.shp_uid, &target);
350             count_units(tenderp);
351             putship(tenderp->shp_uid, tenderp);
352             snxtitem_xy(&pni, EF_PLANE, land.lnd_x, land.lnd_y);
353             while (nxtitem(&pni, (s_char *)&plane)) {
354                 if (plane.pln_flags & PLN_LAUNCHED)
355                     continue;
356                 if (plane.pln_land != land.lnd_uid)
357                     continue;
358                 sprintf(buf, "loaded on %s", prship(&target));
359                 gift(target.shp_own, player->cnum, (s_char *)&plane,
360                      EF_PLANE, buf);
361                 makelost(EF_PLANE, plane.pln_own, plane.pln_uid,
362                          plane.pln_x, plane.pln_y);
363                 plane.pln_own = target.shp_own;
364                 makenotlost(EF_PLANE, plane.pln_own, plane.pln_uid,
365                             plane.pln_x, plane.pln_y);
366                 plane.pln_mission = 0;
367                 putplane(plane.pln_uid, &plane);
368             }
369         }
370     }
371     return 0;
372 }