ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/rune.C
Revision: 1.4
Committed: Sun Sep 10 15:59:57 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.3: +384 -324 lines
Log Message:
indent

File Contents

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