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