ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/rune.C
Revision: 1.11
Committed: Wed Dec 13 03:28:42 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.10: +1 -1 lines
Log Message:
further cleanups and oofication

File Contents

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