/*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik Strfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * Much time and thought has gone into this software and you are * * benefitting. We hope that you share your changes too. What goes * * around, comes around. * ***************************************************************************/ /*************************************************************************** * ROM 2.4 is copyright 1993-1998 Russ Taylor * * ROM has been brought to you by the ROM consortium * * Russ Taylor (rtaylor@hypercube.org) * * Gabrielle Taylor (gtaylor@hypercube.org) * * Brian Moore (zump@rom.org) * * By using this code, you have agreed to follow the terms of the * * ROM license, in the file Rom24/doc/rom.license * ***************************************************************************/ // DragonBall Arena 2 has been written by: // Matt Brown (Antor), arkaine@sympatico.ca, 2000-2002 // Please follow all previous licenses. Enjoy! #include <sys/types.h> #include <sys/time.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include "merc.h" #include "interp.h" #include "recycle.h" // Locals void UpdateSkills args ( ( CHAR_DATA *pCh ) ); long long int ApplyMisc (CHAR_DATA *pCh, long long int llValue); long long int ApplyCharge (CHAR_DATA *pCh, long long int llValue, int nSn); /* * Lookup a skill by name. */ int skill_lookup (const char *name) { int sn; for (sn = 0; sn < MAX_SKILL; sn++) { if (skill_table[sn].name == NULL) break; if (LOWER (name[0]) == LOWER (skill_table[sn].name[0]) && !str_prefix (name, skill_table[sn].name)) return sn; } return -1; } /* skill_driver: * Does a skill. Checks for targets, shows messages, performs the skill. * Returns true if the character completes the skill. */ bool skill_driver (CHAR_DATA * ch, char *argument, int sn) { char arg1[MAX_INPUT_LENGTH]; char arg2[MAX_INPUT_LENGTH]; void *vo; int target, sn_target; CHAR_DATA *victim; OBJ_DATA *obj; if (sn < 1) return FALSE; if (ch->position < skill_table[sn].minimum_position) { sendch ("You can't do that in your current position.\n\r", ch); return FALSE; } if (get_skill(ch, sn) < 1) { sendch ("What?\n\r", ch); return FALSE; } argument = one_argument (argument, arg1); one_argument (argument, arg2); // Switch them if needed if (!str_cmp(arg1, "release")) { char t[MAX_INPUT_LENGTH]; sprintf(t, arg2); sprintf(arg2, arg1); sprintf(arg1, t); } /* * Locate targets. */ victim = NULL; obj = NULL; vo = NULL; target = TARGET_NONE; sn_target = skill_table[sn].target; // Look for skills whose targets switch at certain skill levels if (sn_target == TAR_HYBRID100) { if (get_skill(ch, sn) > 99 && !str_cmp(arg1, "all")) sn_target = TAR_AREA_OFF; else sn_target = TAR_CHAR_OFFENSIVE; } switch (sn_target) { default: logstr (LOG_BUG, "skill_driver: bad target for sn %d.", sn); return FALSE; case TAR_IGNORE: case TAR_AREA_OFF: vo = NULL; target = TARGET_NONE; break; case TAR_CHAR_OFFENSIVE: if (arg1[0] == '\0') { if ((victim = ch->fighting) == NULL) { sendch ("Direct that at whom?\n\r", ch); return FALSE; } } else { if ((victim = get_char_room (ch, NULL, arg1)) == NULL) { sendch ("They aren't here.\n\r", ch); return FALSE; } } if (ch == victim) { sendch( "You can't do that to yourself.\n\r", ch ); return FALSE; } if (IS_NPC (victim) && victim->fighting != NULL && !is_same_group (ch, victim->fighting)) { sendch ("Kill stealing is not permitted.\n\r", ch); return FALSE; } if (!IS_NPC (ch)) { if (is_safe (ch, victim) && victim != ch) { sendch ("Not on that target.\n\r", ch); return FALSE; } check_killer (ch, victim); } if (IS_AFFECTED (ch, AFF_CHARM) && ch->master == victim) { sendch ("You can't do that on your own follower.\n\r", ch); return FALSE; } vo = (void *) victim; target = TARGET_CHAR; break; case TAR_CHAR_DEFENSIVE: if (arg1[0] == '\0') { victim = ch; } else { if ((victim = get_char_room (ch, NULL, arg1)) == NULL) { sendch ("They aren't here.\n\r", ch); return FALSE; } } vo = (void *) victim; target = TARGET_CHAR; break; case TAR_CHAR_SELF: if (arg1[0] != '\0' && !is_name (arg1, ch->name)) { sendch ("You cannot do that on another.\n\r", ch); return FALSE; } vo = (void *) ch; target = TARGET_CHAR; break; case TAR_OBJ_INV: if (arg1[0] == '\0') { sendch ("What object should that be done on?\n\r", ch); return FALSE; } if ((obj = get_obj_carry (ch, arg1, ch)) == NULL) { sendch ("You are not carrying that.\n\r", ch); return FALSE; } vo = (void *) obj; target = TARGET_OBJ; break; case TAR_OBJ_CHAR_OFF: if (arg1[0] == '\0') { if ((victim = ch->fighting) == NULL) { sendch ("Do that on whom or what?\n\r", ch); return FALSE; } target = TARGET_CHAR; } else if ((victim = get_char_room (ch, NULL, arg1)) != NULL) { target = TARGET_CHAR; } if (target == TARGET_CHAR) { /* check the sanity of the attack */ if (is_safe_spell (ch, victim, FALSE) && victim != ch) { sendch ("Not on that target.\n\r", ch); return FALSE; } if (IS_AFFECTED (ch, AFF_CHARM) && ch->master == victim) { sendch ("You can't do that on your own follower.\n\r", ch); return FALSE; } if (!IS_NPC (ch)) check_killer (ch, victim); vo = (void *) victim; } else if ((obj = get_obj_here (ch, NULL, arg1)) != NULL) { vo = (void *) obj; target = TARGET_OBJ; } else { sendch ("You don't see that here.\n\r", ch); return FALSE; } break; case TAR_OBJ_CHAR_DEF: if (arg1[0] == '\0') { vo = (void *) ch; target = TARGET_CHAR; } else if ((victim = get_char_room (ch, NULL, arg1)) != NULL) { vo = (void *) victim; target = TARGET_CHAR; } else if ((obj = get_obj_carry (ch, arg1, ch)) != NULL) { vo = (void *) obj; target = TARGET_OBJ; } else { sendch ("You don't see that here.\n\r", ch); return FALSE; } break; } if (IS_EXHAUSTED(ch)) { sendch ("You're too exhausted.\n\r", ch); return FALSE; } // Hard coded checks for skills if (sn == gsn_hyperpunch) { if (get_eq_char(ch, WEAR_WIELD) != NULL) { sendch("You cannot hyperpunch with a weapon.\n\r", ch); return FALSE; } } else if (sn == gsn_energy_slash) { if (get_eq_char(ch, WEAR_WIELD) == NULL) { sendch("You need a weapon to energy slash with.\n\r", ch); return FALSE; } } // Use a ki_loss value based on the skill // Ki loss. // If its a charge type skill, make the initial loss much greater if (skill_table[sn].type == SKILL_CHARGE) ki_loss(ch, skill_table[sn].ki_mod*5); else ki_loss(ch, skill_table[sn].ki_mod); // Message if the skill is started if (skill_table[sn].msg_immediate1) act(skill_table[sn].msg_immediate1,ch,NULL,NULL,TO_CHAR); if (skill_table[sn].msg_immediate2) act(skill_table[sn].msg_immediate2,ch,NULL,NULL,TO_ROOM); // Make the char wait, and get the skill ready to fire (or fire it) if (skill_table[sn].type == SKILL_DELAY) { ch->wait_skill = skill_table[sn].wait; ch->wait_skill_sn = sn; ch->wait_skill_vo = vo; ch->wait_skill_target = target; } else if (skill_table[sn].type == SKILL_IMM) { wait (ch, skill_table[sn].wait); (*skill_table[sn].skill_fun) (ch, vo, target); } else if (skill_table[sn].type == SKILL_CHARGE) { ch->wait_skill = 0; ch->charge = 1; ch->wait_skill_sn = sn; ch->wait_skill_vo = vo; ch->wait_skill_target = target; } if ((sn_target == TAR_CHAR_OFFENSIVE || (sn_target == TAR_OBJ_CHAR_OFF && target == TARGET_CHAR)) && victim != ch && victim->master != ch && victim->fighting == NULL && IS_AWAKE(victim) && !IS_AFFECTED (victim, AFF_CALM) ) { check_killer (victim, ch); begin_combat (victim, ch); } else if (sn_target == TAR_AREA_OFF) { CHAR_DATA *vch; for (vch = ch->in_room->people; vch; vch = vch->next_in_room) { if (IS_NPC(vch) && vch->fighting == NULL && !IS_AFFECTED (vch, AFF_CALM) && !IS_AFFECTED (vch, AFF_CHARM) && IS_AWAKE (vch) && !IS_SET (vch->act, ACT_WIMPY) && !is_same_group(vch, ch) && can_see (vch, ch) ) { check_killer (vch, ch); begin_combat (vch, ch); break; } } } // Immediate "charged" attack if (skill_table[sn].type == SKILL_CHARGE && (!str_cmp(arg1, "release") || !str_cmp(arg2, "release")) ) { // Pulled from do_release act("You release!",ch,NULL,NULL,TO_CHAR); act("$n releases!",ch,NULL,NULL,TO_ROOM); if (skill_table[ch->wait_skill_sn].msg_delay1) act(skill_table[ch->wait_skill_sn].msg_delay1,ch,NULL,NULL,TO_CHAR); if (skill_table[ch->wait_skill_sn].msg_delay2) act(skill_table[ch->wait_skill_sn].msg_delay2,ch,NULL,NULL,TO_ROOM); if (skill_table[ch->wait_skill_sn].skill_fun) (*skill_table[ch->wait_skill_sn].skill_fun) (ch, ch->wait_skill_vo, ch->wait_skill_target); ch->charge = 0; ch->wait_skill_sn = 0; ch->wait_skill_vo = NULL; ch->wait_skill_target = 0; wait (ch, 3 * PULSE_SECOND); } return TRUE; } void do_skills (CHAR_DATA * ch, char *argument) { BUFFER *buffer; char buf[MAX_STRING_LENGTH], buf2[MAX_STRING_LENGTH], buf3[MAX_STRING_LENGTH]; char buf_temp[MAX_STRING_LENGTH]; bool found=FALSE, bColumn=TRUE, bOtherFound=FALSE; int i; if (IS_NPC (ch)) return; sprintf(buf, "Statistics:\n\r "); for (i = 0; i < MAX_STATS; i++) { if (ch->perm_stat[i] > 0) { sprintf (buf_temp, "%-20s %3d.%-2.2d ", stat_table[i], ch->perm_stat[i], 100*ch->pcdata->nStatProgress[i]/(ch->perm_stat[i]*3 + 1800)); strcat (buf, buf_temp); if ((bColumn=!bColumn)) strcat (buf, "\n\r "); } } if (!bColumn) strcat (buf, "\n\r"); sprintf(buf2, "\n\rSkills:\n\r "); bColumn = TRUE; for (i = 0; i < MAX_SKILL; i++) { if (skill_table[i].name == NULL) break; if (ch->pcdata->learned[i] > 0 && skill_table[i].bCanImprove) { found = TRUE; sprintf (buf_temp, "%-20s %3d.%-2.2d ", skill_table[i].name, ch->pcdata->learned[i], 100*ch->pcdata->nSkillProgress[i]/(3240 + ch->pcdata->learned[i]*36)); strcat (buf2, buf_temp); if ((bColumn=!bColumn)) strcat (buf2, "\n\r "); } } if (!found) sprintf (buf2, "\n\rSkills:\n\r No skills found.\n\r"); else if (!bColumn) strcat (buf2, "\n\r"); // Other skills that dont show any skill level sprintf(buf3, "\n\rOther Skills:\n\r "); bColumn = TRUE; for (i = 0; i < MAX_SKILL; i++) { if (skill_table[i].name == NULL) break; if (ch->pcdata->learned[i] > 0 && !skill_table[i].bCanImprove) { bOtherFound = TRUE; sprintf (buf_temp, "%-20s ", skill_table[i].name); strcat (buf3, buf_temp); if ((bColumn=!bColumn)) strcat (buf3, "\n\r "); } } if (!bColumn) strcat (buf3, "\n\r"); buffer = new_buf (); add_buf (buffer, buf); if (bOtherFound && !found) add_buf (buffer, buf3); else { add_buf (buffer, buf2); if (bOtherFound) add_buf (buffer, buf3); } page_to_char (buf_string (buffer), ch); free_buf (buffer); } // If the multiplier is negative, its easier. // For example, nMultiplier = 5 : 5 times harder to learn // nMultiplier = -4 : 4 times easier to learn void ImproveSkill (CHAR_DATA *pCh, int nSn, bool bSuccess, float nMultiplier, int nViDifficulty) { char szBuf[MAX_STRING_LENGTH]; int nGain, nNext; if (IS_NPC (pCh)) return; if (nSn < 0 || nSn >= MAX_SKILL || pCh->pcdata->learned[nSn] <= 0 || !skill_table[nSn].bCanImprove || !skill_table[nSn].bCanLearn) return; if (nViDifficulty < pCh->nDifficulty / 4) return; // could all the counts (teach, train, chaos) be added in here. // pass a flag in ie IMPROVE_PENALTY_TEACH and automatically // increment the necessary counter? if (IS_SET(pCh->in_room->room_flags, ROOM_CHAOS) && pCh->fighting) { nMultiplier += pCh->pcdata->nChaosCount / 70; ++pCh->pcdata->nChaosCount; } // This does not increase linearally, since its based on int. As // int increases, skills have to become harder to learn (so, it // actually seems linear) // The magic number is 360, which is the number of 5 second actions // to make up half an hour. We're aiming for a skill to go up every // half an hour. So, nNext / nGain = 360. Also, gains are based on // intelligence, so nNext must increase to offest increasing // intelligence. Since stats are equal to skills in level, if // intelligence is equal to the skill, nNext / nGain will equal 360. nNext = 3240 + pCh->pcdata->learned[nSn]*36; nGain = 9 + get_curr_stat (pCh, STAT_INT) / 10; if (nMultiplier > 0) nGain = UMAX(1, nGain / nMultiplier); // Don't want it to totally destroy any gains else nGain *= -1 * nMultiplier; // Change the gain based on relative difficulties //nGain = URANGE(nGain / 3, nViDifficulty * nGain / pCh->nDifficulty, nGain * 2); // Change the gain based on current power use nGain = UMAX(nGain / 10, pCh->nCurPl * nGain / 100); // Make all skills increase at the same rate, regardless of speed nGain = (nGain * skill_table[nSn].wait) / (5 * PULSE_SECOND); // Limit the gain nGain = UMIN(nGain, nNext / 50); // Get a reward? if (pCh->pcdata->nReward > 0) nGain *= 2; if (pCh->pcdata->nSkillTick[nSn] + nGain > (1800 * PULSE_SECOND * nNext) / PULSE_TICK) { nGain = ((1800 * PULSE_SECOND * nNext) / PULSE_TICK) - pCh->pcdata->nSkillTick[nSn]; pCh->pcdata->nSkillTick[nSn] = (1800 * PULSE_SECOND * nNext) / PULSE_TICK; } pCh->pcdata->nSkillProgress[nSn] += nGain; if (pCh->pcdata->nSkillProgress[nSn] < nNext) return; if (bSuccess) sprintf (szBuf, "{CHoning your skills, your %s improves!{x\n\r", skill_table[nSn].name); else sprintf (szBuf, "{CThrough trial and error, your %s improves!{x\n\r", skill_table[nSn].name); sendch (szBuf, pCh); pCh->pcdata->learned[nSn]++; pCh->pcdata->nSkillProgress[nSn] = 0; ResetDiff(pCh); UpdateSkills (pCh); return; } // If the multiplier is negative, its easier. // For example, nMultiplier = 5 : 5 times harder to learn // nMultiplier = -4 : 4 times easier to learn void ImproveStat (CHAR_DATA *pCh, int nStat, bool bSuccess, float nMultiplier, int nViDifficulty) { char szBuf[MAX_STRING_LENGTH]; int nGain, nNext; if (IS_NPC (pCh)) return; if (nStat < 0 || nStat >= MAX_STATS) return; if (nViDifficulty < pCh->nDifficulty / 4) return; if (IS_SET(pCh->in_room->room_flags, ROOM_CHAOS) && pCh->fighting) { nMultiplier += pCh->pcdata->nChaosCount / 70; ++pCh->pcdata->nChaosCount; } // This increases almost linearally, since int doesn't effect it. // Skills, on the other hand, increase based on int, so they have to // become harder to offset increasing int nNext = pCh->perm_stat[nStat]*3 + 1800; nGain = pc_race_table[pCh->race].stat_gain[nStat]; if (nMultiplier > 0) nGain = UMAX(1, nGain / nMultiplier); // Don't want it to totally destroy any gains else nGain *= -1 * nMultiplier; // Change the gain based on relative difficulties //nGain = URANGE(nGain / 3, nViDifficulty * nGain / pCh->nDifficulty, nGain * 2); // Change the gain based on current power use nGain = UMAX(nGain / 10, pCh->nCurPl * nGain / 100); // Limit the gain nGain = UMIN(nGain, nNext / 50); // Get a reward? if (pCh->pcdata->nReward > 0) nGain *= 2; pCh->pcdata->nStatProgress[nStat] += nGain; if (pCh->pcdata->nStatProgress[nStat] < nNext) return; if (bSuccess) sprintf (szBuf, "{CWith hard work, your %s improves!{x\n\r", stat_table[nStat]); else sprintf (szBuf, "{CThrough trial and error, your %s improves!{x\n\r", stat_table[nStat]); sendch (szBuf, pCh); pCh->perm_stat[nStat]++; if (nStat == STAT_STR) { pCh->max_hit += HP_STR; pCh->pcdata->perm_hit += HP_STR; pCh->max_ki += KI_STR; pCh->pcdata->perm_ki += KI_STR; } else if (nStat == STAT_WIL) { pCh->max_hit += HP_WIL; pCh->pcdata->perm_hit += HP_WIL; pCh->max_ki += KI_WIL; pCh->pcdata->perm_ki += KI_WIL; } pCh->pcdata->nStatProgress[nStat] = 0; ResetDiff(pCh); UpdateSkills (pCh); return; } void UpdateSkills (CHAR_DATA *pCh) { char szBuf[MAX_STRING_LENGTH]; int nSn; if (IS_NPC(pCh)) return; for (nSn = 0; nSn < MAX_SKILL; ++nSn) { if (pCh->pcdata->learned[nSn] < 1 && MeetsPrereq(pCh, nSn)) { pCh->pcdata->learned[nSn] = 1; sprintf(szBuf, "{CWith your new insights, you now know %s!{x\n\r", skill_table[nSn].name); sendch (szBuf, pCh); } } } void ResetDiff (CHAR_DATA *pCh) { int i, nCount = 0; pCh->nDifficulty = 0; pCh->nTrueDiff = 0; for (i = 0; i < MAX_SKILL; ++i) { // Limit the number of skills NPCs get, to protect against // artificially high difficult ratings if (IS_NPC(pCh) && ++nCount > get_curr_stat (pCh, STAT_INT) + 1) break; pCh->nDifficulty += get_skill (pCh, i); } pCh->nDifficulty += get_curr_stat (pCh, STAT_STR); pCh->nDifficulty += get_curr_stat (pCh, STAT_DEX); pCh->nDifficulty += get_curr_stat (pCh, STAT_CHA); pCh->nDifficulty += get_curr_stat (pCh, STAT_INT); pCh->nDifficulty += get_curr_stat (pCh, STAT_WIL); pCh->llPl = pCh->nDifficulty * pCh->nDifficulty; for (i = 0; i < MAX_SKILL; ++i) { if (IS_NPC(pCh) && ++nCount > pCh->perm_stat[STAT_INT] + 1) break; pCh->nTrueDiff += get_skill (pCh, i); } pCh->nTrueDiff += pCh->perm_stat[STAT_STR]; pCh->nTrueDiff += pCh->perm_stat[STAT_DEX]; pCh->nTrueDiff += pCh->perm_stat[STAT_CHA]; pCh->nTrueDiff += pCh->perm_stat[STAT_INT]; pCh->nTrueDiff += pCh->perm_stat[STAT_WIL]; pCh->llTruePl = pCh->nTrueDiff * pCh->nTrueDiff; UpdateSkills (pCh); } /* // Total of all skills and statistics void ResetDifficulty (CHAR_DATA *pCh) { int i, nCount = 0; pCh->nDifficulty = 0; for (i = 0; i < MAX_SKILL; ++i) { // Limit the number of skills NPCs get, to protect against // artificially high difficult ratings if (IS_NPC(pCh) && ++nCount > get_curr_stat (pCh, STAT_INT) + 1) break; pCh->nDifficulty += get_skill (pCh, i); } pCh->nDifficulty += get_curr_stat (pCh, STAT_STR); pCh->nDifficulty += get_curr_stat (pCh, STAT_DEX); pCh->nDifficulty += get_curr_stat (pCh, STAT_CHA); pCh->nDifficulty += get_curr_stat (pCh, STAT_INT); pCh->nDifficulty += get_curr_stat (pCh, STAT_WIL); } // Total based on skills and statistics. // Skills are worth 50 pl each, plus the summation of each number from 1 to 20 * skill_level (1 + 2 + 3 + 4 + ... + 20 * lvl). // Stats are worth the same as skills // The formulas used are based on (n/2)(n+1) void ResetPl (CHAR_DATA *pCh) { int i, nCount = 0; // Maximum powerlevel, including affects pCh->llPl = 0; for (i = 0; i < MAX_SKILL; ++i) { // Limit the number of skills NPCs get, to protect against // artificially high powerlevels if (IS_NPC(pCh) && ++nCount > get_curr_stat (pCh, STAT_INT) + 1) break; pCh->llPl += 50 + (10 * get_skill (pCh, i)) * (20 * get_skill (pCh, i) + 1); } pCh->llPl += 50 + (10 * get_curr_stat (pCh, STAT_STR)) * (20 * get_curr_stat (pCh, STAT_STR) + 1); pCh->llPl += 50 + (10 * get_curr_stat (pCh, STAT_DEX)) * (20 * get_curr_stat (pCh, STAT_DEX) + 1); pCh->llPl += 50 + (10 * get_curr_stat (pCh, STAT_CHA)) * (20 * get_curr_stat (pCh, STAT_CHA) + 1); pCh->llPl += 50 + (10 * get_curr_stat (pCh, STAT_INT)) * (20 * get_curr_stat (pCh, STAT_INT) + 1); pCh->llPl += 50 + (10 * get_curr_stat (pCh, STAT_WIL)) * (20 * get_curr_stat (pCh, STAT_WIL) + 1); // Find permanent, unadultered powerlevel pCh->llTruePl = 0; for (i = 0; i < MAX_SKILL; ++i) { if (IS_NPC(pCh) && ++nCount > pCh->perm_stat[STAT_INT] + 1) break; pCh->llTruePl += 50 + (10 * get_skill (pCh, i)) * (20 * get_skill (pCh, i) + 1); } pCh->llTruePl += 50 + (10 * pCh->perm_stat[STAT_STR]) * (20 * pCh->perm_stat[STAT_STR] + 1); pCh->llTruePl += 50 + (10 * pCh->perm_stat[STAT_DEX]) * (20 * pCh->perm_stat[STAT_DEX] + 1); pCh->llTruePl += 50 + (10 * pCh->perm_stat[STAT_CHA]) * (20 * pCh->perm_stat[STAT_CHA] + 1); pCh->llTruePl += 50 + (10 * pCh->perm_stat[STAT_INT]) * (20 * pCh->perm_stat[STAT_INT] + 1); pCh->llTruePl += 50 + (10 * pCh->perm_stat[STAT_WIL]) * (20 * pCh->perm_stat[STAT_WIL] + 1); } */ // ch_gsn is the gsn of the skill the character is using to attack with // vi_gsn is the gsn he is defending with // multiplier is used against the victim's chance (therefore, high multiplier makes it harder // to hit) bool check_hit (CHAR_DATA *ch, CHAR_DATA *victim, int ch_gsn, int vi_gsn, float multiplier, bool bLearn) { CHAR_DATA *vch; char buf[MAX_STRING_LENGTH]; int ch_roll, vi_roll; if (bLearn) { ImproveSkill (ch, ch_gsn, TRUE, 1, victim->nDifficulty); ImproveStat (ch, STAT_DEX, TRUE, 2, victim->nDifficulty); // 2 since it's called twice as often (to hit and dodge) ImproveSkill (victim, vi_gsn, TRUE, 1, ch->nDifficulty); ImproveStat (victim, STAT_DEX, TRUE, 2, ch->nDifficulty); } ch_roll = Hitroll (ch, ch_gsn, TRUE); vi_roll = Armour (victim, vi_gsn, TRUE); for (vch = ch->in_room->people; vch; vch = vch->next_in_room) { if (!IS_NPC(vch) && IS_IMMORTAL(vch) && IS_SET(vch->act, PLR_COMBATINFO)) { sprintf(buf, "{WCI:hit [%s] %d -vs- %d [%s]{x\n\r", NAME(ch), ch_roll, vi_roll, NAME(victim)); sendch(buf, vch); } } ki_loss (victim, 6); if (ch_roll > vi_roll) return TRUE; if (vi_gsn == gsn_dodge) { act ("{2You dodge!{x", victim, NULL, NULL, TO_CHAR); act ("{3$n dodges!{x", victim, NULL, NULL, TO_ROOM); } else if (vi_gsn == gsn_shield_block) { act ("{2You block with a shield!{x", victim, NULL, NULL, TO_CHAR); act ("{3$n blocks with a shield!{x", victim, NULL, NULL, TO_ROOM); } else if (vi_gsn == gsn_parry) { act ("{2You parry!{x", victim, NULL, NULL, TO_CHAR); act ("{3$n parries!{x", victim, NULL, NULL, TO_ROOM); } else { act ("{2You defend!{x", victim, NULL, NULL, TO_CHAR); act ("{3$n defends!{x", victim, NULL, NULL, TO_ROOM); } return FALSE; } bool check_kihit (CHAR_DATA *ch, CHAR_DATA *victim, int ch_gsn, int vi_gsn, float multiplier, bool bLearn) { CHAR_DATA *vch; char buf[MAX_STRING_LENGTH]; int ch_roll, vi_roll; if (bLearn) { ImproveSkill (ch, ch_gsn, TRUE, 1, victim->nDifficulty); ImproveStat (ch, STAT_INT, TRUE, 2, victim->nDifficulty); // 2 since its used twice as often (to hit and dodge) ImproveSkill (victim, vi_gsn, TRUE, 1, ch->nDifficulty); ImproveStat (victim, STAT_INT, TRUE, 2, ch->nDifficulty); } ch_roll = Hitroll (ch, ch_gsn, FALSE); vi_roll = Armour (victim, vi_gsn, FALSE); for (vch = ch->in_room->people; vch; vch = vch->next) { if (!IS_NPC(vch) && IS_IMMORTAL(vch) && IS_SET(vch->act, PLR_COMBATINFO)) { sprintf(buf, "{WCI:ki [%s] %d -vs- %d [%s]{x\n\r", NAME(ch), ch_roll, vi_roll, NAME(victim)); sendch(buf, vch); } } ki_loss (victim, 6); if (ch_roll > vi_roll) return TRUE; if (vi_gsn == gsn_dodge) { act ("{2You dodge!{x", victim, NULL, NULL, TO_CHAR); act ("{3$n dodges!{x", victim, NULL, NULL, TO_ROOM); } else if (vi_gsn == gsn_shield_block) { act ("{2You block with a shield!{x", victim, NULL, NULL, TO_CHAR); act ("{3$n blocks with a shield!{x", victim, NULL, NULL, TO_ROOM); } else if (vi_gsn == gsn_parry) { act ("{2You parry!{x", victim, NULL, NULL, TO_CHAR); act ("{3$n parries!{x", victim, NULL, NULL, TO_ROOM); } else { act ("{2You defend!{x", victim, NULL, NULL, TO_CHAR); act ("{3$n defends!{x", victim, NULL, NULL, TO_ROOM); } return FALSE; } int Hitroll (CHAR_DATA *pCh, int nSn, bool bPhysical) { int nRoll, nRand; nRand = number_range(1,50); // Critical miss if (nRand <= 3) return 0; nRoll = nRand; nRoll += get_curr_stat(pCh, bPhysical ? STAT_DEX : STAT_INT); nRoll += get_skill(pCh, nSn); nRoll += GET_HITROLL(pCh); nRoll += (pCh->balance - 5) * 2; nRoll -= (100 - pCh->nCurPl) / 10; if (nSn == gsn_heart_shot) nRoll -= 2; else if (nSn == gsn_eye_gouge) nRoll -= 5; if (IS_AFFECTED (pCh, AFF_BLIND)) nRoll -= 5; if (IS_AFFECTED (pCh, AFF_FLYING)) nRoll += 5; if (pCh->stance == STANCE_OFFEN) nRoll += 4; else if (pCh->stance == STANCE_DEFEN) nRoll -= 4; if (!IS_AWAKE (pCh)) nRoll -= 5; else if (pCh->position < POS_FIGHTING) nRoll -= 2; if (IS_EXHAUSTED(pCh)) nRoll -= 10; if (pCh->charge > 0) nRoll = (pCh->charge * nRoll) / skill_table[nSn].wait; if (pCh->charge == 1) // Immediately released skill -- deduction nRoll /= 4; // Critical: if (nRand == 50) nRoll *= 3; return nRoll; } int Damroll (CHAR_DATA *pCh, int nLow, int nHigh, int nSn, bool bPhysical) { int nDam; nDam = number_range(nLow, nHigh); nDam += get_curr_stat(pCh, bPhysical ? STAT_STR : STAT_WIL) + get_skill(pCh, nSn); nDam += GET_DAMROLL(pCh); nDam += pCh->balance - 5; nDam -= (100 - pCh->nCurPl) / 10; /* if (sn == gsn_throat_shot || sn == gsn_sweep || sn == gsn_eye_gouge) nDam -= 15; else if (sn == gsn_knee) nDam -= 3; else if (sn == gsn_elbow) nDam -= 7; else if (sn == gsn_heart_shot) nDam += 10; else if (sn == gsn_power_bomb) nDam += 15; else if (sn == gsn_spirit_bomb || sn == gsn_death_ball) nDam += 10; else if (sn == gsn_finalflash || sn == gsn_kamehameha) nDam += 5; else if (sn == gsn_scattershot || sn == gsn_solarflare) nDam -= 5; */ if (pCh->stance == STANCE_OFFEN) nDam += 4; else if (pCh->stance == STANCE_DEFEN) nDam -= 4; if (!IS_AWAKE (pCh)) nDam -= 5; else if (pCh->position < POS_FIGHTING) nDam -= 2; if (IS_EXHAUSTED(pCh)) nDam -= 10; if (pCh->charge > 0) nDam = (pCh->charge * nDam) / skill_table[nSn].wait; if (pCh->charge == 1) // Immediately released skill -- deduction nDam /= 4; // Critical: if (number_range(1,50) == 1) nDam *= 2; nDam = UMAX(1, nDam); return nDam; } int Armour (CHAR_DATA *pCh, int nSn, bool bPhysical) { int nArmour = 25; nArmour += get_curr_stat(pCh, bPhysical ? STAT_DEX : STAT_INT); nArmour += get_skill(pCh, nSn); //nArmour += GET_ARMROLL(pCh); nArmour += (pCh->balance - 5) * 2; nArmour -= (100 - pCh->nCurPl) / 10; if (IS_AFFECTED (pCh, AFF_BLIND)) nArmour -= 5; if (IS_AFFECTED (pCh, AFF_FLYING)) nArmour += 5; if (pCh->stance == STANCE_OFFEN) nArmour -= 4; else if (pCh->stance == STANCE_DEFEN) nArmour += 4; if (!IS_AWAKE (pCh)) nArmour -= 5; else if (pCh->position < POS_FIGHTING) nArmour -= 2; if (IS_EXHAUSTED(pCh)) nArmour -= 10; return nArmour; } int Absorb (CHAR_DATA *pCh, int nSn, int nDamType, bool bPhysical) { int nAbsorb = 0; switch (nDamType) { case DAM_NONE: break; case DAM_PIERCE: nAbsorb += pCh->armor[AC_PIERCE] / 5; break; case DAM_BASH: nAbsorb += pCh->armor[AC_BASH] / 5; break; case DAM_SLASH: nAbsorb += pCh->armor[AC_SLASH] / 5; break; default: nAbsorb += pCh->armor[AC_EXOTIC] / 5; break; } if (pCh->stance == STANCE_OFFEN) nAbsorb -= 4; else if (pCh->stance == STANCE_DEFEN) nAbsorb += 4; if (IS_AFFECTED (pCh, AFF_SANCTUARY)) nAbsorb += 15; if ((IS_AFFECTED (pCh, AFF_PROTECT_EVIL) && IS_EVIL (pCh)) || (IS_AFFECTED (pCh, AFF_PROTECT_GOOD) && IS_GOOD (pCh))) nAbsorb += 10; if (!IS_AWAKE (pCh)) nAbsorb -= 5; else if (pCh->position < POS_FIGHTING) nAbsorb -= 2; return nAbsorb; } /* long long int ApplyMisc (CHAR_DATA *pCh, long long int llValue) { llValue = llValue * pCh->balance / 5; if (!IS_AWAKE (pCh)) llValue /= 4; else if (pCh->position < POS_FIGHTING) llValue /= 2; if (IS_EXHAUSTED(pCh)) llValue /= 50; llValue = pCh->nCurPl * llValue / 100; return llValue; } long long int ApplyCharge (CHAR_DATA *pCh, long long int llValue, int nSn) { if (pCh->charge > 0) llValue = (pCh->charge * llValue) / skill_table[nSn].wait; if (pCh->charge == 1) // Immediately released skill -- deduction llValue /= 4; return llValue; } // Returns a calculated value for chance to hit in melee based on powerlevel, // stats, skill and other conditions, for some skill long long int get_attackhit (CHAR_DATA *ch, int sn) { long long int value; value = get_curr_stat(ch, STAT_DEX); value += get_skill(ch, sn) / 2; value += GET_HITROLL(ch); if (sn == gsn_heart_shot) value /= 2; else if (sn == gsn_eye_gouge) value /= 10; if (IS_AFFECTED (ch, AFF_BLIND)) value -= value / 5; if (IS_AFFECTED (ch, AFF_FLYING)) value += value / 4; if (ch->stance == STANCE_OFFEN) value += value / 4; else if (ch->stance == STANCE_DEFEN) value -= value / 4; else if (ch->stance == STANCE_KAMIK) value += 1 + get_skill(ch, gsn_kamikaze); value = ApplyMisc (ch, value); return value; } long long int get_attackdam (CHAR_DATA *ch, int sn) { long long int value; value = get_curr_stat(ch, STAT_STR); value += get_skill(ch, sn) / 2; value += GET_DAMROLL(ch); if (sn == gsn_throat_shot || sn == gsn_sweep || sn == gsn_eye_gouge) value = 1; else if (sn == gsn_knee) value /= 2; else if (sn == gsn_elbow) value /= 5; else if (sn == gsn_heart_shot) value *= 2; if (ch->stance == STANCE_OFFEN) value += value / 4; else if (ch->stance == STANCE_DEFEN) value -= value / 4; else if (ch->stance == STANCE_KAMIK) value += 1 + get_skill(ch, gsn_kamikaze); value = ApplyMisc (ch, value); value = UMAX(1, value); return value; } long long int get_attackabsorb (CHAR_DATA *ch, int sn, int dam_type) { long long int value = 0; switch (dam_type) { case DAM_NONE: break; case DAM_PIERCE: value += ch->armor[AC_PIERCE] / 5; break; case DAM_BASH: value += ch->armor[AC_BASH] / 5; break; case DAM_SLASH: value += ch->armor[AC_SLASH] / 5; break; default: value += ch->armor[AC_EXOTIC] / 5; break; } if (ch->stance == STANCE_OFFEN) value -= value / 4; else if (ch->stance == STANCE_DEFEN) value += value / 4; else if (ch->stance == STANCE_KAMIK) value = 0; if (IS_AFFECTED (ch, AFF_SANCTUARY)) value *= 4; if ((IS_AFFECTED (ch, AFF_PROTECT_EVIL) && IS_EVIL (ch)) || (IS_AFFECTED (ch, AFF_PROTECT_GOOD) && IS_GOOD (ch))) value += value / 4; value = ApplyMisc (ch, value); return value; } long long int get_attackdodge (CHAR_DATA *ch, int sn) { long long int value; value = get_curr_stat(ch, STAT_DEX); value += get_skill(ch, sn) / 2; if (IS_AFFECTED (ch, AFF_BLIND)) value -= value / 5; if (IS_AFFECTED (ch, AFF_FLYING)) value += value / 4; if (IS_AFFECTED (ch, AFF_SANCTUARY)) value *= 4; if (ch->stance == STANCE_OFFEN) value -= value / 4; else if (ch->stance == STANCE_DEFEN) value += value / 4; else if (ch->stance == STANCE_KAMIK) value -= 3 * value / 4; value = ApplyMisc (ch, value); return value; } long long int get_kihit (CHAR_DATA *ch, int sn) { long long int value; value = get_curr_stat(ch, STAT_INT); value += get_skill(ch, sn) / 2; value += GET_HITROLL(ch); if (IS_AFFECTED (ch, AFF_BLIND)) value -= value / 5; if (IS_AFFECTED (ch, AFF_FLYING)) value += value / 4; if (sn == gsn_scattershot) value /= 2; value = ApplyCharge (ch, value, sn); value = ApplyMisc (ch, value); return value; } long long int get_kidam (CHAR_DATA *ch, int sn) { long long int value; value = get_curr_stat(ch,STAT_WIL); value += get_skill(ch, sn) / 2; if (sn == gsn_power_bomb) value *= 4; else if (sn == gsn_spirit_bomb || sn == gsn_death_ball) value *= 3; else if (sn == gsn_finalflash || sn == gsn_kamehameha) value *= 2; else if (sn == gsn_scattershot || sn == gsn_solarflare) value /= 5; value = ApplyCharge (ch, value, sn); value = ApplyMisc (ch, value); value = UMAX(1, value); return value; } long long int get_kiabsorb (CHAR_DATA *ch, int sn, int dam_type) { long long int value = 0; // Armor! switch (dam_type) { case DAM_NONE: break; case DAM_PIERCE: value += ch->armor[AC_PIERCE] / 5; break; case DAM_BASH: value += ch->armor[AC_BASH] / 5; break; case DAM_SLASH: value += ch->armor[AC_SLASH] / 5; break; default: value += ch->armor[AC_EXOTIC] / 5; break; } if (IS_AFFECTED (ch, AFF_SANCTUARY)) value *= 4; value = ApplyMisc (ch, value); return value; } */