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