]> git.pond.sub.org Git - empserver/blob - src/lib/commands/trad.c
Get rid of RET_SYS, just use RET_FAIL
[empserver] / src / lib / commands / trad.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, 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 files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  trad.c: Buy units/ships/planes/nukes from other nations.
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Pat Loney, 1992
33  *     Steve McClure, 1996-2000
34  */
35
36 #include <config.h>
37
38 #include <ctype.h>
39 #include "commands.h"
40 #include "commodity.h"
41 #include "empobj.h"
42 #include "land.h"
43 #include "loan.h"
44 #include "news.h"
45 #include "nuke.h"
46 #include "optlist.h"
47 #include "plane.h"
48 #include "ship.h"
49 #include "trade.h"
50
51 /*
52  * format: trade
53  */
54 int
55 trad(void)
56 {
57     struct sctstr sect;
58     struct natstr *natp;
59     struct comstr comt;
60     int lotno;
61     float price;
62     coord sx, sy;
63     int n;
64     char *p;
65     struct nstr_item ni;
66     struct trdstr trade;
67     struct trdstr tmpt;
68     union empobj_storage tg;
69     double canspend;
70     time_t now;
71     int bid;
72     double tleft;
73     double tally;
74     int q;
75     char buf[1024];
76
77     if (!opt_MARKET) {
78         pr("The market is disabled.\n");
79         return RET_FAIL;
80     }
81     /* First, we execute all trades, so that we can only buy what is available. */
82     check_market();
83     check_trade();
84
85     pr("\n     Empire Trade Report\n  ");
86     prdate();
87     n = 0;
88     pr(" lot high bid  by time left owner  description\n");
89     pr(" --- --------  -- --------- -----  -------------------------\n");
90
91     snxtitem_all(&ni, EF_TRADE);
92     while (nxtitem(&ni, &trade)) {
93         if (trade.trd_owner == 0)
94             continue;
95         if (!trade_getitem(&trade, &tg)) {
96             continue;
97         };
98         pr(" %3d ", ni.cur);
99         (void)time(&now);
100         tleft =
101             TRADE_DELAY / 3600.0 - (now - trade.trd_markettime) / 3600.0;
102         if (tleft < 0.0)
103             tleft = 0.0;
104         pr("$%7ld  %2d %5.2f hrs ",
105            trade.trd_price, trade.trd_maxbidder, tleft);
106         (void)trade_desc(&trade, &tg);  /* XXX */
107         pr("\n");
108         if (trade.trd_owner == player->cnum && !player->god)
109             pr(" (your own lot)\n");
110         n++;
111     }
112     if (n == 0) {
113         pr("Nothing to buy at the moment...\n");
114         return RET_OK;
115     }
116     if ((p = getstring("Which lot to buy: ", buf)) == 0 || *p == 0)
117         return RET_OK;
118     if (isdigit(*p) == 0)
119         return RET_OK;
120     lotno = atoi(p);
121     if (lotno < 0 || lotno >= ni.cur) {
122         pr("Bad lot number\n");
123         return RET_OK;
124     }
125     if (!gettrade(lotno, &trade)) {
126         pr("No such lot number\n");
127         return RET_OK;
128     }
129     if (trade.trd_unitid < 0) {
130         pr("Invalid lot number.\n");
131         return RET_OK;
132     }
133     if (!trade_getitem(&trade, &tg)) {
134         pr("Can't find trade #%d!\n", trade.trd_unitid);
135         trade.trd_unitid = -1;
136         if (!puttrade(lotno, &trade)) {
137             logerror("trad: can't write trade");
138             pr("Couldn't save after getitem failed; get help!\n");
139             return RET_FAIL;
140         }
141         return RET_OK;
142     }
143     switch (trade.trd_type) {
144     case EF_NUKE:
145     case EF_PLANE:
146     case EF_SHIP:
147     case EF_LAND:
148         break;
149     default:
150         pr("Bad unit type on lot number %d\n", lotno);
151         return RET_FAIL;
152     }
153     if (trade.trd_owner == player->cnum) {
154         pr("You can't buy from yourself!\n");
155         return RET_OK;
156     }
157     price = trade.trd_price;
158     natp = getnatp(player->cnum);
159     if (natp->nat_money < price) {
160         pr("You don't have %.2f to spend!\n", price);
161         return RET_OK;
162     }
163     tally = 0.0;
164     for (q = 0; gettrade(q, &tmpt); q++) {
165         if (tmpt.trd_maxbidder == player->cnum &&
166             tmpt.trd_unitid >= 0 && tmpt.trd_owner != player->cnum) {
167             tally += tmpt.trd_price * tradetax;
168         }
169     }
170     for (q = 0; getcomm(q, &comt); q++) {
171         if (comt.com_maxbidder == player->cnum &&
172             comt.com_owner != 0 && comt.com_owner != player->cnum) {
173             tally += (comt.com_price * comt.com_amount) * buytax;
174         }
175     }
176     canspend = natp->nat_money - tally;
177     /*
178      * Find the destination sector for the plane before the trade is
179      * actually made, except for satellites in orbit.  Must be owned
180      * and must be a 60% airfield (except for VTOL planes).
181      */
182     if (((trade.trd_type == EF_PLANE) && !pln_is_in_orbit(&tg.plane))
183         || (trade.trd_type == EF_NUKE)) {
184         while (1) {
185             p = getstring("Destination sector: ", buf);
186             if (!trade_check_ok(&trade, &tg))
187                 return RET_FAIL;
188             if (p == 0) {
189                 return RET_FAIL;
190             }
191             if (!sarg_xy(p, &sx, &sy) || !getsect(sx, sy, &sect)) {
192                 pr("Bad sector designation; try again!\n");
193                 continue;
194             }
195             if (!player->owner) {
196                 pr("You don't own that sector; try again!\n");
197                 continue;
198             }
199             if (!(plchr[tg.plane.pln_type].pl_flags & P_V)) {
200                 if (!player->god && (sect.sct_type != SCT_AIRPT)) {
201                     pr("Destination sector is not an airfield!\n");
202                     continue;
203                 }
204                 if (!player->god && (sect.sct_effic < 60)) {
205                     pr("That airport still under construction!\n");
206                     continue;
207                 }
208             }
209             break;
210         }
211     }
212     if (trade.trd_type == EF_LAND) {
213         while (1) {
214             p = getstring("Destination sector: ", buf);
215             if (!trade_check_ok(&trade, &tg))
216                 return RET_FAIL;
217             if (p == 0) {
218                 return RET_FAIL;
219             }
220             if (!sarg_xy(p, &sx, &sy) || !getsect(sx, sy, &sect)) {
221                 pr("Bad sector designation; try again!\n");
222                 continue;
223             }
224             if (!player->owner) {
225                 pr("You don't own that sector; try again!\n");
226                 continue;
227             }
228             if (!player->god && (sect.sct_type != SCT_HEADQ)) {
229                 pr("Destination sector is not a headquarters!\n");
230                 continue;
231             }
232             if (!player->god && (sect.sct_effic < 60)) {
233                 pr("That headquarters still under construction!\n");
234                 continue;
235             }
236             break;
237         }
238     }
239
240     if ((p = getstring("How much do you bid: ", buf)) == 0 || *p == 0)
241         return RET_OK;
242     if (!trade_check_ok(&trade, &tg))
243         return RET_FAIL;
244     bid = atoi(p);
245     if (bid < price)
246         bid = price;
247     if (bid > canspend) {
248         pr("You don't have %.2f to spend!\n", price);
249         return RET_OK;
250     }
251     if (bid > trade.trd_price) {
252         /* Add five minutes to the time if less than 5 minutes left. */
253         time(&now);
254         if (((TRADE_DELAY - (now - trade.trd_markettime)) < 300) &&
255             trade.trd_maxbidder != player->cnum)
256             trade.trd_markettime += 300;
257         trade.trd_price = bid;
258         trade.trd_maxbidder = player->cnum;
259         trade.trd_x = sx;
260         trade.trd_y = sy;
261         pr("Your bid on lot #%d is being considered.\n", lotno);
262         if (!puttrade(lotno, &trade))
263             pr("Problems with the trade file.  Get help\n");
264     } else
265         pr("Your bid wasn't high enough (you need to bid more than someone else.)\n");
266
267     check_trade();
268
269     return RET_OK;
270 }
271
272 int
273 check_trade(void)
274 {
275     int n;
276     struct nstr_item ni;
277     struct plnstr plane;
278     struct lndstr land;
279     struct natstr *natp;
280     struct trdstr trade;
281     union empobj_storage tg;
282     time_t now;
283     double tleft;
284     float price;
285     int saveid;
286
287 /*    logerror("Checking the trades.\n");*/
288     for (n = 0; gettrade(n, &trade); n++) {
289         if (trade.trd_unitid < 0)
290             continue;
291         if (!trade_getitem(&trade, &tg))
292             continue;
293         if (tg.gen.own == 0) {
294             trade.trd_unitid = -1;
295             puttrade(n, &trade);
296             continue;
297         }
298         if (tg.gen.own != trade.trd_owner) {
299             logerror("Something weird, tg.gen.own != trade.trd_owner!\n");
300             trade.trd_unitid = -1;
301             puttrade(n, &trade);
302             continue;
303         }
304
305         if (trade.trd_owner == trade.trd_maxbidder)
306             continue;
307
308         (void)time(&now);
309         tleft =
310             TRADE_DELAY / 3600.0 - (now - trade.trd_markettime) / 3600.0;
311         if (tleft < 0.0)
312             tleft = 0.0;
313         if (tleft > 0.0)
314             continue;
315
316         saveid = trade.trd_unitid;
317         trade.trd_unitid = -1;
318         if (!puttrade(n, &trade)) {
319             logerror("Couldn't save trade after purchase; get help!\n");
320             continue;
321         }
322
323         price = trade.trd_price;
324         natp = getnatp(trade.trd_maxbidder);
325         if (natp->nat_money < price) {
326             nreport(trade.trd_maxbidder, N_WELCH_DEAL, trade.trd_owner, 1);
327             wu(0, trade.trd_owner,
328                "%s tried to buy a %s #%d from you for $%.2f\n",
329                cname(trade.trd_maxbidder), trade_nameof(&trade, &tg),
330                saveid, price * tradetax);
331             wu(0, trade.trd_owner, "   but couldn't afford it.\n");
332             wu(0, trade.trd_owner,
333                "   Your item was taken off the market.\n");
334             wu(0, trade.trd_maxbidder,
335                "You tried to buy %s #%d from %s for $%.2f\n",
336                trade_nameof(&trade, &tg), saveid, cname(trade.trd_owner),
337                price);
338             wu(0, trade.trd_maxbidder, "but couldn't afford it.\n");
339             continue;
340         }
341
342 /* If we get this far, the sale will go through. */
343
344         natp->nat_money -= price;
345         putnat(natp);
346
347         natp = getnatp(trade.trd_owner);
348         natp->nat_money += roundavg(price * tradetax);
349         putnat(natp);
350
351         switch (trade.trd_type) {
352         case EF_NUKE:
353             tg.nuke.nuk_x = trade.trd_x;
354             tg.nuke.nuk_y = trade.trd_y;
355             makelost(EF_NUKE, tg.nuke.nuk_own, tg.nuke.nuk_uid,
356                      tg.nuke.nuk_x, tg.nuke.nuk_y);
357             tg.nuke.nuk_own = trade.trd_maxbidder;
358             makenotlost(EF_NUKE, tg.nuke.nuk_own, tg.nuke.nuk_uid,
359                         tg.nuke.nuk_x, tg.nuke.nuk_y);
360             break;
361         case EF_PLANE:
362             if (!pln_is_in_orbit(&tg.plane)) {
363                 tg.plane.pln_x = trade.trd_x;
364                 tg.plane.pln_y = trade.trd_y;
365             }
366             makelost(EF_PLANE, tg.plane.pln_own, tg.plane.pln_uid,
367                      tg.plane.pln_x, tg.plane.pln_y);
368             tg.plane.pln_own = trade.trd_maxbidder;
369             makenotlost(EF_PLANE, tg.plane.pln_own, tg.plane.pln_uid,
370                         tg.plane.pln_x, tg.plane.pln_y);
371             tg.plane.pln_wing = 0;
372             /* no cheap version of fly */
373             if (opt_MOB_ACCESS) {
374                 tg.plane.pln_mobil = -(etu_per_update / sect_mob_neg_factor);
375                 game_tick_to_now(&tg.plane.pln_access);
376             } else {
377                 tg.plane.pln_mobil = 0;
378             }
379             tg.plane.pln_mission = 0;
380             tg.plane.pln_harden = 0;
381             tg.plane.pln_ship = -1;
382             tg.plane.pln_land = -1;
383             break;
384         case EF_SHIP:
385             takeover_ship(&tg.ship, trade.trd_maxbidder, 0);
386             break;
387         case EF_LAND:
388             tg.land.lnd_x = trade.trd_x;
389             tg.land.lnd_y = trade.trd_y;
390             if (tg.land.lnd_ship >= 0) {
391                 struct shpstr ship;
392                 getship(tg.land.lnd_ship, &ship);
393                 ship.shp_nland--;
394                 putship(ship.shp_uid, &ship);
395             }
396             makelost(EF_LAND, tg.land.lnd_own, tg.land.lnd_uid,
397                      tg.land.lnd_x, tg.land.lnd_y);
398             tg.land.lnd_own = trade.trd_maxbidder;
399             makenotlost(EF_LAND, tg.land.lnd_own, tg.land.lnd_uid,
400                         tg.land.lnd_x, tg.land.lnd_y);
401             tg.land.lnd_army = 0;
402             /* no cheap version of fly */
403             if (opt_MOB_ACCESS) {
404                 tg.land.lnd_mobil = -(etu_per_update / sect_mob_neg_factor);
405                 game_tick_to_now(&tg.land.lnd_access);
406             } else {
407                 tg.land.lnd_mobil = 0;
408             }
409             tg.land.lnd_harden = 0;
410             tg.land.lnd_mission = 0;
411             /* Drop any land units this unit was carrying */
412             snxtitem_xy(&ni, EF_LAND, tg.land.lnd_x, tg.land.lnd_y);
413             while (nxtitem(&ni, &land)) {
414                 if (land.lnd_land != tg.land.lnd_uid)
415                     continue;
416                 land.lnd_land = -1;
417                 wu(0, land.lnd_own, "unit #%d dropped in %s\n",
418                    land.lnd_uid,
419                    xyas(land.lnd_x, land.lnd_y, land.lnd_own));
420                 putland(land.lnd_uid, &land);
421             }
422             /* Drop any planes this unit was carrying */
423             snxtitem_xy(&ni, EF_PLANE, tg.land.lnd_x, tg.land.lnd_y);
424             while (nxtitem(&ni, &plane)) {
425                 if (plane.pln_flags & PLN_LAUNCHED)
426                     continue;
427                 if (plane.pln_land != land.lnd_uid)
428                     continue;
429                 plane.pln_land = -1;
430                 wu(0, plane.pln_own, "plane #%d dropped in %s\n",
431                    plane.pln_uid,
432                    xyas(plane.pln_x, plane.pln_y, plane.pln_own));
433                 putplane(plane.pln_uid, &plane);
434             }
435             tg.land.lnd_ship = -1;
436             tg.land.lnd_land = -1;
437             break;
438         default:
439             logerror("Bad trade type %d in trade\n", trade.trd_type);
440             break;
441         }
442         if (!ef_write(trade.trd_type, saveid, &tg)) {
443             logerror("Couldn't write unit to disk; seek help.\n");
444             continue;
445         }
446         nreport(trade.trd_owner, N_MAKE_SALE, trade.trd_maxbidder, 1);
447         wu(0, trade.trd_owner, "%s bought a %s #%d from you for $%.2f\n",
448            cname(trade.trd_maxbidder), trade_nameof(&trade, &tg),
449            saveid, price * tradetax);
450         wu(0, trade.trd_maxbidder,
451            "The bidding is over & you bought %s #%d from %s for $%.2f\n",
452            trade_nameof(&trade, &tg), saveid, cname(trade.trd_owner),
453            price);
454     }
455 /*    logerror("Done checking the trades.\n");*/
456     return RET_OK;
457 }
458
459 int
460 ontradingblock(int type, void *ptr)
461 {
462     struct trdstr trade;
463     union empobj_storage tg;
464     int n;
465
466     for (n = 0; gettrade(n, &trade); n++) {
467         if (trade.trd_unitid < 0)
468             continue;
469         if (!trade_getitem(&trade, &tg))
470             continue;
471         if (trade.trd_type != type)
472             continue;
473         if (tg.gen.uid == ((struct empobj *)ptr)->uid)
474             return 1;
475     }
476     return 0;
477 }
478
479 void
480 trdswitchown(int type, void *ptr, int newown)
481 {
482     struct trdstr trade;
483     union empobj_storage tg;
484     int n;
485
486     for (n = 0; gettrade(n, &trade); n++) {
487         if (trade.trd_unitid < 0)
488             continue;
489         if (!trade_getitem(&trade, &tg))
490             continue;
491         if (trade.trd_type != type)
492             continue;
493         if (tg.gen.uid != ((struct empobj *)ptr)->uid)
494             continue;
495         if (trade.trd_owner == trade.trd_maxbidder)
496             trade.trd_maxbidder = newown;
497         trade.trd_owner = newown;
498         if (newown == 0)
499             trade.trd_unitid = -1;
500         puttrade(n, &trade);
501         return;
502     }
503 }