Crossfire Server, Branches 1.12  R18729
rune.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_rune_c =
3  * "$Id: rune.c 11578 2009-02-23 22:02:27Z lalo $";
4  */
5 
6 /*
7  CrossFire, A Multiplayer game for X-windows
8 
9  Copyright (C) 2003,2006 Mark Wedel & Crossfire Development Team
10  Copyright (C) 1992 Frank Tore Johansen
11 
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 
26  The authors can be reached via e-mail at crossfire-devel@real-time.com
27 */
28 
35 #include <global.h>
36 #ifndef __CEXTRACT__
37 #include <sproto.h>
38 #endif
39 #include <spells.h>
40 
41 
42 #ifndef sqr
43 #define sqr(x) ((x)*(x))
44 #endif
45 
46 
66 int write_rune(object *op, object *caster, object *spell, int dir, const char *runename) {
67  object *tmp, *rune_spell, *rune;
68  char buf[MAX_BUF];
69  mapstruct *m;
70  sint16 nx, ny;
71 
72  if (!dir) {
73  dir = 1;
74  }
75 
76  nx = op->x+freearr_x[dir];
77  ny = op->y+freearr_y[dir];
78  m = op->map;
79 
80  if (get_map_flags(m, &m, nx, ny, &nx, &ny)) {
82  "Can't make a rune there!", NULL);
83  return 0;
84  }
85  for (tmp = GET_MAP_OB(m, nx, ny); tmp != NULL; tmp = tmp->above)
86  if (tmp->type == RUNE)
87  break;
88 
89  if (tmp) {
91  "You can't write a rune there.", NULL);
92  return 0;
93  }
94 
95  if (spell->other_arch) {
96  rune_spell = arch_to_object(spell->other_arch);
97  } else {
98  /* Player specified spell. The player has to know the spell, so
99  * lets just look through the players inventory see if they know it
100  * use the item_matched_string for our typical matching method.
101  */
102  int bestmatch = 0, ms;
103 
104  if (!runename || *runename == 0) {
106  "Write a rune of what?", NULL);
107  return 0;
108  }
109 
110  rune_spell = NULL;
111  for (tmp = op->inv; tmp; tmp = tmp->below) {
112  if (tmp->type == SPELL) {
113  ms = item_matched_string(op, tmp, runename);
114  if (ms > bestmatch) {
115  bestmatch = ms;
116  rune_spell = tmp;
117  }
118  }
119  }
120  if (!rune_spell) {
123  "You don't know any spell named %s",
124  "You don't know any spell named %s",
125  runename);
126  return 0;
127  }
128  if (rune_spell->skill != spell->skill) {
131  "You can't cast %s with %s",
132  "You can't cast %s with %s",
133  rune_spell->name, spell->name);
134  return 0;
135  }
136  if (caster->path_denied&spell->path_attuned) {
138  "%s belongs to a spell path denied to you.",
139  "%s belongs to a spell path denied to you.",
140  rune_spell->name);
141  return 0;
142  }
143  if (caster_level(caster, rune_spell) < rune_spell->level) {
145  "%s is beyond your ability to cast!",
146  "%s is beyond your ability to cast!",
147  rune_spell->name);
148  return 0;
149  }
150  if (SP_level_spellpoint_cost(caster, rune_spell, SPELL_MANA) > op->stats.sp) {
152  "You don't have enough mana.", NULL);
153  return 0;
154  }
155  if (SP_level_spellpoint_cost(caster, rune_spell, SPELL_GRACE) > op->stats.grace) {
157  "You don't have enough grace.", NULL);
158  return 0;
159  }
160  op->stats.grace -= SP_level_spellpoint_cost(caster, rune_spell, SPELL_GRACE);
161  op->stats.sp -= SP_level_spellpoint_cost(caster, rune_spell, SPELL_MANA);
162  }
163  /* already proper rune. Note this should only be the case if other_arch was set */
164  if (rune_spell->type == RUNE) {
165  rune = rune_spell;
166  } else {
168  snprintf(buf, sizeof(buf), "You set off a rune of %s\n", rune_spell->name);
169  rune->msg = add_string(buf);
170  tmp = get_object();
171  copy_object(rune_spell, tmp);
172  insert_ob_in_ob(tmp, rune);
173  if (spell->face != blank_face)
174  rune->face = spell->face;
175  }
176  rune->level = caster_level(caster, spell);
177  rune->stats.Cha = rune->level/2; /* the invisibility parameter */
178  rune->x = nx;
179  rune->y = ny;
180  rune->map = m;
181  rune->direction = dir; /* where any spell will go upon detonation */
182  set_owner(rune, op); /* runes without need no owner */
183  set_spell_skill(op, caster, spell, rune);
184  insert_ob_in_map(rune, m, op, 0);
185  return 1;
186 }
187 
197 static void rune_attack(object *op, object *victim) {
198  if (victim) {
199  tag_t tag = victim->count;
200  hit_player(victim, op->stats.dam, op, op->attacktype, 1);
201  if (was_destroyed(victim, tag))
202  return;
203  /* if there's a disease in the needle, put it in the player */
204  if (HAS_RANDOM_ITEMS(op))
205  create_treasure(op->randomitems, op, 0, (victim->map ? victim->map->difficulty : 1), 0);
206  if (op->inv && op->inv->type == DISEASE) {
207  object *disease = op->inv;
208 
209  infect_object(victim, disease, 1);
210  remove_ob(disease);
211  free_object(disease);
212  }
213  } else
214  hit_map(op, 0, op->attacktype, 1);
215 }
216 
227 void spring_trap(object *trap, object *victim) {
228  object *env;
229  tag_t trap_tag = trap->count;
230  rv_vector rv;
231  int i, has_spell;
232 
233  /* Prevent recursion */
234  if (trap->stats.hp <= 0)
235  return;
236 
237  if (QUERY_FLAG(trap, FLAG_IS_LINKED))
238  use_trigger(trap);
239 
240  /* Check if this trap casts a spell */
241  has_spell = ((trap->inv && trap->inv->type == SPELL) || (trap->other_arch && trap->other_arch->clone.type == SPELL));
242 
243  env = object_get_env_recursive(trap);
244 
245  /* If the victim is not next to this trap, and the trap doesn't cast
246  * a spell, don't set it off.
247  */
248  get_rangevector(env, victim, &rv, 0);
249  if (rv.distance > 1 && !has_spell)
250  return;
251 
252  /* Only living objects can trigger runes that don't cast spells, as
253  * doing direct damage to a non-living object doesn't work anyway.
254  * Typical example is an arrow attacking a door.
255  */
256  if (!QUERY_FLAG(victim, FLAG_ALIVE) && !has_spell)
257  return;
258 
259  trap->stats.hp--; /*decrement detcount */
260 
261  if (victim && victim->type == PLAYER && trap->msg != NULL)
263  trap->msg, trap->msg);
264 
265  /* Flash an image of the trap on the map so the poor sod
266  * knows what hit him.
267  */
268  trap_show(trap, env);
269 
270  /* Only if it is a spell do we proceed here */
271  if (has_spell) {
272  object *spell;
273 
274  /* This is necessary if the trap is inside something else */
275  remove_ob(trap);
276  trap->x = victim->x;
277  trap->y = victim->y;
278  insert_ob_in_map(trap, victim->map, trap, 0);
279 
280  if (was_destroyed(trap, trap_tag))
281  return;
282 
283  for (i = 0; i < MAX(1, trap->stats.maxhp); i++) {
284  if (trap->inv)
285  cast_spell(trap, trap, trap->direction, trap->inv, NULL);
286  else {
287  spell = arch_to_object(trap->other_arch);
288  cast_spell(trap, trap, trap->direction, spell, NULL);
289  free_object(spell);
290  }
291  }
292  } else {
293  rune_attack(trap, victim);
294  if (was_destroyed(trap, trap_tag))
295  return;
296  }
297 
298  if (trap->stats.hp <= 0) {
299  trap->type = SIGN; /* make the trap impotent */
300  trap->stats.food = 20; /* make it stick around until its spells are gone */
301  SET_FLAG(trap, FLAG_IS_USED_UP);
302  }
303 }
304 
323 int dispel_rune(object *op, object *caster, object *spell, object *skill, int dir) {
324  object *tmp, *tmp2;
325  int searchflag = 1, mflags;
326  sint16 x, y;
327  mapstruct *m;
328 
329  x = op->x+freearr_x[dir];
330  y = op->y+freearr_y[dir];
331  m = op->map;
332 
333  mflags = get_map_flags(m, &m, x, y, &x, &y);
334 
335  /* Should we perhaps not allow player to disable traps if a monster/
336  * player is standing on top?
337  */
338  if (mflags&P_OUT_OF_MAP) {
340  "There's nothing there!", NULL);
341  return 0;
342  }
343 
344  /* This can happen if a player does a 'magic rune of dispel'. Without
345  * any skill, chance of success is zero, and we don't know who to tell
346  * (as otherwise we would have a skill pointer). Plus, trap_disarm()
347  * presumes skill is not null and will crash if it is.
348  */
349  if (!skill)
350  return 0;
351 
352  for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
353  if (tmp->type == RUNE || tmp->type == TRAP)
354  break;
355 
356  /* we could put a probability chance here, but since nothing happens
357  * if you fail, no point on that. I suppose we could do a level
358  * comparison so low level players can't erase high level players runes.
359  */
360  if (tmp->type == SIGN && !strcmp(tmp->arch->name, "rune_mark")) {
361  remove_ob(tmp);
362  free_object(tmp);
364  "You wipe out the rune of marking!", NULL);
365  return 1;
366  }
367 
368  /* now search tmp's inventory for traps
369  * This is for chests, where the rune is in the chests inventory.
370  */
371  for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below) {
372  if (tmp2->type == RUNE || tmp2->type == TRAP) {
373  tmp = tmp2;
374  searchflag = 0;
375  break;
376  }
377  }
378  if (!searchflag)
379  break;
380  }
381 
382  /* no rune there. */
383  if (tmp == NULL) {
385  "There's nothing there!", NULL);
386  return 0;
387  }
388  trap_disarm(op, tmp, 0, skill);
389  return 1;
390 }
391 
403 int trap_see(object *op, object *trap) {
404  int chance;
405 
406  chance = random_roll(0, 99, op, PREFER_HIGH);
407 
408  /* decide if we see the rune or not */
409  if ((trap->stats.Cha == 1)
410  || (chance > MIN(95, MAX(5, ((int)((float)(op->map->difficulty+trap->level+trap->stats.Cha-op->level)/10.0*50.0)))))) {
412  "You spot a %s!",
413  "You spot a %s!",
414  trap->name);
415  return 1;
416  }
417  return 0;
418 }
419 
431 int trap_show(object *trap, object *where) {
432  object *tmp2;
433 
434  if (where == NULL)
435  return 0;
436  tmp2 = create_archetype("runedet");
437  tmp2->face = &new_faces[GET_ANIMATION(trap, 0)];
438  tmp2->x = where->x;
439  tmp2->y = where->y;
440  tmp2->map = where->map;
441  insert_ob_in_map(tmp2, where->map, NULL, 0);
442  return 1;
443 }
444 
459 int trap_disarm(object *disarmer, object *trap, int risk, object *skill) {
460  int trapworth; /* need to compute the experience worth of the trap
461  before we kill it */
462 
463  /* this formula awards a more reasonable amount of exp */
464  trapworth = MAX(1, trap->level)*disarmer->map->difficulty*
465  sqr(MAX(trap->stats.dam, trap->inv ? trap->inv->level : 1))/skill->level;
466 
467  if (!(random_roll(0, (MAX(2, MIN(20, trap->level-skill->level+5-disarmer->stats.Dex/2))-1), disarmer, PREFER_LOW))) {
468  draw_ext_info_format(NDI_UNIQUE, 0, disarmer,
470  "You successfully disarm the %s!",
471  "You successfully disarm the %s!",
472  trap->name);
473  destroy_object(trap);
474  /* If it is your own trap, (or any players trap), don't you don't
475  * get exp for it.
476  */
477  if (trap->owner && trap->owner->type != PLAYER && risk)
478  return trapworth;
479  else
480  return 1; /* give minimal exp and say success */
481  } else {
482  draw_ext_info_format(NDI_UNIQUE, 0, disarmer,
484  "You fail to disarm the %s.",
485  "You fail to disarm the %s.",
486  trap->name);
487  if (!(random_roll(0, (MAX(2, skill->level-trap->level+disarmer->stats.Dex/2-6))-1, disarmer, PREFER_LOW))
488  && risk) {
489  draw_ext_info(NDI_UNIQUE, 0, disarmer,
491  "In fact, you set it off!", NULL);
492  spring_trap(trap, disarmer);
493  }
494  return 0;
495  }
496 }
497 
508 void trap_adjust(object *trap, int difficulty) {
509  int i;
510 
511  /* now we set the trap level to match the difficulty of the level
512  * the formula below will give a level from 1 to (2*difficulty) with
513  * a peak probability at difficulty
514  */
515 
516  trap->level = rndm(0, difficulty-1)+rndm(0, difficulty-1);
517  if (trap->level < 1)
518  trap->level = 1;
519 
520  /* set the hiddenness of the trap, similar formula to above */
521  trap->stats.Cha = rndm(0, 19)+rndm(0, difficulty-1)+rndm(0, difficulty-1);
522 
523  if (!trap->other_arch && !trap->inv) {
524  /* set the damage of the trap.
525  * we get 0-4 pts of damage per level of difficulty of the map in
526  * the trap
527  */
528 
529  trap->stats.dam = 0;
530  for (i = 0; i < difficulty; i++)
531  trap->stats.dam += rndm(0, 4);
532 
533  /* the poison trap special case */
534  if (trap->attacktype&AT_POISON) {
535  trap->stats.dam = rndm(0, difficulty-1);
536  if (trap->stats.dam < 1)
537  trap->stats.dam = 1;
538  }
539 
540  /* so we get an appropriate amnt of exp for AT_DEATH traps */
541  if (trap->attacktype&AT_DEATH)
542  trap->stats.dam = 127;
543  }
544 
545 }
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, sint16 x, sint16 y, sint16 *nx, sint16 *ny)
Definition: map.c:330
#define DISEASE
Definition: define.h:331
#define FLAG_IS_LINKED
Definition: define.h:612
signed short sint16
Definition: global.h:72
Definition: map.h:399
void set_owner(object *op, object *owner)
Definition: object.c:564
int caster_level(const object *caster, const object *spell)
Definition: spell_util.c:237
#define SET_FLAG(xyz, p)
Definition: define.h:510
New_Face * new_faces
Definition: image.c:38
#define TRAP
Definition: define.h:326
#define FLAG_IS_USED_UP
Definition: define.h:556
object * object_get_env_recursive(object *op)
Definition: object.c:339
#define SPELL_GRACE
Definition: spells.h:87
#define sqr(x)
Definition: rune.c:43
#define MSG_TYPE_SPELL_FAILURE
Definition: newclient.h:548
struct treasureliststruct * randomitems
Definition: object.h:236
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:448
int item_matched_string(object *pl, object *op, const char *name)
Definition: object.c:3901
object clone
Definition: object.h:326
short freearr_x[SIZEOFFREE]
Definition: object.c:75
#define PREFER_LOW
Definition: define.h:909
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
Definition: standalone.c:171
#define MSG_TYPE_SPELL_ERROR
Definition: newclient.h:551
#define GET_ANIMATION(ob, anim)
Definition: global.h:248
#define MSG_TYPE_SPELL
Definition: newclient.h:333
#define RUNE
Definition: define.h:325
struct obj * above
Definition: object.h:146
sint16 x
Definition: object.h:179
uint32 path_attuned
Definition: object.h:194
sint16 sp
Definition: living.h:83
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format,...)
Definition: standalone.c:175
struct archt * other_arch
Definition: object.h:264
#define PLAYER
Definition: define.h:113
#define MSG_TYPE_COMMAND_FAILURE
Definition: newclient.h:449
sint16 hp
Definition: living.h:81
short freearr_y[SIZEOFFREE]
Definition: object.c:81
void create_treasure(treasurelist *t, object *op, int flag, int difficulty, int tries)
Definition: treasure.c:499
int rndm(int min, int max)
Definition: utils.c:174
uint32 tag_t
Definition: object.h:40
void remove_ob(object *op)
Definition: object.c:1515
sint16 maxhp
Definition: living.h:82
uint32 path_denied
Definition: object.h:196
#define FLAG_ALIVE
Definition: define.h:526
object * create_archetype(const char *name)
Definition: arch.c:625
struct mapdef * map
Definition: object.h:155
void get_rangevector(object *op1, const object *op2, rv_vector *retval, int flags)
Definition: map.c:2504
sint16 dam
Definition: living.h:87
const char * name
Definition: object.h:167
int trap_disarm(object *disarmer, object *trap, int risk, object *skill)
Definition: rune.c:459
#define SPELL
Definition: define.h:283
int hit_map(object *op, int dir, uint32 type, int full_hit)
Definition: attack.c:292
#define GENERIC_RUNE
Definition: spells.h:194
struct obj * below
Definition: object.h:145
uint16 difficulty
Definition: map.h:364
int infect_object(object *victim, object *disease, int force)
Definition: disease.c:336
sint8 Cha
Definition: living.h:78
#define P_OUT_OF_MAP
Definition: map.h:272
sint16 y
Definition: object.h:179
void use_trigger(object *op)
Definition: button.c:268
#define MAX(x, y)
Definition: define.h:70
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
int write_rune(object *op, object *caster, object *spell, int dir, const char *runename)
Definition: rune.c:66
#define MSG_TYPE_APPLY
Definition: newclient.h:330
object * insert_ob_in_ob(object *op, object *where)
Definition: object.c:2510
void trap_adjust(object *trap, int difficulty)
Definition: rune.c:508
void spring_trap(object *trap, object *victim)
Definition: rune.c:227
#define MAX_BUF
Definition: define.h:81
#define MSG_TYPE_APPLY_TRAP
Definition: newclient.h:521
object * get_object(void)
Definition: object.c:921
object * insert_ob_in_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1992
const char * skill
Definition: object.h:174
#define SIGN
Definition: define.h:280
#define MIN(x, y)
Definition: define.h:67
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
#define AT_POISON
Definition: attack.h:114
int dispel_rune(object *op, object *caster, object *spell, object *skill, int dir)
Definition: rune.c:323
#define PREFER_HIGH
Definition: define.h:908
uint32 attacktype
Definition: object.h:193
sint8 direction
Definition: object.h:185
struct obj * owner
Definition: object.h:228
sint16 grace
Definition: living.h:85
New_Face * blank_face
Definition: image.c:66
tag_t count
Definition: object.h:157
living stats
Definition: object.h:219
sint8 Dex
Definition: living.h:78
struct archt * arch
Definition: object.h:263
void set_spell_skill(object *op, object *caster, object *spob, object *dest)
Definition: spell_util.c:105
void destroy_object(object *op)
Definition: login.c:201
unsigned int distance
Definition: map.h:400
#define MSG_TYPE_COMMAND
Definition: newclient.h:326
sint16 SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.c:281
const char * msg
Definition: object.h:175
static void rune_attack(object *op, object *victim)
Definition: rune.c:197
#define MSG_TYPE_SPELL_SUCCESS
Definition: newclient.h:550
int trap_see(object *op, object *trap)
Definition: rune.c:403
sstring add_string(const char *str)
Definition: shstr.c:116
#define GET_MAP_OB(M, X, Y)
Definition: map.h:193
struct obj * inv
Definition: object.h:148
#define NDI_UNIQUE
Definition: newclient.h:219
#define was_destroyed(op, old_tag)
Definition: object.h:94
void copy_object(object *op2, object *op)
Definition: object.c:758
int trap_show(object *trap, object *where)
Definition: rune.c:431
#define SPELL_MANA
Definition: spells.h:86
void free_object(object *ob)
Definition: object.c:1238
Definition: map.h:346
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:51
New_Face * face
Definition: object.h:183
sint16 level
Definition: object.h:202
#define AT_DEATH
Definition: attack.h:121
object * arch_to_object(archetype *at)
Definition: arch.c:576
const char * name
Definition: object.h:322
int hit_player(object *op, int dam, object *hitter, uint32 type, int full_hit)
Definition: attack.c:1868
#define HAS_RANDOM_ITEMS(op)
Definition: define.h:470
int cast_spell(object *op, object *caster, int dir, object *spell_ob, char *stringarg)
Definition: spell_util.c:1308
uint8 type
Definition: object.h:189
sint32 food
Definition: living.h:89