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