1 | /* |
1 | /* |
2 | * static char *rcsid_spell_effect_c = |
2 | * static char *rcsid_spell_effect_c = |
3 | * "$Id: spell_effect.C,v 1.2 2006/08/17 20:23:32 root Exp $"; |
3 | * "$Id: spell_effect.C,v 1.3 2006/08/26 23:36:34 root Exp $"; |
4 | */ |
4 | */ |
5 | |
5 | |
6 | |
6 | |
7 | /* |
7 | /* |
8 | CrossFire, A Multiplayer game for X-windows |
8 | CrossFire, A Multiplayer game for X-windows |
… | |
… | |
107 | wand->speed = wand->arch->clone.speed; |
107 | wand->speed = wand->arch->clone.speed; |
108 | update_ob_speed(wand); |
108 | update_ob_speed(wand); |
109 | } |
109 | } |
110 | return 1; |
110 | return 1; |
111 | } |
111 | } |
112 | |
|
|
113 | /****************************************************************************** |
|
|
114 | * Start of polymorph related functions. |
|
|
115 | * |
|
|
116 | * Changed around for 0.94.3 - it will now look through and use all the |
|
|
117 | * possible choices for objects/monsters (before it was teh first 80 - |
|
|
118 | * arbitrary hardcoded limit in this file.) Doing this will be a bit |
|
|
119 | * slower however - while before, it traversed the archetypes once and |
|
|
120 | * stored them into an array, it will now potentially traverse it |
|
|
121 | * an average of 1.5 times. This is probably more costly on the polymorph |
|
|
122 | * item function, since it is possible a couple lookups might be needed before |
|
|
123 | * an item of proper value is generated. |
|
|
124 | */ |
|
|
125 | |
|
|
126 | /* polymorph_living - takes a living object (op) and turns it into |
|
|
127 | * another monster of some sort. Currently, we only deal with single |
|
|
128 | * space monsters. |
|
|
129 | */ |
|
|
130 | |
|
|
131 | void polymorph_living(object *op) { |
|
|
132 | archetype *at; |
|
|
133 | int nr = 0, x = op->x, y = op->y, numat=0, choice,friendly; |
|
|
134 | mapstruct *map = op->map; |
|
|
135 | object *tmp, *next, *owner; |
|
|
136 | |
|
|
137 | if(op->head != NULL || op->more != NULL) |
|
|
138 | return; |
|
|
139 | |
|
|
140 | /* High level creatures are immune, as are creatures immune to magic. Otherwise, |
|
|
141 | * give the creature a saving throw. |
|
|
142 | */ |
|
|
143 | if (op->level>=20 || did_make_save(op, op->level, op->resist[ATNR_MAGIC]/10) || |
|
|
144 | (op->resist[ATNR_MAGIC]==100)) |
|
|
145 | return; |
|
|
146 | |
|
|
147 | /* First, count up the number of legal matches */ |
|
|
148 | for(at = first_archetype ; at != NULL; at = at->next) |
|
|
149 | if(QUERY_FLAG((&at->clone),FLAG_MONSTER) == QUERY_FLAG(op, FLAG_MONSTER) && |
|
|
150 | at->more == NULL && EDITABLE((&at->clone))) |
|
|
151 | { |
|
|
152 | numat++; |
|
|
153 | } |
|
|
154 | if (!numat) return; /* no valid matches? if so, return */ |
|
|
155 | |
|
|
156 | /* Next make a choice, and loop through until we get to it */ |
|
|
157 | choice = rndm(0, numat-1); |
|
|
158 | for(at = first_archetype ; at != NULL; at = at->next) |
|
|
159 | if(QUERY_FLAG((&at->clone),FLAG_MONSTER) == QUERY_FLAG(op, FLAG_MONSTER) && |
|
|
160 | at->more == NULL && EDITABLE((&at->clone))) |
|
|
161 | { |
|
|
162 | if (!choice) break; |
|
|
163 | else choice--; |
|
|
164 | } |
|
|
165 | |
|
|
166 | /* Look through the monster. Unapply anything they have applied, |
|
|
167 | * and remove any spells. Note that if this is extended |
|
|
168 | * to players, that would need to get fixed somehow. |
|
|
169 | */ |
|
|
170 | for(tmp = op->inv; tmp != NULL; tmp = next) { |
|
|
171 | next = tmp->below; |
|
|
172 | if(QUERY_FLAG(tmp, FLAG_APPLIED)) |
|
|
173 | manual_apply(op,tmp,0); |
|
|
174 | if(tmp->type == SPELL) { |
|
|
175 | remove_ob(tmp); |
|
|
176 | free_object(tmp); |
|
|
177 | } |
|
|
178 | } |
|
|
179 | |
|
|
180 | /* Remove the object, preserve some values for the new object */ |
|
|
181 | remove_ob(op); |
|
|
182 | owner = get_owner(op); |
|
|
183 | friendly = QUERY_FLAG(op, FLAG_FRIENDLY); |
|
|
184 | if (friendly) |
|
|
185 | remove_friendly_object(op); |
|
|
186 | |
|
|
187 | copy_object(&(at->clone),op); |
|
|
188 | if (owner != NULL) |
|
|
189 | set_owner(op,owner); |
|
|
190 | if (friendly) { |
|
|
191 | SET_FLAG(op, FLAG_FRIENDLY); |
|
|
192 | op->attack_movement = PETMOVE; |
|
|
193 | add_friendly_object(op); |
|
|
194 | } |
|
|
195 | |
|
|
196 | /* Put the new creature on the map */ |
|
|
197 | op->x = x; op->y = y; |
|
|
198 | if ((op = insert_ob_in_map (op, map, owner,0)) == NULL) |
|
|
199 | return; |
|
|
200 | |
|
|
201 | if (HAS_RANDOM_ITEMS(op)) |
|
|
202 | /* No GT_APPLY here because we'll do it manually. */ |
|
|
203 | create_treasure(op->randomitems,op,GT_INVISIBLE,map->difficulty,0); |
|
|
204 | |
|
|
205 | /* Apply any objects. This limits it to the first 20 items, which |
|
|
206 | * I guess is reasonable. |
|
|
207 | */ |
|
|
208 | for(tmp = op->inv, nr = 0; tmp != NULL && ++nr < 20; tmp = next) { |
|
|
209 | next = tmp->below; |
|
|
210 | (void) monster_check_apply(op,tmp); |
|
|
211 | } |
|
|
212 | } |
|
|
213 | |
|
|
214 | |
|
|
215 | /* polymorph_melt Destroys item from polymorph failure |
|
|
216 | * who is the caster of the polymorph, op is the |
|
|
217 | * object destroyed. We should probably do something |
|
|
218 | * more clever ala nethack - create an iron golem or |
|
|
219 | * something. |
|
|
220 | */ |
|
|
221 | void polymorph_melt(object *who, object *op) |
|
|
222 | { |
|
|
223 | /* Not unique */ |
|
|
224 | new_draw_info_format(NDI_GREY, 0, who, |
|
|
225 | "%s%s glows red, melts and evaporates!", |
|
|
226 | op->nrof?"":"The ",query_name(op)); |
|
|
227 | play_sound_map(op->map, op->x, op->y, SOUND_OB_EVAPORATE); |
|
|
228 | remove_ob(op); |
|
|
229 | free_object(op); |
|
|
230 | return; |
|
|
231 | } |
|
|
232 | |
|
|
233 | /* polymorph_item - changes an item to another item of similar type. |
|
|
234 | * who is the caster of spell, op is the object to be changed. |
|
|
235 | */ |
|
|
236 | void polymorph_item(object *who, object *op) { |
|
|
237 | archetype *at; |
|
|
238 | int max_value, difficulty, tries=0,choice, charges=op->stats.food,numat=0; |
|
|
239 | object *new_ob; |
|
|
240 | |
|
|
241 | /* We try and limit the maximum value of the changd object. */ |
|
|
242 | max_value = op->value * 2; |
|
|
243 | if(max_value > 20000) |
|
|
244 | max_value = 20000 + (max_value - 20000) / 3; |
|
|
245 | |
|
|
246 | /* Look through and try to find matching items. Can't turn into something |
|
|
247 | * invisible. Also, if the value is too high now, it would almost |
|
|
248 | * certainly be too high below. |
|
|
249 | */ |
|
|
250 | for(at = first_archetype ; at != NULL; at = at->next) { |
|
|
251 | if(at->clone.type == op->type && !at->clone.invisible && |
|
|
252 | at->clone.value > 0 && at->clone.value < max_value && |
|
|
253 | !QUERY_FLAG(&at->clone, FLAG_NO_DROP) && |
|
|
254 | !QUERY_FLAG(&at->clone, FLAG_STARTEQUIP)) |
|
|
255 | numat++; |
|
|
256 | } |
|
|
257 | |
|
|
258 | if(!numat) |
|
|
259 | return; |
|
|
260 | |
|
|
261 | difficulty = op->magic * 5; |
|
|
262 | if (difficulty<0) difficulty=0; |
|
|
263 | new_ob = get_object(); |
|
|
264 | do { |
|
|
265 | choice = rndm(0, numat-1); |
|
|
266 | for(at = first_archetype ; at != NULL; at = at->next) { |
|
|
267 | if(at->clone.type == op->type && !at->clone.invisible && |
|
|
268 | at->clone.value > 0 && at->clone.value < max_value && |
|
|
269 | !QUERY_FLAG(&at->clone, FLAG_NO_DROP) && |
|
|
270 | !QUERY_FLAG(&at->clone, FLAG_STARTEQUIP)) { |
|
|
271 | if (!choice) break; |
|
|
272 | else choice--; |
|
|
273 | } |
|
|
274 | } |
|
|
275 | copy_object(&(at->clone),new_ob); |
|
|
276 | fix_generated_item(new_ob,op,difficulty,FABS(op->magic),GT_ENVIRONMENT); |
|
|
277 | ++tries; |
|
|
278 | } while (new_ob->value > max_value && tries<10); |
|
|
279 | if (new_ob->invisible) { |
|
|
280 | LOG(llevError,"polymorph_item: fix_generated_object made %s invisible?!\n", new_ob->name); |
|
|
281 | free_object(new_ob); |
|
|
282 | } |
|
|
283 | |
|
|
284 | /* Unable to generate an acceptable item? Melt it */ |
|
|
285 | if (tries==10) { |
|
|
286 | polymorph_melt(who, op); |
|
|
287 | free_object(new_ob); |
|
|
288 | return; |
|
|
289 | } |
|
|
290 | |
|
|
291 | if(op->nrof && new_ob->nrof) { |
|
|
292 | new_ob->nrof = op->nrof; |
|
|
293 | /* decrease the number of items */ |
|
|
294 | if (new_ob->nrof>2) new_ob->nrof -= rndm(0, op->nrof/2-1); |
|
|
295 | } |
|
|
296 | |
|
|
297 | /* We don't want rings to keep sustenance/hungry status. There are propably |
|
|
298 | * other cases too that should be checked. |
|
|
299 | */ |
|
|
300 | if(charges && op->type != RING && op->type != FOOD) |
|
|
301 | op->stats.food = charges; |
|
|
302 | |
|
|
303 | new_ob->x = op->x; |
|
|
304 | new_ob->y = op->y; |
|
|
305 | remove_ob(op); |
|
|
306 | free_object(op); |
|
|
307 | /* |
|
|
308 | * Don't want objects merged or re-arranged, as it then messes up the |
|
|
309 | * order |
|
|
310 | */ |
|
|
311 | insert_ob_in_map(new_ob,who->map,new_ob,INS_NO_MERGE | INS_NO_WALK_ON); |
|
|
312 | } |
|
|
313 | |
|
|
314 | /* polymorh - caster who has hit object op. */ |
|
|
315 | void polymorph(object *op, object *who) { |
|
|
316 | |
|
|
317 | int tmp; |
|
|
318 | |
|
|
319 | /* Can't polymorph players right now */ |
|
|
320 | /* polymorphing generators opens up all sorts of abuses */ |
|
|
321 | if(op->type == PLAYER || QUERY_FLAG(op, FLAG_GENERATOR)) |
|
|
322 | return; |
|
|
323 | |
|
|
324 | if(QUERY_FLAG(op, FLAG_MONSTER)) { |
|
|
325 | polymorph_living(op); |
|
|
326 | return; |
|
|
327 | } |
|
|
328 | /* If it is a living object of some other type, don't handle |
|
|
329 | * it now. |
|
|
330 | */ |
|
|
331 | if(QUERY_FLAG(op, FLAG_ALIVE)) |
|
|
332 | return; |
|
|
333 | |
|
|
334 | /* Don't want to morph flying arrows, etc... */ |
|
|
335 | if(FABS(op->speed) > 0.001 && !QUERY_FLAG(op, FLAG_ANIMATE)) |
|
|
336 | return; |
|
|
337 | |
|
|
338 | /* Do some sanity checking here. type=0 is unknown, objects |
|
|
339 | * without archetypes are not good. As are a few other |
|
|
340 | * cases. |
|
|
341 | */ |
|
|
342 | if(op->type == 0 || op->arch == NULL || |
|
|
343 | QUERY_FLAG(op,FLAG_NO_PICK) |
|
|
344 | || op->move_block || op->type == TREASURE) |
|
|
345 | return; |
|
|
346 | |
|
|
347 | tmp = rndm(0, 7); |
|
|
348 | if (tmp) polymorph_item(who, op); |
|
|
349 | else polymorph_melt(who, op); |
|
|
350 | } |
|
|
351 | |
|
|
352 | |
|
|
353 | /* cast_polymorph - |
|
|
354 | * object *op is the player/monster |
|
|
355 | * caster is object that cast it. |
|
|
356 | * spell_ob is the actually spell object. |
|
|
357 | * dir is the direction. |
|
|
358 | * Returns 0 on illegal cast, otherwise 1. |
|
|
359 | */ |
|
|
360 | |
|
|
361 | int cast_polymorph(object *op, object *caster, object *spell_ob, int dir) { |
|
|
362 | object *tmp, *next; |
|
|
363 | int range, mflags, maxrange; |
|
|
364 | mapstruct *m; |
|
|
365 | |
|
|
366 | if(dir == 0) |
|
|
367 | return 0; |
|
|
368 | |
|
|
369 | maxrange = spell_ob->range + SP_level_range_adjust(caster, spell_ob); |
|
|
370 | for(range = 1;range < maxrange; range++) { |
|
|
371 | sint16 x=op->x+freearr_x[dir]*range,y=op->y+freearr_y[dir]*range; |
|
|
372 | object *image; |
|
|
373 | |
|
|
374 | m = op->map; |
|
|
375 | mflags = get_map_flags(m, &m, x, y, &x, &y); |
|
|
376 | |
|
|
377 | if (mflags & (P_NO_MAGIC | P_OUT_OF_MAP)) |
|
|
378 | break; |
|
|
379 | |
|
|
380 | if (GET_MAP_MOVE_BLOCK(m, x, y) & MOVE_FLY_LOW) |
|
|
381 | break; |
|
|
382 | |
|
|
383 | /* Get the top most object */ |
|
|
384 | for(tmp = get_map_ob(m,x,y); tmp != NULL && tmp->above != NULL; |
|
|
385 | tmp = tmp->above); |
|
|
386 | |
|
|
387 | /* Now start polymorphing the objects, top down */ |
|
|
388 | while (tmp!=NULL) { |
|
|
389 | /* Once we find the floor, no need to go further */ |
|
|
390 | if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) break; |
|
|
391 | next = tmp->below; |
|
|
392 | polymorph(tmp, op); |
|
|
393 | tmp = next; |
|
|
394 | } |
|
|
395 | image = arch_to_object(spell_ob->other_arch); |
|
|
396 | image->x = x; |
|
|
397 | image->y = y; |
|
|
398 | image->stats.food = 5; |
|
|
399 | image->speed_left = 0.1; |
|
|
400 | insert_ob_in_map(image,m,op,0); |
|
|
401 | } |
|
|
402 | return 1; |
|
|
403 | } |
|
|
404 | |
|
|
405 | |
|
|
406 | |
112 | |
407 | /* Create a missile (nonmagic - magic +4). Will either create bolts or arrows |
113 | /* Create a missile (nonmagic - magic +4). Will either create bolts or arrows |
408 | * based on whether a crossbow or bow is equiped. If neither, it defaults to |
114 | * based on whether a crossbow or bow is equiped. If neither, it defaults to |
409 | * arrows. |
115 | * arrows. |
410 | * Sets the plus based on the casters level. It is also settable with the |
116 | * Sets the plus based on the casters level. It is also settable with the |
… | |
… | |
544 | new_draw_info(NDI_UNIQUE, 0, op, "You don't have enough experience to create any food."); |
250 | new_draw_info(NDI_UNIQUE, 0, op, "You don't have enough experience to create any food."); |
545 | return 0; |
251 | return 0; |
546 | } |
252 | } |
547 | |
253 | |
548 | food_value/=at->clone.stats.food; |
254 | food_value/=at->clone.stats.food; |
549 | new_op = get_object(); |
255 | new_op = arch_to_object (at); |
550 | copy_object(&at->clone, new_op); |
|
|
551 | new_op->nrof = food_value; |
256 | new_op->nrof = food_value; |
552 | |
257 | |
553 | new_op->value = 0; |
258 | new_op->value = 0; |
554 | if (new_op->nrof<1) new_op->nrof = 1; |
259 | if (new_op->nrof<1) new_op->nrof = 1; |
555 | |
260 | |