ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/rune.C
Revision: 1.2
Committed: Tue Aug 29 08:01:38 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +164 -164 lines
Log Message:
expand initial tabs to spaces

File Contents

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