ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/rune.C
Revision: 1.28
Committed: Sun Jul 1 05:00:20 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.27: +10 -11 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

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