ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/rune.C
Revision: 1.56
Committed: Wed Nov 16 23:42:03 2016 UTC (7 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.55: +1 -1 lines
Log Message:
copyright update 2016

File Contents

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