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  // Try to find a rune of the given name (and a .111 suffix) and animation, and,
155  // if yes, override with the image *and* the animation.
156  // If either is missing, don't do it. Let's not make more edge cases for now :)
157  char buf[100];
158  snprintf(buf, 100, "%s.111", spell->name);
159  const Face *face_override = try_find_face(buf, NULL);
160  const Animations *anim_override = try_find_animation(spell->name);
161  if (face_override != NULL && anim_override != NULL) {
162  rune->face = face_override;
163  rune->animation = anim_override;
164  }
165  }
166  rune->level = caster_level(caster, spell);
167  rune->stats.Cha = rune->level/2; /* the invisibility parameter */
168  rune->direction = dir; /* where any spell will go upon detonation */
169  object_set_owner(rune, op); /* runes without need no owner */
170  set_spell_skill(op, caster, spell, rune);
171  object_insert_in_map_at(rune, m, op, 0, nx, ny);
172  return 1;
173 }
174 
184 static void rune_attack(object *op, object *victim) {
185  if (victim) {
186  tag_t tag = victim->count;
187  hit_player(victim, op->stats.dam, op, op->attacktype, 1);
189  return;
190  /* if there's a disease in the needle, put it in the player */
191  if (HAS_RANDOM_ITEMS(op))
192  create_treasure(op->randomitems, op, 0, (victim->map ? victim->map->difficulty : 1), 0);
193  if (op->inv && op->inv->type == DISEASE) {
194  object *disease = op->inv;
195 
196  infect_object(victim, disease, 1);
197  object_remove(disease);
199  }
200  } else
201  hit_map(op, 0, op->attacktype, 1);
202 }
203 
214 void spring_trap(object *trap, object *victim) {
215  object *env;
216  tag_t trap_tag = trap->count;
217  rv_vector rv;
218  int i, has_spell;
219 
220  /* Prevent recursion */
221  if (trap->stats.hp <= 0)
222  return;
223 
224  if (QUERY_FLAG(trap, FLAG_IS_LINKED))
225  use_trigger(trap);
226 
227  /* Check if this trap casts a spell */
228  has_spell = ((trap->inv && trap->inv->type == SPELL) || (trap->other_arch && trap->other_arch->clone.type == SPELL));
229 
231 
232  /* If the victim is not next to this trap, and the trap doesn't cast
233  * a spell, don't set it off.
234  */
235  if (!get_rangevector(env, victim, &rv, 0) || (rv.distance > 1 && !has_spell))
236  return;
237 
238  /* Only living objects can trigger runes that don't cast spells, as
239  * doing direct damage to a non-living object doesn't work anyway.
240  * Typical example is an arrow attacking a door.
241  */
242  if (!QUERY_FLAG(victim, FLAG_ALIVE) && !has_spell)
243  return;
244 
245  if (!QUERY_FLAG(trap, FLAG_LIFESAVE))
246  trap->stats.hp--; /*decrement detcount */
247 
248  if (victim->type == PLAYER && trap->msg != NULL && trap->msg[0] != '\0')
250  trap->msg);
251 
252  /* Flash an image of the trap on the map so the poor sod
253  * knows what hit him.
254  */
255  trap_show(trap, env);
256 
257  /* Only if it is a spell do we proceed here */
258  if (has_spell) {
259  object *spell;
260 
261  /* This is necessary if the trap is inside something else */
262  object_remove(trap);
263  object_insert_in_map_at(trap, victim->map, trap, 0, victim->x, victim->y);
264 
265  if (object_was_destroyed(trap, trap_tag))
266  return;
267 
268  for (i = 0; i < MAX(1, trap->stats.maxhp); i++) {
269  if (trap->inv)
270  cast_spell(trap, trap, trap->direction, trap->inv, NULL);
271  else {
272  spell = arch_to_object(trap->other_arch);
273  cast_spell(trap, trap, trap->direction, spell, NULL);
275  }
276  }
277  } else {
278  rune_attack(trap, victim);
279  if (object_was_destroyed(trap, trap_tag))
280  return;
281  }
282 
283  if (trap->stats.hp <= 0) {
284  trap->type = SIGN; /* make the trap impotent */
285  trap->stats.food = 20; /* make it stick around until its spells are gone */
286  SET_FLAG(trap, FLAG_IS_USED_UP);
287  }
288 }
289 
304 int dispel_rune(object *op, object *skill, int dir) {
305  object *rune;
306  int mflags;
307  int16_t x, y;
308  mapstruct *m;
309 
310  x = op->x+freearr_x[dir];
311  y = op->y+freearr_y[dir];
312  m = op->map;
313 
314  mflags = get_map_flags(m, &m, x, y, &x, &y);
315 
316  /* Should we perhaps not allow player to disable traps if a monster/
317  * player is standing on top?
318  */
319  if (mflags&P_OUT_OF_MAP) {
321  "There's nothing there!");
322  return 0;
323  }
324 
325  /* This can happen if a player does a 'magic rune of dispel'. Without
326  * any skill, chance of success is zero, and we don't know who to tell
327  * (as otherwise we would have a skill pointer). Plus, trap_disarm()
328  * presumes skill is not null and will crash if it is.
329  */
330  if (!skill)
331  return 0;
332 
333  rune = NULL;
334  FOR_MAP_PREPARE(m, x, y, tmp) {
335  object *tmp2;
336 
337  if (tmp->type == RUNE || tmp->type == TRAP) {
338  rune = tmp;
339  break;
340  }
341 
342  /* we could put a probability chance here, but since nothing happens
343  * if you fail, no point on that. I suppose we could do a level
344  * comparison so low level players can't erase high level players runes.
345  */
346  if (tmp->type == SIGN && !strcmp(tmp->arch->name, "rune_mark")) {
350  "You wipe out the rune of marking!");
351  return 1;
352  }
353 
354  /* now search tmp's inventory for traps
355  * This is for chests, where the rune is in the chests inventory.
356  */
357  tmp2 = object_find_by_type2(tmp, RUNE, TRAP);
358  if (tmp2 != NULL) {
359  rune = tmp2;
360  break;
361  }
362  } FOR_MAP_FINISH();
363 
364  /* no rune there. */
365  if (rune == NULL) {
367  "There's nothing there!");
368  return 0;
369  }
370  trap_disarm(op, rune, 0, skill);
371  return 1;
372 }
373 
385 int trap_see(object *op, object *trap) {
386  int chance;
387 
388  chance = random_roll(0, 99, op, PREFER_HIGH);
389 
390  /* decide if we see the rune or not */
391  if ((trap->stats.Cha == 1)
392  || (chance > MIN(95, MAX(5, ((int)((float)(op->map->difficulty+trap->level+trap->stats.Cha-op->level)/10.0*50.0)))))) {
394  "You spot a %s!",
395  trap->name);
396  return 1;
397  }
398  return 0;
399 }
400 
412 int trap_show(object *trap, object *where) {
413  object *tmp2;
414 
415  if (where == NULL)
416  return 0;
417  tmp2 = create_archetype("runedet");
418  // Custom-made traps can have no animation.
419  // The GET_ANIMATION macro does not handle this scenario
420  if (trap->animation || trap->temp_animation)
421  tmp2->face = GET_ANIMATION(trap, 0);
422  else
423  tmp2->face = trap->face;
424  object_insert_in_map_at(tmp2, where->map, NULL, 0, where->x, where->y);
425  return 1;
426 }
427 
442 int trap_disarm(object *disarmer, object *trap, int risk, object *skill) {
443  int trapworth; /* need to compute the experience worth of the trap
444  before we kill it */
445 
446  /* this formula awards a more reasonable amount of exp */
447  trapworth = MAX(1, trap->level)*disarmer->map->difficulty*
448  sqr(MAX(trap->stats.dam, trap->inv ? trap->inv->level : 1))/skill->level;
449 
450  if (!(random_roll(0, (MAX(2, MIN(20, trap->level-skill->level+5-disarmer->stats.Dex/2))-1), disarmer, PREFER_LOW))) {
451  object *owner;
452 
453  draw_ext_info_format(NDI_UNIQUE, 0, disarmer,
455  "You successfully disarm the %s!",
456  trap->name);
457  destroy_object(trap);
458  /* If it is your own trap, (or any players trap), don't you don't
459  * get exp for it.
460  */
461  owner = object_get_owner(trap);
462  if (owner != NULL && owner->type != PLAYER && risk)
463  return trapworth;
464  else
465  return 1; /* give minimal exp and say success */
466  } else {
467  draw_ext_info_format(NDI_UNIQUE, 0, disarmer,
469  "You fail to disarm the %s.",
470  trap->name);
471  if (!(random_roll(0, (MAX(2, skill->level-trap->level+disarmer->stats.Dex/2-6))-1, disarmer, PREFER_LOW))
472  && risk) {
473  draw_ext_info(NDI_UNIQUE, 0, disarmer,
475  "In fact, you set it off!");
476  spring_trap(trap, disarmer);
477  }
478  return 0;
479  }
480 }
object_was_destroyed
#define object_was_destroyed(op, old_tag)
Definition: object.h:70
Face
Definition: face.h:14
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:533
trap_see
int trap_see(object *op, object *trap)
Definition: rune.cpp:385
trap_disarm
int trap_disarm(object *disarmer, object *trap, int risk, object *skill)
Definition: rune.cpp:442
mapstruct::difficulty
uint16_t difficulty
Definition: map.h:331
object::path_attuned
uint32_t path_attuned
Definition: object.h:353
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
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
spring_trap
void spring_trap(object *trap, object *victim)
Definition: rune.cpp:214
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
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
Ice.tmp
int tmp
Definition: Ice.py:207
dispel_rune
int dispel_rune(object *op, object *skill, int dir)
Definition: rune.cpp:304
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:1565
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2857
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:407
MAX
#define MAX(x, y)
Definition: compat.h:24
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:184
MSG_TYPE_SPELL_SUCCESS
#define MSG_TYPE_SPELL_SUCCESS
Definition: newclient.h:638
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:1560
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:4037
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.cpp:305
archetype::clone
object clone
Definition: object.h:487
trap_show
int trap_show(object *trap, object *where)
Definition: rune.cpp:412
sword_of_souls.victim
victim
Definition: sword_of_souls.py:12
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
CFweardisguise.tag
tag
Definition: CFweardisguise.py:25
MSG_TYPE_APPLY_TRAP
#define MSG_TYPE_APPLY_TRAP
Definition: newclient.h:609
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:4574
sproto.h
MSG_TYPE_SPELL
#define MSG_TYPE_SPELL
Definition: newclient.h:414
SP_level_spellpoint_cost
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.cpp:236
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.cpp:42
object::animation
const Animations * animation
Definition: object.h:428
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:2100
object::other_arch
struct archetype * other_arch
Definition: object.h:425
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:247
env
static std::shared_ptr< inja::Environment > env
Definition: mapper.cpp:2168
MAX_BUF
#define MAX_BUF
Definition: define.h:35
object_new
object * object_new(void)
Definition: object.cpp:1273
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
use_trigger
void use_trigger(object *op)
Definition: button.cpp:254
try_find_animation
Animations * try_find_animation(const char *name)
Definition: assets.cpp:278
MSG_TYPE_COMMAND_FAILURE
#define MSG_TYPE_COMMAND_FAILURE
Definition: newclient.h:534
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:265
spells.h
object::name
sstring name
Definition: object.h:319
MSG_TYPE_SPELL_FAILURE
#define MSG_TYPE_SPELL_FAILURE
Definition: newclient.h:636
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:300
DISEASE
@ DISEASE
Definition: object.h:249
mapstruct
Definition: map.h:313
living::Cha
int8_t Cha
Definition: living.h:36
give.op
op
Definition: give.py:33
Animations
Definition: face.h:25
object::skill
sstring skill
Definition: object.h:329
MSG_TYPE_SPELL_ERROR
#define MSG_TYPE_SPELL_ERROR
Definition: newclient.h:639
object_set_msg
void object_set_msg(object *op, const char *msg)
Definition: object.cpp:4811
object::msg
sstring msg
Definition: object.h:330
rv_vector
Definition: map.h:368
hit_player
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
Definition: attack.cpp:1903
diamondslots.y
y
Definition: diamondslots.py:16
python_pickup.where
where
Definition: python_pickup.py:7
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:2492
FLAG_IS_USED_UP
#define FLAG_IS_USED_UP
Definition: define.h:260
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.cpp:308
object_remove
void object_remove(object *op)
Definition: object.cpp:1833
try_find_face
const Face * try_find_face(const char *name, const Face *error)
Definition: assets.cpp:286
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:411
object::temp_animation
const Animations * temp_animation
Definition: object.h:431
rv_vector::distance
unsigned int distance
Definition: map.h:369
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