]> git.pond.sub.org Git - empserver/blob - src/lib/commands/torp.c
Sectors need space for items, deliveries and distribution thresholds.
[empserver] / src / lib / commands / torp.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  *  torp.c: Fire torpedoes at enemy ships
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare
32  *     Thomas Ruschak, 1992
33  *     Ken Stevens, 1995
34  *     Steve McClure, 2000
35  */
36
37 #include <stdio.h>
38 #include "misc.h"
39 #include "player.h"
40 #include "var.h"
41 #include "ship.h"
42 #include "file.h"
43 #include "xy.h"
44 #include "nat.h"
45 #include "nsc.h"
46 #include "news.h"
47 #include "retreat.h"
48 #include "damage.h"
49 #include "commands.h"
50 #include "optlist.h"
51
52 static void anti_torp(int f, int ntorping, int vshipown);
53 static int candchrg(struct shpstr *, struct shpstr *);
54 static int canshoot(struct shpstr *, struct shpstr *);
55 static int cantorp(struct shpstr *, struct shpstr *);
56 static void fire_dchrg(struct shpstr *sp, struct shpstr *targ, int ntargets);
57 static int fire_torp(struct shpstr *, struct shpstr *, int, int);
58
59 int
60 torp(void)
61 {
62     natid vshipown;
63     int range;
64     int dam;
65     int shells;
66     int subno;
67     int victno;
68     double erange;
69     double hitchance;
70     struct shpstr vship;
71     struct shpstr sub;
72     s_char *ptr;
73     double mobcost;
74     struct mchrstr *mcp;
75     struct nstr_item nbst;
76     s_char buf[1024];
77     s_char *sav;
78     int ntorping = 0;
79     s_char prompt[128];
80
81     if (!(sav = getstarg(player->argp[1], "From ship(s)? ", buf)))
82         return RET_SYN;
83     if (!snxtitem(&nbst, EF_SHIP, sav))
84         return RET_SYN;
85     while (nxtitem(&nbst, (s_char *)&sub)) {
86         if (sub.shp_own != player->cnum)
87             continue;
88         if ((mchr[(int)sub.shp_type].m_flags & M_TORP) == 0)
89             continue;
90         shells = sub.shp_item[I_SHELL];
91         if (shells < 3)
92             shells +=
93                 supply_commod(sub.shp_own, sub.shp_x, sub.shp_y, I_SHELL,
94                               3 - shells);
95         if (sub.shp_item[I_GUN] == 0 || shells < 3)
96             continue;
97         if (sub.shp_item[I_MILIT] < 1)
98             continue;
99         if (sub.shp_effic < 60)
100             continue;
101         if (sub.shp_mobil <= 0)
102             continue;
103         ntorping++;
104     }
105     pr("%d ships are eligible to torp\n", ntorping);
106     snxtitem(&nbst, EF_SHIP, sav);
107     while (nxtitem(&nbst, (s_char *)&sub)) {
108         if (!sub.shp_own)
109             continue;
110         if (sub.shp_own != player->cnum) {
111             continue;
112         }
113         if ((mchr[(int)sub.shp_type].m_flags & M_TORP) == 0) {
114             pr("Ship # %d: A %s can't fire torpedoes!\n", sub.shp_uid,
115                mchr[(int)sub.shp_type].m_name);
116             continue;
117         }
118         shells = sub.shp_item[I_SHELL];
119         if (shells < 3)
120             shells +=
121                 supply_commod(sub.shp_own, sub.shp_x, sub.shp_y, I_SHELL,
122                               3 - shells);
123         if (sub.shp_item[I_GUN] == 0 || shells < 3) {
124             pr("Ship #%d has insufficient armament\n", sub.shp_uid);
125             continue;
126         }
127         if (sub.shp_item[I_MILIT] < 1) {
128             pr("Ship #%d has insufficient crew\n", sub.shp_uid);
129             continue;
130         }
131         if (sub.shp_effic < 60) {
132             pr("Ship #%d torpedo tubes inoperative.\n", sub.shp_uid);
133             continue;
134         }
135         if (sub.shp_mobil <= 0) {
136             pr("Ship #%d has insufficient mobility\n", sub.shp_uid);
137             continue;
138         }
139         subno = sub.shp_uid;
140         sprintf(prompt, "Ship %d, target? ", sub.shp_uid);
141         if ((ptr = getstarg(player->argp[2], prompt, buf)) == 0)
142             return RET_SYN;
143         if (!check_ship_ok(&sub))
144             return RET_FAIL;
145         if ((victno = atoi(ptr)) < 0)
146             return RET_SYN;
147         if (!getship(victno, &vship))
148             return RET_FAIL;
149         if (!vship.shp_own)
150             return RET_FAIL;
151         vshipown = vship.shp_own;
152         if (victno == subno) {
153             pr("Shooting yourself, eh?  How strange...\n");
154             continue;
155         }
156         if (mchr[(int)vship.shp_type].m_flags & M_SUB) {
157             if (!(mchr[(int)sub.shp_type].m_flags & M_SUBT)) {
158                 pr("You can't torpedo a submarine!\n");
159                 continue;
160             }
161         }
162         if ((mchr[(int)sub.shp_type].m_flags & M_SUB) == 0)
163             anti_torp(sub.shp_uid, ntorping, vshipown);
164         getship(sub.shp_uid, &sub);
165         if (sub.shp_own == 0) {
166             continue;
167         }
168         erange = ((double)sub.shp_effic / 100.0) *
169             techfact(sub.shp_tech, ((double)sub.shp_frnge));
170         erange = (double)roundrange(erange);
171         pr("Effective torpedo range is %.1f\n", erange);
172         shells -= 3;
173         sub.shp_item[I_SHELL] = shells;
174         putship(sub.shp_uid, &sub);
175         mcp = &mchr[(int)sub.shp_type];
176         mobcost = sub.shp_effic * 0.01 * sub.shp_speed;
177         mobcost = (480.0 / (mobcost + techfact(sub.shp_tech, mobcost)));
178
179         /* Mob cost for a torp is equal to the cost of 1/2 sector of movement */
180         mobcost /= 2.0;
181         sub.shp_mobil -= mobcost;
182         pr("Whooosh... ");
183         getship(victno, &vship);
184         vshipown = vship.shp_own;
185         range = mapdist(sub.shp_x, sub.shp_y, vship.shp_x, vship.shp_y);
186         hitchance = DTORP_HITCHANCE(range, sub.shp_visib);
187         if (range <= erange) {
188             pr("Hitchance = %d%%\n", (int)(hitchance * 100));
189         }
190         /* Now, can the torpedo even get there? */
191         if (!line_of_sight((s_char **)0, sub.shp_x, sub.shp_y,
192                            vship.shp_x, vship.shp_y)) {
193             pr("BOOM!... Torpedo slams into land before reaching target.\n");
194             /* We only tell the victim if we were within range. */
195             if (range <= erange) {
196                 if (vshipown != 0)
197                     wu(0, vshipown, "Torpedo sighted @ %s by %s\n",
198                        xyas(sub.shp_x, sub.shp_y, vshipown),
199                        prship(&vship));
200             }
201         } else if (range > erange) {
202             pr("Out of range\n");
203         } else if (hitchance >= 1.0 || chance(hitchance)) {
204             pr("BOOM!...\n");
205             dam = TORP_DAMAGE();
206             if (vshipown != 0)
207                 wu(0, vshipown, "%s in %s torpedoed %s for %d damage.\n",
208                    prsub(&sub), xyas(sub.shp_x, sub.shp_y, vshipown),
209                    prship(&vship), dam);
210             if (vship.shp_rflags & RET_TORPED) {
211                 retreat_ship(&vship, 't');
212                 shipdamage(&vship, dam);
213             } else
214                 shipdamage(&vship, dam);
215             pr("Torpedo hit %s for %d damage.\n", prship(&vship), dam);
216
217             if (vship.shp_effic < SHIP_MINEFF)
218                 pr("%s sunk!\n", prship(&vship));
219             putship(vship.shp_uid, &vship);
220             if (mchr[(int)sub.shp_type].m_flags & M_SUB)
221                 nreport(vshipown, N_TORP_SHIP, 0, 1);
222             else
223                 nreport(vshipown, N_SHIP_TORP, player->cnum, 1);
224         } else {
225             pr("Missed\n");
226             if (vshipown != 0)
227                 wu(0, vshipown, "Torpedo sighted @ %s by %s\n",
228                    xyas(sub.shp_x, sub.shp_y, vshipown), prship(&vship));
229         }
230         sub.shp_mission = 0;
231         putship(sub.shp_uid, &sub);
232         if (mchr[(int)sub.shp_type].m_flags & M_SUB)
233             anti_torp(sub.shp_uid, ntorping, vshipown);
234     }
235     return RET_OK;
236 }
237
238 static void
239 anti_torp(int f, int ntorping, int vshipown)
240 {
241     int range;
242     double erange;
243     struct shpstr sub;
244     struct shpstr dd;
245     int x;
246
247     getship(f, &sub);
248
249     if (sub.shp_own == vshipown)
250         return;
251
252     if ((mchr[(int)sub.shp_type].m_flags & M_SUB) == 0)
253         pr("Starting our attack run...\n");
254
255     x = 0;
256     while (getship(x++, &dd) && sub.shp_effic >= SHIP_MINEFF) {
257         if (dd.shp_own == 0)
258             continue;
259         if (dd.shp_own != vshipown)
260             continue;
261         if (dd.shp_effic < 60)
262             continue;
263
264         if (!canshoot(&dd, &sub))
265             continue;
266
267         erange = techfact(dd.shp_tech, ((double)dd.shp_frnge)) / 2.0;
268
269         erange = (double)roundrange(erange);
270
271         range = mapdist(sub.shp_x, sub.shp_y, dd.shp_x, dd.shp_y);
272
273         if (range > erange)
274             continue;
275
276         if (!line_of_sight((s_char **)0, sub.shp_x, sub.shp_y,
277                            dd.shp_x, dd.shp_y))
278             continue;
279
280         if (cantorp(&dd, &sub)) {
281             /* Try torping.. if we can, maybe we can fire */
282             if (!fire_torp(&dd, &sub, range, ntorping))
283                 if (candchrg(&dd, &sub))
284                     fire_dchrg(&dd, &sub, ntorping);
285         } else
286             fire_dchrg(&dd, &sub, ntorping);
287     }
288 }
289
290 /* Can ship A shoot at ship B? */
291 static int
292 canshoot(struct shpstr *a, struct shpstr *b)
293 {
294     /* Anyone can shoot a normal ship */
295     if ((mchr[(int)b->shp_type].m_flags & M_SUB) == 0)
296         return 1;
297
298     /* You can depth-charge a sub */
299     if (mchr[(int)a->shp_type].m_flags & M_DCH)
300         return 1;
301
302     /* If you have SUBT flag, you can torp a sub */
303     if (mchr[(int)a->shp_type].m_flags & M_SUBT)
304         return 1;
305
306     return 0;
307 }
308
309 /* Can ship A torp ship B? */
310 static int
311 cantorp(struct shpstr *a, struct shpstr *b)
312 {
313     if ((mchr[(int)a->shp_type].m_flags & M_TORP) == 0)
314         return 0;
315
316     /* Anyone with TORP flag can torp a normal ship */
317     if ((mchr[(int)b->shp_type].m_flags & M_SUB) == 0)
318         return 1;
319
320     /* Ship b is a sub, so we need to have the SUBT flag */
321     if (mchr[(int)a->shp_type].m_flags & M_SUBT)
322         return 1;
323
324     return 0;
325 }
326
327 /* Can ship A depth-charge (or fire guns at) ship B? */
328 static int
329 candchrg(struct shpstr *a, struct shpstr *b)
330 {
331     if ((mchr[(int)b->shp_type].m_flags & M_SUB) == 0) {
332         if ((mchr[(int)a->shp_type].m_flags & M_SUB) == 0)
333             return 1;
334
335         return 0;
336     }
337
338     if ((mchr[(int)a->shp_type].m_flags & M_DCH) == 0)
339         return 0;
340
341     return 1;
342 }
343
344 static void
345 fire_dchrg(struct shpstr *sp, struct shpstr *targ, int ntargets)
346 {
347     int dam;
348     int shells;
349     int gun;
350     double guneff;
351
352     shells = sp->shp_item[I_SHELL];
353     gun = sp->shp_item[I_GUN];
354     gun = min(gun, sp->shp_glim);
355     gun = min(gun, sp->shp_item[I_MILIT] / 2);
356
357     shells +=
358         supply_commod(sp->shp_own, sp->shp_x, sp->shp_y, I_SHELL,
359                       (gun + 1) / 2 - shells);
360
361     gun = min(gun, shells * 2);
362     if (gun == 0)
363         return;
364
365     /* ok, all set.. now, we shoot */
366     shells -= ldround(((double)gun) / 2.0, 1);
367     sp->shp_item[I_SHELL] = shells;
368     putship(sp->shp_uid, sp);
369
370     guneff = seagun(sp->shp_effic, gun);
371     dam = (int)guneff;
372
373     if ((mchr[(int)targ->shp_type].m_flags & M_SUB) == 0) {
374         pr_beep();
375         pr("Kaboom!!! Incoming shells!\n");
376         if (sp->shp_own != 0)
377             wu(0, sp->shp_own,
378                "%s fired at %s\n", prship(sp), prship(targ));
379
380         if (ntargets > 2)
381             dam /= ((float)ntargets / 2.0);
382         pr_beep();
383         pr("BLAM! %d damage!\n", dam);
384         shipdamage(targ, dam);
385         putship(targ->shp_uid, targ);
386     } else {
387         pr("\nCAPTAIN!  !!Depth charges!!...\n");
388         if (sp->shp_own != 0)
389             wu(0, sp->shp_own,
390                "%s depth charged %s\n", prship(sp), prsub(targ));
391
392         if (ntargets > 2)
393             dam /= ((float)ntargets / 2.0);
394
395         pr("click...WHAM!  %d damage!\n", dam);
396         shipdamage(targ, dam);
397         putship(targ->shp_uid, targ);
398     }
399 }
400
401 static int
402 fire_torp(struct shpstr *sp, struct shpstr *targ, int range, int ntargets)
403 {
404     int dam;
405     int shells;
406     double hitchance;
407     double mobcost;
408     struct mchrstr *mcp;
409
410     shells = sp->shp_item[I_SHELL];
411
412     if (shells < 3)
413         shells += supply_commod(sp->shp_own, sp->shp_x, sp->shp_y, I_SHELL,
414                                 3 - shells);
415
416     if (sp->shp_item[I_GUN] == 0 || shells < 3)
417         return 0;
418
419     if (sp->shp_item[I_MILIT] < 1)
420         return 0;
421
422     if (sp->shp_effic < 60)
423         return 0;
424
425     if (sp->shp_mobil <= 0)
426         return 0;
427
428     /* All set.. fire! */
429     shells -= 3;
430     sp->shp_item[I_SHELL] = shells;
431     putship(sp->shp_uid, sp);
432
433     mcp = &mchr[(int)sp->shp_type];
434     mobcost = sp->shp_effic * 0.01 * sp->shp_speed;
435     mobcost = (480.0 / (mobcost + techfact(sp->shp_tech, mobcost)));
436
437     /* Mob cost for a torp is equal to the cost of 1/2 sector of movement */
438     mobcost /= 2.0;
439     sp->shp_mobil -= mobcost;
440
441     hitchance = DTORP_HITCHANCE(range, sp->shp_visib);
442
443     pr("Captain! Torpedoes sighted!\n");
444
445     if (chance(hitchance)) {
446         pr("BOOM!...\n");
447         if (sp->shp_own != 0)
448             wu(0, sp->shp_own, "%s @ %s torpedoed %s\n",
449                prship(sp),
450                xyas(sp->shp_x, sp->shp_y, sp->shp_own), prsub(targ));
451         dam = TORP_DAMAGE();
452
453         if (ntargets > 2)
454             dam /= ((float)ntargets / 2.0);
455
456         shipdamage(targ, dam);
457         putship(targ->shp_uid, targ);
458
459         if (mchr[(int)sp->shp_type].m_flags & M_SUB)
460             nreport(targ->shp_own, N_TORP_SHIP, 0, 1);
461         else
462             nreport(targ->shp_own, N_SHIP_TORP, player->cnum, 1);
463     } else {
464         pr("Missed!\n");
465         if (sp->shp_own != 0)
466             wu(0, sp->shp_own,
467                "%s missed %s with a torp at %s\n",
468                prship(sp), prsub(targ),
469                xyas(sp->shp_x, sp->shp_y, sp->shp_own));
470     }
471
472     return 1;
473 }
474
475 s_char *
476 prsub(struct shpstr *sp)
477 {
478     if (mchr[(int)sp->shp_type].m_flags & M_SUB)
479         return "sub";
480     else
481         return prship(sp);
482 }