Crossfire Server, Trunk
rune.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
20 #include "global.h"
21 
22 #include <string.h>
23 
24 #include "sproto.h"
25 #include "spells.h"
26 
27 #ifndef sqr
28 #define sqr(x) ((x)*(x))
29 #endif
30 
50 int write_rune(object *op, object *caster, object *spell, int dir, const char *runename) {
51  object *rune_spell, *rune;
52  char buf[MAX_BUF];
53  mapstruct *m;
54  int16_t nx, ny, gr;
55 
56  if (!dir) {
57  dir = 1;
58  }
59 
60  nx = op->x+freearr_x[dir];
61  ny = op->y+freearr_y[dir];
62  m = op->map;
63 
64  if (get_map_flags(m, &m, nx, ny, &nx, &ny)) {
66  "Can't make a rune there!");
67  return 0;
68  }
69  FOR_MAP_PREPARE(m, nx, ny, tmp)
70  if (tmp->type == RUNE) {
72  "You can't write a rune there.");
73  return 0;
74  }
76 
77  if (spell->other_arch) {
78  rune_spell = arch_to_object(spell->other_arch);
79  } else {
80  /* Player specified spell. The player has to know the spell, so
81  * lets just look through the players inventory see if they know it
82  * use the object_matches_string() for our typical matching method.
83  */
84  int bestmatch = 0, ms;
85 
86  if (!runename || *runename == 0) {
88  "Write a rune of what?");
89  return 0;
90  }
91 
92  rune_spell = NULL;
94  if (tmp->type == SPELL) {
95  ms = object_matches_string(op, tmp, runename);
96  if (ms > bestmatch) {
97  bestmatch = ms;
98  rune_spell = tmp;
99  }
100  }
101  } FOR_INV_FINISH();
102  if (!rune_spell) {
105  "You don't know any spell named %s",
106  runename);
107  return 0;
108  }
109  if (rune_spell->skill != spell->skill) {
112  "You can't cast %s with %s",
113  rune_spell->name, spell->name);
114  return 0;
115  }
116  if (caster->path_denied&spell->path_attuned) {
118  "%s belongs to a spell path denied to you.",
119  rune_spell->name);
120  return 0;
121  }
122  if (caster_level(caster, rune_spell) < rune_spell->level) {
124  "%s is beyond your ability to cast!",
125  rune_spell->name);
126  return 0;
127  }
128  if (SP_level_spellpoint_cost(caster, rune_spell, SPELL_MANA) > op->stats.sp) {
130  "You don't have enough mana.");
131  return 0;
132  }
133  gr = SP_level_spellpoint_cost(caster, rune_spell, SPELL_GRACE);
134  if ((gr > 0) && (gr > op->stats.grace)) {
136  "You don't have enough grace.");
137  return 0;
138  }
139  op->stats.grace -= gr;
140  op->stats.sp -= SP_level_spellpoint_cost(caster, rune_spell, SPELL_MANA);
141  }
142  /* already proper rune. Note this should only be the case if other_arch was set */
143  if (rune_spell->type == RUNE) {
144  rune = rune_spell;
145  } else {
146  object *tmp;
147 
149  snprintf(buf, sizeof(buf), "You set off a rune of %s\n", rune_spell->name);
150  object_set_msg(rune, buf);
151  tmp = object_new();
152  object_copy(rune_spell, tmp);
153  object_insert_in_ob(tmp, rune);
154  if (spell->face != blank_face)
155  rune->face = spell->face;
156  }
157  rune->level = caster_level(caster, spell);
158  rune->stats.Cha = rune->level/2; /* the invisibility parameter */
159  rune->direction = dir; /* where any spell will go upon detonation */
160  object_set_owner(rune, op); /* runes without need no owner */
161  set_spell_skill(op, caster, spell, rune);
162  object_insert_in_map_at(rune, m, op, 0, nx, ny);
163  return 1;
164 }
165 
175 static void rune_attack(object *op, object *victim) {
176  if (victim) {
177  tag_t tag = victim->count;
178  hit_player(victim, op->stats.dam, op, op->attacktype, 1);
180  return;
181  /* if there's a disease in the needle, put it in the player */
182  if (HAS_RANDOM_ITEMS(op))
183  create_treasure(op->randomitems, op, 0, (victim->map ? victim->map->difficulty : 1), 0);
184  if (op->inv && op->inv->type == DISEASE) {
185  object *disease = op->inv;
186 
187  infect_object(victim, disease, 1);
188  object_remove(disease);
190  }
191  } else
192  hit_map(op, 0, op->attacktype, 1);
193 }
194 
205 void spring_trap(object *trap, object *victim) {
206  object *env;
207  tag_t trap_tag = trap->count;
208  rv_vector rv;
209  int i, has_spell;
210 
211  /* Prevent recursion */
212  if (trap->stats.hp <= 0)
213  return;
214 
215  if (QUERY_FLAG(trap, FLAG_IS_LINKED))
216  use_trigger(trap);
217 
218  /* Check if this trap casts a spell */
219  has_spell = ((trap->inv && trap->inv->type == SPELL) || (trap->other_arch && trap->other_arch->clone.type == SPELL));
220 
222 
223  /* If the victim is not next to this trap, and the trap doesn't cast
224  * a spell, don't set it off.
225  */
226  if (!get_rangevector(env, victim, &rv, 0) || (rv.distance > 1 && !has_spell))
227  return;
228 
229  /* Only living objects can trigger runes that don't cast spells, as
230  * doing direct damage to a non-living object doesn't work anyway.
231  * Typical example is an arrow attacking a door.
232  */
233  if (!QUERY_FLAG(victim, FLAG_ALIVE) && !has_spell)
234  return;
235 
236  if (!QUERY_FLAG(trap, FLAG_LIFESAVE))
237  trap->stats.hp--; /*decrement detcount */
238 
239  if (victim && victim->type == PLAYER && trap->msg != NULL && trap->msg[0] != '\0')
241  trap->msg);
242 
243  /* Flash an image of the trap on the map so the poor sod
244  * knows what hit him.
245  */
246  trap_show(trap, env);
247 
248  /* Only if it is a spell do we proceed here */
249  if (has_spell) {
250  object *spell;
251 
252  /* This is necessary if the trap is inside something else */
253  object_remove(trap);
254  object_insert_in_map_at(trap, victim->map, trap, 0, victim->x, victim->y);
255 
256  if (object_was_destroyed(trap, trap_tag))
257  return;
258 
259  for (i = 0; i < MAX(1, trap->stats.maxhp); i++) {
260  if (trap->inv)
261  cast_spell(trap, trap, trap->direction, trap->inv, NULL);
262  else {
263  spell = arch_to_object(trap->other_arch);
264  cast_spell(trap, trap, trap->direction, spell, NULL);
266  }
267  }
268  } else {
269  rune_attack(trap, victim);
270  if (object_was_destroyed(trap, trap_tag))
271  return;
272  }
273 
274  if (trap->stats.hp <= 0) {
275  trap->type = SIGN; /* make the trap impotent */
276  trap->stats.food = 20; /* make it stick around until its spells are gone */
277  SET_FLAG(trap, FLAG_IS_USED_UP);
278  }
279 }
280 
295 int dispel_rune(object *op, object *skill, int dir) {
296  object *rune;
297  int mflags;
298  int16_t x, y;
299  mapstruct *m;
300 
301  x = op->x+freearr_x[dir];
302  y = op->y+freearr_y[dir];
303  m = op->map;
304 
305  mflags = get_map_flags(m, &m, x, y, &x, &y);
306 
307  /* Should we perhaps not allow player to disable traps if a monster/
308  * player is standing on top?
309  */
310  if (mflags&P_OUT_OF_MAP) {
312  "There's nothing there!");
313  return 0;
314  }
315 
316  /* This can happen if a player does a 'magic rune of dispel'. Without
317  * any skill, chance of success is zero, and we don't know who to tell
318  * (as otherwise we would have a skill pointer). Plus, trap_disarm()
319  * presumes skill is not null and will crash if it is.
320  */
321  if (!skill)
322  return 0;
323 
324  rune = NULL;
325  FOR_MAP_PREPARE(m, x, y, tmp) {
326  object *tmp2;
327 
328  if (tmp->type == RUNE || tmp->type == TRAP) {
329  rune = tmp;
330  break;
331  }
332 
333  /* we could put a probability chance here, but since nothing happens
334  * if you fail, no point on that. I suppose we could do a level
335  * comparison so low level players can't erase high level players runes.
336  */
337  if (tmp->type == SIGN && !strcmp(tmp->arch->name, "rune_mark")) {
341  "You wipe out the rune of marking!");
342  return 1;
343  }
344 
345  /* now search tmp's inventory for traps
346  * This is for chests, where the rune is in the chests inventory.
347  */
348  tmp2 = object_find_by_type2(tmp, RUNE, TRAP);
349  if (tmp2 != NULL) {
350  rune = tmp2;
351  break;
352  }
353  } FOR_MAP_FINISH();
354 
355  /* no rune there. */
356  if (rune == NULL) {
358  "There's nothing there!");
359  return 0;
360  }
361  trap_disarm(op, rune, 0, skill);
362  return 1;
363 }
364 
376 int trap_see(object *op, object *trap) {
377  int chance;
378 
379  chance = random_roll(0, 99, op, PREFER_HIGH);
380 
381  /* decide if we see the rune or not */
382  if ((trap->stats.Cha == 1)
383  || (chance > MIN(95, MAX(5, ((int)((float)(op->map->difficulty+trap->level+trap->stats.Cha-op->level)/10.0*50.0)))))) {
385  "You spot a %s!",
386  trap->name);
387  return 1;
388  }
389  return 0;
390 }
391 
403 int trap_show(object *trap, object *where) {
404  object *tmp2;
405 
406  if (where == NULL)
407  return 0;
408  tmp2 = create_archetype("runedet");
409  // Custom-made traps can have no animation.
410  // The GET_ANIMATION macro does not handle this scenario
411  if (trap->animation || trap->temp_animation)
412  tmp2->face = GET_ANIMATION(trap, 0);
413  else
414  tmp2->face = trap->face;
415  object_insert_in_map_at(tmp2, where->map, NULL, 0, where->x, where->y);
416  return 1;
417 }
418 
433 int trap_disarm(object *disarmer, object *trap, int risk, object *skill) {
434  int trapworth; /* need to compute the experience worth of the trap
435  before we kill it */
436 
437  /* this formula awards a more reasonable amount of exp */
438  trapworth = MAX(1, trap->level)*disarmer->map->difficulty*
439  sqr(MAX(trap->stats.dam, trap->inv ? trap->inv->level : 1))/skill->level;
440 
441  if (!(random_roll(0, (MAX(2, MIN(20, trap->level-skill->level+5-disarmer->stats.Dex/2))-1), disarmer, PREFER_LOW))) {
442  object *owner;
443 
444  draw_ext_info_format(NDI_UNIQUE, 0, disarmer,
446  "You successfully disarm the %s!",
447  trap->name);
448  destroy_object(trap);
449  /* If it is your own trap, (or any players trap), don't you don't
450  * get exp for it.
451  */
452  owner = object_get_owner(trap);
453  if (owner != NULL && owner->type != PLAYER && risk)
454  return trapworth;
455  else
456  return 1; /* give minimal exp and say success */
457  } else {
458  draw_ext_info_format(NDI_UNIQUE, 0, disarmer,
460  "You fail to disarm the %s.",
461  trap->name);
462  if (!(random_roll(0, (MAX(2, skill->level-trap->level+disarmer->stats.Dex/2-6))-1, disarmer, PREFER_LOW))
463  && risk) {
464  draw_ext_info(NDI_UNIQUE, 0, disarmer,
466  "In fact, you set it off!");
467  spring_trap(trap, disarmer);
468  }
469  return 0;
470  }
471 }
object_was_destroyed
#define object_was_destroyed(op, old_tag)
Definition: object.h:68
HAS_RANDOM_ITEMS
#define HAS_RANDOM_ITEMS(op)
Definition: define.h:184
PLAYER
@ PLAYER
Definition: object.h:107
set_spell_skill
void set_spell_skill(object *op, object *caster, object *spob, object *dest)
Definition: spell_util.c:92
global.h
liv::dam
int16_t dam
Definition: living.h:46
object_remove
void object_remove(object *op)
Definition: object.c:1806
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:728
obj::face
const Face * face
Definition: object.h:334
dispel_rune
int dispel_rune(object *op, object *skill, int dir)
Definition: rune.c:295
object_find_by_type2
object * object_find_by_type2(const object *who, int type1, int type2)
Definition: object.c:4045
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:530
GET_ANIMATION
#define GET_ANIMATION(ob, anim)
Definition: global.h:161
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
diamondslots.x
x
Definition: diamondslots.py:15
obj::count
tag_t count
Definition: object.h:300
obj::map
struct mapdef * map
Definition: object.h:298
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
object_new
object * object_new(void)
Definition: object.c:1242
cast_spell
int cast_spell(object *op, object *caster, int dir, object *spell_ob, char *stringarg)
Definition: spell_util.c:1420
obj::path_attuned
uint32_t path_attuned
Definition: object.h:346
liv::maxhp
int16_t maxhp
Definition: living.h:41
TRAP
@ TRAP
Definition: object.h:241
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
infect_object
int infect_object(object *victim, object *disease, int force)
Definition: disease.c:317
object_matches_string
int object_matches_string(object *pl, object *op, const char *name)
Definition: object.c:4564
liv::hp
int16_t hp
Definition: living.h:40
trap_see
int trap_see(object *op, object *trap)
Definition: rune.c:376
blank_face
const Face * blank_face
Definition: image.c:35
MIN
#define MIN(x, y)
Definition: compat.h:21
RUNE
@ RUNE
Definition: object.h:240
destroy_object
void destroy_object(object *op)
Definition: login.c:209
obj::path_denied
uint32_t path_denied
Definition: object.h:348
Ice.tmp
int tmp
Definition: Ice.py:207
trap_show
int trap_show(object *trap, object *where)
Definition: rune.c:403
hit_map
int hit_map(object *op, int dir, uint32_t type, int full_hit)
Definition: attack.c:327
SIGN
@ SIGN
Definition: object.h:211
obj::msg
sstring msg
Definition: object.h:323
get_rangevector
int get_rangevector(object *op1, const object *op2, rv_vector *retval, int flags)
Definition: map.c:2535
create_treasure
void create_treasure(treasurelist *t, object *op, int flag, int difficulty, int tries)
Definition: treasure.c:242
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:404
MAX
#define MAX(x, y)
Definition: compat.h:24
freearr_x
short freearr_x[SIZEOFFREE]
Definition: object.c:288
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.c:294
FLAG_ALIVE
#define FLAG_ALIVE
Definition: define.h:230
MSG_TYPE_SPELL_SUCCESS
#define MSG_TYPE_SPELL_SUCCESS
Definition: newclient.h:635
m
static event_registration m
Definition: citylife.cpp:425
GENERIC_RUNE
#define GENERIC_RUNE
Definition: spells.h:167
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
obj::name
sstring name
Definition: object.h:312
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.c:1053
liv::Cha
int8_t Cha
Definition: living.h:36
mapdef::difficulty
uint16_t difficulty
Definition: map.h:342
rune_attack
static void rune_attack(object *op, object *victim)
Definition: rune.c:175
SPELL_GRACE
#define SPELL_GRACE
Definition: spells.h:59
CFweardisguise.tag
tag
Definition: CFweardisguise.py:25
MSG_TYPE_APPLY_TRAP
#define MSG_TYPE_APPLY_TRAP
Definition: newclient.h:606
obj::other_arch
struct archt * other_arch
Definition: object.h:417
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:675
caster_level
int caster_level(const object *caster, const object *spell)
Definition: spell_util.c:193
tag_t
uint32_t tag_t
Definition: object.h:12
sproto.h
liv::food
int32_t food
Definition: living.h:48
mapdef
Definition: map.h:324
MSG_TYPE_SPELL
#define MSG_TYPE_SPELL
Definition: newclient.h:411
SP_level_spellpoint_cost
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.c:235
obj::animation
const Animations * animation
Definition: object.h:420
object_set_owner
void object_set_owner(object *op, object *owner)
Definition: object.c:833
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:250
env
static std::shared_ptr< inja::Environment > env
Definition: mapper.cpp:2215
MAX_BUF
#define MAX_BUF
Definition: define.h:35
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:281
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
MSG_TYPE_COMMAND_FAILURE
#define MSG_TYPE_COMMAND_FAILURE
Definition: newclient.h:531
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:721
object_set_msg
void object_set_msg(object *op, const char *msg)
Definition: object.c:4798
obj::type
uint8_t type
Definition: object.h:341
obj::temp_animation
const Animations * temp_animation
Definition: object.h:423
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:262
spells.h
obj::stats
living stats
Definition: object.h:371
obj::direction
int8_t direction
Definition: object.h:337
archt::clone
object clone
Definition: object.h:472
MSG_TYPE_SPELL_FAILURE
#define MSG_TYPE_SPELL_FAILURE
Definition: newclient.h:633
use_trigger
void use_trigger(object *op)
Definition: button.c:254
liv::Dex
int8_t Dex
Definition: living.h:36
reputation.victim
victim
Definition: reputation.py:14
DISEASE
@ DISEASE
Definition: object.h:244
give.op
op
Definition: give.py:33
MSG_TYPE_SPELL_ERROR
#define MSG_TYPE_SPELL_ERROR
Definition: newclient.h:636
rv_vector
Definition: map.h:379
hit_player
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
Definition: attack.c:1853
diamondslots.y
y
Definition: diamondslots.py:16
python_pickup.where
where
Definition: python_pickup.py:7
buf
StringBuffer * buf
Definition: readable.c:1606
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2820
object_get_env_recursive
object * object_get_env_recursive(object *op)
Definition: object.c:583
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:232
FLAG_IS_USED_UP
#define FLAG_IS_USED_UP
Definition: define.h:260
obj::skill
sstring skill
Definition: object.h:322
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:2067
draw_ext_info
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:309
sqr
#define sqr(x)
Definition: rune.c:28
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.c:1533
trap_disarm
int trap_disarm(object *disarmer, object *trap, int risk, object *skill)
Definition: rune.c:433
FLAG_IS_LINKED
#define FLAG_IS_LINKED
Definition: define.h:315
spring_trap
void spring_trap(object *trap, object *victim)
Definition: rune.c:205
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.c:311
SPELL
@ SPELL
Definition: object.h:214
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Definition: newclient.h:408
rv_vector::distance
unsigned int distance
Definition: map.h:380
write_rune
int write_rune(object *op, object *caster, object *spell, int dir, const char *runename)
Definition: rune.c:50
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:668
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:319
obj::level
int16_t level
Definition: object.h:354
FLAG_LIFESAVE
#define FLAG_LIFESAVE
Definition: define.h:305
obj::inv
struct obj * inv
Definition: object.h:291
SPELL_MANA
#define SPELL_MANA
Definition: spells.h:58
object_get_owner
object * object_get_owner(object *op)
Definition: object.c:797