]> git.pond.sub.org Git - empserver/blob - src/lib/update/sail.c
Update copyright notice
[empserver] / src / lib / update / sail.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2012, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  sail.c: Sail ships during the update
28  *
29  *  Known contributors to this file:
30  *     Doug Hay
31  *     Robert Forsman
32  *     Ken Stevens, 1995
33  *     Steve McClure, 1998-2000
34  */
35
36 #include <config.h>
37
38 #include <math.h>
39 #include "nsc.h"
40 #include "path.h"
41 #include "update.h"
42 #include "empobj.h"
43 #include "unit.h"
44
45 struct fltelemstr {
46     int num;
47     int own;
48     double mobil, mobcost;
49     struct fltelemstr *next;
50 };
51
52 struct fltheadstr {
53     int leader;
54     signed char real_q;
55 /* defines for the real_q member */
56 #define LEADER_VIRTUAL  0
57 #define LEADER_REAL     1
58 #define LEADER_WRONGSECT        2
59     coord x, y;
60     natid own;
61     unsigned maxmoves;
62     struct fltelemstr *head;
63     struct fltheadstr *next;
64 };
65
66 static void fltp_to_list(struct fltheadstr *, struct emp_qelem *);
67
68 static void
69 cost_ship(struct shpstr *sp, struct fltelemstr *ep, struct fltheadstr *fp)
70 {
71     double mobcost = shp_mobcost(sp);
72     int howfar;
73
74     howfar = 0;
75     if (mobcost > 0) {
76         howfar = (int)sp->shp_mobil - (int)sp->shp_mobquota;
77         howfar = ceil((howfar / mobcost));
78     }
79     if (howfar < 0)
80         howfar = 0;
81 #ifdef SAILDEBUG
82     wu(0, fp->own,
83        "Ship #%d can move %d spaces on mobility %d (cost/sect %f)\n",
84        sp->shp_uid, howfar, sp->shp_mobil, mobcost);
85 #endif
86     if ((unsigned)howfar < fp->maxmoves)
87         fp->maxmoves = howfar;
88
89     ep->mobil = sp->shp_mobil;
90     ep->mobcost = mobcost;
91 }
92
93 static int
94 sail_find_fleet(struct fltheadstr **head, struct shpstr *sp)
95 {
96     struct fltheadstr *fltp;
97     struct shpstr *ap;
98     struct fltelemstr *this;
99     int len = 0;
100     int follow = -1;
101     int stop;
102     char *cp;
103
104     if (sp->shp_own == 0)
105         return 0;
106
107
108
109     /* If this ship is following, find the head of the follow list. */
110     for (ap = sp; ap; len++, ap = getshipp(follow)) {
111         follow = ap->shp_follow;
112         /* Not same owner */
113         if (ap->shp_own != sp->shp_own) {
114             wu(0, sp->shp_own,
115                "Ship #%d, following #%d, which you don't own.\n",
116                sp->shp_uid, ap->shp_uid);
117             return 0;
118         }
119         /* Not a follower. */
120         if (ap->shp_path[0] != 'f')
121             break;
122         /* Following itself */
123         if (follow == ap->shp_uid || follow == sp->shp_uid)
124             break;
125     }
126     if (!ap) {
127         wu(0, sp->shp_own,
128            "Ship #%d, following #%d, which you don't own.\n",
129            sp->shp_uid, follow);
130         return 0;
131     }
132
133     /* This should prevent infinite loops. */
134     if (len >= 10) {
135         wu(0, sp->shp_own,
136            "Ship #%d, too many follows (circular follow?).\n",
137            sp->shp_uid);
138         return 0;
139     }
140
141     for (stop = 0, cp = ap->shp_path; !stop && *cp; cp++) {
142         switch (*cp) {
143         case 'y':
144         case 'u':
145         case 'g':
146         case 'j':
147         case 'b':
148         case 'n':
149         case 'h':
150         case 't':
151             break;
152         default:
153             stop = 1;
154         }
155     }
156
157     /* we found a non-valid char in the path. */
158     if (*cp) {
159         wu(0, ap->shp_own, "invalid char '\\%03o' in path of ship %d\n",
160            (unsigned char)*cp, ap->shp_uid);
161         *cp = 0;
162     }
163
164     /* if this ship is not sailing anywhere then ignore it. */
165     if (!*ap->shp_path)
166         return 0;
167
168     /* Find the fleet structure we belong to. */
169     for (fltp = *head; fltp && fltp->leader != follow; fltp = fltp->next) ;
170
171     if (!fltp) {
172         fltp = malloc(sizeof(*fltp));
173         memset(fltp, 0, sizeof(*fltp));
174
175         /* Fix the links. */
176         fltp->next = *head;
177         *head = fltp;
178
179         /* Set the leader. */
180         fltp->leader = ap->shp_uid;
181         fltp->real_q = LEADER_REAL;
182         fltp->x = ap->shp_x;
183         fltp->y = ap->shp_y;
184         fltp->own = ap->shp_own;
185         fltp->maxmoves = 500;
186     }
187
188     /* If the fleet is not in the same sector as us, no go. */
189     if ((fltp->x != sp->shp_x) || (fltp->y != sp->shp_y)) {
190         wu(0, sp->shp_own,
191            "Ship %d not in same sector as its sailing fleet\n",
192            sp->shp_uid);
193         fltp->real_q = LEADER_WRONGSECT;
194         return 0;
195     }
196
197     this = malloc(sizeof(*this));
198     memset(this, 0, sizeof(*this));
199     this->num = sp->shp_uid;
200     this->own = sp->shp_own;
201     this->next = fltp->head;
202     fltp->head = this;
203     cost_ship(sp, this, fltp);
204
205     return 1;
206 }
207
208 static int
209 sail_nav_fleet(struct fltheadstr *fltp)
210 {
211     struct fltelemstr *fe;
212     struct shpstr *sp, ship;
213     struct sctstr *sectp;
214     int error = 0;
215     char *s, *p;
216     natid own;
217     struct emp_qelem ship_list;
218     int dir;
219
220 #ifdef SAILDEBUG
221     switch (fltp->real_q) {
222     case LEADER_VIRTUAL:
223         s = "leaderless";
224         break;
225     case LEADER_REAL:
226         s = "real";
227         break;
228     case LEADER_WRONGSECT:
229         s = "scattered";
230         break;
231     default:
232         s = "inconsistent";
233     }
234     wu(0, fltp->own,
235        "Fleet lead by %d is %s, can go %d spaces\n  contains ships:",
236        fltp->leader, s, fltp->maxmoves);
237     for (fe = fltp->head; fe; fe = fe->next)
238         wu(0, fltp->own, " %d", fe->num);
239     wu(0, fltp->own, "\n");
240 #endif
241     sectp = getsectp(fltp->x, fltp->y);
242     for (fe = fltp->head; fe; fe = fe->next) {
243         sp = getshipp(fe->num);
244         if (sp->shp_item[I_MILIT] == 0 && sp->shp_item[I_CIVIL] == 0) {
245             wu(0, fltp->own,
246                "   ship #%d (%s) is crewless and can't go on\n",
247                fe->num, cname(fe->own));
248             error = 1;
249         }
250         if ((shp_check_nav(sectp, sp) == CN_LANDLOCKED) &&
251             (dchr[sectp->sct_type].d_nav == NAV_CANAL)) {
252             wu(0, fltp->own,
253                "Your ship #%d (%s) is too big to fit through the canal.\n",
254                fe->num, cname(fe->own));
255             error = 1;
256         }
257     }
258     if (error)
259         return 0;
260     sp = getshipp(fltp->leader);
261     sectp = getsectp(fltp->x, fltp->y);
262     if (shp_check_nav(sectp, sp) != CN_NAVIGABLE)
263         wu(0, fltp->own, "Your fleet lead by %d is trapped by land.\n",
264            fltp->leader);
265     sp = getshipp(fltp->leader);
266     own = sp->shp_own;
267     fltp_to_list(fltp, &ship_list);     /* hack -KHS 1995 */
268     for (s = sp->shp_path; *s && fltp->maxmoves > 0; s++) {
269         dir = diridx(*s);
270         if (0 != shp_nav_one_sector(&ship_list, dir, own, 0))
271             fltp->maxmoves = 1;
272         --fltp->maxmoves;
273     }
274     unit_put(&ship_list, own);
275     getship(sp->shp_uid, &ship);
276     fltp->x = ship.shp_x;
277     fltp->y = ship.shp_y;
278     for (p = &ship.shp_path[0]; *s; p++, s++)
279         *p = *s;
280     *p = 0;
281     putship(ship.shp_uid, &ship);
282 #ifdef SAILDEBUG
283     if (sp->shp_path[0]) {
284         wu(0, fltp->own,
285            "Fleet lead by #%d nav'd to %s, path left = %s\n",
286            fltp->leader, xyas(fltp->x, fltp->y, fltp->own), &sp->shp_path);
287     } else
288         wu(0, fltp->own,
289            "Fleet lead by #%d nav'd to %s, finished.\n",
290            fltp->leader, xyas(fltp->x, fltp->y, fltp->own));
291     wu(0, sp->shp_own, "Ship #%d has %d mobility now.\n",
292        fe->num, (int)fe->mobil);
293 #endif
294     return 1;
295 }
296
297 void
298 sail_ship(natid cn)
299 {
300     struct shpstr *sp;
301     struct fltheadstr *head = NULL;
302     struct fltheadstr *fltp;
303     int n;
304
305
306     for (n = 0; NULL != (sp = getshipp(n)); n++)
307         if (sp->shp_own == cn) {
308             sail_find_fleet(&head, sp);
309         }
310
311     /* see what the fleets fall out into */
312     for (fltp = head; fltp; fltp = fltp->next) {
313         if (sail_nav_fleet(fltp))
314             wu(0, fltp->own, "Your fleet lead by ship #%d has reached %s.\n",
315                fltp->leader, xyas(fltp->x, fltp->y, fltp->own));
316     }
317
318     /* Free up the memory, 'cause I want to. */
319     for (fltp = head; fltp;) {
320         struct fltelemstr *fe;
321         struct fltheadstr *saveh;
322         saveh = fltp->next;
323         for (fe = fltp->head; fe;) {
324             struct fltelemstr *saveel;
325             saveel = fe->next;
326             free(fe);
327             fe = saveel;
328         }
329         free(fltp);
330         fltp = saveh;
331     }
332 }
333
334 /* The following is a total hack by Ken Stevens to cut down dramatically on repeated code 1995 */
335
336 static void
337 fltp_to_list(struct fltheadstr *fltp, struct emp_qelem *list)
338 {
339     struct fltelemstr *fe;
340     struct ulist *mlp;
341     struct shpstr *sp;
342
343     emp_initque(list);
344     for (fe = fltp->head; fe; fe = fe->next) {
345         mlp = malloc(sizeof(struct ulist));
346         sp = getshipp(fe->num);
347         mlp->chrp = (struct empobj_chr *)(mchr + sp->shp_type);
348         mlp->unit.ship = *sp;
349         ef_mark_fresh(EF_SHIP, &mlp->unit.ship);
350         mlp->mobil = fe->mobil;
351         emp_insque(&mlp->queue, list);
352     }
353 }