Crossfire Server, Trunk
rune.cpp
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->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 {
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:70
HAS_RANDOM_ITEMS
#define HAS_RANDOM_ITEMS(op)
Definition: define.h:184
PLAYER
@ PLAYER
Definition: object.h:112
object_get_owner
object * object_get_owner(object *op)
Definition: object.cpp:804
set_spell_skill
void set_spell_skill(object *op, object *caster, object *spob, object *dest)
Definition: spell_util.cpp:94
global.h
object_get_env_recursive
object * object_get_env_recursive(object *op)
Definition: object.cpp:590
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:730
living::maxhp
int16_t maxhp
Definition: living.h:41
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:519
trap_see
int trap_see(object *op, object *trap)
Definition: rune.cpp:376
trap_disarm
int trap_disarm(object *disarmer, object *trap, int risk, object *skill)
Definition: rune.cpp:433
mapstruct::difficulty
uint16_t difficulty
Definition: map.h:333
GET_ANIMATION
#define GET_ANIMATION(ob, anim)
Definition: global.h:163
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
object::inv
object * inv
Definition: object.h:298
diamondslots.x
x
Definition: diamondslots.py:15
CFweardisguise.tag
tag
Definition: CFweardisguise.py:25
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
spring_trap
void spring_trap(object *trap, object *victim)
Definition: rune.cpp:205
cast_spell
int cast_spell(object *op, object *caster, int dir, object *spell_ob, char *stringarg)
Definition: spell_util.cpp:1424
living::Dex
int8_t Dex
Definition: living.h:36
TRAP
@ TRAP
Definition: object.h:246
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
object::map
struct mapstruct * map
Definition: object.h:305
infect_object
int infect_object(object *victim, object *disease, int force)
Definition: disease.cpp:317
object_set_owner
void object_set_owner(object *op, object *owner)
Definition: object.cpp:840
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,...) PRINTF_ARGS(6
MIN
#define MIN(x, y)
Definition: compat.h:21
write_rune
int write_rune(object *op, object *caster, object *spell, int dir, const char *runename)
Definition: rune.cpp:50
object::direction
int8_t direction
Definition: object.h:344
object::count
tag_t count
Definition: object.h:307
Ice.tmp
int tmp
Definition: Ice.py:207
RUNE
@ RUNE
Definition: object.h:245
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.cpp:1192
destroy_object
void destroy_object(object *op)
Definition: login.cpp:209
dispel_rune
int dispel_rune(object *op, object *skill, int dir)
Definition: rune.cpp:295
blank_face
const Face * blank_face
Definition: image.cpp:36
hit_map
int hit_map(object *op, int dir, uint32_t type, int full_hit)
Definition: attack.cpp:355
SIGN
@ SIGN
Definition: object.h:216
create_treasure
void create_treasure(treasurelist *t, object *op, int flag, int difficulty, int tries)
Definition: treasure.cpp:263
object::level
int16_t level
Definition: object.h:361
buf
StringBuffer * buf
Definition: readable.cpp:1552
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2848
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:393
MAX
#define MAX(x, y)
Definition: compat.h:24
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
FLAG_ALIVE
#define FLAG_ALIVE
Definition: define.h:230
object::path_denied
uint32_t path_denied
Definition: object.h:355
rune_attack
static void rune_attack(object *op, object *victim)
Definition: rune.cpp:175
MSG_TYPE_SPELL_SUCCESS
#define MSG_TYPE_SPELL_SUCCESS
Definition: newclient.h:624
m
static event_registration m
Definition: citylife.cpp:425
GENERIC_RUNE
#define GENERIC_RUNE
Definition: spells.h:167
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.cpp:1555
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
object_find_by_type2
object * object_find_by_type2(const object *who, int type1, int type2)
Definition: object.cpp:4028
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.cpp:305
archetype::clone
object clone
Definition: object.h:478
trap_show
int trap_show(object *trap, object *where)
Definition: rune.cpp:403
SPELL_GRACE
#define SPELL_GRACE
Definition: spells.h:59
object::face
const Face * face
Definition: object.h:341
object::type
uint8_t type
Definition: object.h:348
living::dam
int16_t dam
Definition: living.h:46
MSG_TYPE_APPLY_TRAP
#define MSG_TYPE_APPLY_TRAP
Definition: newclient.h:595
spell
with a maximum of six This is not so if you are wearing plate you receive no benefit Armour is additive with all the supplementry forms of which means that it lasts until the next semi permanent spell effect is cast upon the character spell
Definition: tome-of-magic.txt:44
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
living::food
int32_t food
Definition: living.h:48
caster_level
int caster_level(const object *caster, const object *spell)
Definition: spell_util.cpp:194
tag_t
uint32_t tag_t
Definition: object.h:14
object_matches_string
int object_matches_string(object *pl, object *op, const char *name)
Definition: object.cpp:4565
sproto.h
MSG_TYPE_SPELL
#define MSG_TYPE_SPELL
Definition: newclient.h:400
SP_level_spellpoint_cost
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.cpp:236
object::animation
const Animations * animation
Definition: object.h:426
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.cpp:2095
object::other_arch
struct archetype * other_arch
Definition: object.h:423
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:248
env
static std::shared_ptr< inja::Environment > env
Definition: mapper.cpp:2170
MAX_BUF
#define MAX_BUF
Definition: define.h:35
object_new
object * object_new(void)
Definition: object.cpp:1268
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
use_trigger
void use_trigger(object *op)
Definition: button.cpp:254
MSG_TYPE_COMMAND_FAILURE
#define MSG_TYPE_COMMAND_FAILURE
Definition: newclient.h:520
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:723
sqr
#define sqr(x)
Definition: rune.cpp:28
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
spells.h
object::name
sstring name
Definition: object.h:319
MSG_TYPE_SPELL_FAILURE
#define MSG_TYPE_SPELL_FAILURE
Definition: newclient.h:622
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.cpp:299
reputation.victim
victim
Definition: reputation.py:14
DISEASE
@ DISEASE
Definition: object.h:249
mapstruct
Definition: map.h:314
living::Cha
int8_t Cha
Definition: living.h:36
give.op
op
Definition: give.py:33
object::skill
sstring skill
Definition: object.h:329
MSG_TYPE_SPELL_ERROR
#define MSG_TYPE_SPELL_ERROR
Definition: newclient.h:625
object_set_msg
void object_set_msg(object *op, const char *msg)
Definition: object.cpp:4802
object::msg
sstring msg
Definition: object.h:330
rv_vector
Definition: map.h:370
hit_player
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
Definition: attack.cpp:1902
diamondslots.y
y
Definition: diamondslots.py:16
python_pickup.where
where
Definition: python_pickup.py:7
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.cpp:42
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:229
get_rangevector
int get_rangevector(object *op1, const object *op2, rv_vector *retval, int flags)
Definition: map.cpp:2519
FLAG_IS_USED_UP
#define FLAG_IS_USED_UP
Definition: define.h:260
skill
skill
Definition: arch-handbook.txt:585
object_remove
void object_remove(object *op)
Definition: object.cpp:1828
FLAG_IS_LINKED
#define FLAG_IS_LINKED
Definition: define.h:315
object::stats
living stats
Definition: object.h:378
freearr_x
short freearr_x[SIZEOFFREE]
Definition: object.cpp:299
SPELL
@ SPELL
Definition: object.h:219
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Definition: newclient.h:397
object::temp_animation
const Animations * temp_animation
Definition: object.h:429
rv_vector::distance
unsigned int distance
Definition: map.h:371
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
living::hp
int16_t hp
Definition: living.h:40
FLAG_LIFESAVE
#define FLAG_LIFESAVE
Definition: define.h:305
SPELL_MANA
#define SPELL_MANA
Definition: spells.h:58