ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/main.C
(Generate patch)

Comparing deliantra/server/server/main.C (file contents):
Revision 1.23 by root, Sun Sep 10 15:59:57 2006 UTC vs.
Revision 1.69 by root, Sun Dec 31 17:17:23 2006 UTC

1
2/*
3 * static char *rcsid_main_c =
4 * "$Id: main.C,v 1.23 2006/09/10 15:59:57 root Exp $";
5 */
6
7/* 1/*
8 CrossFire, A Multiplayer game for X-windows 2 CrossFire, A Multiplayer game for X-windows
9 3
10 Copyright (C) 2001-2003 Mark Wedel & Crossfire Development Team 4 Copyright (C) 2001-2003 Mark Wedel & Crossfire Development Team
11 Copyright (C) 1992 Frank Tore Johansen 5 Copyright (C) 1992 Frank Tore Johansen
22 16
23 You should have received a copy of the GNU General Public License 17 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software 18 along with this program; if not, write to the Free Software
25 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 20
27 The authors can be reached via e-mail at crossfire-devel@real-time.com 21 The authors can be reached via e-mail at <crossfire@schmorp.de>
28*/ 22*/
29 23
30#include <global.h> 24#include <global.h>
31#include <object.h> 25#include <object.h>
32#include <tod.h> 26#include <tod.h>
37# ifdef HAVE_CRYPT_H 31# ifdef HAVE_CRYPT_H
38# include <crypt.h> 32# include <crypt.h>
39# endif 33# endif
40#endif 34#endif
41 35
42#ifndef __CEXTRACT__
43# include <sproto.h> 36#include <sproto.h>
44#endif
45
46#ifdef HAVE_TIME_H
47# include <time.h> 37#include <time.h>
48#endif
49 38
50#include <../random_maps/random_map.h> 39#include <../random_maps/random_map.h>
51#include <../random_maps/rproto.h> 40#include <../random_maps/rproto.h>
52#include "path.h" 41#include "path.h"
53 42
66/* If in a socket, don't print out the list of authors. It confuses the 55/* If in a socket, don't print out the list of authors. It confuses the
67 * crossclient program. 56 * crossclient program.
68 */ 57 */
69 if (op == NULL) 58 if (op == NULL)
70 return; 59 return;
60
71 new_draw_info (NDI_UNIQUE, 0, op, "Authors and contributors to this program:"); 61 new_draw_info (NDI_UNIQUE, 0, op, "Authors and contributors to this program:");
62 new_draw_info (NDI_UNIQUE, 0, op, "Marc A. Lehmann <pcg@goof.com>");
63 new_draw_info (NDI_UNIQUE, 0, op, "Robin Redeker <elmex@x-paste.de>");
64 new_draw_info (NDI_UNIQUE, 0, op, "Pippijn van Steenhoven <pip88nl@gmail.com>");
72 new_draw_info (NDI_UNIQUE, 0, op, "mwedel@sonic.net (Mark Wedel)"); 65 new_draw_info (NDI_UNIQUE, 0, op, "Mark Wedel <mwedel@sonic.net>");
73 new_draw_info (NDI_UNIQUE, 0, op, "frankj@ifi.uio.no (Frank Tore Johansen)"); 66 new_draw_info (NDI_UNIQUE, 0, op, "Frank Tore Johansen <frankj@ifi.uio.no>");
74 new_draw_info (NDI_UNIQUE, 0, op, "kjetilho@ifi.uio.no (Kjetil Torgrim Homme)"); 67 new_draw_info (NDI_UNIQUE, 0, op, "Kjetil Torgrim Homme <kjetilho@ifi.uio.no>");
75 new_draw_info (NDI_UNIQUE, 0, op, "tvangod@ecst.csuchico.edu (Tyler Van Gorder)"); 68 new_draw_info (NDI_UNIQUE, 0, op, "Tyler Van Gorder <tvangod@ecst.csuchico.edu>");
76 new_draw_info (NDI_UNIQUE, 0, op, "elmroth@cd.chalmers.se (Tony Elmroth)"); 69 new_draw_info (NDI_UNIQUE, 0, op, "Tony Elmroth <elmroth@cd.chalmers.se>");
77 new_draw_info (NDI_UNIQUE, 0, op, "dougal.scott@fcit.monasu.edu.au (Dougal Scott)"); 70 new_draw_info (NDI_UNIQUE, 0, op, "Dougal Scott <dougal.scott@fcit.monasu.edu.au>");
78 new_draw_info (NDI_UNIQUE, 0, op, "wchuang@athena.mit.edu (William)"); 71 new_draw_info (NDI_UNIQUE, 0, op, "William <wchuang@athena.mit.edu>");
79 new_draw_info (NDI_UNIQUE, 0, op, "ftww@cs.su.oz.au (Geoff Bailey)"); 72 new_draw_info (NDI_UNIQUE, 0, op, "Geoff Bailey <ftww@cs.su.oz.au>");
80 new_draw_info (NDI_UNIQUE, 0, op, "jorgens@flipper.pvv.unit.no (Kjetil Wiekhorst Jxrgensen)"); 73 new_draw_info (NDI_UNIQUE, 0, op, "Kjetil Wiekhorst Jxrgensen <jorgens@flipper.pvv.unit.no>");
81 new_draw_info (NDI_UNIQUE, 0, op, "c.blackwood@rdt.monash.edu.au (Cameron Blackwood)"); 74 new_draw_info (NDI_UNIQUE, 0, op, "Cameron Blackwood <c.blackwood@rdt.monash.edu.au>");
82 new_draw_info (NDI_UNIQUE, 0, op, "jtraub+@cmu.edu (Joseph L. Traub)"); 75 new_draw_info (NDI_UNIQUE, 0, op, "Joseph L. Traub <jtraub+@cmu.edu>");
83 new_draw_info (NDI_UNIQUE, 0, op, "rgg@aaii.oz.au (Rupert G. Goldie)"); 76 new_draw_info (NDI_UNIQUE, 0, op, "Rupert G. Goldie <rgg@aaii.oz.au>");
84 new_draw_info (NDI_UNIQUE, 0, op, "eanders+@cmu.edu (Eric A. Anderson)"); 77 new_draw_info (NDI_UNIQUE, 0, op, "Eric A. Anderson <eanders+@cmu.edu>");
85 new_draw_info (NDI_UNIQUE, 0, op, "eneq@Prag.DoCS.UU.SE (Rickard Eneqvist)"); 78 new_draw_info (NDI_UNIQUE, 0, op, "Rickard Eneqvist <eneq@Prag.DoCS.UU.SE>");
86 new_draw_info (NDI_UNIQUE, 0, op, "Jarkko.Sonninen@lut.fi (Jarkko Sonninen)"); 79 new_draw_info (NDI_UNIQUE, 0, op, "Jarkko Sonninen <Jarkko.Sonninen@lut.fi>");
87 new_draw_info (NDI_UNIQUE, 0, op, "kholland@sunlab.cit.cornell.du (Karl Holland)"); 80 new_draw_info (NDI_UNIQUE, 0, op, "Karl Holland <kholland@sunlab.cit.cornell.du>");
88 new_draw_info (NDI_UNIQUE, 0, op, "vick@bern.docs.uu.se (Mikael Lundgren)"); 81 new_draw_info (NDI_UNIQUE, 0, op, "Mikael Lundgren <vick@bern.docs.uu.se>");
89 new_draw_info (NDI_UNIQUE, 0, op, "mol@meryl.csd.uu.se (Mikael Olsson)"); 82 new_draw_info (NDI_UNIQUE, 0, op, "Mikael Olsson <mol@meryl.csd.uu.se>");
90 new_draw_info (NDI_UNIQUE, 0, op, "Tero.Haatanen@lut.fi (Tero Haatanen)"); 83 new_draw_info (NDI_UNIQUE, 0, op, "Tero Haatanen <Tero.Haatanen@lut.fi>");
91 new_draw_info (NDI_UNIQUE, 0, op, "ylitalo@student.docs.uu.se (Lasse Ylitalo)"); 84 new_draw_info (NDI_UNIQUE, 0, op, "Lasse Ylitalo <ylitalo@student.docs.uu.se>");
92 new_draw_info (NDI_UNIQUE, 0, op, "anipa@guru.magic.fi (Niilo Neuvo)"); 85 new_draw_info (NDI_UNIQUE, 0, op, "Niilo Neuvo <anipa@guru.magic.fi>");
93 new_draw_info (NDI_UNIQUE, 0, op, "mta@modeemi.cs.tut.fi (Markku J{rvinen)"); 86 new_draw_info (NDI_UNIQUE, 0, op, "Markku J{rvinen <mta@modeemi.cs.tut.fi>");
94 new_draw_info (NDI_UNIQUE, 0, op, "meunier@inf.enst.fr (Sylvain Meunier)"); 87 new_draw_info (NDI_UNIQUE, 0, op, "Sylvain Meunier <meunier@inf.enst.fr>");
95 new_draw_info (NDI_UNIQUE, 0, op, "jfosback@darmok.uoregon.edu (Jason Fosback)"); 88 new_draw_info (NDI_UNIQUE, 0, op, "Jason Fosback <jfosback@darmok.uoregon.edu>");
96 new_draw_info (NDI_UNIQUE, 0, op, "cedman@capitalist.princeton.edu (Carl Edman)"); 89 new_draw_info (NDI_UNIQUE, 0, op, "Carl Edman <cedman@capitalist.princeton.edu>");
97 new_draw_info (NDI_UNIQUE, 0, op, "henrich@crh.cl.msu.edu (Charles Henrich)"); 90 new_draw_info (NDI_UNIQUE, 0, op, "Charles Henrich <henrich@crh.cl.msu.edu>");
98 new_draw_info (NDI_UNIQUE, 0, op, "schmid@fb3-s7.math.tu-berlin.de (Gregor Schmid)"); 91 new_draw_info (NDI_UNIQUE, 0, op, "Gregor Schmid <schmid@fb3-s7.math.tu-berlin.de>");
99 new_draw_info (NDI_UNIQUE, 0, op, "quinet@montefiore.ulg.ac.be (Raphael Quinet)"); 92 new_draw_info (NDI_UNIQUE, 0, op, "Raphael Quinet <quinet@montefiore.ulg.ac.be>");
100 new_draw_info (NDI_UNIQUE, 0, op, "jam@modeemi.cs.tut.fi (Jari Vanhala)"); 93 new_draw_info (NDI_UNIQUE, 0, op, "Jari Vanhala <jam@modeemi.cs.tut.fi>");
101 new_draw_info (NDI_UNIQUE, 0, op, "kivinen@joker.cs.hut.fi (Tero Kivinen)"); 94 new_draw_info (NDI_UNIQUE, 0, op, "Tero Kivinen <kivinen@joker.cs.hut.fi>");
102 new_draw_info (NDI_UNIQUE, 0, op, "peterm@soda.berkeley.edu (Peter Mardahl)"); 95 new_draw_info (NDI_UNIQUE, 0, op, "Peter Mardahl <peterm@soda.berkeley.edu>");
103 new_draw_info (NDI_UNIQUE, 0, op, "matt@cs.odu.edu (Matthew Zeher)"); 96 new_draw_info (NDI_UNIQUE, 0, op, "Matthew Zeher <matt@cs.odu.edu>");
104 new_draw_info (NDI_UNIQUE, 0, op, "srt@sun-dimas.aero.org (Scott R. Turner)"); 97 new_draw_info (NDI_UNIQUE, 0, op, "Scott R. Turner <srt@sun-dimas.aero.org>");
105 new_draw_info (NDI_UNIQUE, 0, op, "huma@netcom.com (Ben Fennema)"); 98 new_draw_info (NDI_UNIQUE, 0, op, "Ben Fennema <huma@netcom.com>");
106 new_draw_info (NDI_UNIQUE, 0, op, "njw@cs.city.ac.uk (Nick Williams)"); 99 new_draw_info (NDI_UNIQUE, 0, op, "Nick Williams <njw@cs.city.ac.uk>");
107 new_draw_info (NDI_UNIQUE, 0, op, "Wacren@Gin.ObsPM.Fr (Laurent Wacrenier)"); 100 new_draw_info (NDI_UNIQUE, 0, op, "Laurent Wacrenier <Wacren@Gin.ObsPM.Fr>");
108 new_draw_info (NDI_UNIQUE, 0, op, "thomas@astro.psu.edu (Brian Thomas)"); 101 new_draw_info (NDI_UNIQUE, 0, op, "Brian Thomas <thomas@astro.psu.edu>");
109 new_draw_info (NDI_UNIQUE, 0, op, "jsm@axon.ksc.nasa.gov (John Steven Moerk)"); 102 new_draw_info (NDI_UNIQUE, 0, op, "John Steven Moerk <jsm@axon.ksc.nasa.gov>");
110 new_draw_info (NDI_UNIQUE, 0, op, "Delbecq David [david.delbecq@mailandnews.com]"); 103 new_draw_info (NDI_UNIQUE, 0, op, "Delbecq David <david.delbecq@mailandnews.com>");
111 new_draw_info (NDI_UNIQUE, 0, op, "Chachkoff Yann [yann.chachkoff@mailandnews.com]\n"); 104 new_draw_info (NDI_UNIQUE, 0, op, "Chachkoff Yann <yann.chachkoff@mailandnews.com>\n");
105
112 new_draw_info (NDI_UNIQUE, 0, op, "Images and art:"); 106 new_draw_info (NDI_UNIQUE, 0, op, "Images and art:");
113 new_draw_info (NDI_UNIQUE, 0, op, "Peter Gardner"); 107 new_draw_info (NDI_UNIQUE, 0, op, "Peter Gardner");
114 new_draw_info (NDI_UNIQUE, 0, op, "David Gervais [david_eg@mail.com]"); 108 new_draw_info (NDI_UNIQUE, 0, op, "David Gervais <david_eg@mail.com>");
115 new_draw_info (NDI_UNIQUE, 0, op, "Mitsuhiro Itakura [ita@gold.koma.jaeri.go.jp]"); 109 new_draw_info (NDI_UNIQUE, 0, op, "Mitsuhiro Itakura <ita@gold.koma.jaeri.go.jp>");
116 new_draw_info (NDI_UNIQUE, 0, op, "Hansjoerg Malthaner [hansjoerg.malthaner@danet.de]"); 110 new_draw_info (NDI_UNIQUE, 0, op, "Hansjoerg Malthaner <hansjoerg.malthaner@danet.de>");
117 new_draw_info (NDI_UNIQUE, 0, op, "Mårten Woxberg [maxmc@telia.com]"); 111 new_draw_info (NDI_UNIQUE, 0, op, "MÃ¥rten Woxberg <maxmc@telia.com>");
118 new_draw_info (NDI_UNIQUE, 0, op, "And many more!"); 112 new_draw_info (NDI_UNIQUE, 0, op, "And many more!");
119}
120
121void
122info_keys (object *op)
123{
124 clear_win_info (op);
125 new_draw_info (NDI_UNIQUE, 0, op, "Push `hjklynub' to walk in a direction.");
126 new_draw_info (NDI_UNIQUE, 0, op, "Shift + dir = fire, Ctrl + dir = run");
127 new_draw_info (NDI_UNIQUE, 0, op, "(To fire at yourself, hit `.'");
128 new_draw_info (NDI_UNIQUE, 0, op, "To attack, walk into the monsters.");
129 new_draw_info (NDI_UNIQUE, 0, op, "\" = speak ' = extended command");
130 new_draw_info (NDI_UNIQUE, 0, op, "i = inventory , = get : = look");
131 new_draw_info (NDI_UNIQUE, 0, op, "<> = rotate d = drop ? = help");
132 new_draw_info (NDI_UNIQUE, 0, op, "a = apply A = apply below t = throw");
133 new_draw_info (NDI_UNIQUE, 0, op, "e = examine E = exa below @ = autopick");
134 new_draw_info (NDI_UNIQUE, 0, op, "C = configure s = brace v = version");
135 new_draw_info (NDI_UNIQUE, 0, op, "+- = change range <tab> = browse spells");
136 new_draw_info (NDI_UNIQUE, 0, op, "x = change inventory type");
137 new_draw_info (NDI_UNIQUE, 0, op, "Mouse: L = examine, M = apply, R = drop/get");
138 new_draw_info (NDI_UNIQUE, 0, op, "'help = info about extended commands.");
139 new_draw_info (NDI_UNIQUE, 0, op, "Ctrl-R = refresh Ctrl-C = clear");
140 new_draw_info (NDI_UNIQUE, 0, op, "You can type a number before most commands.");
141 new_draw_info (NDI_UNIQUE, 0, op, "(For instance 3d drops 3 items.)");
142}
143
144void
145start_info (object *op)
146{
147 char buf[MAX_BUF];
148
149 sprintf (buf, "Welcome to Crossfire, v%s!", VERSION);
150 new_draw_info (NDI_UNIQUE, 0, op, buf);
151 new_draw_info (NDI_UNIQUE, 0, op, "Press `?' for help");
152 new_draw_info (NDI_UNIQUE, 0, op, " ");
153 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, op, "%s entered the game.", &op->name);
154 if (!op->contr->name_changed)
155 {
156 new_draw_info (NDI_UNIQUE, 0, op, "Note that you must set your name with the name");
157 new_draw_info (NDI_UNIQUE, 0, op, "command to enter the highscore list.");
158 new_draw_info (NDI_UNIQUE, 0, op, "(You can also use the crossfire.name X-resource.)");
159 }
160}
161
162/* Really, there is no reason to crypt the passwords any system. But easier
163 * to just leave this enabled for backward compatibility. Put the
164 * simple case at top - no encryption - makes it easier to read.
165 */
166char *
167crypt_string (char *str, char *salt)
168{
169#if defined(WIN32) || (defined(__FreeBSD__) && !defined(HAVE_LIBDES))
170 return (str);
171#else
172 static char *c = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
173 char s[2];
174
175 if (salt == NULL)
176 s[0] = c[RANDOM () % (int) strlen (c)], s[1] = c[RANDOM () % (int) strlen (c)];
177 else
178 s[0] = salt[0], s[1] = salt[1];
179
180# ifdef HAVE_LIBDES
181 return (char *) des_crypt (str, s);
182# endif
183 /* Default case - just use crypt */
184 return (char *) crypt (str, s);
185#endif
186}
187
188int
189check_password (char *typed, char *crypted)
190{
191 return !strcmp (crypt_string (typed, crypted), crypted);
192} 113}
193 114
194/* This is a basic little function to put the player back to his 115/* This is a basic little function to put the player back to his
195 * savebed. We do some error checking - its possible that the 116 * savebed. We do some error checking - its possible that the
196 * savebed map may no longer exist, so we make sure the player 117 * savebed map may no longer exist, so we make sure the player
197 * goes someplace. 118 * goes someplace.
198 */ 119 */
199void 120void
200enter_player_savebed (object *op) 121enter_player_savebed (object *op)
201{ 122{
202 mapstruct *oldmap = op->map; 123 object *tmp = object::create ();
203 object *tmp;
204
205 tmp = get_object ();
206
207 EXIT_PATH (tmp) = op->contr->savebed_map; 124 EXIT_PATH (tmp) = op->contr->savebed_map;
208 EXIT_X (tmp) = op->contr->bed_x; 125 EXIT_X (tmp) = op->contr->bed_x;
209 EXIT_Y (tmp) = op->contr->bed_y; 126 EXIT_Y (tmp) = op->contr->bed_y;
210 enter_exit (op, tmp); 127 op->enter_exit (tmp);
211 /* If the player has not changed maps and the name does not match 128 tmp->destroy ();
212 * that of the savebed, his savebed map is gone. Lets go back
213 * to the emergency path. Update what the players savebed is
214 * while we're at it.
215 */
216 if (oldmap == op->map && strcmp (op->contr->savebed_map, oldmap->path))
217 {
218 LOG (llevDebug, "Player %s savebed location %s is invalid - going to emergency location (%s)\n",
219 settings.emergency_mapname, &op->name, op->contr->savebed_map);
220 strcpy (op->contr->savebed_map, settings.emergency_mapname);
221 op->contr->bed_x = settings.emergency_x;
222 op->contr->bed_y = settings.emergency_y;
223 EXIT_PATH (tmp) = op->contr->savebed_map;
224 EXIT_X (tmp) = op->contr->bed_x;
225 EXIT_Y (tmp) = op->contr->bed_y;
226 enter_exit (op, tmp);
227 }
228 free_object (tmp);
229}
230
231/* All this really is is a glorified remove_object that also updates
232 * the counts on the map if needed.
233 */
234void
235leave_map (object *op)
236{
237 mapstruct *oldmap = op->map;
238
239 remove_ob (op);
240
241 if (oldmap)
242 {
243 if (!op->contr->hidden)
244 oldmap->players--;
245 if (oldmap->players <= 0)
246 { /* can be less than zero due to errors in tracking this */
247 set_map_timeout (oldmap);
248 }
249 }
250} 129}
251 130
252/* 131/*
253 * enter_map(): Moves the player and pets from current map (if any) to 132 * enter_map(): Moves the player and pets from current map (if any) to
254 * new map. map, x, y must be set. map is the map we are moving the 133 * new map. map, x, y must be set. map is the map we are moving the
255 * player to - it could be the map he just came from if the load failed for 134 * player to - it could be the map he just came from if the load failed for
256 * whatever reason. If default map coordinates are to be used, then 135 * whatever reason. If default map coordinates are to be used, then
257 * the function that calls this should figure them out. 136 * the function that calls this should figure them out.
258 */ 137 */
259static void 138void
260enter_map (object *op, mapstruct *newmap, int x, int y) 139object::enter_map (maptile *newmap, int x, int y)
261{ 140{
262 mapstruct *oldmap = op->map; 141 if (destroyed () || !newmap)
142 return;
263 143
264 if (out_of_map (newmap, x, y)) 144 if (out_of_map (newmap, x, y))
265 { 145 {
266 LOG (llevError, "enter_map: supplied coordinates are not within the map! (%s: %d, %d)\n", newmap->path, x, y); 146 LOG (llevError, "enter_map: supplied coordinates are not within the map! (%s: %d, %d)\n", &newmap->path, x, y);
267 x = MAP_ENTER_X (newmap); 147 x = newmap->enter_x;
268 y = MAP_ENTER_Y (newmap); 148 y = newmap->enter_y;
269 if (out_of_map (newmap, x, y)) 149 if (out_of_map (newmap, x, y))
270 { 150 {
271 LOG (llevError, "enter_map: map %s provides invalid default enter location (%d, %d) > (%d, %d)\n", 151 LOG (llevError, "enter_map: map %s provides invalid default enter location (%d, %d) > (%d, %d)\n",
272 newmap->path, x, y, MAP_WIDTH (newmap), MAP_HEIGHT (newmap)); 152 &newmap->path, x, y, newmap->width, newmap->height);
273 new_draw_info (NDI_UNIQUE, 0, op, "The exit is closed"); 153 new_draw_info (NDI_UNIQUE, 0, this, "The exit is closed");
274 return; 154 return;
275 } 155 }
276 } 156 }
157
158 if (map && map != newmap)
159 if (INVOKE_MAP (LEAVE, map, ARG_PLAYER (contr)))
160 return;
161
162 /* If it is a player login, he has yet to be inserted anyplace.
163 * otherwise, we need to deal with removing the player here.
164 */
165 remove ();
166
277 /* try to find a spot for the player */ 167 /* try to find a spot for the player */
278 if (ob_blocked (op, newmap, x, y)) 168 if (ob_blocked (this, newmap, x, y))
279 { /* First choice blocked */ 169 { /* First choice blocked */
280 /* We try to find a spot for the player, starting closest in. 170 /* We try to find a spot for the player, starting closest in.
281 * We could use find_first_free_spot, but that doesn't randomize it at all, 171 * We could use find_first_free_spot, but that doesn't randomize it at all,
282 * So for example, if the north space is free, you would always end up there even 172 * So for example, if the north space is free, you would always end up there even
283 * if other spaces around are available. 173 * if other spaces around are available.
284 * Note that for the second and third calls, we could start at a position other 174 * Note that for the second and third calls, we could start at a position other
285 * than one, but then we could end up on the other side of walls and so forth. 175 * than one, but then we could end up on the other side of walls and so forth.
286 */ 176 */
287 int i = find_free_spot (op, newmap, x, y, 1, SIZEOFFREE1 + 1); 177 int i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE1 + 1);
288 178
289 if (i == -1) 179 if (i == -1)
290 { 180 {
291 i = find_free_spot (op, newmap, x, y, 1, SIZEOFFREE2 + 1); 181 i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE2 + 1);
292 if (i == -1) 182 if (i == -1)
293 i = find_free_spot (op, newmap, x, y, 1, SIZEOFFREE); 183 i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE);
294 } 184 }
185
295 if (i != -1) 186 if (i != -1)
296 { 187 {
297 x += freearr_x[i]; 188 x += freearr_x[i];
298 y += freearr_y[i]; 189 y += freearr_y[i];
299 } 190 }
300 else 191 else
301 {
302 /* not much we can do in this case. */ 192 /* not much we can do in this case. */
303 LOG (llevInfo, "enter_map: Could not find free spot for player - will dump on top of object (%s: %d, %d)\n", newmap->path, x, y); 193 LOG (llevInfo, "enter_map: Could not find free spot for player - will dump on top of object (%s: %d, %d)\n", &newmap->path, x, y);
304 }
305 } /* end if looking for free spot */
306
307 if (op->map != NULL)
308 { 194 }
309 INVOKE_PLAYER (MAP_CHANGE, op->contr, ARG_MAP (op->map), ARG_MAP (newmap)); 195
310 INVOKE_MAP (LEAVE, op->map, ARG_PLAYER (op->contr)); 196 if (map != newmap && contr)
311 } 197 {
198 if (INVOKE_PLAYER (MAP_CHANGE, contr, ARG_MAP (newmap), ARG_INT (x), ARG_INT (y)))
199 return;
312 200
313 /* If it is a player login, he has yet to be inserted anyplace. 201 if (INVOKE_MAP (ENTER, newmap, ARG_PLAYER (contr), ARG_INT (x), ARG_INT (y)))
314 * otherwise, we need to deal with removing the player here. 202 return;
315 */ 203 }
316 if (!QUERY_FLAG (op, FLAG_REMOVED))
317 remove_ob (op);
318 204
319 /* remove_ob clears these so they must be reset after the remove_ob call */
320 op->x = x; 205 this->x = x;
321 op->y = y; 206 this->y = y;
322 op->map = newmap; 207 map = newmap;
208
323 insert_ob_in_map (op, op->map, NULL, INS_NO_WALK_ON); 209 insert_ob_in_map (this, map, 0, INS_NO_WALK_ON);
324 210
325 INVOKE_MAP (ENTER, op->map, ARG_PLAYER (op->contr));
326
327 if (!op->contr->hidden)
328 newmap->players++;
329
330 newmap->timeout = 0;
331 op->enemy = NULL; 211 enemy = NULL;
332 212
333 if (op->contr) 213 if (contr)
334 { 214 {
335 strcpy (op->contr->maplevel, newmap->path); 215 strcpy (contr->maplevel, newmap->path);
336 op->contr->count = 0; 216 contr->count = 0;
337 } 217 }
338 218
339 /* Update any golems */ 219 /* Update any golems */
340 if (op->type == PLAYER && op->contr->ranges[range_golem] != NULL) 220 if (type == PLAYER && contr->ranges[range_golem])
341 { 221 {
342 int i = find_free_spot (op->contr->ranges[range_golem], newmap, 222 int i = find_free_spot (contr->ranges[range_golem], newmap,
343 x, y, 1, SIZEOFFREE); 223 x, y, 1, SIZEOFFREE);
344 224
345 remove_ob (op->contr->ranges[range_golem]); 225 contr->ranges[range_golem]->remove ();
226
346 if (i == -1) 227 if (i == -1)
347 { 228 {
348 remove_friendly_object (op->contr->ranges[range_golem]); 229 remove_friendly_object (contr->ranges[range_golem]);
349 free_object (op->contr->ranges[range_golem]); 230 contr->ranges[range_golem]->destroy ();
350 op->contr->ranges[range_golem] = NULL; 231 contr->ranges[range_golem] = 0;
351 op->contr->golem_count = 0;
352 } 232 }
353 else 233 else
354 { 234 {
355 object *tmp;
356
357 for (tmp = op->contr->ranges[range_golem]; tmp != NULL; tmp = tmp->more) 235 for (object *tmp = contr->ranges[range_golem]; tmp != NULL; tmp = tmp->more)
358 { 236 {
359 tmp->x = x + freearr_x[i] + (tmp->arch == NULL ? 0 : tmp->arch->clone.x); 237 tmp->x = x + freearr_x[i] + (tmp->arch == NULL ? 0 : tmp->arch->clone.x);
360 tmp->y = y + freearr_y[i] + (tmp->arch == NULL ? 0 : tmp->arch->clone.y); 238 tmp->y = y + freearr_y[i] + (tmp->arch == NULL ? 0 : tmp->arch->clone.y);
361 tmp->map = newmap; 239 tmp->map = newmap;
362 } 240 }
241
363 insert_ob_in_map (op->contr->ranges[range_golem], newmap, NULL, 0); 242 insert_ob_in_map (contr->ranges[range_golem], newmap, NULL, 0);
364 op->contr->ranges[range_golem]->direction = 243 contr->ranges[range_golem]->direction =
365 find_dir_2 (op->x - op->contr->ranges[range_golem]->x, op->y - op->contr->ranges[range_golem]->y); 244 find_dir_2 (x - contr->ranges[range_golem]->x, y - contr->ranges[range_golem]->y);
366 } 245 }
367 } 246 }
247
368 op->direction = 0; 248 direction = 0;
369 249
370 /* since the players map is already loaded, we don't need to worry 250 /* since the players map is already loaded, we don't need to worry
371 * about pending objects. 251 * about pending objects.
372 */ 252 */
373 remove_all_pets (newmap); 253 remove_all_pets (newmap);
374
375 /* If the player is changing maps, we need to do some special things
376 * Do this after the player is on the new map - otherwise the force swap of the
377 * old map does not work.
378 */
379 if (oldmap != newmap)
380 {
381 if (oldmap) /* adjust old map */
382 {
383 oldmap->players--;
384
385 if (oldmap->players <= 0) /* can be less than zero due to errors in tracking this */
386 set_map_timeout (oldmap);
387 }
388 }
389} 254}
390
391void
392set_map_timeout (mapstruct *oldmap)
393{
394#if MAP_MAXTIMEOUT
395 oldmap->timeout = MAP_TIMEOUT (oldmap);
396 /* Do MINTIMEOUT first, so that MAXTIMEOUT is used if that is
397 * lower than the min value.
398 */
399# if MAP_MINTIMEOUT
400 if (oldmap->timeout < MAP_MINTIMEOUT)
401 {
402 oldmap->timeout = MAP_MINTIMEOUT;
403 }
404# endif
405 if (oldmap->timeout > MAP_MAXTIMEOUT)
406 {
407 oldmap->timeout = MAP_MAXTIMEOUT;
408 }
409#else
410 /* save out the map */
411 swap_map (oldmap);
412#endif /* MAP_MAXTIMEOUT */
413}
414
415
416/* clean_path takes a path and replaces all / with _
417 * We do a strcpy so that we do not change the original string.
418 */
419char *
420clean_path (const char *file)
421{
422 static char newpath[MAX_BUF], *cp;
423
424 strncpy (newpath, file, MAX_BUF - 1);
425 newpath[MAX_BUF - 1] = '\0';
426 for (cp = newpath; *cp != '\0'; cp++)
427 {
428 if (*cp == '/')
429 *cp = '_';
430 }
431 return newpath;
432}
433
434
435/* unclean_path takes a path and replaces all _ with /
436 * This basically undoes clean path.
437 * We do a strcpy so that we do not change the original string.
438 * We are smart enough to start after the last / in case we
439 * are getting passed a string that points to a unique map
440 * path.
441 */
442char *
443unclean_path (const char *src)
444{
445 static char newpath[MAX_BUF], *cp;
446
447 cp = strrchr (src, '/');
448 if (cp)
449 strncpy (newpath, cp + 1, MAX_BUF - 1);
450 else
451 strncpy (newpath, src, MAX_BUF - 1);
452 newpath[MAX_BUF - 1] = '\0';
453
454 for (cp = newpath; *cp != '\0'; cp++)
455 {
456 if (*cp == '_')
457 *cp = '/';
458 }
459 return newpath;
460}
461
462
463/* The player is trying to enter a randomly generated map. In this case, generate the
464 * random map as needed.
465 */
466
467static void
468enter_random_map (object *pl, object *exit_ob)
469{
470 mapstruct *new_map;
471 char newmap_name[HUGE_BUF], *cp;
472 static int reference_number = 0;
473 RMParms rp;
474
475 memset (&rp, 0, sizeof (RMParms));
476 rp.Xsize = -1;
477 rp.Ysize = -1;
478 rp.region = get_region_by_map (exit_ob->map);
479 if (exit_ob->msg)
480 set_random_map_variable (&rp, exit_ob->msg);
481 rp.origin_x = exit_ob->x;
482 rp.origin_y = exit_ob->y;
483 strcpy (rp.origin_map, pl->map->path);
484
485 /* If we have a final_map, use it as a base name to give some clue
486 * as where the player is. Otherwise, use the origin map.
487 * Take the last component (after the last slash) to give
488 * shorter names without bogus slashes.
489 */
490 if (rp.final_map[0])
491 {
492 cp = strrchr (rp.final_map, '/');
493 if (!cp)
494 cp = rp.final_map;
495 }
496 else
497 {
498 char buf[HUGE_BUF];
499
500 cp = strrchr (rp.origin_map, '/');
501 if (!cp)
502 cp = rp.origin_map;
503 /* Need to strip of any trailing digits, if it has them */
504 strcpy (buf, cp);
505 while (isdigit (buf[strlen (buf) - 1]))
506 buf[strlen (buf) - 1] = 0;
507 cp = buf;
508 }
509
510 sprintf (newmap_name, "/random/%s%04d", cp + 1, reference_number++);
511
512 /* now to generate the actual map. */
513 new_map = generate_random_map (newmap_name, &rp);
514
515 /* Update the exit_ob so it now points directly at the newly created
516 * random maps. Not that it is likely to happen, but it does mean that a
517 * exit in a unique map leading to a random map will not work properly.
518 * It also means that if the created random map gets reset before
519 * the exit leading to it, that the exit will no longer work.
520 */
521 if (new_map)
522 {
523 int x, y;
524
525 x = EXIT_X (exit_ob) = MAP_ENTER_X (new_map);
526 y = EXIT_Y (exit_ob) = MAP_ENTER_Y (new_map);
527 EXIT_PATH (exit_ob) = newmap_name;
528 strcpy (new_map->path, newmap_name);
529 enter_map (pl, new_map, x, y);
530 }
531}
532
533/* The player is trying to enter a non-randomly generated template map. In this
534 * case, use a map file for a template
535 */
536
537static void
538enter_fixed_template_map (object *pl, object *exit_ob)
539{
540 mapstruct *new_map;
541 char tmpnum[32], exitpath[HUGE_BUF], resultname[HUGE_BUF], tmpstring[HUGE_BUF], *sourcemap;
542 const char *new_map_name;
543
544 /* Split the exit path string into two parts, one
545 * for where to store the map, and one for were
546 * to generate the map from.
547 */
548 snprintf (exitpath, sizeof (exitpath), "%s", EXIT_PATH (exit_ob) + 2);
549 sourcemap = strchr (exitpath, '!');
550 if (!sourcemap)
551 {
552 new_draw_info_format (NDI_UNIQUE, 0, pl, "The %s is closed.", &exit_ob->name);
553 /* Should only occur when no source map is set.
554 */
555 LOG (llevError, "enter_fixed_template_map: Exit %s (%d,%d) on map %s has no source template.\n",
556 &exit_ob->name, exit_ob->x, exit_ob->y, exit_ob->map->path);
557 return;
558 }
559 *sourcemap++ = '\0';
560
561 /* If we are not coming from a template map, we can use relative directories
562 * for the map to generate from.
563 */
564 if (!exit_ob->map->templatemap)
565 {
566 sourcemap = path_combine_and_normalize (exit_ob->map->path, sourcemap);
567 }
568
569 /* Do replacement of %x, %y, and %n to the x coord of the exit, the y coord
570 * of the exit, and the name of the map the exit is on, respectively.
571 */
572 sprintf (tmpnum, "%d", exit_ob->x);
573 replace (exitpath, "%x", tmpnum, resultname, sizeof (resultname));
574
575 sprintf (tmpnum, "%d", exit_ob->y);
576 sprintf (tmpstring, "%s", resultname);
577 replace (tmpstring, "%y", tmpnum, resultname, sizeof (resultname));
578
579 sprintf (tmpstring, "%s", resultname);
580 replace (tmpstring, "%n", exit_ob->map->name, resultname, sizeof (resultname));
581
582 /* If we are coming from another template map, use reletive paths unless
583 * indicated otherwise.
584 */
585 if (exit_ob->map->templatemap && (resultname[0] != '/'))
586 {
587 new_map_name = path_combine_and_normalize (exit_ob->map->path, resultname);
588 }
589 else
590 {
591 new_map_name = create_template_pathname (resultname);
592 }
593
594 /* Attempt to load the map, if unable to, then
595 * create the map from the template.
596 */
597 new_map = ready_map_name (new_map_name, MAP_PLAYER_UNIQUE);
598 if (!new_map)
599 {
600 new_map = load_original_map (create_pathname (sourcemap), MAP_PLAYER_UNIQUE);
601 if (new_map)
602 fix_auto_apply (new_map);
603 }
604
605 if (new_map)
606 {
607 /* set the path of the map to where it should be
608 * so we don't just save over the source map.
609 */
610 strcpy (new_map->path, new_map_name);
611 new_map->templatemap = 1;
612 enter_map (pl, new_map, EXIT_X (exit_ob), EXIT_Y (exit_ob));
613 }
614 else
615 {
616 new_draw_info_format (NDI_UNIQUE, 0, pl, "The %s is closed.", &exit_ob->name);
617 /* Should only occur when an invalid source map is set.
618 */
619 LOG (llevDebug, "enter_fixed_template_map: Exit %s (%d,%d) on map %s leads no where.\n",
620 &exit_ob->name, exit_ob->x, exit_ob->y, exit_ob->map->path);
621 }
622}
623
624
625/* The player is trying to enter a randomly generated template map. In this
626 * case, generate the map as needed.
627 */
628
629static void
630enter_random_template_map (object *pl, object *exit_ob)
631{
632 mapstruct *new_map;
633 char tmpnum[32], resultname[HUGE_BUF], tmpstring[HUGE_BUF];
634 const char *new_map_name;
635 RMParms rp;
636
637 /* Do replacement of %x, %y, and %n to the x coord of the exit, the y coord
638 * of the exit, and the name of the map the exit is on, respectively.
639 */
640 sprintf (tmpnum, "%d", exit_ob->x);
641 replace (EXIT_PATH (exit_ob) + 3, "%x", tmpnum, resultname, sizeof (resultname));
642
643 sprintf (tmpnum, "%d", exit_ob->y);
644 sprintf (tmpstring, "%s", resultname);
645 replace (tmpstring, "%y", tmpnum, resultname, sizeof (resultname));
646
647 sprintf (tmpstring, "%s", resultname);
648 replace (tmpstring, "%n", exit_ob->map->name, resultname, sizeof (resultname));
649
650 /* If we are coming from another template map, use reletive paths unless
651 * indicated otherwise.
652 */
653 if (exit_ob->map->templatemap && (resultname[0] != '/'))
654 {
655 new_map_name = path_combine_and_normalize (exit_ob->map->path, resultname);
656 }
657 else
658 {
659 new_map_name = create_template_pathname (resultname);
660 }
661
662 new_map = ready_map_name (new_map_name, MAP_PLAYER_UNIQUE);
663 if (!new_map)
664 {
665 memset (&rp, 0, sizeof (RMParms));
666 rp.Xsize = -1;
667 rp.Ysize = -1;
668 rp.region = get_region_by_map (exit_ob->map);
669 if (exit_ob->msg)
670 set_random_map_variable (&rp, exit_ob->msg);
671 rp.origin_x = exit_ob->x;
672 rp.origin_y = exit_ob->y;
673 strcpy (rp.origin_map, pl->map->path);
674
675 /* now to generate the actual map. */
676 new_map = generate_random_map (new_map_name, &rp);
677 }
678
679
680 /* Update the exit_ob so it now points directly at the newly created
681 * random maps. Not that it is likely to happen, but it does mean that a
682 * exit in a unique map leading to a random map will not work properly.
683 * It also means that if the created random map gets reset before
684 * the exit leading to it, that the exit will no longer work.
685 */
686 if (new_map)
687 {
688 int x, y;
689
690 x = EXIT_X (exit_ob) = MAP_ENTER_X (new_map);
691 y = EXIT_Y (exit_ob) = MAP_ENTER_Y (new_map);
692 new_map->templatemap = 1;
693 enter_map (pl, new_map, x, y);
694 }
695}
696
697
698/* Code to enter/detect a character entering a unique map.
699 */
700static void
701enter_unique_map (object *op, object *exit_ob)
702{
703 char apartment[HUGE_BUF];
704 mapstruct *newmap;
705
706 if (EXIT_PATH (exit_ob)[0] == '/')
707 {
708 sprintf (apartment, "%s/%s/%s/%s", settings.localdir, settings.playerdir, &op->name, clean_path (EXIT_PATH (exit_ob)));
709 newmap = ready_map_name (apartment, MAP_PLAYER_UNIQUE);
710 if (!newmap)
711 {
712 newmap = load_original_map (create_pathname (EXIT_PATH (exit_ob)), MAP_PLAYER_UNIQUE);
713 if (newmap)
714 fix_auto_apply (newmap);
715 }
716 }
717 else
718 { /* relative directory */
719 char reldir[HUGE_BUF], tmpc[HUGE_BUF], *cp;
720
721 if (exit_ob->map->unique)
722 {
723
724 strcpy (reldir, unclean_path (exit_ob->map->path));
725
726 /* Need to copy this over, as clean_path only has one static return buffer */
727 strcpy (tmpc, clean_path (reldir));
728 /* Remove final component, if any */
729 if ((cp = strrchr (tmpc, '_')) != NULL)
730 *cp = 0;
731
732 sprintf (apartment, "%s/%s/%s/%s_%s", settings.localdir, settings.playerdir, &op->name, tmpc, clean_path (EXIT_PATH (exit_ob)));
733
734 newmap = ready_map_name (apartment, MAP_PLAYER_UNIQUE);
735 if (!newmap)
736 {
737 newmap = load_original_map (create_pathname (path_combine_and_normalize (reldir, EXIT_PATH (exit_ob))), MAP_PLAYER_UNIQUE);
738 if (newmap)
739 fix_auto_apply (newmap);
740 }
741 }
742 else
743 {
744 /* The exit is unique, but the map we are coming from is not unique. So
745 * use the basic logic - don't need to demangle the path name
746 */
747 sprintf (apartment, "%s/%s/%s/%s", settings.localdir,
748 settings.playerdir, &op->name, clean_path (path_combine_and_normalize (exit_ob->map->path, EXIT_PATH (exit_ob))));
749 newmap = ready_map_name (apartment, MAP_PLAYER_UNIQUE);
750 if (!newmap)
751 {
752 newmap = ready_map_name (path_combine_and_normalize (exit_ob->map->path, EXIT_PATH (exit_ob)), 0);
753 if (newmap)
754 fix_auto_apply (newmap);
755 }
756 }
757 }
758
759 if (newmap)
760 {
761 strcpy (newmap->path, apartment);
762 newmap->unique = 1;
763 enter_map (op, newmap, EXIT_X (exit_ob), EXIT_Y (exit_ob));
764 }
765 else
766 {
767 new_draw_info_format (NDI_UNIQUE, 0, op, "The %s is closed.", &exit_ob->name);
768 /* Perhaps not critical, but I would think that the unique maps
769 * should be new enough this does not happen. This also creates
770 * a strange situation where some players could perhaps have visited
771 * such a map before it was removed, so they have the private
772 * map, but other players can't get it anymore.
773 */
774 LOG (llevDebug, "enter_unique_map: Exit %s (%d,%d) on map %s is leads no where.\n",
775 &exit_ob->name, exit_ob->x, exit_ob->y, exit_ob->map->path);
776 }
777
778}
779
780
781/* Tries to move 'op' to exit_ob. op is the character or monster that is
782 * using the exit, where exit_ob is the exit object (boat, door, teleporter,
783 * etc.) if exit_ob is null, then op->contr->maplevel contains that map to
784 * move the object to. This is used when loading the player.
785 *
786 * Largely redone by MSW 2001-01-21 - this function was overly complex
787 * and had some obscure bugs.
788 */
789
790void
791enter_exit (object *op, object *exit_ob)
792{
793#define PORTAL_DESTINATION_NAME "Town portal destination" /* this one should really be in a header file */
794 object *tmp;
795
796 /* It may be nice to support other creatures moving across
797 * exits, but right now a lot of the code looks at op->contr,
798 * so thta is an RFE.
799 */
800 if (op->type != PLAYER)
801 return;
802
803 /* First, lets figure out what map the player is going to go to */
804 if (exit_ob)
805 {
806
807 /* check to see if we make a template map */
808 if (EXIT_PATH (exit_ob) && EXIT_PATH (exit_ob)[1] == '@')
809 {
810 if (EXIT_PATH (exit_ob)[2] == '!')
811 {
812 /* generate a template map randomly */
813 enter_random_template_map (op, exit_ob);
814 }
815 else
816 {
817 /* generate a template map from a fixed template */
818 enter_fixed_template_map (op, exit_ob);
819 }
820 }
821 /* check to see if we make a randomly generated map */
822 else if (EXIT_PATH (exit_ob) && EXIT_PATH (exit_ob)[1] == '!')
823 {
824 enter_random_map (op, exit_ob);
825 }
826 else if (QUERY_FLAG (exit_ob, FLAG_UNIQUE))
827 {
828 enter_unique_map (op, exit_ob);
829 }
830 else
831 {
832 int x = EXIT_X (exit_ob), y = EXIT_Y (exit_ob);
833
834 /* 'Normal' exits that do not do anything special
835 * Simple enough we don't need another routine for it.
836 */
837 mapstruct *newmap;
838
839 if (exit_ob->map)
840 {
841 newmap = ready_map_name (path_combine_and_normalize (exit_ob->map->path, EXIT_PATH (exit_ob)), 0);
842 /* Random map was previously generated, but is no longer about. Lets generate a new
843 * map.
844 */
845 if (!newmap && !strncmp (EXIT_PATH (exit_ob), "/random/", 8))
846 {
847 /* Maps that go down have a message set. However, maps that go
848 * up, don't. If the going home has reset, there isn't much
849 * point generating a random map, because it won't match the maps.
850 */
851 if (exit_ob->msg)
852 {
853 enter_random_map (op, exit_ob);
854 }
855 else
856 {
857 new_draw_info_format (NDI_UNIQUE, 0, op, "The %s is closed.", &exit_ob->name);
858 return;
859 }
860
861 /* For exits that cause damages (like pits). Don't know if any
862 * random maps use this or not.
863 */
864 if (exit_ob->stats.dam && op->type == PLAYER)
865 hit_player (op, exit_ob->stats.dam, exit_ob, exit_ob->attacktype, 1);
866 return;
867 }
868 }
869 else
870 {
871 /* For word of recall and other force objects
872 * They contain the full pathname of the map to go back to,
873 * so we don't need to normalize it.
874 * But we do need to see if it is unique or not
875 */
876 if (!strncmp (EXIT_PATH (exit_ob), settings.localdir, strlen (settings.localdir)))
877 newmap = ready_map_name (EXIT_PATH (exit_ob), MAP_PLAYER_UNIQUE);
878 else
879 newmap = ready_map_name (EXIT_PATH (exit_ob), 0);
880 }
881 if (!newmap)
882 {
883 if (exit_ob->name)
884 new_draw_info_format (NDI_UNIQUE, 0, op, "The %s is closed.", &exit_ob->name);
885 /* don't cry to momma if name is not set - as in tmp objects
886 * used by the savebed code and character creation */
887 return;
888 }
889
890 /* This supports the old behaviour, but it really should not be used.
891 * I will note for example that with this method, it is impossible to
892 * set 0,0 destination coordinates. Really, if we want to support
893 * using the new maps default coordinates, the exit ob should use
894 * something like -1, -1 so it is clear to do that.
895 */
896 if (x == 0 && y == 0)
897 {
898 x = MAP_ENTER_X (newmap);
899 y = MAP_ENTER_Y (newmap);
900 LOG (llevDebug, "enter_exit: Exit %s (%d,%d) on map %s is 0 destination coordinates\n",
901 &exit_ob->name, exit_ob->x, exit_ob->y, exit_ob->map ? exit_ob->map->path : "<nil>");
902 }
903
904 /* mids 02/13/2002 if exit is damned, update players death & WoR home-position and delete town portal */
905 if (QUERY_FLAG (exit_ob, FLAG_DAMNED))
906 {
907 /* remove an old force with a slaying field == PORTAL_DESTINATION_NAME */
908 for (tmp = op->inv; tmp != NULL; tmp = tmp->below)
909 {
910 if (tmp->type == FORCE && tmp->slaying && !strcmp (tmp->slaying, PORTAL_DESTINATION_NAME))
911 break;
912 }
913 if (tmp)
914 {
915 remove_ob (tmp);
916 free_object (tmp);
917 }
918
919 strcpy (op->contr->savebed_map, path_combine_and_normalize (exit_ob->map->path, EXIT_PATH (exit_ob)));
920 op->contr->bed_x = EXIT_X (exit_ob), op->contr->bed_y = EXIT_Y (exit_ob);
921 save_player (op, 1);
922 /* LOG(llevDebug,"enter_exit: Taking damned exit %s to (%d,%d) on map %s\n",
923 * exit_ob->name?exit_ob->name:"(none)", exit_ob->x, exit_ob->y,
924 * path_combine_and_normalize(exit_ob->map->path, EXIT_PATH(exit_ob))); */
925 }
926
927 enter_map (op, newmap, x, y);
928 }
929 /* For exits that cause damages (like pits) */
930 if (exit_ob->stats.dam && op->type == PLAYER)
931 hit_player (op, exit_ob->stats.dam, exit_ob, exit_ob->attacktype, 1);
932 }
933 else
934 {
935 int flags = 0;
936 mapstruct *newmap;
937
938
939 /* Hypothetically, I guess its possible that a standard map matches
940 * the localdir, but that seems pretty unlikely - unlikely enough that
941 * I'm not going to attempt to try to deal with that possibility.
942 * We use the fact that when a player saves on a unique map, it prepends
943 * the localdir to that name. So its an easy way to see of the map is
944 * unique or not.
945 */
946 if (!strncmp (op->contr->maplevel, settings.localdir, strlen (settings.localdir)))
947 flags = MAP_PLAYER_UNIQUE;
948
949 /* newmap returns the map (if already loaded), or loads it for
950 * us.
951 */
952 newmap = ready_map_name (op->contr->maplevel, flags);
953 if (!newmap)
954 {
955 LOG (llevError, "enter_exit: Pathname to map does not exist! (%s)\n", op->contr->maplevel);
956 newmap = ready_map_name (settings.emergency_mapname, 0);
957 op->x = settings.emergency_x;
958 op->y = settings.emergency_y;
959 /* If we can't load the emergency map, something is probably really
960 * screwed up, so bail out now.
961 */
962 if (!newmap)
963 {
964 LOG (llevError, "enter_exit: could not load emergency map? Fatal error\n");
965 abort ();
966 }
967 }
968 enter_map (op, newmap, op->x, op->y);
969 }
970}
971
972/*
973 * process_active_maps(): Works like process_events(), but it only
974 * processes maps which a player is on.
975 *
976 */
977
978#if 0 // dead code, schmorp
979void
980process_active_maps ()
981{
982 for (mapstruct *map = first_map; map != NULL; map = map->next)
983 if (map->in_memory == MAP_IN_MEMORY)
984 if (players_on_map (map, TRUE))
985 process_events (map);
986}
987#endif
988 255
989/* process_players1 and process_players2 do all the player related stuff. 256/* process_players1 and process_players2 do all the player related stuff.
990 * I moved it out of process events and process_map. This was to some 257 * I moved it out of process events and process_map. This was to some
991 * extent for debugging as well as to get a better idea of the time used 258 * extent for debugging as well as to get a better idea of the time used
992 * by the various functions. process_players1() does the processing before 259 * by the various functions. process_players1() does the processing before
993 * objects have been updated, process_players2() does the processing that 260 * objects have been updated, process_players2() does the processing that
994 * is needed after the players have been updated. 261 * is needed after the players have been updated.
995 */ 262 */
996 263static void
997void 264process_players1 ()
998process_players1 (mapstruct *map)
999{ 265{
1000 int flag; 266 int flag;
1001 player *pl, *plnext;
1002 267
1003 /* Basically, we keep looping until all the players have done their actions. */ 268 /* Basically, we keep looping until all the players have done their actions. */
1004 for (flag = 1; flag != 0;) 269 for (flag = 1; flag != 0;)
1005 { 270 {
1006 flag = 0; 271 flag = 0;
1007 for (pl = first_player; pl != NULL; pl = plnext) 272 for_all_players (pl)
1008 { 273 {
1009 plnext = pl->next; /* In case a player exits the game in handle_player() */ 274 pl->refcnt_chk ();
1010 275
1011 if (pl->ob == NULL) 276 if (!pl->ob || !pl->ns || !pl->ob->active ())
1012 continue; 277 continue;
1013 278
1014 if (map != NULL && pl->ob->map != map)
1015 continue;
1016
1017 if (pl->ob->speed_left > 0) 279 if (pl->ob->speed_left > 0)
1018 {
1019 if (handle_newcs_player (pl->ob)) 280 if (handle_newcs_player (pl->ob))
1020 flag = 1; 281 flag = 1;
1021 } /* end if player has speed left */
1022 282
1023 /* If the player is not actively playing, don't make a 283 /* If the player is not actively playing, don't make a
1024 * backup save - nothing to save anyway. Plus, the 284 * backup save - nothing to save anyway. Plus, the
1025 * map may not longer be valid. This can happen when the 285 * map may not longer be valid. This can happen when the
1026 * player quits - they exist for purposes of tracking on the map, 286 * player quits - they exist for purposes of tracking on the map,
1031 291
1032#ifdef AUTOSAVE 292#ifdef AUTOSAVE
1033 /* check for ST_PLAYING state so that we don't try to save off when 293 /* check for ST_PLAYING state so that we don't try to save off when
1034 * the player is logging in. 294 * the player is logging in.
1035 */ 295 */
1036 if ((pl->last_save_tick + AUTOSAVE) < (uint32) pticks && pl->state == ST_PLAYING) 296 if ((pl->last_save_tick + AUTOSAVE) < (uint32) pticks && pl->ns->state == ST_PLAYING)
1037 { 297 {
1038 /* Don't save the player on unholy ground. Instead, increase the 298 pl->ob->contr->save ();
1039 * tick time so it will be about 10 seconds before we try and save
1040 * again.
1041 */
1042// if (get_map_flags(pl->ob->map, NULL, pl->ob->x, pl->ob->y, NULL, NULL) & P_NO_CLERIC) {
1043// pl->last_save_tick += 100;
1044// } else {
1045 save_player (pl->ob, 1);
1046 pl->last_save_tick = pticks; 299 pl->last_save_tick = pticks;
1047// }
1048 } 300 }
1049#endif 301#endif
1050 } /* end of for loop for all the players */ 302 } /* end of for loop for all the players */
1051 } /* for flag */ 303 } /* for flag */
1052 for (pl = first_player; pl != NULL; pl = pl->next) 304
305 for_all_players (pl)
1053 { 306 {
1054 if (map != NULL && (pl->ob == NULL || pl->ob->map != map)) 307 if (!pl->ob || !pl->ns || !pl->ob->active ())
1055 continue; 308 continue;
309
1056 if (settings.casting_time == TRUE) 310 if (settings.casting_time)
1057 { 311 {
1058 if (pl->ob->casting_time > 0) 312 if (pl->ob->casting_time > 0)
1059 { 313 {
1060 pl->ob->casting_time--; 314 pl->ob->casting_time--;
1061 pl->ob->start_holding = 1; 315 pl->ob->start_holding = 1;
1062 } 316 }
317
1063 /* set spell_state so we can update the range in stats field */ 318 /* set spell_state so we can update the range in stats field */
1064 if ((pl->ob->casting_time == 0) && (pl->ob->start_holding == 1)) 319 if ((pl->ob->casting_time == 0) && (pl->ob->start_holding == 1))
1065 {
1066 pl->ob->start_holding = 0; 320 pl->ob->start_holding = 0;
1067 } 321 }
1068 } 322
1069 do_some_living (pl->ob); 323 do_some_living (pl->ob);
1070 /* draw(pl->ob); *//* updated in socket code */
1071 } 324 }
1072} 325}
1073 326
1074void 327static void
1075process_players2 (mapstruct *map) 328process_players2 ()
1076{ 329{
1077 player *pl;
1078
1079 /* Then check if any players should use weapon-speed instead of speed */ 330 /* Then check if any players should use weapon-speed instead of speed */
1080 for (pl = first_player; pl != NULL; pl = pl->next) 331 for_all_players (pl)
1081 { 332 {
1082 if (map != NULL)
1083 {
1084 if (pl->ob == NULL || QUERY_FLAG (pl->ob, FLAG_REMOVED))
1085 continue;
1086 else if (pl->loading != NULL) /* Player is blocked */
1087 pl->ob->speed_left -= pl->ob->speed;
1088 if (pl->ob->map != map)
1089 continue;
1090 }
1091
1092 /* The code that did weapon_sp handling here was out of place - 333 /* The code that did weapon_sp handling here was out of place -
1093 * this isn't called until after the player has finished there 334 * this isn't called until after the player has finished there
1094 * actions, and is thus out of place. All we do here is bounds 335 * actions, and is thus out of place. All we do here is bounds
1095 * checking. 336 * checking.
1096 */ 337 */
1102 /* This needs to be here - if the player is running, we need to 343 /* This needs to be here - if the player is running, we need to
1103 * clear this each tick, but new commands are not being received 344 * clear this each tick, but new commands are not being received
1104 * so execute_newserver_command() is never called 345 * so execute_newserver_command() is never called
1105 */ 346 */
1106 pl->has_hit = 0; 347 pl->has_hit = 0;
1107
1108 } 348 }
1109 else if (pl->ob->speed_left > pl->ob->speed) 349 else if (pl->ob->speed_left > pl->ob->speed)
1110 pl->ob->speed_left = pl->ob->speed; 350 pl->ob->speed_left = pl->ob->speed;
1111 } 351 }
1112} 352}
1113 353
1114void 354void
1115process_events (mapstruct *map) 355process_events ()
1116{ 356{
1117 object *op; 357 object *op;
1118 object *marker = get_object ();
1119 tag_t tag;
1120 358
359 static object_ptr marker_;
360
361 if (!marker_)
362 marker_ = object::create ();
363
364 object *marker = marker_;
365
1121 process_players1 (map); 366 process_players1 ();
1122 367
1123 marker->active_next = active_objects; 368 marker->active_next = active_objects;
1124 369
1125 if (marker->active_next) 370 if (marker->active_next)
1126 marker->active_next->active_prev = marker; 371 marker->active_next->active_prev = marker;
1127 372
1128 marker->active_prev = NULL; 373 marker->active_prev = 0;
1129 active_objects = marker; 374 active_objects = marker;
1130 375
1131 while (marker->active_next) 376 while (marker->active_next)
1132 { 377 {
1133 op = marker->active_next; 378 op = marker->active_next;
1134 tag = op->count;
1135 379
1136 /* Move marker forward - swap op and marker */ 380 /* Move marker forward - swap op and marker */
1137 op->active_prev = marker->active_prev; 381 op->active_prev = marker->active_prev;
1138 382
1139 if (op->active_prev) 383 if (op->active_prev)
1151 395
1152 /* Now process op */ 396 /* Now process op */
1153 if (QUERY_FLAG (op, FLAG_FREED)) 397 if (QUERY_FLAG (op, FLAG_FREED))
1154 { 398 {
1155 LOG (llevError, "BUG: process_events(): Free object on list\n"); 399 LOG (llevError, "BUG: process_events(): Free object on list\n");
1156 op->speed = 0; 400 op->set_speed (0);
1157 update_ob_speed (op);
1158 continue; 401 continue;
1159 } 402 }
1160 403
1161 /* I've seen occasional crashes due to this - the object is removed, 404 /* I've seen occasional crashes due to this - the object is removed,
1162 * and thus the map it points to (last map it was on) may be bogus 405 * and thus the map it points to (last map it was on) may be bogus
1169 * around. 412 * around.
1170 */ 413 */
1171 if (QUERY_FLAG (op, FLAG_REMOVED) && op->type != PLAYER && op->map && op->map->in_memory != MAP_IN_MEMORY) 414 if (QUERY_FLAG (op, FLAG_REMOVED) && op->type != PLAYER && op->map && op->map->in_memory != MAP_IN_MEMORY)
1172 { 415 {
1173 LOG (llevError, "BUG: process_events(): Removed object on list\n"); 416 LOG (llevError, "BUG: process_events(): Removed object on list\n");
1174 dump_object (op); 417 char *dump = dump_object (op);
1175 LOG (llevError, errmsg); 418 LOG (llevError, dump);
1176 free_object (op); 419 free (dump);
420 op->destroy ();
1177 continue; 421 continue;
1178 } 422 }
1179 423
1180 if (!op->speed) 424 if (!op->has_active_speed ())
1181 { 425 {
1182 LOG (llevError, "BUG: process_events(): Object %s has no speed, " "but is on active list\n", &op->arch->name); 426 LOG (llevError, "BUG: process_events(): Object %s has no speed (%f), "
1183 update_ob_speed (op); 427 "but is on active list\n", op->debug_desc (), op->speed);
428 op->set_speed (0);
1184 continue; 429 continue;
1185 } 430 }
1186 431
1187 if (op->map == NULL && op->env == NULL && op->name && op->type != MAP && map == NULL) 432 if (op->map == NULL && op->env == NULL && op->name && op->type != MAP)
1188 { 433 {
1189 LOG (llevError, "BUG: process_events(): Object without map or " "inventory is on active list: %s (%d)\n", &op->name, op->count); 434 LOG (llevError, "BUG: process_events(): Object without map or "
435 "inventory is on active list: %s (%d)\n", &op->name, op->count);
1190 op->speed = 0; 436 op->set_speed (0);
1191 update_ob_speed (op);
1192 continue; 437 continue;
1193 } 438 }
1194 439
1195 if (map != NULL && op->map != map)
1196 continue;
1197
1198 /* Animate the object. Bug of feature that andim_speed 440 /* Animate the object. Bug or feature that anim_speed
1199 * is based on ticks, and not the creatures speed? 441 * is based on ticks, and not the creatures speed?
1200 */ 442 */
1201 if (op->anim_speed && op->last_anim >= op->anim_speed) 443 if (op->anim_speed && op->last_anim >= op->anim_speed)
1202 { 444 {
1203 if ((op->type == PLAYER) || (op->type == MONSTER)) 445 if ((op->type == PLAYER))
1204 animate_object (op, op->facing); 446 animate_object (op, op->facing);
1205 else 447 else
1206 animate_object (op, op->direction); 448 animate_object (op, op->direction);
1207 449
1208 op->last_anim = 1; 450 op->last_anim = 1;
1226 LOG (llevDebug, "process_events: calling process_object with removed object %s\n", op->name ? op->name : "null"); 468 LOG (llevDebug, "process_events: calling process_object with removed object %s\n", op->name ? op->name : "null");
1227 } 469 }
1228#endif 470#endif
1229 --op->speed_left; 471 --op->speed_left;
1230 process_object (op); 472 process_object (op);
473
1231 if (was_destroyed (op, tag)) 474 if (op->destroyed ())
1232 continue; 475 continue;
1233 } 476 }
477
1234 if (settings.casting_time == TRUE && op->casting_time > 0) 478 if (settings.casting_time == TRUE && op->casting_time > 0)
1235 op->casting_time--; 479 op->casting_time--;
480
1236 if (op->speed_left <= 0) 481 if (op->speed_left <= 0)
1237 op->speed_left += FABS (op->speed); 482 op->speed_left += FABS (op->speed);
1238 } 483 }
1239 484
1240 /* Remove marker object from active list */ 485 /* Remove marker object from active list */
1241 if (marker->active_prev != NULL) 486 if (marker->active_prev != NULL)
1242 marker->active_prev->active_next = NULL; 487 marker->active_prev->active_next = NULL;
1243 else 488 else
1244 active_objects = NULL; 489 active_objects = NULL;
1245 490
1246 process_players2 (map); 491 process_players2 ();
1247
1248 free_object (marker);
1249} 492}
1250 493
494/* clean up everything before exiting */
1251void 495void
1252clean_tmp_files (void) 496emergency_save ()
1253{ 497{
1254 mapstruct *m, *next; 498 LOG (llevDebug, "emergency save begin.\n");
1255 499
1256 LOG (llevInfo, "Cleaning up...\n"); 500 LOG (llevDebug, "saving players.\n");
501 for_all_players (pl)
502 pl->save (1);
1257 503
1258 /* We save the maps - it may not be intuitive why, but if there are unique 504// for_all_players (pl)
1259 * items, we need to save the map so they get saved off. Perhaps we should 505// if (pl->ob)
1260 * just make a special function that only saves the unique items. 506// pl->ob->remove ();
1261 */ 507
1262 for (m = first_map; m != NULL; m = next) 508 LOG (llevDebug, "saving maps.\n");
509 maptile::emergency_save ();
510
511 LOG (llevDebug, "saving book archive.\n");
512 write_book_archive ();
513
514 LOG (llevDebug, "emergency save done.\n");
515}
516
517/* clean up everything before exiting */
518void
519cleanup (bool make_core)
520{
521 LOG (llevDebug, "cleanup begin.\n");
522
523 if (init_done && !in_cleanup)
524 {
525 in_cleanup = true;
526 emergency_save ();
1263 { 527 }
1264 next = m->next; 528 else
1265 if (m->in_memory == MAP_IN_MEMORY) 529 in_cleanup = true;
530
531 LOG (llevDebug, "running cleanup handlers.\n");
532 INVOKE_GLOBAL (CLEANUP);
533
534 LOG (llevDebug, "cleanup done.\n");
535
536 if (make_core)
537 abort ();
538 else
539 _exit (0);
540}
541
542void
543leave (player *pl, int draw_exit)
544{
545 if (pl)
546 {
547 if (pl->ob->type != DEAD_OBJECT)
1266 { 548 {
1267 /* If we want to reuse the temp maps, swap it out (note that will also 549 /* If a hidden dm dropped connection do not create
1268 * update the log file. Otherwise, save the map (mostly for unique item 550 * inconsistencies by showing that they have left the game
1269 * stuff). Note that the clean_tmp_map is called after the end of
1270 * the for loop but is in the #else bracket. IF we are recycling the maps,
1271 * we certainly don't want the temp maps removed.
1272 */ 551 */
1273 552 if (!(QUERY_FLAG (pl->ob, FLAG_WIZ) && pl->ob->contr->hidden)
1274 /* XXX The above comment is dead wrong */ 553 && draw_exit)
1275 if (settings.recycle_tmp_maps == TRUE)
1276 swap_map (m);
1277 else
1278 { 554 {
1279 new_save_map (m, 0); /* note we save here into a overlay map */ 555 if (pl->ob->map)
1280 clean_tmp_map (m); 556 {
557 INVOKE_PLAYER (LOGOUT, pl);
558 LOG (llevInfo, "LOGOUT: Player named %s from ip %s\n", &pl->ob->name, pl->ns->host);
559 }
560
561 char buf[MAX_BUF];
562
563 sprintf (buf, "%s left the game.", &pl->ob->name);
564 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, 0, buf);
1281 } 565 }
1282 }
1283 }
1284 write_todclock (); /* lets just write the clock here */
1285}
1286 566
1287/* clean up everything before exiting */ 567 pl->ob->remove ();
1288void 568 pl->ob->type = DEAD_OBJECT; /* To avoid problems with inventory window */
1289cleanup (void) 569 }
1290{
1291 LOG (llevDebug, "Cleanup called. freeing data.\n");
1292 clean_tmp_files ();
1293 write_book_archive ();
1294#ifdef MEMORY_DEBUG
1295 free_all_maps ();
1296 free_style_maps ();
1297 free_all_object_data ();
1298 free_all_archs ();
1299 free_all_treasures ();
1300 free_all_images ();
1301 free_all_newserver ();
1302 free_all_recipes ();
1303 free_all_readable ();
1304 free_all_god ();
1305 free_all_anim ();
1306 /* See what the string data that is out there that hasn't been freed. */
1307 570
1308/* LOG(llevDebug, ss_dump_table(0xff));*/
1309#endif
1310 exit (0);
1311}
1312
1313void
1314leave (player *pl, int draw_exit)
1315{
1316 if (pl != NULL)
1317 {
1318 /* We do this so that the socket handling routine can do the final 571 /* We do this so that the socket handling routine can do the final
1319 * cleanup. We also leave that loop to actually handle the freeing 572 * cleanup. We also leave that loop to actually handle the freeing
1320 * of the data. 573 * of the data.
1321 */ 574 */
1322 if (pl->ob->type != DEAD_OBJECT) 575 if (pl->ns)
1323 { 576 pl->ns->destroy ();
1324 pl->socket.status = Ns_Dead;
1325 577
1326 /* If a hidden dm dropped connection do not create
1327 * inconsistencies by showing that they have left the game
1328 */
1329 if (!(QUERY_FLAG (pl->ob, FLAG_WIZ) && pl->ob->contr->hidden)
1330 && draw_exit && (pl->state != ST_GET_NAME && pl->state != ST_GET_PASSWORD && pl->state != ST_CONFIRM_PASSWORD))
1331 {
1332 if (pl->ob->map)
1333 {
1334 INVOKE_PLAYER (LOGOUT, pl);
1335 LOG (llevInfo, "LOGOUT: Player named %s from ip %s\n", &pl->ob->name, pl->socket.host);
1336 }
1337
1338 char buf[MAX_BUF];
1339
1340 sprintf (buf, "%s left the game.", &pl->ob->name);
1341 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, buf);
1342 }
1343
1344 if (!QUERY_FLAG (pl->ob, FLAG_REMOVED))
1345 leave_map (pl->ob);
1346
1347 pl->ob->type = DEAD_OBJECT; /* To avoid problems with inventory window */
1348 }
1349 } 578 }
1350} 579}
1351 580
1352int 581int
1353forbid_play (void) 582forbid_play (void)
1416extern unsigned long todtick; 645extern unsigned long todtick;
1417 646
1418void 647void
1419do_specials (void) 648do_specials (void)
1420{ 649{
1421
1422#ifdef WATCHDOG
1423 if (!(pticks % 503))
1424 watchdog ();
1425#endif
1426
1427 if (!(pticks % PTICKS_PER_CLOCK)) 650 if (!(pticks % PTICKS_PER_CLOCK))
1428 tick_the_clock (); 651 tick_the_clock ();
1429 652
1430 if (!(pticks % 7)) 653 if (!(pticks % 7))
1431 shstr::gc (); 654 shstr::gc ();
1432 655
1433 if (!(pticks % 79))
1434 flush_old_maps (); /* Clears the tmp-files of maps which have reset */
1435
1436 if (!(pticks % 2503)) 656 if (!(pticks % 2503))
1437 fix_weight (); /* Hack to fix weightproblems caused by bugs */ 657 fix_weight (); /* Hack to fix weightproblems caused by bugs */
1438 658
1439 if (!(pticks % 2521))
1440 metaserver_update (); /* 2500 ticks is about 5 minutes */
1441
1442 if (!(pticks % 5003)) 659 if (!(pticks % 5003))
1443 write_book_archive (); 660 write_book_archive ();
1444 661
1445 if (!(pticks % 5009)) 662 if (!(pticks % 5009))
1446 clean_friendly_list (); 663 clean_friendly_list ();
1453} 670}
1454 671
1455void 672void
1456server_tick () 673server_tick ()
1457{ 674{
1458 nroferrors = 0; 675 // first do the user visible stuff
1459
1460 doeric_server (); 676 doeric_server ();
1461 INVOKE_GLOBAL (CLOCK); 677 INVOKE_GLOBAL (CLOCK);
1462 process_events (NULL); /* "do" something with objects with speed */ 678 process_events (); /* "do" something with objects with speed */
1463 flush_sockets (); 679 flush_sockets ();
1464 check_active_maps (); /* Removes unused maps after a certain timeout */ 680
681 // then do some bookkeeping, should not really be here
1465 do_specials (); /* Routines called from time to time. */ 682 do_specials (); /* Routines called from time to time. */
1466 object::free_mortals (); 683 attachable::check_mortals ();
1467 684
1468 ++pticks; 685 ++pticks;
1469} 686}
1470 687
1471int 688int
1472main (int argc, char **argv) 689main (int argc, char **argv)
1473{ 690{
1474 settings.argc = argc; 691 settings.argc = argc;
1475 settings.argv = argv; 692 settings.argv = argv;
1476 693
1477 cfperl_init ();
1478
1479 init (argc, argv); 694 init (argc, argv);
1480 695
1481 initPlugins (); 696 initPlugins ();
1482 697
1483 for (;;) 698 for (;;)
1484 cfperl_main (); 699 cfperl_main ();
1485 700
1486 // unreached
1487 emergency_save (0);
1488 cleanup (); 701 cleanup (true);
1489
1490 return 0;
1491} 702}
703

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines