ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_range.C
Revision: 1.18
Committed: Tue Apr 24 12:32:16 2007 UTC (17 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.17: +8 -12 lines
Log Message:
server crashed this morning because a freed golem tried to follow
his owner, triggering the assertion failure.

the golem had no owner, but the owner still had him in his range slots.

I refactored a bit of the code and rearranged it to hopefully increase
chances of this not occuring again.

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.18 * CrossFire, A Multiplayer game
3     *
4 pippijn 1.15 * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5     * Copyright (C) 2002 Mark Wedel & Crossfire Development Team
6     * Copyright (C) 1992 Frank Tore Johansen
7 root 1.18 *
8 pippijn 1.15 * This program is free software; you can redistribute it and/or modify
9     * it under the terms of the GNU General Public License as published by
10     * the Free Software Foundation; either version 2 of the License, or
11     * (at your option) any later version.
12 root 1.18 *
13 pippijn 1.15 * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU General Public License for more details.
17 root 1.18 *
18 pippijn 1.15 * You should have received a copy of the GNU General Public License
19     * along with this program; if not, write to the Free Software
20     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21     *
22     * The authors can be reached via e-mail at <crossfire@schmorp.de>
23     */
24 elmex 1.1
25     /* This file deals with range related commands (casting, shooting,
26     * throwing, etc.
27     */
28    
29     #include <global.h>
30 root 1.13 #include <sproto.h>
31 elmex 1.1 #include <spells.h>
32     #include <skills.h>
33     #include <commands.h>
34    
35 root 1.5 int
36     command_invoke (object *op, char *params)
37 elmex 1.1 {
38 root 1.5 return command_cast_spell (op, params, 'i');
39 elmex 1.1 }
40    
41 root 1.5 int
42     command_cast (object *op, char *params)
43 elmex 1.1 {
44 root 1.5 return command_cast_spell (op, params, 'c');
45 elmex 1.1 }
46    
47 root 1.5 int
48     command_prepare (object *op, char *params)
49 elmex 1.1 {
50 root 1.5 return command_cast_spell (op, params, 'p');
51 elmex 1.1 }
52    
53     /* Shows all spells that op knows. If params is supplied, the must match
54     * that. Given there is more than one skill, we can't supply break
55     * them down to cleric/wizardry.
56     */
57 root 1.5 static void
58     show_matching_spells (object *op, char *params)
59 elmex 1.1 {
60 root 1.5 object *spell;
61     char spell_sort[NROFREALSPELLS][MAX_BUF], tmp[MAX_BUF], *cp;
62     int num_found = 0, i;
63    
64     /* We go and see what spells the player has. We put them
65     * into the spell_sort array so that we can sort them -
66     * we prefix the skill in the name so that the sorting
67     * works better.
68     */
69     for (spell = op->inv; spell != NULL; spell = spell->below)
70     {
71     /* If it is a spell, and no params are passed, or they
72     * match the name, process this spell.
73     */
74     if (spell->type == SPELL && (!params || !strncmp (params, spell->name, strlen (params))))
75     {
76     if (spell->path_attuned & op->path_denied)
77     {
78     sprintf (spell_sort[num_found++], "%s:%-22s %3s %3s", spell->skill ? &spell->skill : "generic", &spell->name, "den", "den");
79     }
80     else
81     {
82     sprintf (spell_sort[num_found++],
83     "%s:%-22s %3d %3d", spell->skill ? &spell->skill : "generic",
84     &spell->name, spell->level, SP_level_spellpoint_cost (op, spell, SPELL_HIGHEST));
85 root 1.2 }
86     }
87 elmex 1.1 }
88 root 1.5 if (!num_found)
89     {
90     /* If a matching string was passed along, now try it without that
91     * string. It is odd to do something like 'cast trans',
92     * and it say you have no spells, when really, you do, but just
93     * nothing that matches.
94     */
95     if (params)
96     show_matching_spells (op, NULL);
97     else
98     new_draw_info (NDI_UNIQUE, 0, op, "You know no spells");
99     }
100     else
101     {
102     /* Note in the code below that we make some
103     * presumptions that there will be a colon in the
104     * string. given the code above, this is always
105     * the case.
106     */
107 root 1.16 qsort (spell_sort, num_found, MAX_BUF, (int (*)(const void *, const void *)) std::strcmp);
108 root 1.5 strcpy (tmp, "asdfg"); /* Dummy string so initial compare fails */
109     for (i = 0; i < num_found; i++)
110     {
111     /* Different skill name, so print banner */
112     if (strncmp (tmp, spell_sort[i], strlen (tmp)))
113     {
114     strcpy (tmp, spell_sort[i]);
115     cp = strchr (tmp, ':');
116     *cp = '\0';
117     new_draw_info (NDI_UNIQUE, 0, op, "");
118     new_draw_info_format (NDI_UNIQUE, 0, op, "%s spells %.*s [lvl] [sp]", tmp, 12 - strlen (tmp), " ");
119 root 1.2 }
120 root 1.5 new_draw_info (NDI_UNIQUE, 0, op, strchr (spell_sort[i], ':') + 1);
121 root 1.2 }
122 elmex 1.1 }
123     }
124    
125     /* sets up to cast a spell. op is the caster, params is the spell name,
126     * and command is the first letter of the spell type (c=cast, i=invoke,
127     * p=prepare). Invoke casts a spell immediately, where as cast (and I believe
128     * prepare) just set up the range type.
129     */
130 root 1.5 int
131     command_cast_spell (object *op, char *params, char command)
132 elmex 1.1 {
133 root 1.5 int castnow = 0;
134     char *cp;
135     object *spob;
136    
137     if (command == 'i')
138     castnow = 1;
139    
140     /* Remove control of the golem */
141 root 1.18 if (object *golem = op->contr->ranges[range_golem])
142     golem->destroy ();
143 elmex 1.1
144 root 1.18 if (params)
145 root 1.5 {
146     int spellnumber = 0;
147    
148     if ((spellnumber = atoi (params)))
149 root 1.9 for (spob = op->inv; spob && spob->count != spellnumber; spob = spob->below)
150 root 1.6 /* nop */;
151 root 1.5 else
152     spob = lookup_spell_by_name (op, params);
153    
154     if (spob && spob->type == SPELL)
155     {
156     /* Now grab any extra data, if there is any. Forward pass
157     * any 'of' delimiter
158     */
159     if (spellnumber)
160     {
161     /* if we passed a number, the options start at the second word */
162     cp = strchr (params, ' ');
163     if (cp)
164     {
165     cp++;
166     if (!strncmp (cp, "of ", 3))
167     cp += 3;
168 root 1.2 }
169     }
170 root 1.5 else if (strlen (params) > (size_t) strlen (spob->name))
171     {
172     cp = params + strlen (spob->name);
173     *cp = 0;
174     cp++;
175     if (!strncmp (cp, "of ", 3))
176     cp += 3;
177 root 1.2 }
178 root 1.5 else
179     cp = NULL;
180    
181     if (spob->skill && !find_skill_by_name (op, spob->skill))
182     {
183     new_draw_info_format (NDI_UNIQUE, 0, op, "You need the skill %s to cast %s!", &spob->skill, &spob->name);
184     return 1;
185     }
186    
187     if (castnow)
188 root 1.6 cast_spell (op, op, op->facing, spob, cp);
189 root 1.5 else
190     {
191     op->contr->ranges[range_magic] = spob;
192     op->contr->shoottype = range_magic;
193 root 1.7 assign (op->contr->spellparam, cp ? cp : "");
194 root 1.5 new_draw_info_format (NDI_UNIQUE, 0, op, "You ready the spell %s", &spob->name);
195 root 1.2 }
196 root 1.6
197 root 1.5 return 0;
198     } /* else fall through to below and print spells */
199     } /* params supplied */
200    
201     /* We get here if cast was given without options or we could not find
202     * the requested spell. List all the spells the player knows.
203     */
204     new_draw_info (NDI_UNIQUE, 0, op, "Cast what spell? Choose one of:");
205     show_matching_spells (op, params);
206     return 1;
207 elmex 1.1 }
208    
209     /**************************************************************************/
210    
211     /* Returns TRUE if the range specified (int r) is legal - that is,
212     * the character has an item that is equipped for that range type.
213     * return 0 if there is no item of that range type that is usable.
214     * This function could probably be simplified, eg, everything
215     * should index into the ranges[] array.
216     */
217    
218 root 1.5 int
219     legal_range (object *op, int r)
220     {
221     switch (r)
222     {
223     case range_none: /* "Nothing" is always legal */
224     return 1;
225 root 1.10
226 root 1.2 case range_bow:
227     case range_misc:
228 root 1.5 case range_magic: /* cast spells */
229 root 1.10 return !!op->contr->ranges[r];
230 root 1.5
231     case range_golem: /* Use scrolls */
232 root 1.10 return !!op->contr->ranges[range_golem];
233 root 1.2
234     case range_skill:
235 root 1.10 return !!op->chosen_skill;
236 elmex 1.1 }
237 root 1.10
238 root 1.5 /* No match above, must not be valid */
239     return 0;
240 elmex 1.1 }
241    
242 root 1.5 void
243     change_spell (object *op, char k)
244     {
245     do
246     {
247     op->contr->shoottype = (rangetype) (op->contr->shoottype + ((k == '+') ? 1 : -1));
248     if (op->contr->shoottype >= range_size)
249     op->contr->shoottype = range_none;
250     else if (op->contr->shoottype <= range_bottom)
251     op->contr->shoottype = (rangetype) (range_size - 1);
252     }
253     while (!legal_range (op, op->contr->shoottype));
254 pippijn 1.17
255 root 1.5 /* Legal range has already checked that we have an appropriate item
256     * that uses the slot, so we don't need to be too careful about
257     * checking the status of the object.
258     */
259     switch (op->contr->shoottype)
260     {
261 root 1.2 case range_none:
262 root 1.5 new_draw_info (NDI_UNIQUE, 0, op, "No ranged attack chosen.");
263     break;
264 root 1.2
265     case range_golem:
266 root 1.5 new_draw_info (NDI_UNIQUE, 0, op, "You regain control of your golem.");
267     break;
268 root 1.2
269     case range_bow:
270 root 1.5 new_draw_info_format (NDI_UNIQUE, 0, op, "Switched to %s and %s.", query_name (op->contr->ranges[range_bow]),
271     op->contr->ranges[range_bow]->race ? &op->contr->ranges[range_bow]->race : "nothing");
272     break;
273 root 1.2
274     case range_magic:
275 root 1.5 new_draw_info_format (NDI_UNIQUE, 0, op, "Switched to spells (%s).", &op->contr->ranges[range_magic]->name);
276     break;
277 root 1.2
278     case range_misc:
279 root 1.5 new_draw_info_format (NDI_UNIQUE, 0, op, "Switched to %s.", query_base_name (op->contr->ranges[range_misc], 0));
280     break;
281 root 1.2
282 root 1.5 case range_skill:
283     new_draw_info_format (NDI_UNIQUE, 0, op, "Switched to skill: %s", op->chosen_skill ? &op->chosen_skill->name : "none");
284     break;
285 elmex 1.1
286 root 1.2 default:
287 root 1.5 break;
288 elmex 1.1 }
289     }
290    
291    
292 root 1.5 int
293     command_rotateshoottype (object *op, char *params)
294 elmex 1.1 {
295     if (!params)
296 root 1.5 change_spell (op, '+');
297 elmex 1.1 else
298 root 1.5 change_spell (op, params[0]);
299 elmex 1.1 return 0;
300     }