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