]> git.pond.sub.org Git - empserver/blob - src/lib/commands/torp.c
Indented with src/scripts/indent-emp.
[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 void anti_torp(int f, int ntorping, int vshipown);
52 void fire_dchrg(struct shpstr *sp, struct shpstr *targ, int range,
53                 int ntargets);
54
55 s_char *prsub(struct shpstr *sp);
56
57 int
58 torp(void)
59 {
60     extern int torpedo_damage;
61     natid vshipown;
62     int range;
63     int dam;
64     int shells;
65     int subno;
66     int victno;
67     double erange;
68     double hitchance;
69     struct shpstr vship;
70     struct shpstr sub;
71     s_char *ptr;
72     double mobcost;
73     struct mchrstr *mcp;
74     struct nstr_item nbst;
75     s_char buf[1024];
76     s_char *sav;
77     int ntorping = 0;
78     s_char prompt[128];
79
80     if (!(sav = getstarg(player->argp[1], "From ship(s)? ", buf)))
81         return RET_SYN;
82     if (!snxtitem(&nbst, EF_SHIP, sav))
83         return RET_SYN;
84     while (nxtitem(&nbst, (s_char *)&sub)) {
85         if (sub.shp_own != player->cnum)
86             continue;
87         if ((mchr[(int)sub.shp_type].m_flags & M_TORP) == 0)
88             continue;
89         shells = getvar(V_SHELL, (s_char *)&sub, EF_SHIP);
90         if (shells < 3)
91             shells +=
92                 supply_commod(sub.shp_own, sub.shp_x, sub.shp_y, I_SHELL,
93                               3 - shells);
94         if (getvar(V_GUN, (s_char *)&sub, EF_SHIP) == 0 || shells < 3)
95             continue;
96         if (getvar(V_MILIT, (s_char *)&sub, EF_SHIP) < 1)
97             continue;
98         if (sub.shp_effic < 60)
99             continue;
100         if (sub.shp_mobil <= 0)
101             continue;
102         ntorping++;
103     }
104     pr("%d ships are eligible to torp\n", ntorping);
105     snxtitem(&nbst, EF_SHIP, sav);
106     while (nxtitem(&nbst, (s_char *)&sub)) {
107         if (!sub.shp_own)
108             continue;
109         if (sub.shp_own != player->cnum) {
110             continue;
111         }
112         if ((mchr[(int)sub.shp_type].m_flags & M_TORP) == 0) {
113             pr("Ship # %d: A %s can't fire torpedoes!\n", sub.shp_uid,
114                mchr[(int)sub.shp_type].m_name);
115             continue;
116         }
117         shells = getvar(V_SHELL, (s_char *)&sub, EF_SHIP);
118         if (shells < 3)
119             shells +=
120                 supply_commod(sub.shp_own, sub.shp_x, sub.shp_y, I_SHELL,
121                               3 - shells);
122         if (getvar(V_GUN, (s_char *)&sub, EF_SHIP) == 0 || shells < 3) {
123             pr("Ship #%d has insufficient armament\n", sub.shp_uid);
124             continue;
125         }
126         if (getvar(V_MILIT, (s_char *)&sub, EF_SHIP) < 1) {
127             pr("Ship #%d has insufficient crew\n", sub.shp_uid);
128             continue;
129         }
130         if (sub.shp_effic < 60) {
131             pr("Ship #%d torpedo tubes inoperative.\n", sub.shp_uid);
132             continue;
133         }
134         if (sub.shp_mobil <= 0) {
135             pr("Ship #%d has insufficient mobility\n", sub.shp_uid);
136             continue;
137         }
138         bzero(buf, 80);
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         putvar(V_SHELL, shells, (s_char *)&sub, EF_SHIP);
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 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, range, ntorping);
285         } else
286             fire_dchrg(&dd, &sub, range, ntorping);
287     }
288 }
289
290 /* Can ship A shoot at ship B? */
291 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 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 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 void
345 fire_dchrg(struct shpstr *sp, struct shpstr *targ, int range, int ntargets)
346 {
347     int dam;
348     int shells;
349     int gun;
350     double guneff;
351
352     shells = getvar(V_SHELL, (s_char *)sp, EF_SHIP);
353     gun = getvar(V_GUN, (s_char *)sp, EF_SHIP);
354     gun = min(gun, sp->shp_glim);
355     gun = min(gun, getvar(V_MILIT, (s_char *)sp, EF_SHIP) / 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     putvar(V_SHELL, shells, (s_char *)sp, EF_SHIP);
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 int
402 fire_torp(struct shpstr *sp, struct shpstr *targ, int range, int ntargets)
403 {
404     extern int torpedo_damage;
405     int dam;
406     int shells;
407     double hitchance;
408     double mobcost;
409     struct mchrstr *mcp;
410
411     shells = getvar(V_SHELL, (s_char *)sp, EF_SHIP);
412
413     if (shells < 3)
414         shells += supply_commod(sp->shp_own, sp->shp_x, sp->shp_y, I_SHELL,
415                                 3 - shells);
416
417     if (getvar(V_GUN, (s_char *)sp, EF_SHIP) == 0 || shells < 3)
418         return 0;
419
420     if (getvar(V_MILIT, (s_char *)sp, EF_SHIP) < 1)
421         return 0;
422
423     if (sp->shp_effic < 60)
424         return 0;
425
426     if (sp->shp_mobil <= 0)
427         return 0;
428
429     /* All set.. fire! */
430     shells -= 3;
431     putvar(V_SHELL, shells, (s_char *)sp, EF_SHIP);
432     putship(sp->shp_uid, sp);
433
434     mcp = &mchr[(int)sp->shp_type];
435     mobcost = sp->shp_effic * 0.01 * sp->shp_speed;
436     mobcost = (480.0 / (mobcost + techfact(sp->shp_tech, mobcost)));
437
438     /* Mob cost for a torp is equal to the cost of 1/2 sector of movement */
439     mobcost /= 2.0;
440     sp->shp_mobil -= mobcost;
441
442     hitchance = DTORP_HITCHANCE(range, sp->shp_visib);
443
444     pr("Captain! Torpedoes sighted!\n");
445
446     if (chance(hitchance)) {
447         pr("BOOM!...\n");
448         if (sp->shp_own != 0)
449             wu(0, sp->shp_own, "%s @ %s torpedoed %s\n",
450                prship(sp),
451                xyas(sp->shp_x, sp->shp_y, sp->shp_own), prsub(targ));
452         dam = TORP_DAMAGE();
453
454         if (ntargets > 2)
455             dam /= ((float)ntargets / 2.0);
456
457         shipdamage(targ, dam);
458         putship(targ->shp_uid, targ);
459
460         if (mchr[(int)sp->shp_type].m_flags & M_SUB)
461             nreport(targ->shp_own, N_TORP_SHIP, 0, 1);
462         else
463             nreport(targ->shp_own, N_SHIP_TORP, player->cnum, 1);
464     } else {
465         pr("Missed!\n");
466         if (sp->shp_own != 0)
467             wu(0, sp->shp_own,
468                "%s missed %s with a torp at %s\n",
469                prship(sp), prsub(targ),
470                xyas(sp->shp_x, sp->shp_y, sp->shp_own));
471     }
472
473     return 1;
474 }
475
476 s_char *
477 prsub(struct shpstr *sp)
478 {
479     if (mchr[(int)sp->shp_type].m_flags & M_SUB)
480         return "sub";
481     else
482         return prship(sp);
483 }