]> git.pond.sub.org Git - empserver/blob - src/lib/commands/sona.c
Import of Empire 4.2.12
[empserver] / src / lib / commands / sona.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  *  sona.c: Sonar from a sub (or other sonar-equipped ship)
29  * 
30  *  Known contributors to this file:
31  *     Jim Griffith, 1989
32  *     Ken Stevens, 1995
33  */
34
35 #include "misc.h"
36 #include "player.h"
37 #include "var.h"
38 #include "xy.h"
39 #include "sect.h"
40 #include "nsc.h"
41 #include "retreat.h"
42 #include "ship.h"
43 #include "nat.h"
44 #include "path.h"
45 #include "file.h"
46 #include "queue.h"
47 #include "plane.h"
48 #include <fcntl.h>
49 #include <ctype.h>
50 #include "commands.h"
51 #include "optlist.h"
52
53 int
54 sona(void)
55 {
56         struct  nstr_item ni, nit;
57         struct  sctstr sect;
58         struct  shpstr ship;
59         struct  shpstr targ;
60         struct  natstr *natp;
61         struct  mchrstr *mcp;
62         struct  mchrstr *tmcp;
63         struct  nstr_sect ns;
64         int     range;
65         int     pingrange;
66         int     srange;
67         int     vrange;
68         int     dist;
69         int     x,y;
70         int     cx,cy;
71         int     mines;
72         int     changed = 0;
73         int     row;
74         /* Where these are used are non-re-entrant, so we keep 'em around */
75         static s_char  **rad = (s_char **)0;
76         static s_char  *radbuf = (s_char *)0;
77         static s_char  **vis = (s_char **)0;
78         static s_char  *visbuf = (s_char *)0;
79
80         if (!snxtitem(&ni, EF_SHIP, player->argp[1]))
81                 return RET_SYN;
82         if (!radbuf)
83             radbuf = (s_char *)malloc((WORLD_Y*(WORLD_X+1)) * sizeof(s_char));
84         if (!visbuf)
85             visbuf = (s_char *)malloc((WORLD_Y*(WORLD_X+1)) * sizeof(s_char));
86         if (!rad) {
87             rad = (s_char **)malloc(WORLD_Y * sizeof(s_char *));
88             if (rad && radbuf) {
89                 for (x = 0; x < WORLD_Y; x++) {
90                     rad[x] = &radbuf[(WORLD_X + 1) * x];
91                 }
92             } else if (rad) {
93                 free((s_char *)rad);
94                 rad = (s_char **)0;
95             }
96         }
97         if (!vis) {
98             vis = (s_char **)malloc(WORLD_Y * sizeof(s_char *));
99             if (vis && visbuf) {
100                 for (x = 0; x < WORLD_Y; x++) {
101                     vis[x] = &visbuf[(WORLD_X + 1) * x];
102                 }
103             } else if (vis) {
104                 free((s_char *)vis);
105                 vis = (s_char **)0;
106             }
107         }
108         if (!radbuf || !visbuf || !rad || !vis) {
109             pr("Memory error, tell the deity.\n");
110             logerror("malloc failed in sona\n");
111             return RET_FAIL;
112         }
113         while (nxtitem(&ni, (s_char *)&ship)) {
114                 if (!player->owner)
115                         continue;
116                 mcp = &mchr[(int)ship.shp_type];
117                 if (!(mcp->m_flags & M_SONAR))
118                         continue;
119                 getsect(ship.shp_x, ship.shp_y, &sect);
120                 if (sect.sct_type != SCT_WATER)
121                         continue;
122                 range = (int) techfact(ship.shp_tech,
123                         (double) mcp->m_vrnge);
124                 srange = min(7, 7 * range * ship.shp_effic / 200);
125                 pr("%s at %s efficiency %d%%, max range %d\n",
126                    prship(&ship),
127                    xyas(ship.shp_x, ship.shp_y, player->cnum),
128                    ship.shp_effic,
129                    srange);
130                 snxtsct_dist(&ns,ship.shp_x,ship.shp_y,srange);
131                 blankfill((s_char *)radbuf, &ns.range, 1);
132                 while(nxtsct(&ns,&sect)){
133                     if (player->owner || sect.sct_type == SCT_WATER)
134                         rad[ns.dy][ns.dx] = dchr[sect.sct_type].d_mnem;
135                     else {
136                         rad[ns.dy][ns.dx] = '?';
137                     }
138                 }
139                 snxtsct_dist(&ns,ship.shp_x,ship.shp_y,srange);
140                 cx = deltax(ship.shp_x, ns.range.lx);
141                 cy = deltay(ship.shp_y, ns.range.ly);
142                 while(nxtsct(&ns,&sect)) {
143                         if (!line_of_sight(rad, cx, cy, ns.dx, ns.dy)) {
144                                 rad[ns.dy][ns.dx] = ' ';
145                                 continue;
146                         }
147                         if (ship.shp_tech >= 310 &&
148                             sect.sct_type == SCT_WATER) {
149                                 mines=getvar(V_MINE,(s_char *)&sect,EF_SECTOR);
150                                 if (mines) {
151                                         pr("Sonar detects %d mines in %s!\n",
152                                            mines,
153                                            xyas(sect.sct_x,sect.sct_y,player->cnum));
154                                         rad[ns.dy][ns.dx] = 'X';
155                                 }
156                         }
157                         changed |= map_set(player->cnum, sect.sct_x, sect.sct_y,
158                                           rad[ns.dy][ns.dx], 0);
159
160                 }
161                 bzero((s_char *)visbuf, (WORLD_Y * (WORLD_X + 1)));
162                 snxtitem_dist(&nit, EF_SHIP, ship.shp_x, ship.shp_y, range);
163                 while (nxtitem(&nit, (caddr_t)&targ)) {
164
165                         if (targ.shp_own == player->cnum || targ.shp_own == 0)
166                                 continue;
167                         tmcp = &mchr[(int)targ.shp_type];
168                         pingrange = min(7, max(targ.shp_visib,10)*range/10);
169                         vrange = pingrange * ship.shp_effic / 200;
170                         dist = mapdist(targ.shp_x, targ.shp_y,
171                                 ship.shp_x, ship.shp_y);
172                         pingrange = (max(pingrange, 2) * targ.shp_effic)/100; 
173                         if (dist > pingrange)
174                                 continue;
175                         if (tmcp->m_flags & M_SONAR && targ.shp_own) {
176                                 natp = getnatp(targ.shp_own);
177                                 if (natp->nat_flags & NF_SONAR)
178                                         wu(0, targ.shp_own,
179                                            "Sonar ping from %s detected by %s!\n",
180                                            
181                                            xyas(ship.shp_x, ship.shp_y,
182                                                 targ.shp_own),
183                                            prship(&targ));
184                                 if (targ.shp_rflags & RET_SONARED){
185                                         retreat_ship(&targ, 's');
186                                         putship(targ.shp_uid,&targ);
187                                 }
188                         }
189                         if (dist > vrange)
190                                 continue;
191                         x = deltx(&ns.range,(int)targ.shp_x);
192                         y = delty(&ns.range,(int)targ.shp_y);
193                         if (rad[y][x] != dchr[SCT_WATER].d_mnem &&
194                             rad[y][x] != 'X')
195                                 continue;
196                         if (tmcp->m_flags & M_SUB &&
197                             getrel(getnatp(targ.shp_own), player->cnum) < FRIENDLY) {
198                                 if (mcp->m_vrnge + targ.shp_visib < 8)
199                                         pr("Sonar detects sub #%d @ %s\n",
200                                            targ.shp_uid,
201                                            xyas(targ.shp_x, targ.shp_y, player->cnum));
202                                 else if (mcp->m_vrnge + targ.shp_visib < 10)
203                                         pr("Sonar detects %s @ %s\n",
204                                            prship(&targ),
205                                            xyas(targ.shp_x, targ.shp_y, player->cnum));
206                                 else
207                                         pr("Sonar detects %s %s @ %s\n", cname(targ.shp_own),
208                                            prship(&targ),
209                                            xyas(targ.shp_x, targ.shp_y, player->cnum));
210                         } else
211                                 pr("Sonar detects %s %s @ %s\n", cname(targ.shp_own),
212                                    prship(&targ),
213                                    xyas(targ.shp_x, targ.shp_y, player->cnum));
214
215                         if (targ.shp_visib > vis[y][x]) {
216                                 vis[y][x] = targ.shp_visib;
217                                 /* &~0x20 makes it a cap letter */
218                                 rad[y][x] = (*mchr[(int)targ.shp_type].m_name) & ~0x20;
219                         }
220                 }
221                 if (!player->argp[2]) {
222                         rad[cy][cx] = '0';
223                         for (row=0; row < ns.range.height; row++)
224                                 if (!blankrow(rad[row]))
225                                         pr("%s\n", rad[row]);
226                 }
227                 pr("\n");
228                 
229         }
230         if (changed)
231                 writemap(player->cnum);         
232         return RET_OK;
233 }
234
235 void
236 plane_sona(struct emp_qelem *plane_list, int x, int y, struct shiplook *head)
237 {
238         struct  plnstr  *pp;
239         struct  plchrstr *pcp;
240         struct  mchrstr *tmcp;
241         struct  shpstr *targ,s;
242         struct  natstr *natp;
243         struct  emp_qelem   *qp;
244         struct  emp_qelem   *next;
245         struct  plist   *ip;
246         struct  sctstr  sect;
247         int     found=0;
248         int     range,i;
249         int     pingrange;
250         int     vrange;
251         int     dist;
252
253         getsect(x,y,&sect);
254         if ((sect.sct_type != SCT_WATER) && (sect.sct_type != SCT_HARBR))
255                 return;
256         for (qp = plane_list->q_forw; qp != plane_list; qp = next) {
257                 next = qp->q_forw;
258                 ip = (struct plist *) qp;
259                 pp = &ip->plane;
260                 pcp = ip->pcp;
261                 if (!(pcp->pl_flags & P_A)) /* if it isn't an ASW plane */
262                         continue;
263                 range = (int) techfact(pp->pln_tech, (double) ((100-pp->pln_acc)/10));
264 /*
265                 for (i=0; targ = getshipp(i); i++) {
266 */
267                 for (i=0; getship(i,&s) ; i++) {
268                         targ = &s;
269                         if (targ->shp_own == pp->pln_own || targ->shp_own == 0)
270                                 continue;
271 /*
272                         if (have_looked(targ->shp_uid,head))
273                                 continue;
274 */
275                         if (have_found(targ->shp_uid,head))
276                                 continue;
277                         set_have_looked(targ->shp_uid,head);
278                         tmcp = &mchr[(int)targ->shp_type];
279                         if (!(tmcp->m_flags & M_SUB))
280                                 continue;
281                         if (roll(100) > pln_identchance(pp, shp_hardtarget(targ), EF_SHIP))
282                                 continue;
283                         pingrange = max(targ->shp_visib, 10) * range / 10;
284                         vrange = ((float)pingrange) * ((float)pp->pln_effic / 200.0);
285                         dist = mapdist(targ->shp_x, targ->shp_y, x, y);
286                         pingrange = (max(pingrange, 2) * targ->shp_effic);
287                         pingrange = roundavg(pingrange/100.0); 
288                         if (dist > pingrange)
289                                 continue;
290                         if (tmcp->m_flags & M_SONAR && targ->shp_own) {
291                                 natp = getnatp(targ->shp_own);
292                                 if (natp->nat_flags & NF_SONAR)
293                                 wu(0, targ->shp_own,
294                                    "Sonar ping from %s detected by %s!\n",
295                                    xyas(x, y, targ->shp_own),
296                                    prship(targ));
297                         }
298                         if ((dist > vrange))
299                                 continue;
300                         set_have_found(targ->shp_uid,head);
301                         if (!found){
302                                 mpr(pp->pln_own,
303                                     "\nSonar contact in %s\n",xyas(x,y,pp->pln_own));
304                                 found=1;
305                         }
306                         if (getrel(getnatp(targ->shp_own), pp->pln_own) < FRIENDLY &&
307                             roll(100) > pln_identchance(pp, shp_hardtarget(targ), EF_SHIP))
308                                 if (roll(100) > pln_identchance(pp, shp_hardtarget(targ), EF_SHIP))
309                                         mpr(pp->pln_own,
310                                             "sub #%d %s\n",
311                                             targ->shp_uid,
312                                             xyas(targ->shp_x, targ->shp_y, pp->pln_own));
313                                 else
314                                         mpr(pp->pln_own,
315                                             "%s %s\n",
316                                             prship(targ),
317                                             xyas(targ->shp_x, targ->shp_y, pp->pln_own));
318                         else
319                                 mpr(pp->pln_own,
320                                     "%s %s @ %s\n", cname(targ->shp_own),
321                                     prship(targ),
322                                     xyas(targ->shp_x, targ->shp_y, pp->pln_own));
323                 }
324         }
325 }
326
327 /* 
328  * line_of_sight() - is there a "straight" all water path from (x,y) to (tx,ty)
329  * Ken & Irina Stevens, 1995
330  */
331
332 #define DOT(ax,ay,bx,by) ((ax)*(bx) + (ay)*(by))
333 #define LEN(x,y) ((x)*(x) + (y)*(y))
334 #define DIST(ax,ay,bx,by) LEN(bx - ax, by -ay)
335
336 int
337 line_of_sight(s_char **rad, int ax, int ay, int bx, int by)
338 {
339         int     dx = bx - ax;
340         int     dy = by - ay;
341         int     dlen = LEN(dx, dy);
342         int     n;
343         int     cx = 0;
344         int     cy = 0;
345         int     tx, ty;         /* test point */
346         double  cd_dist = dlen; /* closest distance from c to vector d */
347         double  md_dist;        /* minimum distance from t to vector d */
348         double  td_dist;        /* distance from t to vector d */
349         double  td_proj;        /* the projection of t onto vector d */
350         int     closest;        /* index of closest */
351         int     blocked = 0;
352         struct  sctstr *sectp;
353
354         while (cd_dist) {
355                 if (blocked)
356                         return 0;
357                 md_dist = 100;  /* will always be <= 2 */
358                 closest = -1;
359                 for (n = 1; n <= 6; ++n) { /* Directions */
360                         tx = cx + diroff[n][0];
361                         ty = cy + diroff[n][1];
362                         if (DIST(tx,ty,dx,dy) >= cd_dist)
363                                 continue;
364                         td_proj = (double)DOT(tx,ty,dx,dy) / dlen;
365                         td_dist = DIST(tx,ty,td_proj*dx,td_proj*dy);
366                         if (td_dist < md_dist) {
367                                 md_dist = td_dist;
368                                 closest = n;
369                         }                       
370                 }
371                 if (closest < 0) /* not possible */
372                         return 0;
373                 cx = cx + diroff[closest][0];
374                 cy = cy + diroff[closest][1];
375                 if (rad) {
376                     blocked = (rad[ay+cy][ax+cx] != dchr[SCT_WATER].d_mnem);
377                 } else {
378                     sectp = getsectp((ax + cx), (ay + cy));
379                     if (sectp) {
380                         if (sectp->sct_type == SCT_WATER ||
381                             sectp->sct_type == SCT_BSPAN) {
382                             blocked = 0;
383                         } else {
384                             blocked = 1;
385                         }
386                     }
387                 }
388                 cd_dist = DIST(cx,cy,dx,dy);
389         }
390         return 1;
391 }
392
393 int
394 blankrow(s_char *s)
395 {
396         while (*s) {
397                 if (*s != ' ')
398                         return 0;
399                 ++s;
400         }
401         return 1;
402 }