ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/rune.c
Revision: 1.4
Committed: Sun Aug 13 17:16:04 2006 UTC (17 years, 9 months ago) by elmex
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +0 -0 lines
State: FILE REMOVED
Log Message:
Made server compile with C++.
Removed cfanim plugin and crossedit.
C++ here we come.

File Contents

# User Rev Content
1 root 1.1 /*
2     * static char *rcsid_rune_c =
3 root 1.2 * "$Id$";
4 root 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     dir=1;
62     }
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     new_draw_info(NDI_UNIQUE, 0,op,"Can't make a rune there!");
70     return 0;
71     }
72     for(tmp=get_map_ob(m,nx,ny);tmp!=NULL;tmp=tmp->above)
73     if(tmp->type==RUNE) break;
74    
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     rune_spell = arch_to_object(spell->other_arch);
82     } else {
83     /* 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     if (SP_level_spellpoint_cost(caster, rune_spell, SPELL_MANA) > op->stats.sp) {
125     new_draw_info(NDI_UNIQUE, 0,op,"You don't have enough mana.");
126     return 0;
127     }
128     if (SP_level_spellpoint_cost(caster, rune_spell, SPELL_GRACE) > op->stats.grace) {
129     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     }
135     /* already proper rune. Note this should only be the case if other_arch was set */
136     if (rune_spell->type == RUNE) {
137     rune = rune_spell;
138     } else {
139     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     }
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     op->invisible=0;
182     op->speed_left-=1;
183     }
184     else
185     op->invisible=1;
186     if(op->invisible!=det)
187     update_object(op,UP_OBJ_FACE);
188     }
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     hit_player(victim,op->stats.dam,op,op->attacktype,1);
201     if (was_destroyed (victim, tag))
202     return;
203     /* 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     }
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     return;
230    
231     if (QUERY_FLAG(trap,FLAG_IS_LINKED))
232     use_trigger(trap);
233    
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     return;
240    
241     trap->stats.hp--; /*decrement detcount */
242    
243     if(victim && victim->type==PLAYER)
244     new_draw_info(NDI_UNIQUE, 0,victim,trap->msg);
245    
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     (trap->other_arch && trap->other_arch->clone.type == SPELL)) {
263 root 1.2
264 root 1.1 if (was_destroyed (trap, trap_tag))
265     return;
266    
267 root 1.3 // 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 root 1.2
273 root 1.1 for(i = 0; i < MAX(1, trap->stats.maxhp); i++) {
274     if (trap->inv)
275 root 1.3 cast_spell(env,trap,trap->direction,trap->inv,NULL);
276 root 1.1 else {
277 root 1.2 object *spell = arch_to_object(trap->other_arch);
278 root 1.3 cast_spell(env,trap,trap->direction,spell,NULL);
279 root 1.1 free_object(spell);
280     }
281     }
282     } else {
283     rune_attack(trap,victim);
284     if (was_destroyed (trap, trap_tag))
285     return;
286     }
287    
288     if (trap->stats.hp <= 0) {
289     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     }
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     new_draw_info(NDI_UNIQUE, 0,op,"There's nothing there!");
318     return 0;
319     }
320    
321     for(tmp=get_map_ob(m,x, y); tmp!=NULL; tmp=tmp->above) {
322     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    
335     /* 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     }
347    
348     /* no rune there. */
349     if(tmp==NULL) {
350     new_draw_info(NDI_UNIQUE, 0,op,"There's nothing there!");
351     return 0;
352     }
353     trap_disarm(op,tmp,0, skill);
354     return 1;
355    
356     }
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     + 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     }
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     sqr(MAX(trap->stats.dam,trap->inv?trap->inv->level:1)) /
392     skill->level;
393    
394     if(!(random_roll(0, (MAX(2, MIN(20,trap->level-skill->level
395     +5 - disarmer->stats.Dex/2))-1), disarmer, PREFER_LOW)))
396     {
397     new_draw_info_format(NDI_UNIQUE, 0,disarmer,
398     "You successfully disarm the %s!",trap->name);
399     destroy_object(trap);
400     /* 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     }
407     else
408     {
409     new_draw_info_format(NDI_UNIQUE, 0,disarmer,
410     "You fail to disarm the %s.",trap->name);
411     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     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     trap->level = 1;
437    
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     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    
451     /* 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    
458     /* so we get an appropriate amnt of exp for AT_DEATH traps */
459     if(trap->attacktype & AT_DEATH) trap->stats.dam = 127;
460     }
461    
462     }
463