|
Atrinik Server 2.5
|
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 }
1.7.4