Atrinik Server 2.5
server/rune.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00037 static void rune_attack(object *op, object *victim)
00038 {
00039     int dam = op->stats.dam;
00040 
00041     op->stats.dam = (sint16) ((float) dam * (LEVEL_DAMAGE(op->level) * 0.925f));
00042 
00043     if (victim)
00044     {
00045         tag_t tag = victim->count;
00046         hit_player(victim, op->stats.dam, op, AT_INTERNAL);
00047 
00048         if (was_destroyed(victim, tag))
00049         {
00050             op->stats.dam = dam;
00051             return;
00052         }
00053 
00054         /* If there's a disease in the needle, put it in the player */
00055         if (op->randomitems != NULL)
00056         {
00057             create_treasure(op->randomitems, op, 0, op->level ? op->level : victim->map->difficulty, T_STYLE_UNSET, ART_CHANCE_UNSET, 0, NULL);
00058         }
00059 
00060         if (op->inv && op->inv->type == DISEASE)
00061         {
00062             object *disease = op->inv;
00063 
00064             infect_object(victim, disease, 1);
00065             remove_ob(disease);
00066             check_walk_off(disease, NULL, MOVE_APPLY_VANISHED);
00067         }
00068     }
00069     else
00070     {
00071         hit_map(op, 0, 0);
00072     }
00073 
00074     op->stats.dam = dam;
00075 }
00076 
00083 void spring_trap(object *trap, object *victim)
00084 {
00085     object *env;
00086 
00087     /* Prevent recursion. */
00088     if (trap->stats.hp == 0)
00089     {
00090         return;
00091     }
00092 
00093     /* Only living objects can trigger runes that don't cast spells, as
00094      * doing direct damage to a non-living object doesn't work anyway.
00095      * Typical example is an arrow attacking a door. */
00096     if (!IS_LIVE(victim) && trap->stats.sp == -1)
00097     {
00098         return;
00099     }
00100 
00101     if (victim && victim->type == PLAYER && trap->msg)
00102     {
00103         new_draw_info(0, COLOR_WHITE, victim, trap->msg);
00104     }
00105 
00106     env = get_env_recursive(trap);
00107     trap_show(trap, env);
00108 
00109     if (victim->type == PLAYER)
00110     {
00111         CONTR(victim)->stat_traps_sprung++;
00112     }
00113 
00114     /* No spell, simple attack. */
00115     if (trap->stats.sp == -1)
00116     {
00117         tag_t trap_tag = trap->count;
00118 
00119         rune_attack(trap, victim);
00120 
00121         if (was_destroyed(trap, trap_tag))
00122         {
00123             return;
00124         }
00125     }
00126     else
00127     {
00128         object *old_env = trap->env;
00129 
00130         /* This is necessary if the trap is inside something else, otherwise
00131          * the rune couldn't cast its spell. */
00132         if (!trap->map)
00133         {
00134             remove_ob(trap);
00135             trap->x = env->x;
00136             trap->y = env->y;
00137 
00138             if (!insert_ob_in_map(trap, victim->map, trap, 0))
00139             {
00140                 return;
00141             }
00142         }
00143 
00144         cast_spell(trap, trap, trap->stats.maxsp, trap->stats.sp, 1, CAST_NORMAL, NULL);
00145 
00146         /* Add the trap back to the object it was in, unless it was on
00147          * map to begin with. */
00148         if (old_env)
00149         {
00150             remove_ob(trap);
00151             check_walk_off(trap, NULL, MOVE_APPLY_VANISHED);
00152             insert_ob_in_ob(trap, old_env);
00153         }
00154     }
00155 
00156     /* Decrement detonation count and see if it's the last one, but only
00157      * if the count is not -1 already (infinite). */
00158     if (trap->stats.hp != -1 && --trap->stats.hp == 0)
00159     {
00160         /* Make the trap impotent */
00161         trap->type = MISC_OBJECT;
00162         CLEAR_FLAG(trap, FLAG_FLY_ON);
00163         CLEAR_FLAG(trap, FLAG_WALK_ON);
00164         FREE_AND_CLEAR_HASH2(trap->msg);
00165         /* Make it stick around until its spells are gone */
00166         trap->stats.food = 20;
00167         SET_FLAG(trap, FLAG_IS_USED_UP);
00168         trap->speed = trap->speed_left = 1.0f;
00169         update_ob_speed(trap);
00170         /* Clear trapped flag. */
00171         set_trapped_flag(env);
00172         return;
00173     }
00174 }
00175 
00183 int trap_see(object *op, object *trap, int level)
00184 {
00185     int chance = rndm(0, 99);
00186 
00187     /* Decide if we can see the rune or not */
00188     if ((trap->level <= level && rndm_chance(10)) || trap->stats.Cha == 1 || (chance > MIN(95, MAX(5, ((int) ((float) (op->map->difficulty + trap->level + trap->stats.Cha - op->level) / 10.0 * 50.0))))))
00189     {
00190         new_draw_info_format(0, COLOR_WHITE, op, "You spot a %s (lvl %d)!", trap->name, trap->level);
00191 
00192         if (trap->stats.Cha != 1)
00193         {
00194             CONTR(op)->stat_traps_found++;
00195         }
00196 
00197         return 1;
00198     }
00199 
00200     return 0;
00201 }
00202 
00208 int trap_show(object *trap, object *where)
00209 {
00210     object *env;
00211 
00212     if (where == NULL)
00213     {
00214         return 0;
00215     }
00216 
00217     env = trap->env;
00218     /* We must remove and reinsert it so the layer is updated correctly. */
00219     remove_ob(trap);
00220     CLEAR_FLAG(trap, FLAG_SYS_OBJECT);
00221     CLEAR_MULTI_FLAG(trap, FLAG_IS_INVISIBLE);
00222     trap->layer = LAYER_EFFECT;
00223 
00224     /* The trap is not hidden anymore. */
00225     if (trap->stats.Cha > 1)
00226     {
00227         trap->stats.Cha = 1;
00228     }
00229 
00230     if (env && env->type != PLAYER && env->type != MONSTER && env->type != DOOR && !QUERY_FLAG(env, FLAG_NO_PASS))
00231     {
00232         insert_ob_in_ob(trap, env);
00233         set_trapped_flag(env);
00234     }
00235     else
00236     {
00237         insert_ob_in_map(trap, where->map, NULL, 0);
00238     }
00239 
00240     return 1;
00241 }
00242 
00248 int trap_disarm(object *disarmer, object *trap)
00249 {
00250     object *env = trap->env;
00251     int disarmer_level = CONTR(disarmer)->exp_ptr[EXP_AGILITY]->level;
00252 
00253     if ((trap->level <= disarmer_level && rndm_chance(10)) || !(rndm(0, (MAX(2, MIN(20, trap->level - disarmer_level + 5 - disarmer->stats.Dex / 2)) - 1))))
00254     {
00255         new_draw_info_format(0, COLOR_WHITE, disarmer, "You successfully remove the %s (lvl %d)!", trap->name, trap->level);
00256         remove_ob(trap);
00257         check_walk_off(trap, NULL, MOVE_APPLY_VANISHED);
00258         set_trapped_flag(env);
00259         CONTR(disarmer)->stat_traps_disarmed++;
00260         return 1;
00261     }
00262     else
00263     {
00264         new_draw_info_format(0, COLOR_WHITE, disarmer, "You fail to remove the %s (lvl %d).", trap->name, trap->level);
00265 
00266         if (trap->level > disarmer_level * 1.4f || rndm(0, 2))
00267         {
00268             if (!(rndm(0, (MAX(2, disarmer_level - trap->level + disarmer->stats.Dex / 2 - 6)) - 1)))
00269             {
00270                 new_draw_info(0, COLOR_WHITE, disarmer, "In fact, you set it off!");
00271                 spring_trap(trap, disarmer);
00272             }
00273         }
00274 
00275         return 0;
00276     }
00277 }
00278 
00284 void trap_adjust(object *trap, int difficulty)
00285 {
00286     int off, level, hide;
00287 
00288     if (difficulty < 1)
00289     {
00290         difficulty = 1;
00291     }
00292 
00293     off = (int) ((float) difficulty * 0.2f);
00294     level = rndm(difficulty - off, difficulty + off);
00295     level = MAX(1, MIN(level, MAXLEVEL));
00296     hide = rndm(0, 19) + rndm(difficulty - off, difficulty + off);
00297     hide = MAX(1, MIN(hide, SINT8_MAX));
00298 
00299     trap->level = level;
00300     trap->stats.Cha = hide;
00301 }