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