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

# Content
1 /*
2 * static char *rcsid_rune_c =
3 * "$Id$";
4 */
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
264 if (was_destroyed (trap, trap_tag))
265 return;
266
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 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 } 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