Atrinik Server 2.5
server/skill_util.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 /* define the following for skills utility debugging */
00031 /* #define SKILL_UTIL_DEBUG */
00032 
00033 #include <global.h>
00034 
00036 static object *exp_cat[MAX_EXP_CAT];
00038 static int nrofexpcat = 0;
00039 
00041 float stat_exp_mult[MAX_STAT + 1] =
00042 {
00043     0.0f,   0.01f,  0.1f,   0.3f,   0.5f,
00044     0.6f,   0.7f,   0.8f,   0.85f,  0.9f,
00045     0.95f,  0.96f,  0.97f,  0.98f,  0.99f,
00046     1.0f,   1.01f,  1.02f,  1.03f,  1.04f,
00047     1.05f,  1.07f,  1.09f,  1.12f,  1.15f,
00048     1.2f,   1.3f,   1.4f,   1.5f,   1.7f,
00049     2.0f
00050 };
00051 
00054 static float lev_exp[MAXLEVEL + 1] =
00055 {
00056     0.0f,     1.0f,     1.11f,    1.75f,    3.2f,
00057     5.5f,     10.0f,    20.0f,    35.25f,   66.1f,
00058     137.0f,   231.58f,  240.00f,  247.62f,  254.55f,
00059     260.87f,  266.67f,  272.00f,  276.92f,  281.48f,
00060     285.71f,  289.66f,  293.33f,  296.77f,  300.00f,
00061     303.03f,  305.88f,  308.57f,  311.11f,  313.51f,
00062     315.79f,  317.95f,  320.00f,  321.95f,  323.81f,
00063     325.58f,  327.27f,  328.89f,  330.43f,  331.91f,
00064     333.33f,  334.69f,  336.00f,  337.25f,  338.46f,
00065     339.62f,  340.74f,  341.82f,  342.86f,  343.86f,
00066     344.83f,  345.76f,  346.67f,  347.54f,  348.39f,
00067     349.21f,  350.00f,  350.77f,  351.52f,  352.24f,
00068     352.94f,  353.62f,  354.29f,  354.93f,  355.56f,
00069     356.16f,  356.76f,  357.33f,  357.89f,  358.44f,
00070     358.97f,  359.49f,  360.00f,  360.49f,  360.98f,
00071     361.45f,  361.90f,  362.35f,  362.79f,  365.22f,
00072     367.64f,  369.04f,  373.44f,  378.84f,  384.22f,
00073     389.59f,  395.96f,  402.32f,  410.67f,  419.01f,
00074     429.35f,  440.68f,  452.00f,  465.32f,  479.63f,
00075     494.93f,  510.23f,  527.52f,  545.81f,  562.09f,
00076     580.37f,  599.64f,  619.91f,  640.17f,  662.43f,
00077     685.68f,  709.93f,  773.17f,  852.41f,  932.65f,
00078     1013.88f, 1104.11f, 1213.35f, 1324.60f, 1431.86f,
00079     1542.13f
00080 };
00081 
00083 typedef struct _skill_name_table
00084 {
00086     char *name;
00088     int id;
00089 }_skill_name_table;
00090 
00092 static _skill_name_table skill_name_table[] =
00093 {
00094     {"agility", CS_STAT_SKILLEXP_AGILITY},
00095     {"personality", CS_STAT_SKILLEXP_PERSONAL},
00096     {"mental", CS_STAT_SKILLEXP_MENTAL},
00097     {"physique", CS_STAT_SKILLEXP_PHYSIQUE},
00098     {"magic", CS_STAT_SKILLEXP_MAGIC},
00099     {"wisdom", CS_STAT_SKILLEXP_WISDOM},
00100     {"", -1}
00101 };
00102 
00103 static int item_skill_cs_stat[] =
00104 {
00105     0,
00106     CS_STAT_SKILLEXP_AGILITY,
00107     CS_STAT_SKILLEXP_PERSONAL,
00108     CS_STAT_SKILLEXP_MENTAL,
00109     CS_STAT_SKILLEXP_PHYSIQUE,
00110     CS_STAT_SKILLEXP_MAGIC,
00111     CS_STAT_SKILLEXP_WISDOM
00112 };
00113 
00114 static int change_skill_to_skill(object *who, object *skl);
00115 static int attack_melee_weapon(object *op, int dir);
00116 static int attack_hth(object *pl, int dir, char *string);
00117 static int do_skill_attack(object *tmp, object *op, char *string);
00118 
00124 static void find_skill_exp_name(object *pl, object *exp, int idx)
00125 {
00126     int s;
00127 
00128     for (s = 0; skill_name_table[s].id != -1; s++)
00129     {
00130         if (!strcmp(skill_name_table[s].name, exp->name))
00131         {
00132             CONTR(pl)->last_skill_ob[idx] = exp;
00133             CONTR(pl)->last_skill_id[idx] = skill_name_table[s].id;
00134             CONTR(pl)->last_skill_index++;
00135             return;
00136         }
00137     }
00138 }
00139 
00146 int find_skill_exp_level(object *pl, int item_skill)
00147 {
00148     int s;
00149 
00150     for (s = 0; s < CONTR(pl)->last_skill_index; s++)
00151     {
00152         if (CONTR(pl)->last_skill_id[s] == item_skill_cs_stat[item_skill])
00153         {
00154             return CONTR(pl)->last_skill_ob[s]->level;
00155         }
00156     }
00157 
00158     return 0;
00159 }
00160 
00165 char *find_skill_exp_skillname(int item_skill)
00166 {
00167     if (!item_skill || item_skill > 6)
00168     {
00169         return skill_name_table[6].name;
00170     }
00171 
00172     return skill_name_table[item_skill - 1].name;
00173 }
00174 
00185 sint64 do_skill(object *op, int dir, const char *params)
00186 {
00187     sint64 success = 0;
00188     int skill = op->chosen_skill->stats.sp;
00189 
00190     /* Trigger the map-wide skill event. */
00191     if (op->map && op->map->events)
00192     {
00193         int retval = trigger_map_event(MEVENT_SKILL_USED, op->map, op, NULL, NULL, NULL, dir);
00194 
00195         /* So the plugin's return value can affect the returned value. */
00196         if (retval)
00197         {
00198             return retval - 1;
00199         }
00200     }
00201 
00202     switch (skill)
00203     {
00204         case SK_KARATE:
00205             attack_hth(op, dir, "karate-chopped");
00206             break;
00207 
00208         case SK_BOXING:
00209             attack_hth(op, dir, "punched");
00210             break;
00211 
00212         case SK_MELEE_WEAPON:
00213         case SK_SLASH_WEAP:
00214         case SK_CLEAVE_WEAP:
00215         case SK_PIERCE_WEAP:
00216             attack_melee_weapon(op, dir);
00217             break;
00218 
00219         case SK_FIND_TRAPS:
00220             success = find_traps(op, CONTR(op)->exp_ptr[EXP_AGILITY]->level);
00221             break;
00222 
00223         case SK_REMOVE_TRAP:
00224             success = remove_trap(op);
00225             break;
00226 
00227         case SK_THROWING:
00228             new_draw_info(0, COLOR_WHITE, op, "This skill is not usable in this way.");
00229             break;
00230 
00231         case SK_USE_MAGIC_ITEM:
00232         case SK_MISSILE_WEAPON:
00233             new_draw_info(0, COLOR_WHITE, op, "There is no special attack for this skill.");
00234             return success;
00235             break;
00236 
00237         case SK_PRAYING:
00238             new_draw_info(0, COLOR_WHITE, op, "This skill is not usable in this way.");
00239             return success;
00240             break;
00241 
00242         case SK_SPELL_CASTING:
00243         case SK_BARGAINING:
00244             new_draw_info(0, COLOR_WHITE, op, "This skill is already in effect.");
00245             return success;
00246             break;
00247 
00248         case SK_CONSTRUCTION:
00249             construction_do(op, dir);
00250             return success;
00251 
00252         case SK_INSCRIPTION:
00253             success = skill_inscription(op, params);
00254             break;
00255 
00256         default:
00257             LOG(llevDebug, "%s attempted to use unknown skill: %d\n", query_name(op, NULL), op->chosen_skill->stats.sp);
00258             return success;
00259             break;
00260     }
00261 
00262     /* For players we now update the speed_left from using the skill.
00263      * Monsters have no skill use time because of the random nature in
00264      * which use_monster_skill is called already simulates this. -b.t. */
00265     if (op->type == PLAYER)
00266     {
00267         op->speed_left -= get_skill_time(op,skill);
00268     }
00269 
00270     /* This is a good place to add experience for successfull use of skills.
00271      * Note that add_exp() will figure out player/monster experience
00272      * gain problems. */
00273     if (success && skills[skill].category != EXP_NONE)
00274     {
00275         add_exp(op, success, op->chosen_skill->stats.sp, 0);
00276     }
00277 
00278     return success;
00279 }
00280 
00289 sint64 calc_skill_exp(object *who, object *op, int level)
00290 {
00291     int who_lvl = level;
00292     sint64 op_exp = 0;
00293     int op_lvl = 0;
00294     float exp_mul, max_mul, tmp;
00295 
00296     /* No exp for non players. */
00297     if (!who || who->type != PLAYER)
00298     {
00299         LOG(llevDebug, "calc_skill_exp() called with who != PLAYER or NULL (%s (%s)- %s)\n", query_name(who, NULL), !who ? "NULL" : "", query_name(op, NULL));
00300         return 0;
00301     }
00302 
00303     if (level == -1)
00304     {
00305         /* The related skill level */
00306         who_lvl = SK_level(who);
00307     }
00308 
00309     if (!op)
00310     {
00311         LOG(llevBug, "calc_skill_exp() called with op == NULL (%s - %s)\n", query_name(who, NULL), query_name(op, NULL));
00312         op_lvl = who->map->difficulty < 1 ? 1: who->map->difficulty;
00313         op_exp = 0;
00314     }
00315     /* All other items/living creatures */
00316     else
00317     {
00318         op_exp = op->stats.exp;
00319         op_lvl = op->level;
00320     }
00321 
00322     /* No exp for no level and no exp ;) */
00323     if (op_lvl < 1 || op_exp < 1)
00324     {
00325         return 0;
00326     }
00327 
00328     if (who_lvl < 2)
00329     {
00330         max_mul = 0.85f;
00331     }
00332 
00333     if (who_lvl < 3)
00334     {
00335         max_mul = 0.7f;
00336     }
00337     else if (who_lvl < 4)
00338     {
00339         max_mul = 0.6f;
00340     }
00341     else if (who_lvl < 5)
00342     {
00343         max_mul = 0.45f;
00344     }
00345     else if (who_lvl < 7)
00346     {
00347         max_mul = 0.35f;
00348     }
00349     else if (who_lvl < 8)
00350     {
00351         max_mul = 0.3f;
00352     }
00353     else
00354     {
00355         max_mul = 0.25f;
00356     }
00357 
00358     /* We first get a global level difference multiplicator */
00359     exp_mul = calc_level_difference(who_lvl, op_lvl);
00360     op_exp = (int) ((float) op_exp * lev_exp[op_lvl] * exp_mul);
00361     tmp = ((float) (new_levels[who_lvl + 1] - new_levels[who_lvl]) * 0.1f) * max_mul;
00362 
00363     if ((float) op_exp > tmp)
00364     {
00365         op_exp = (int) tmp;
00366     }
00367 
00368     return op_exp;
00369 }
00370 
00374 static void init_exp_obj()
00375 {
00376     archetype *at;
00377 
00378     for (at = first_archetype; at; at = at->next)
00379     {
00380         if (at->clone.type == EXPERIENCE)
00381         {
00382             exp_cat[nrofexpcat] = get_object();
00383             exp_cat[nrofexpcat]->level = 1;
00384             exp_cat[nrofexpcat]->stats.exp = 0;
00385             copy_object(&at->clone, exp_cat[nrofexpcat], 0);
00386             /* avoid gc on these objects */
00387             insert_ob_in_ob(exp_cat[nrofexpcat], &void_container);
00388             nrofexpcat++;
00389 
00390             if (nrofexpcat == MAX_EXP_CAT)
00391             {
00392                 LOG(llevSystem, "Aborting! Reached limit of available experience\n");
00393                 LOG(llevError, "categories. Need to increase value of MAX_EXP_CAT.\n");
00394                 exit(0);
00395             }
00396         }
00397     }
00398 
00399     if (!nrofexpcat)
00400     {
00401         LOG(llevError, "Aborting! No experience objects found in archetypes.\n");
00402         exit(0);
00403     }
00404 }
00405 
00408 void init_new_exp_system()
00409 {
00410     static int init_new_exp_done = 0;
00411     int i;
00412     FILE *fp;
00413     char filename[MAX_BUF];
00414 
00415     if (init_new_exp_done)
00416     {
00417         return;
00418     }
00419 
00420     init_new_exp_done = 1;
00421 
00422     /* Locate the experience objects and create list of them */
00423     init_exp_obj();
00424 
00425     for (i = 0; i < NROFSKILLS; i++)
00426     {
00427         /* Link the skill archetype ptr to skill list for fast access.
00428          * Now we can access the skill archetype by skill number or skill name. */
00429         if (!(skills[i].at = get_skill_archetype(i)))
00430         {
00431             LOG(llevError, "Aborting! Skill #%d (%s) not found in archlist!\n", i, skills[i].name);
00432         }
00433     }
00434 
00435     snprintf(filename, sizeof(filename), "%s/%s", settings.localdir, SRV_CLIENT_SKILLS_FILENAME);
00436     fp = fopen(filename, "w");
00437 
00438     if (!fp)
00439     {
00440         LOG(llevError, "Cannot open file '%s' for writing.\n", filename);
00441     }
00442 
00443     for (i = 0; i < NROFSKILLS; i++)
00444     {
00445         if (skills[i].description)
00446         {
00447             char icon[MAX_BUF], tmpresult[MAX_BUF];
00448 
00449             replace(skills[i].name, " ", "_", tmpresult, sizeof(tmpresult));
00450             snprintf(icon, sizeof(icon), "icon_%s.101", tmpresult);
00451 
00452             if (!find_face(icon, 0))
00453             {
00454                 LOG(llevError, "Skill '%s' needs face '%s', but it could not be found.\n", skills[i].name, icon);
00455             }
00456 
00457             fprintf(fp, "%s\n%d\n%s\n%s\nend\n", skills[i].name, skills[i].category - 1, icon, skills[i].description);
00458         }
00459     }
00460 
00461     fclose(fp);
00462 }
00463 
00466 void free_exp_objects()
00467 {
00468     int i;
00469 
00470     for (i = 0; i < MAX_EXP_CAT; i++)
00471     {
00472         if (!exp_cat[i])
00473         {
00474             continue;
00475         }
00476 
00477         SET_FLAG(exp_cat[i], FLAG_REMOVED);
00478         return_poolchunk(exp_cat[i], pool_object);
00479     }
00480 }
00481 
00484 void dump_skills()
00485 {
00486     int i;
00487 
00488     LOG(llevInfo, "exper_catgry \t str \t dex \t con \t wis \t cha \t int \t pow \n");
00489 
00490     for (i = 0; i < nrofexpcat; i++)
00491     {
00492         LOG(llevInfo, "%d-%s \t %d \t %d \t %d \t %d \t %d \t %d \t %d \n", i, exp_cat[i]->name, exp_cat[i]->stats.Str, exp_cat[i]->stats.Dex, exp_cat[i]->stats.Con, exp_cat[i]->stats.Wis, exp_cat[i]->stats.Cha, exp_cat[i]->stats.Int, exp_cat[i]->stats.Pow);
00493     }
00494 
00495     LOG(llevInfo, "\n");
00496     LOG(llevInfo, "%20s  %12s  %4s %4s %4s  %5s %5s %5s\n", "sk#       Skill name", "ExpCat", "Time", "Base", "xlvl", "Stat1", "Stat2", "Stat3");
00497     LOG(llevInfo, "%20s  %12s  %4s %4s %4s  %5s %5s %5s\n", "---       ----------", "------", "----", "----", "----", "-----", "-----", "-----");
00498 
00499     for (i = 0; i < NROFSKILLS; i++)
00500     {
00501         LOG(llevInfo, "%2d-%17s  %12s  %4d\n", i, skills[i].name, exp_cat[skills[i].category] != NULL ? exp_cat[skills[i].category]->name : "NONE", skills[i].time);
00502     }
00503 }
00504 
00510 int check_skill_known(object *op, int skillnr)
00511 {
00512     object *tmp;
00513 
00514     for (tmp = op->inv; tmp; tmp = tmp->below)
00515     {
00516         if (tmp->type == SKILL && tmp->stats.sp == skillnr)
00517         {
00518             return 1;
00519         }
00520     }
00521 
00522     return 0;
00523 }
00524 
00529 int lookup_skill_by_name(const char *string)
00530 {
00531     int skillnr = 0;
00532     size_t nmlen;
00533     char name[MAX_BUF];
00534 
00535     if (!string)
00536     {
00537         return -1;
00538     }
00539 
00540     strcpy(name, string);
00541     nmlen = strlen(name);
00542 
00543     for (skillnr = 0; skillnr < NROFSKILLS; skillnr++)
00544     {
00545         if (strlen(name) >= strlen(skills[skillnr].name))
00546         {
00547             if (!strncmp(name, skills[skillnr].name, MIN(strlen(skills[skillnr].name), nmlen)))
00548             {
00549                 return skillnr;
00550             }
00551         }
00552     }
00553 
00554     return -1;
00555 }
00556 
00561 int check_skill_to_fire(object *who)
00562 {
00563     int skillnr = 0;
00564     object *tmp;
00565     rangetype shoottype = range_none;
00566 
00567     if (who->type != PLAYER)
00568     {
00569         return 1;
00570     }
00571 
00572     switch ((shoottype = CONTR(who)->shoottype))
00573     {
00574         case range_bow:
00575             if (!(tmp = CONTR(who)->equipment[PLAYER_EQUIP_BOW]))
00576             {
00577                 return 0;
00578             }
00579 
00580             if (tmp->sub_type == 2)
00581             {
00582                 skillnr = SK_SLING_WEAP;
00583             }
00584             else if (tmp->sub_type == 1)
00585             {
00586                 skillnr = SK_XBOW_WEAP;
00587             }
00588             else
00589             {
00590                 skillnr = SK_MISSILE_WEAPON;
00591             }
00592 
00593             break;
00594 
00595         case range_none:
00596         case range_skill:
00597             return 1;
00598             break;
00599 
00600         case range_magic:
00601             if (spells[CONTR(who)->chosen_spell].type == SPELL_TYPE_PRIEST)
00602             {
00603                 skillnr = SK_PRAYING;
00604             }
00605             else
00606             {
00607                 skillnr = SK_SPELL_CASTING;
00608             }
00609 
00610             break;
00611 
00612         case range_scroll:
00613         case range_rod:
00614         case range_horn:
00615         case range_wand:
00616             skillnr = SK_USE_MAGIC_ITEM;
00617             break;
00618 
00619         default:
00620             LOG(llevBug, "bad call of check_skill_to_fire() from %s\n", query_name(who, NULL));
00621             return 0;
00622     }
00623 
00624     if (change_skill(who, skillnr))
00625     {
00626 #ifdef SKILL_UTIL_DEBUG
00627         LOG(llevDebug, "check_skill_to_fire(): got skill:%s for %s\n", skills[skillnr].name, who->name);
00628 #endif
00629         CONTR(who)->shoottype = shoottype;
00630         return 1;
00631     }
00632 
00633     return 0;
00634 }
00635 
00642 int check_skill_to_apply(object *who, object *item)
00643 {
00644     int skill = 0, tmp;
00645     /* perhaps we need a additional skill to use */
00646     int add_skill = NO_SKILL_READY;
00647 
00648     /* Only for players. */
00649     if (who->type != PLAYER)
00650     {
00651         return 1;
00652     }
00653 
00654     /* First figure out the required skills from the item */
00655     switch (item->type)
00656     {
00657         case WEAPON:
00658             tmp = item->sub_type;
00659 
00660             /* Polearm */
00661             if (tmp >= WEAP_POLE_IMPACT)
00662             {
00663                 /* Select the right weapon type. */
00664                 tmp = item->sub_type - WEAP_POLE_IMPACT;
00665                 add_skill = SK_POLEARMS;
00666             }
00667             /* Two handed  */
00668             else if (tmp >= WEAP_2H_IMPACT)
00669             {
00670                 /* Select the right weapon type. */
00671                 tmp = item->sub_type - WEAP_2H_IMPACT;
00672                 add_skill = SK_TWOHANDS;
00673             }
00674 
00675             if (tmp == WEAP_1H_IMPACT)
00676             {
00677                 skill = SK_MELEE_WEAPON;
00678             }
00679             else if (tmp == WEAP_1H_SLASH)
00680             {
00681                 skill = SK_SLASH_WEAP;
00682             }
00683             else if (tmp == WEAP_1H_CLEAVE)
00684             {
00685                 skill = SK_CLEAVE_WEAP;
00686             }
00687             else if (tmp == WEAP_1H_PIERCE)
00688             {
00689                 skill = SK_PIERCE_WEAP;
00690             }
00691 
00692             break;
00693 
00694         case BOW:
00695             skill = bow_get_skill(item);
00696             break;
00697 
00698         case POTION:
00699             skill = SK_USE_MAGIC_ITEM;
00700             break;
00701 
00702         case SCROLL:
00703             skill = SK_LITERACY;
00704             break;
00705 
00706         case ROD:
00707             skill = SK_USE_MAGIC_ITEM;
00708             break;
00709 
00710         case WAND:
00711             skill = SK_USE_MAGIC_ITEM;
00712             break;
00713 
00714         case HORN:
00715             skill = SK_USE_MAGIC_ITEM;
00716             break;
00717 
00718         default:
00719             LOG(llevDebug, "Warning: bad call of check_skill_to_apply()\n");
00720             LOG(llevDebug, "No skill exists for item: %s\n", query_name(item, NULL));
00721             return 0;
00722     }
00723 
00724     /* This should not happen */
00725     if (skill == NO_SKILL_READY)
00726     {
00727         LOG(llevBug, "check_skill_to_apply() called for %s and item %s with skill NO_SKILL_READY\n", query_name(who, NULL), query_name(item, NULL));
00728     }
00729 
00730     /* Check the additional skill if there is one */
00731     if (add_skill != NO_SKILL_READY)
00732     {
00733         if (!change_skill(who, add_skill))
00734         {
00735             return 0;
00736         }
00737 
00738         change_skill(who, NO_SKILL_READY);
00739     }
00740 
00741     /* If this skill is ready, all is fine. if not, ready it, if it can't
00742      * readied, we can't apply/use/do it. */
00743     if (!who->chosen_skill || (who->chosen_skill && who->chosen_skill->stats.sp != skill))
00744     {
00745         if (!change_skill(who, skill))
00746         {
00747             return 0;
00748         }
00749     }
00750 
00751     return 1;
00752 }
00753 
00759 int init_player_exp(object *pl)
00760 {
00761     int i, j, exp_index = 0;
00762     object *tmp, *exp_ob[MAX_EXP_CAT];
00763 
00764     if (pl->type != PLAYER)
00765     {
00766         LOG(llevBug, "init_player_exp(): called non-player %s.\n", query_name(pl, NULL));
00767         return 0;
00768     }
00769 
00770     CONTR(pl)->last_skill_index = 0;
00771 
00772     /* First-pass find all current exp objects */
00773     for (tmp = pl->inv; tmp; tmp = tmp->below)
00774     {
00775         if (tmp->type == EXPERIENCE)
00776         {
00777             exp_ob[exp_index] = tmp;
00778             CONTR(pl)->exp_ptr[tmp->sub_type] = tmp;
00779             find_skill_exp_name(pl, tmp, CONTR(pl)->last_skill_index);
00780             exp_index++;
00781         }
00782         else if (exp_index == MAX_EXP_CAT)
00783         {
00784             return 0;
00785         }
00786     }
00787 
00788     /* Second - if pl has wrong nrof experience objects
00789      * then give player a complete roster. */
00790 
00791     /* No exp objects - situation for a new player
00792      * or pre-skills/exp code player file */
00793     if (!exp_index)
00794     {
00795         for (j = 0; j < nrofexpcat; j++)
00796         {
00797             tmp = get_object();
00798             copy_object(exp_cat[j], tmp, 0);
00799             insert_ob_in_ob(tmp, pl);
00800             tmp->stats.exp = 0;
00801             exp_ob[j] = tmp;
00802             CONTR(pl)->exp_ptr[tmp->sub_type] = tmp;
00803 
00804             esrv_send_item(pl, tmp);
00805             exp_index++;
00806         }
00807     }
00808 
00809     /* Now we loop through one more time and set "apply" flag on valid
00810      * experience objects. Fix_player() requires this, and we get the
00811      * bonus of being able to ignore invalid experience objects in the
00812      * player inventory (player file from another game set up?). Also, we
00813      * reset the score (or "total" experience) of the player to be the
00814      * sum of all valid experience objects. */
00815     pl->stats.exp = 0;
00816 
00817     for (i = 0; i < exp_index; i++)
00818     {
00819         if (!QUERY_FLAG(exp_ob[i], FLAG_APPLIED))
00820         {
00821             SET_FLAG(exp_ob[i], FLAG_APPLIED);
00822         }
00823 
00824         if (pl->stats.exp < exp_ob[i]->stats.exp && !QUERY_FLAG(exp_ob[i], FLAG_STAND_STILL))
00825         {
00826             pl->stats.exp = exp_ob[i]->stats.exp;
00827             pl->level = exp_ob[i]->level;
00828         }
00829 
00830         player_lvl_adj(NULL, exp_ob[i]);
00831     }
00832 
00833     return 1;
00834 }
00835 
00840 void unlink_skill(object *skillop)
00841 {
00842     object *op = skillop ? skillop->env : NULL;
00843 
00844     if (!op || op->type != PLAYER)
00845     {
00846         LOG(llevBug, "unlink_skill() called for non-player %s!\n", query_name(op, NULL));
00847         return;
00848     }
00849 
00850     send_skilllist_cmd(op, skillop, SPLIST_MODE_REMOVE);
00851     skillop->exp_obj = NULL;
00852 }
00853 
00858 void link_player_skills(object *pl)
00859 {
00860     int i, cat = 0, sk_index = 0, exp_index = 0;
00861     object *tmp, *sk_ob[NROFSKILLS], *exp_ob[MAX_EXP_CAT];
00862 
00863     /* We're going to unapply all skills */
00864     pl->chosen_skill = NULL;
00865     CONTR(pl)->last_skill_index = 0;
00866 
00867     /* First find all exp and skill objects */
00868     for (tmp = pl->inv; tmp && sk_index < 1000; tmp = tmp->below)
00869     {
00870         if (tmp->type == EXPERIENCE)
00871         {
00872             CONTR(pl)->exp_ptr[tmp->sub_type] = tmp;
00873             exp_ob[tmp->sub_type] = tmp;
00874             find_skill_exp_name(pl, tmp, CONTR(pl)->last_skill_index);
00875             tmp->nrof = 1;
00876             exp_index++;
00877         }
00878         else if (tmp->type == SKILL)
00879         {
00880             /* For startup, lets unapply all skills */
00881             CLEAR_FLAG(tmp, FLAG_APPLIED);
00882             tmp->nrof = 1;
00883             sk_ob[sk_index] = tmp;
00884             sk_index++;
00885         }
00886     }
00887 
00888     /* Ok, create linked list and link the associated skills to exp objects */
00889     for (i = 0; i < sk_index; i++)
00890     {
00891         cat = skills[sk_ob[i]->stats.sp].category;
00892 
00893         if (cat == EXP_NONE)
00894         {
00895             continue;
00896         }
00897 
00898         sk_ob[i]->exp_obj = exp_ob[cat];
00899     }
00900 }
00901 
00907 int link_player_skill(object *pl, object *skillop)
00908 {
00909     object *tmp;
00910     int cat = skills[skillop->stats.sp].category;
00911 
00912     /* Ok the skill has an exp object, now find right one in pl inv */
00913     if (cat != EXP_NONE)
00914     {
00915         for (tmp = pl->inv; tmp; tmp = tmp->below)
00916         {
00917             if (tmp->type == EXPERIENCE && tmp->sub_type == cat)
00918             {
00919                 skillop->exp_obj = tmp;
00920                 break;
00921             }
00922         }
00923 
00924         return 1;
00925     }
00926 
00927     skillop->exp_obj = NULL;
00928     return 0;
00929 }
00930 
00942 int learn_skill(object *pl, object *scroll, char *name, int skillnr, int scroll_flag)
00943 {
00944     object *tmp, *tmp2;
00945     archetype *skill = NULL;
00946 
00947     if (scroll)
00948     {
00949         skill = find_archetype(scroll->slaying);
00950     }
00951     else if (name)
00952     {
00953         skill = find_archetype(name);
00954     }
00955     else
00956     {
00957         skill = skills[skillnr].at;
00958     }
00959 
00960     if (!skill)
00961     {
00962         return 2;
00963     }
00964 
00965     tmp = arch_to_object(skill);
00966 
00967     if (!tmp)
00968     {
00969         return 2;
00970     }
00971 
00972     /* Check if player already has it */
00973     for (tmp2 = pl->inv; tmp2; tmp2 = tmp2->below)
00974     {
00975         if (tmp2->type == SKILL)
00976         {
00977             if (tmp2->stats.sp == tmp->stats.sp)
00978             {
00979                 new_draw_info_format(0, COLOR_WHITE, pl, "You already know the skill '%s'!", tmp->name);
00980                 return 0;
00981             }
00982         }
00983     }
00984 
00985     /* Now a random chance to learn, based on player Int */
00986     if (scroll_flag)
00987     {
00988         /* Failure */
00989         if (rndm(0, 99) > learn_spell[pl->stats.Int])
00990         {
00991             return 2;
00992         }
00993     }
00994 
00995     /* Everything is cool. Give 'em the skill */
00996     insert_ob_in_ob(tmp,pl);
00997     link_player_skill(pl, tmp);
00998     play_sound_player_only (CONTR(pl), CMD_SOUND_EFFECT, "learnspell.ogg", 0, 0, 0, 0);
00999     new_draw_info_format(0, COLOR_WHITE, pl, "You have learned the skill %s!", tmp->name);
01000 
01001     send_skilllist_cmd(pl, tmp, SPLIST_MODE_ADD);
01002     esrv_send_item(pl, tmp);
01003 
01004     return 1;
01005 }
01006 
01015 int use_skill(object *op, char *string)
01016 {
01017     int sknum = -1;
01018 
01019     /* The skill name appears at the beginning of the string,
01020      * need to reset the string to next word, if it exists. */
01021     if (string && (sknum = lookup_skill_by_name(string)) >= 0)
01022     {
01023         size_t len;
01024 
01025         if (sknum == -1)
01026         {
01027             new_draw_info_format(0, COLOR_WHITE, op, "Unable to find skill by name %s", string);
01028             return 0;
01029         }
01030 
01031         len = strlen(skills[sknum].name);
01032 
01033         /* All this logic goes and skips over the skill name to find any
01034          * options given to the skill. */
01035         if (len >= strlen(string))
01036         {
01037             *string = '\0';
01038         }
01039         else
01040         {
01041             while (len--)
01042             {
01043                 string++;
01044             }
01045 
01046             while (*string == ' ')
01047             {
01048                 string++;
01049             }
01050         }
01051 
01052         if (strlen(string) == 0)
01053         {
01054             string = NULL;
01055         }
01056     }
01057 
01058 #ifdef SKILL_UTIL_DEBUG
01059     LOG(llevDebug, "use_skill(): got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
01060 #endif
01061 
01062     /* Change to the new skill, then execute it. */
01063     if (change_skill(op, sknum))
01064     {
01065         if (op->chosen_skill->sub_type != ST1_SKILL_USE)
01066         {
01067             new_draw_info(0, COLOR_WHITE, op, "You can't use this skill in this way.");
01068         }
01069         else
01070         {
01071             if (do_skill(op, op->facing, string))
01072             {
01073                 return 1;
01074             }
01075         }
01076     }
01077 
01078     return 0;
01079 }
01080 
01086 int change_skill(object *who, int sk_index)
01087 {
01088     object *tmp;
01089 
01090     if (who->chosen_skill && who->chosen_skill->stats.sp == sk_index)
01091     {
01092         /* optimization for changing skill to current skill */
01093         if (who->type == PLAYER)
01094         {
01095             CONTR(who)->shoottype = range_skill;
01096         }
01097 
01098         return 1;
01099     }
01100 
01101     if (sk_index >= 0 && sk_index < NROFSKILLS && (tmp = find_skill(who, sk_index)) != NULL)
01102     {
01103         if (apply_special(who, tmp, AP_APPLY))
01104         {
01105             LOG(llevBug, "change_skill(): can't apply new skill (%s - %d)\n", who->name, sk_index);
01106             return 0;
01107         }
01108 
01109         return 1;
01110     }
01111 
01112     if (who->chosen_skill)
01113     {
01114         if (apply_special(who, who->chosen_skill, AP_UNAPPLY))
01115         {
01116             LOG(llevBug, "change_skill(): can't unapply old skill (%s - %d)\n", who->name, sk_index);
01117         }
01118     }
01119 
01120     if (sk_index >= 0)
01121     {
01122         new_draw_info_format(0, COLOR_WHITE, who, "You have no knowledge of %s.", skills[sk_index].name);
01123     }
01124 
01125     return 0;
01126 }
01127 
01134 static int change_skill_to_skill(object *who, object *skl)
01135 {
01136     /* Quick sanity check */
01137     if (!skl)
01138     {
01139         return 1;
01140     }
01141 
01142     if (who->chosen_skill == skl)
01143     {
01144         /* Optimization for changing skill to current skill */
01145         if (who->type == PLAYER)
01146         {
01147             CONTR(who)->shoottype = range_skill;
01148         }
01149 
01150         return 0;
01151     }
01152 
01153     if (skl->env != who)
01154     {
01155         LOG(llevBug, "change_skill_to_skill: skill is not in players inventory (%s - %s)\n", query_name(who, NULL), query_name(skl, NULL));
01156         return 1;
01157     }
01158 
01159     if (apply_special(who, skl, AP_APPLY))
01160     {
01161         LOG(llevBug, "change_skill(): can't apply new skill (%s - %s)\n", query_name(who, NULL), query_name(skl, NULL));
01162         return 1;
01163     }
01164 
01165     return 0;
01166 }
01167 
01173 static int attack_melee_weapon(object *op, int dir)
01174 {
01175     if (!QUERY_FLAG(op, FLAG_READY_WEAPON))
01176     {
01177         if (op->type == PLAYER)
01178         {
01179             new_draw_info(0, COLOR_WHITE, op, "You have no ready weapon to attack with!");
01180         }
01181 
01182         return 0;
01183     }
01184 
01185     return skill_attack(NULL, op, dir, NULL);
01186 }
01187 
01194 static int attack_hth(object *pl, int dir, char *string)
01195 {
01196     object *enemy = NULL, *weapon;
01197 
01198     if (QUERY_FLAG(pl, FLAG_READY_WEAPON))
01199     {
01200         for (weapon = pl->inv; weapon; weapon = weapon->below)
01201         {
01202             if (weapon->type != WEAPON || !QUERY_FLAG(weapon, FLAG_APPLIED))
01203             {
01204                 continue;
01205             }
01206 
01207             CLEAR_FLAG(weapon, FLAG_APPLIED);
01208             CLEAR_FLAG(pl, FLAG_READY_WEAPON);
01209             fix_player(pl);
01210 
01211             if (pl->type == PLAYER)
01212             {
01213                 new_draw_info(0, COLOR_WHITE, pl, "You unwield your weapon in order to attack.");
01214                 esrv_update_item(UPD_FLAGS, pl, weapon);
01215             }
01216 
01217             break;
01218         }
01219     }
01220 
01221     return skill_attack(enemy, pl, dir, string);
01222 }
01223 
01237 int skill_attack(object *tmp, object *pl, int dir, char *string)
01238 {
01239     int xt, yt;
01240     mapstruct *m;
01241 
01242     if (!dir)
01243     {
01244         dir = pl->facing;
01245     }
01246 
01247     /* If we don't yet have an opponent, find if one exists, and attack.
01248      * Legal opponents are the same as outlined in move_player() */
01249     if (tmp == NULL)
01250     {
01251         xt = pl->x + freearr_x[dir];
01252         yt = pl->y + freearr_y[dir];
01253 
01254         if (!(m = get_map_from_coord(pl->map, &xt,&yt)))
01255         {
01256             return 0;
01257         }
01258 
01259         for (tmp = get_map_ob(m, xt, yt); tmp; tmp = tmp->above)
01260         {
01261             if ((IS_LIVE(tmp) && (tmp->head == NULL ? tmp->stats.hp > 0 : tmp->head->stats.hp > 0)) || QUERY_FLAG(tmp, FLAG_CAN_ROLL) || tmp->type == DOOR)
01262             {
01263                 if (pl->type == PLAYER && tmp->type == PLAYER && !pvp_area(pl, tmp))
01264                 {
01265                     continue;
01266                 }
01267 
01268                 break;
01269             }
01270         }
01271     }
01272 
01273     if (tmp != NULL)
01274     {
01275         return do_skill_attack(tmp, pl, string);
01276     }
01277 
01278     if (pl->type == PLAYER)
01279     {
01280         new_draw_info(0, COLOR_WHITE, pl, "There is nothing to attack!");
01281     }
01282 
01283     return 0;
01284 }
01285 
01295 static int do_skill_attack(object *tmp, object *op, char *string)
01296 {
01297     int success;
01298     char *name = query_name(tmp, NULL);
01299 
01300     if (op->type == PLAYER)
01301     {
01302         /* No selected weapon, change to our hth skill. */
01303         if (!CONTR(op)->selected_weapon)
01304         {
01305             if (CONTR(op)->skill_weapon)
01306             {
01307                 if (change_skill_to_skill(op, CONTR(op)->skill_weapon))
01308                 {
01309                     LOG(llevBug, "do_skill_attack(): couldn't give new hth skill to %s\n", query_name(op, NULL));
01310                     return 0;
01311                 }
01312             }
01313             else
01314             {
01315                 LOG(llevBug, "do_skill_attack(): no hth skill in player %s\n", query_name(op, NULL));
01316                 return 0;
01317             }
01318         }
01319     }
01320 
01321     /* If we have 'ready weapon' but no 'melee weapons' skill readied
01322      * this will flip to that skill. This is only window dressing for
01323      * the players -- no need to do this for monsters. */
01324     if (op->type == PLAYER && QUERY_FLAG(op, FLAG_READY_WEAPON) && (!op->chosen_skill || op->chosen_skill->stats.sp != CONTR(op)->set_skill_weapon))
01325     {
01326         change_skill(op, CONTR(op)->set_skill_weapon);
01327     }
01328 
01329     success = attack_ob(tmp, op);
01330 
01331     /* Print appropriate messages to the player. */
01332     if (success && string != NULL)
01333     {
01334         if (op->type == PLAYER)
01335         {
01336             new_draw_info_format(0, COLOR_WHITE, op, "You %s %s!", string, name);
01337         }
01338         else if (tmp->type == PLAYER)
01339         {
01340             new_draw_info_format(0, COLOR_WHITE, tmp, "%s %s you!", query_name(op, NULL), string);
01341         }
01342     }
01343 
01344     return success;
01345 }
01346 
01352 int SK_level(object *op)
01353 {
01354     object *head = op->head ? op->head : op;
01355     int level;
01356 
01357     if (head->type == PLAYER && head->chosen_skill && head->chosen_skill->level != 0)
01358     {
01359         level = head->chosen_skill->level;
01360     }
01361     else
01362     {
01363         level = head->level;
01364     }
01365 
01366     /* Safety */
01367     if (level <= 0)
01368     {
01369         LOG(llevBug, "SK_level(arch %s, name %s): level <= 0\n", op->arch->name, query_name(op, NULL));
01370         level = 1;
01371     }
01372 
01373     return level;
01374 }
01375 
01380 object *SK_skill(object *op)
01381 {
01382     object *head = op->head ? op->head : op;
01383 
01384     if (head->type == PLAYER && head->chosen_skill)
01385     {
01386         return head->chosen_skill;
01387     }
01388 
01389     return NULL;
01390 }
01391 
01397 float get_skill_time(object *op, int skillnr)
01398 {
01399     float skill_time = skills[skillnr].time;
01400 
01401     if (op->type != PLAYER)
01402     {
01403         return 0;
01404     }
01405 
01406     if (skillnr == SK_USE_MAGIC_ITEM || skillnr == SK_MISSILE_WEAPON || skillnr == SK_THROWING || skillnr == SK_XBOW_WEAP || skillnr == SK_SLING_WEAP)
01407     {
01408         CONTR(op)->action_range = global_round_tag + op->chosen_skill->stats.maxsp;
01409         return 0;
01410     }
01411 
01412     if (!skill_time)
01413     {
01414         return 0.0f;
01415     }
01416     else
01417     {
01418         int level = SK_level(op) / 10;
01419 
01420         /* Now this should be MUCH harder */
01421         if (skill_time > 1.0f)
01422         {
01423             skill_time -= (level / 3) * 0.1f;
01424 
01425             if (skill_time < 1.0f)
01426             {
01427                 skill_time = 1.0f;
01428             }
01429         }
01430     }
01431 
01432     return FABS(skill_time);
01433 }
01434 
01440 int check_skill_action_time(object *op, object *skill)
01441 {
01442     if (!skill)
01443     {
01444         return 0;
01445     }
01446 
01447     if (skill->stats.sp == SK_PRAYING || skill->stats.sp == SK_SPELL_CASTING)
01448     {
01449         if (CONTR(op)->action_casting > global_round_tag)
01450         {
01451             CONTR(op)->action_timer = (float) (CONTR(op)->action_casting - global_round_tag) / (1000000 / MAX_TIME) * 1000.0f;
01452             return 0;
01453         }
01454     }
01455     else
01456     {
01457         if (CONTR(op)->action_range > global_round_tag)
01458         {
01459             CONTR(op)->action_timer = (float)(CONTR(op)->action_range - global_round_tag) / (1000000 / MAX_TIME) * 1000.0f;
01460             return 0;
01461         }
01462     }
01463 
01464     return 1;
01465 }