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