ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/rune.C
Revision: 1.22
Committed: Wed Mar 14 04:12:29 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.21: +1 -4 lines
Log Message:
- rewrote more face handling code
- automatically send smooth faces, as the client will need them anyways
  and it makes little sense to wait for the client to axk for it. of course,
  gcfclient suffers from weird ordering problems again.
- UP_OBJ_FACE was often abused in situations where other things changed,
  updated lots of spaces, probably more to be done.
- update_smooth became so small that inlining it actually clarified
  the code. similar for update_space, which is not inlined for other reasons.
- faces were not initialised properly
- add versioncheck for face data
- rewrite invisibility handling a bit: god finger etc. now makes you blink,
  blinking routine has changed to be less annoying and more useful while
  still indicating invisibleness.

File Contents

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