ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/rune.C
Revision: 1.24
Committed: Thu May 17 00:33:29 2007 UTC (17 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_1
Changes since 1.23: +1 -1 lines
Log Message:
- do not require an item for the use magic item skill.
  thats because it is often used for "misc" objects such as scrolls,
  which do not get applied as ranged weapons. not requiring an item
  is not likely to be a big deal anyways.

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.24 * CrossFire, A Multiplayer game
3 pippijn 1.17 *
4     * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5     * Copyright (C) 2003 Mark Wedel & Crossfire Development Team
6     * Copyright (C) 1992 Frank Tore Johansen
7     *
8     * 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     *
13     * 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     *
18     * 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     #include <global.h>
26 root 1.14 #include <sproto.h>
27 elmex 1.1 #include <spells.h>
28    
29     #ifndef sqr
30 root 1.4 # define sqr(x) ((x)*(x))
31 elmex 1.1 #endif
32    
33     /* peterm:
34     * write_rune:
35     * op: rune writer
36     * skop: skill object used for casting this rune
37     * dir: orientation of rune, direction rune's contained spell will
38     * be cast in, if applicable
39     * inspell: spell object to put into the rune, can be null if doing
40     * a marking rune.
41     * level: level of casting of the rune
42     * runename: name of the rune or message displayed by the rune for
43     * a rune of marking
44     */
45    
46 root 1.4 int
47     write_rune (object *op, object *caster, object *spell, int dir, const char *runename)
48     {
49     object *tmp, *rune_spell, *rune;
50     char buf[MAX_BUF];
51 root 1.7 maptile *m;
52 root 1.4 sint16 nx, ny;
53    
54     if (!dir)
55 root 1.10 dir = 1;
56 root 1.4
57     nx = op->x + freearr_x[dir];
58     ny = op->y + freearr_y[dir];
59     m = op->map;
60    
61 root 1.20 if (get_map_flags (m, &m, nx, ny, &nx, &ny) & (P_OUT_OF_MAP | P_SAFE | P_NO_MAGIC | P_NO_CLERIC))
62 root 1.4 {
63     new_draw_info (NDI_UNIQUE, 0, op, "Can't make a rune there!");
64     return 0;
65 elmex 1.1 }
66 root 1.10
67 root 1.19 for (tmp = m->at (nx, ny).bot; tmp; tmp = tmp->above)
68 root 1.4 if (tmp->type == RUNE)
69     break;
70    
71     if (tmp)
72     {
73 root 1.20 new_draw_info (NDI_UNIQUE, 0, op, "You can't write a rune on top of another rune.");
74 elmex 1.1 return 0;
75     }
76    
77 root 1.4 if (spell->other_arch)
78 root 1.10 rune_spell = arch_to_object (spell->other_arch);
79 root 1.4 else
80     {
81     /* Player specified spell. The player has to know the spell, so
82     * lets just look through the players inventory see if they know it
83     * use the item_matched_string for our typical matching method.
84     */
85     int bestmatch = 0, ms;
86    
87     if (!runename || *runename == 0)
88     {
89     new_draw_info (NDI_UNIQUE, 0, op, "Write a rune of what?");
90     return 0;
91     }
92    
93     rune_spell = NULL;
94     for (tmp = op->inv; tmp; tmp = tmp->below)
95 root 1.20 if (tmp->type == SPELL)
96     {
97     ms = item_matched_string (op, tmp, runename);
98     if (ms > bestmatch)
99     {
100     bestmatch = ms;
101     rune_spell = tmp;
102     }
103     }
104 root 1.10
105 root 1.4 if (!rune_spell)
106     {
107     new_draw_info_format (NDI_UNIQUE, 0, op, "You don't know any spell named %s", runename);
108     return 0;
109     }
110 root 1.10
111 root 1.4 if (rune_spell->skill != spell->skill)
112     {
113     new_draw_info_format (NDI_UNIQUE, 0, op, "You can't cast %s with %s", &rune_spell->name, &spell->name);
114     return 0;
115     }
116 root 1.10
117 root 1.20 if (caster->path_denied & spell->path_attuned && !caster->flag [FLAG_WIZCAST])
118 root 1.4 {
119     new_draw_info_format (NDI_UNIQUE, 0, op, "%s belongs to a spell path denied to you.", &rune_spell->name);
120     return 0;
121     }
122 root 1.10
123 root 1.20 if (caster_level (caster, rune_spell) < rune_spell->level && !caster->flag [FLAG_WIZCAST])
124 root 1.4 {
125     new_draw_info_format (NDI_UNIQUE, 0, op, "%s is beyond your ability to cast!", &rune_spell->name);
126     return 0;
127     }
128 root 1.10
129 root 1.4 if (SP_level_spellpoint_cost (caster, rune_spell, SPELL_MANA) > op->stats.sp)
130     {
131     new_draw_info (NDI_UNIQUE, 0, op, "You don't have enough mana.");
132     return 0;
133     }
134 root 1.10
135 root 1.4 if (SP_level_spellpoint_cost (caster, rune_spell, SPELL_GRACE) > op->stats.grace)
136     {
137     new_draw_info (NDI_UNIQUE, 0, op, "You don't have enough grace.");
138     return 0;
139     }
140 root 1.10
141 root 1.4 op->stats.grace -= SP_level_spellpoint_cost (caster, rune_spell, SPELL_GRACE);
142     op->stats.sp -= SP_level_spellpoint_cost (caster, rune_spell, SPELL_MANA);
143     }
144 root 1.10
145 root 1.4 /* already proper rune. Note this should only be the case if other_arch was set */
146     if (rune_spell->type == RUNE)
147 root 1.15 rune = rune_spell;
148 root 1.4 else
149     {
150     rune = get_archetype (GENERIC_RUNE);
151     sprintf (buf, "You set off a rune of %s\n", &rune_spell->name);
152     rune->msg = buf;
153 root 1.15 rune->insert (rune_spell->clone ());
154 root 1.10
155 root 1.4 if (spell->face != blank_face)
156     rune->face = spell->face;
157     }
158 root 1.10
159 root 1.4 rune->level = caster_level (caster, spell);
160     rune->stats.Cha = rune->level / 2; /* the invisibility parameter */
161     rune->direction = dir; /* where any spell will go upon detonation */
162 root 1.11 rune->set_owner (op); /* runes without need no owner */
163 root 1.4 set_spell_skill (op, caster, spell, rune);
164 root 1.15
165     m->insert (rune, nx, ny, op);
166 root 1.4 return 1;
167 elmex 1.1 }
168    
169     /* move_rune: peterm
170     comments on runes:
171     rune->level : level at which rune will cast its spell.
172     rune->hp : number of detonations before rune goes away
173     rune->msg : message the rune displays when it goes off
174     rune->direction : direction it will cast a spell in
175     rune->dam : damage the rune will do if it doesn't cast spells
176     rune->attacktype: type of damage it does, if not casting spells
177     rune->other_arch: spell in the rune
178     rune->Cha : how hidden the rune is
179     rune->maxhp : number of spells the rune casts
180     */
181 root 1.4 void
182     move_rune (object *op)
183     {
184 root 1.18 /* runes of level zero cannot detonate. */
185     if (!op->level)
186     return;
187    
188     int det = op->invisible;
189 root 1.4
190     if (!(rndm (0, MAX (1, (op->stats.Cha)) - 1)))
191     {
192     op->invisible = 0;
193     op->speed_left -= 1;
194     }
195     else
196     op->invisible = 1;
197 root 1.18
198 root 1.4 if (op->invisible != det)
199 root 1.22 update_object (op, UP_OBJ_CHANGE);
200 elmex 1.1 }
201    
202     /* peterm: rune_attack
203     * function handles those runes which detonate but do not cast spells.
204     */
205 root 1.4 void
206     rune_attack (object *op, object *victim)
207 elmex 1.1 {
208 root 1.4 if (victim)
209     {
210 root 1.6 hit_player (victim, op->stats.dam, op, op->attacktype, 1);
211 root 1.4
212 root 1.6 if (victim->destroyed ())
213 root 1.4 return;
214 root 1.6
215 root 1.4 /* if there's a disease in the needle, put it in the player */
216 root 1.12 if (op->has_random_items ())
217 root 1.4 create_treasure (op->randomitems, op, 0, (victim->map ? victim->map->difficulty : 1), 0);
218 root 1.6
219 root 1.4 if (op->inv && op->inv->type == DISEASE)
220     {
221     object *disease = op->inv;
222    
223     infect_object (victim, disease, 1);
224 root 1.9 disease->destroy ();
225 root 1.4 }
226 elmex 1.1 }
227 root 1.4 else
228     hit_map (op, 0, op->attacktype, 1);
229 elmex 1.1 }
230    
231     /* This function generalizes attacks by runes/traps. This ought to make
232     * it possible for runes to attack from the inventory,
233     * it'll spring the trap on the victim.
234     */
235 root 1.4 void
236     spring_trap (object *trap, object *victim)
237 elmex 1.1 {
238 root 1.4 object *env;
239     rv_vector rv;
240     int i;
241    
242     /* Prevent recursion */
243     if (trap->stats.hp <= 0)
244     return;
245    
246     if (QUERY_FLAG (trap, FLAG_IS_LINKED))
247     use_trigger (trap);
248    
249     /* Only living objects can trigger runes that don't cast spells, as
250     * doing direct damage to a non-living object doesn't work anyway.
251     * Typical example is an arrow attacking a door.
252     */
253     if (!QUERY_FLAG (victim, FLAG_ALIVE) && !trap->inv && !trap->other_arch)
254     return;
255    
256     trap->stats.hp--; /*decrement detcount */
257    
258     if (victim && victim->type == PLAYER)
259     new_draw_info (NDI_UNIQUE, 0, victim, trap->msg);
260    
261     /* Flash an image of the trap on the map so the poor sod
262     * knows what hit him.
263     */
264     env = object_get_env_recursive (trap);
265    
266     /* If the victim is not next to this trap, don't set it off.
267     * players shouldn't get hit by firing arrows at a door for example.
268     * At the same time, the trap will stick around until detonated
269     */
270     get_rangevector (env, victim, &rv, 0);
271     if (rv.distance > 1)
272     return;
273    
274     trap_show (trap, env);
275    
276 elmex 1.23 if (victim->type == PLAYER) // only count players as enemies
277     trap->enemy = victim; // set the victim as the traps enemy, so that summoned
278     // creatures know who to attack.
279    
280 root 1.4 /* Only if it is a spell do we proceed here */
281     if ((trap->inv && trap->inv->type == SPELL) || (trap->other_arch && trap->other_arch->clone.type == SPELL))
282     {
283 elmex 1.1
284 root 1.6 if (trap->destroyed ())
285 root 1.2 return;
286 elmex 1.1
287 root 1.4 // breaks summon golem spells, which, for inexplicable reasons,
288     // do not work like summon golem spells at all but still require
289     // direction "0" to work at all.
290     //if (trap->direction)
291     // rv.direction = trap->direction;
292 elmex 1.1
293 root 1.4 for (i = 0; i < MAX (1, trap->stats.maxhp); i++)
294     {
295     if (trap->inv)
296     cast_spell (env, trap, trap->direction, trap->inv, NULL);
297     else
298     {
299     object *spell = arch_to_object (trap->other_arch);
300 elmex 1.1
301 root 1.4 cast_spell (env, trap, trap->direction, spell, NULL);
302 root 1.9 spell->destroy ();
303 root 1.2 }
304     }
305 root 1.4 }
306     else
307     {
308     rune_attack (trap, victim);
309 root 1.6 if (trap->destroyed ())
310 root 1.4 return;
311 elmex 1.1 }
312    
313 root 1.4 if (trap->stats.hp <= 0)
314     {
315     trap->type = SIGN; /* make the trap impotent */
316     trap->stats.food = 20; /* make it stick around until its spells are gone */
317     SET_FLAG (trap, FLAG_IS_USED_UP);
318 elmex 1.1 }
319     }
320    
321     /* dispel_rune: by peterm
322     * dispels the target rune, depending on the level of the actor
323     * and the level of the rune risk flag, if true, means that there is
324     * a chance that the trap/rune will detonate
325     */
326 root 1.4 int
327     dispel_rune (object *op, object *caster, object *spell, object *skill, int dir)
328 elmex 1.1 {
329 root 1.4 object *tmp, *tmp2;
330     int searchflag = 1, mflags;
331     sint16 x, y;
332 root 1.7 maptile *m;
333 root 1.4
334     x = op->x + freearr_x[dir];
335     y = op->y + freearr_y[dir];
336     m = op->map;
337    
338     mflags = get_map_flags (m, &m, x, y, &x, &y);
339    
340     /* Should we perhaps not allow player to disable traps if a monster/
341     * player is standing on top?
342     */
343     if (mflags & P_OUT_OF_MAP)
344     {
345     new_draw_info (NDI_UNIQUE, 0, op, "There's nothing there!");
346     return 0;
347     }
348    
349 root 1.13 for (tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above)
350 root 1.4 {
351     if (tmp->type == RUNE || tmp->type == TRAP)
352     break;
353    
354     /* we could put a probability chance here, but since nothing happens
355     * if you fail, no point on that. I suppose we could do a level
356     * comparison so low level players can't erase high level players runes.
357     */
358     if (tmp->type == SIGN && !strcmp (tmp->arch->name, "rune_mark"))
359     {
360 root 1.9 tmp->destroy ();
361 root 1.4 new_draw_info (NDI_UNIQUE, 0, op, "You wipe out the rune of marking!");
362     return 1;
363     }
364    
365     /* now search tmp's inventory for traps
366     * This is for chests, where the rune is in the chests inventory.
367     */
368     for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below)
369     {
370     if (tmp2->type == RUNE || tmp2->type == TRAP)
371     {
372     tmp = tmp2;
373     searchflag = 0;
374     break;
375 root 1.2 }
376     }
377 root 1.4 if (!searchflag)
378     break;
379 elmex 1.1 }
380 root 1.4
381     /* no rune there. */
382     if (tmp == NULL)
383     {
384     new_draw_info (NDI_UNIQUE, 0, op, "There's nothing there!");
385     return 0;
386     }
387     trap_disarm (op, tmp, 0, skill);
388     return 1;
389    
390 elmex 1.1 }
391    
392 root 1.4 int
393     trap_see (object *op, object *trap)
394     {
395     int chance;
396    
397     chance = random_roll (0, 99, op, PREFER_HIGH);;
398 elmex 1.1
399 root 1.4 /* decide if we see the rune or not */
400     if ((trap->stats.Cha == 1) || (chance > MIN (95, MAX (5, ((int) ((float) (op->map->difficulty
401     + trap->level + trap->stats.Cha - op->level) / 10.0 * 50.0))))))
402     {
403     new_draw_info_format (NDI_UNIQUE, 0, op, "You spot a %s!", &trap->name);
404     return 1;
405 elmex 1.1 }
406 root 1.4 return 0;
407 elmex 1.1 }
408    
409 root 1.4 int
410     trap_show (object *trap, object *where)
411     {
412     if (where == NULL)
413     return 0;
414 root 1.15
415     object *tmp = get_archetype ("runedet");
416 root 1.21 tmp->face = GET_ANIMATION (trap, 0);
417 root 1.15 tmp->insert_at (where, 0);
418    
419 root 1.4 return 1;
420 elmex 1.1 }
421    
422 root 1.4 int
423     trap_disarm (object *disarmer, object *trap, int risk, object *skill)
424     {
425     int trapworth; /* need to compute the experience worth of the trap
426     before we kill it */
427    
428     /* this formula awards a more reasonable amount of exp */
429     trapworth = MAX (1, trap->level) * disarmer->map->difficulty *
430     sqr (MAX (trap->stats.dam, trap->inv ? trap->inv->level : 1)) / skill->level;
431    
432     if (!(random_roll (0, (MAX (2, MIN (20, trap->level - skill->level + 5 - disarmer->stats.Dex / 2)) - 1), disarmer, PREFER_LOW)))
433     {
434     new_draw_info_format (NDI_UNIQUE, 0, disarmer, "You successfully disarm the %s!", &trap->name);
435 root 1.9 trap->destroy (1);
436    
437 root 1.4 /* If it is your own trap, (or any players trap), don't you don't
438     * get exp for it.
439     */
440     if (trap->owner && trap->owner->type != PLAYER && risk)
441     return trapworth;
442     else
443     return 1; /* give minimal exp and say success */
444     }
445     else
446     {
447     new_draw_info_format (NDI_UNIQUE, 0, disarmer, "You fail to disarm the %s.", &trap->name);
448     if (!(random_roll (0, (MAX (2, skill->level - trap->level + disarmer->stats.Dex / 2 - 6)) - 1, disarmer, PREFER_LOW)) && risk)
449     {
450     new_draw_info (NDI_UNIQUE, 0, disarmer, "In fact, you set it off!");
451     spring_trap (trap, disarmer);
452 elmex 1.1 }
453 root 1.4 return 0;
454     }
455 elmex 1.1 }
456    
457    
458     /* traps need to be adjusted for the difficulty of the map. The
459     * default traps are too strong for wimpy level 1 players, and
460     * unthreatening to anyone of high level
461     */
462    
463 root 1.4 void
464     trap_adjust (object *trap, int difficulty)
465     {
466     int i;
467 elmex 1.1
468 root 1.4 /* now we set the trap level to match the difficulty of the level
469     * the formula below will give a level from 1 to (2*difficulty) with
470     * a peak probability at difficulty
471     */
472    
473     trap->level = rndm (0, difficulty - 1) + rndm (0, difficulty - 1);
474     if (trap->level < 1)
475     trap->level = 1;
476    
477     /* set the hiddenness of the trap, similar formula to above */
478     trap->stats.Cha = rndm (0, 19) + rndm (0, difficulty - 1) + rndm (0, difficulty - 1);
479    
480     if (!trap->other_arch && !trap->inv)
481     {
482     /* set the damage of the trap.
483     * we get 0-4 pts of damage per level of difficulty of the map in
484     * the trap
485     */
486    
487     trap->stats.dam = 0;
488     for (i = 0; i < difficulty; i++)
489     trap->stats.dam += rndm (0, 4);
490    
491     /* the poison trap special case */
492     if (trap->attacktype & AT_POISON)
493     {
494     trap->stats.dam = rndm (0, difficulty - 1);
495     if (trap->stats.dam < 1)
496     trap->stats.dam = 1;
497 root 1.2 }
498 elmex 1.1
499 root 1.4 /* so we get an appropriate amnt of exp for AT_DEATH traps */
500     if (trap->attacktype & AT_DEATH)
501     trap->stats.dam = 127;
502 elmex 1.1 }
503    
504     }