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

# User Rev Content
1 elmex 1.1 /*
2 root 1.25 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 pippijn 1.17 *
4 root 1.25 * 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 pippijn 1.17 *
8 root 1.28 * 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 pippijn 1.17 *
13 root 1.28 * 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 pippijn 1.17 *
18 root 1.28 * 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 root 1.25 *
21     * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 pippijn 1.17 */
23 elmex 1.1
24     #include <global.h>
25 root 1.14 #include <sproto.h>
26 elmex 1.1 #include <spells.h>
27    
28     #ifndef sqr
29 root 1.4 # define sqr(x) ((x)*(x))
30 elmex 1.1 #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 root 1.4 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 root 1.7 maptile *m;
51 root 1.4 sint16 nx, ny;
52    
53     if (!dir)
54 root 1.10 dir = 1;
55 root 1.4
56     nx = op->x + freearr_x[dir];
57     ny = op->y + freearr_y[dir];
58     m = op->map;
59    
60 root 1.20 if (get_map_flags (m, &m, nx, ny, &nx, &ny) & (P_OUT_OF_MAP | P_SAFE | P_NO_MAGIC | P_NO_CLERIC))
61 root 1.4 {
62     new_draw_info (NDI_UNIQUE, 0, op, "Can't make a rune there!");
63     return 0;
64 elmex 1.1 }
65 root 1.10
66 root 1.19 for (tmp = m->at (nx, ny).bot; tmp; tmp = tmp->above)
67 root 1.4 if (tmp->type == RUNE)
68     break;
69    
70     if (tmp)
71     {
72 root 1.20 new_draw_info (NDI_UNIQUE, 0, op, "You can't write a rune on top of another rune.");
73 elmex 1.1 return 0;
74     }
75    
76 root 1.4 if (spell->other_arch)
77 root 1.10 rune_spell = arch_to_object (spell->other_arch);
78 root 1.4 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 root 1.20 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 root 1.10
104 root 1.4 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 root 1.10
110 root 1.4 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 root 1.10
116 root 1.20 if (caster->path_denied & spell->path_attuned && !caster->flag [FLAG_WIZCAST])
117 root 1.4 {
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 root 1.10
122 root 1.20 if (caster_level (caster, rune_spell) < rune_spell->level && !caster->flag [FLAG_WIZCAST])
123 root 1.4 {
124     new_draw_info_format (NDI_UNIQUE, 0, op, "%s is beyond your ability to cast!", &rune_spell->name);
125     return 0;
126     }
127 root 1.10
128 root 1.4 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 root 1.10
134 root 1.4 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 root 1.10
140 root 1.4 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 root 1.10
144 root 1.4 /* already proper rune. Note this should only be the case if other_arch was set */
145     if (rune_spell->type == RUNE)
146 root 1.15 rune = rune_spell;
147 root 1.4 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 root 1.15 rune->insert (rune_spell->clone ());
153 root 1.10
154 root 1.4 if (spell->face != blank_face)
155     rune->face = spell->face;
156     }
157 root 1.10
158 root 1.4 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 root 1.11 rune->set_owner (op); /* runes without need no owner */
162 root 1.4 set_spell_skill (op, caster, spell, rune);
163 root 1.15
164     m->insert (rune, nx, ny, op);
165 root 1.4 return 1;
166 elmex 1.1 }
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 root 1.4 void
181     move_rune (object *op)
182     {
183 root 1.18 /* runes of level zero cannot detonate. */
184     if (!op->level)
185     return;
186    
187     int det = op->invisible;
188 root 1.4
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 root 1.18
197 root 1.4 if (op->invisible != det)
198 root 1.22 update_object (op, UP_OBJ_CHANGE);
199 elmex 1.1 }
200    
201     /* peterm: rune_attack
202     * function handles those runes which detonate but do not cast spells.
203     */
204 root 1.4 void
205     rune_attack (object *op, object *victim)
206 elmex 1.1 {
207 root 1.4 if (victim)
208     {
209 root 1.6 hit_player (victim, op->stats.dam, op, op->attacktype, 1);
210 root 1.4
211 root 1.6 if (victim->destroyed ())
212 root 1.4 return;
213 root 1.6
214 root 1.4 /* if there's a disease in the needle, put it in the player */
215 root 1.12 if (op->has_random_items ())
216 root 1.4 create_treasure (op->randomitems, op, 0, (victim->map ? victim->map->difficulty : 1), 0);
217 root 1.6
218 root 1.4 if (op->inv && op->inv->type == DISEASE)
219     {
220     object *disease = op->inv;
221    
222     infect_object (victim, disease, 1);
223 root 1.9 disease->destroy ();
224 root 1.4 }
225 elmex 1.1 }
226 root 1.4 else
227     hit_map (op, 0, op->attacktype, 1);
228 elmex 1.1 }
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 root 1.4 void
235     spring_trap (object *trap, object *victim)
236 elmex 1.1 {
237 root 1.4 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 elmex 1.23 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 root 1.4 /* Only if it is a spell do we proceed here */
280 root 1.27 if ((trap->inv && trap->inv->type == SPELL) || (trap->other_arch && trap->other_arch->type == SPELL))
281 root 1.4 {
282 elmex 1.1
283 root 1.6 if (trap->destroyed ())
284 root 1.2 return;
285 elmex 1.1
286 root 1.4 // 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 elmex 1.1
292 root 1.4 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 elmex 1.1
300 root 1.4 cast_spell (env, trap, trap->direction, spell, NULL);
301 root 1.9 spell->destroy ();
302 root 1.2 }
303     }
304 root 1.4 }
305     else
306     {
307     rune_attack (trap, victim);
308 root 1.6 if (trap->destroyed ())
309 root 1.4 return;
310 elmex 1.1 }
311    
312 root 1.4 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 elmex 1.1 }
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 root 1.4 int
326     dispel_rune (object *op, object *caster, object *spell, object *skill, int dir)
327 elmex 1.1 {
328 root 1.4 object *tmp, *tmp2;
329     int searchflag = 1, mflags;
330     sint16 x, y;
331 root 1.7 maptile *m;
332 root 1.4
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 root 1.13 for (tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above)
349 root 1.4 {
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 root 1.26 if (tmp->type == SIGN && !strcmp (tmp->arch->archname, "rune_mark"))
358 root 1.4 {
359 root 1.9 tmp->destroy ();
360 root 1.4 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 root 1.2 }
375     }
376 root 1.4 if (!searchflag)
377     break;
378 elmex 1.1 }
379 root 1.4
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 elmex 1.1 }
390    
391 root 1.4 int
392     trap_see (object *op, object *trap)
393     {
394     int chance;
395    
396     chance = random_roll (0, 99, op, PREFER_HIGH);;
397 elmex 1.1
398 root 1.4 /* 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 elmex 1.1 }
405 root 1.4 return 0;
406 elmex 1.1 }
407    
408 root 1.4 int
409     trap_show (object *trap, object *where)
410     {
411     if (where == NULL)
412     return 0;
413 root 1.15
414     object *tmp = get_archetype ("runedet");
415 root 1.21 tmp->face = GET_ANIMATION (trap, 0);
416 root 1.15 tmp->insert_at (where, 0);
417    
418 root 1.4 return 1;
419 elmex 1.1 }
420    
421 root 1.4 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 root 1.9 trap->destroy (1);
435    
436 root 1.4 /* 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 elmex 1.1 }
452 root 1.4 return 0;
453     }
454 elmex 1.1 }
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 root 1.4 void
463     trap_adjust (object *trap, int difficulty)
464     {
465     int i;
466 elmex 1.1
467 root 1.4 /* 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 root 1.2 }
497 elmex 1.1
498 root 1.4 /* so we get an appropriate amnt of exp for AT_DEATH traps */
499     if (trap->attacktype & AT_DEATH)
500     trap->stats.dam = 127;
501 elmex 1.1 }
502    
503     }