/* Ищем свободный слот под способность. Возвращает число от 0 до MAX_ACC_FEAT-1 - номер слота, 0, если способность врожденная и -1 если слотов не найдено. НЕ проверяет доступность врожденной способности на данном уровне. */ bool find_feat_slot(CHAR_DATA *ch, int feat) { int i, lowfeat, hifeat; //если способность врожденная - ее всегда можно получить if (feat_info[feat].natural_classfeat[(int) GET_CLASS(ch)][(int) GET_KIN(ch)] || PlayerRace::FeatureCheck(GET_KIN(ch),GET_RACE(ch),feat)) return TRUE; //сколько у нас вообще способностей, у которых слот меньше требуемого, и сколько - тех, у которых больше или равно? lowfeat = 0; hifeat = 0; for (i = 1; i < MAX_FEATS; i++) { if (feat_info[i].natural_classfeat[(int) GET_CLASS(ch)][(int) GET_KIN(ch)] || PlayerRace::FeatureCheck(GET_KIN(ch),GET_RACE(ch),i)) continue; if (HAVE_FEAT(ch,i) && (FEAT_SLOT(ch,i) < FEAT_SLOT(ch,feat))) lowfeat++; if (HAVE_FEAT(ch,i) && (FEAT_SLOT(ch,i) >= FEAT_SLOT(ch,feat))) hifeat++; } //из имеющегося количества слотов нужно вычесть: //число высоких слотов, занятых низкоуровневыми способностями, //с учтом, что низкоуровневые могут и не занимать слотов выше им положенных, //а также собственно число слотов, занятых высокоуровневыми способностями if (NUM_LEV_FEAT(ch)-FEAT_SLOT(ch, feat)-hifeat-MAX(0, lowfeat-FEAT_SLOT(ch, feat)) > 0) return TRUE; //oops.. слотов нет return FALSE; }
/* Может ли персонаж использовать способность? Проверка по уровню, ремортам, параметрам персонажа, требованиям. */ bool can_use_feat(CHAR_DATA *ch, int feat) { if (!HAVE_FEAT(ch, feat)) return FALSE; if (IS_NPC(ch)) return TRUE; if (NUM_LEV_FEAT(ch) < feat_info[feat].slot[(int) GET_CLASS(ch)][(int) GET_KIN(ch)]) return FALSE; if (GET_REMORT(ch) < feat_info[feat].min_remort[(int) GET_CLASS(ch)][(int) GET_KIN(ch)]) return FALSE; switch (feat) { case WEAPON_FINESSE_FEAT: if (GET_REAL_DEX(ch) < GET_REAL_STR(ch) || GET_REAL_DEX(ch) < 18) return FALSE; break; case PARRY_ARROW_FEAT: if (GET_REAL_DEX(ch) < 16) return FALSE; break; case POWER_ATTACK_FEAT: if (GET_REAL_STR(ch) < 20) return FALSE; break; case GREAT_POWER_ATTACK_FEAT: if (GET_REAL_STR(ch) < 22) return FALSE; break; case AIMING_ATTACK_FEAT: if (GET_REAL_DEX(ch) < 16) return FALSE; break; case GREAT_AIMING_ATTACK_FEAT: if (GET_REAL_DEX(ch) < 18) return FALSE; break; case DOUBLESHOT_FEAT: if (ch->get_skill(SKILL_BOWS) < 40) return FALSE; break; } return TRUE; }
/** * Выставление чару расовых способностей. */ void set_race_feats(CHAR_DATA *ch) { std::vector<int> feat_list = PlayerRace::GetRaceFeatures((int)GET_KIN(ch),(int)GET_RACE(ch)); for (std::vector<int>::iterator i = feat_list.begin(), iend = feat_list.end(); i != iend; ++i) { if (can_get_feat(ch, *i)) { SET_FEAT(ch, *i); } } }
void handle_recall_spells(CHAR_DATA* ch) { AFFECT_DATA* aff = NULL; for(AFFECT_DATA* af = ch->affected; af; af = af->next) if (af->type == SPELL_RECALL_SPELLS) { aff = af; break; } if (!aff) return; //максимальный доступный чару круг unsigned max_slot = get_max_slot(ch); //обрабатываем только каждые RECALL_SPELLS_INTERVAL секунд int secs_left = (SECS_PER_PLAYER_AFFECT*aff->duration)/SECS_PER_MUD_HOUR -SECS_PER_PLAYER_AFFECT; if (secs_left / RECALL_SPELLS_INTERVAL < max_slot -aff->modifier || secs_left <= 2) { int slot_to_restore = aff->modifier++; bool found_spells = false; struct spell_mem_queue_item *next = NULL, *prev=NULL, *i = ch->MemQueue.queue; while (i) { next = i->link; if (spell_info[i->spellnum].slot_forc[(int) GET_CLASS(ch)][(int) GET_KIN(ch)] == slot_to_restore) { if (!found_spells) { send_to_char("Ваша голова прояснилась, в памяти всплыло несколько новых заклинаний.\r\n", ch); found_spells = true; } if (prev) prev->link = next; if (i == ch->MemQueue.queue) { ch->MemQueue.queue = next; GET_MEM_COMPLETED(ch) = 0; } GET_MEM_TOTAL(ch) = MAX(0, GET_MEM_TOTAL(ch) - mag_manacost(ch, i->spellnum)); sprintf(buf, "Вы вспомнили заклинание \"%s%s%s\".\r\n", CCICYN(ch, C_NRM), spell_info[i->spellnum].name, CCNRM(ch, C_NRM)); send_to_char(buf, ch); GET_SPELL_MEM(ch, i->spellnum)++; free(i); } else prev = i; i = next; } } }
/* Может ли персонаж изучить эту способность? */ bool can_get_feat(CHAR_DATA *ch, int feat) { int i, count = 0; if (feat <= 0 || feat >= MAX_FEATS) { sprintf(buf, "Неверный номер способности (feat=%d, ch=%s) передан в features::can_get_feat!", feat, ch->get_name()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return FALSE; } /* Доступность по классу, реморту. */ if ((!feat_info[feat].classknow[(int) GET_CLASS(ch)][(int) GET_KIN(ch)] && !PlayerRace::FeatureCheck(GET_KIN(ch),GET_RACE(ch),feat)) || (GET_REMORT(ch) < feat_info[feat].min_remort[(int) GET_CLASS(ch)][(int) GET_KIN(ch)])) return FALSE; /* Наличие свободных слотов */ if (!find_feat_slot(ch, feat)) return FALSE; /* Специальные требования для изучения */ switch (feat) { case PARRY_ARROW_FEAT: if (!ch->get_skill(SKILL_MULTYPARRY) && !ch->get_skill(SKILL_PARRY)) return FALSE; break; case CONNOISEUR_FEAT: if (!ch->get_skill(SKILL_IDENTIFY)) return FALSE; break; case EXORCIST_FEAT: if (!ch->get_skill(SKILL_TURN_UNDEAD)) return FALSE; break; case HEALER_FEAT: if (!ch->get_skill(SKILL_AID)) return FALSE; break; case STEALTHY_FEAT: if (!ch->get_skill(SKILL_HIDE) && !ch->get_skill(SKILL_SNEAK) && !ch->get_skill(SKILL_CAMOUFLAGE)) return FALSE; break; case TRACKER_FEAT: if (!ch->get_skill(SKILL_TRACK) && !ch->get_skill(SKILL_SENSE)) return FALSE; break; case PUNCH_MASTER_FEAT: case CLUBS_MASTER_FEAT: case AXES_MASTER_FEAT: case LONGS_MASTER_FEAT: case SHORTS_MASTER_FEAT: case NONSTANDART_MASTER_FEAT: case BOTHHANDS_MASTER_FEAT: case PICK_MASTER_FEAT: case SPADES_MASTER_FEAT: case BOWS_MASTER_FEAT: if (!HAVE_FEAT(ch, (ubyte) feat_info[feat].affected[1].location)) return FALSE; for (i = PUNCH_MASTER_FEAT; i <= BOWS_MASTER_FEAT; i++) if (HAVE_FEAT(ch, i)) count++; if (count >= 1+GET_REMORT(ch)/7) return FALSE; break; case SPIRIT_WARRIOR_FEAT: if (!HAVE_FEAT(ch, GREAT_FORTITUDE_FEAT)) return FALSE; break; case NIMBLE_FINGERS_FEAT: if (!ch->get_skill(SKILL_STEAL) && !ch->get_skill(SKILL_PICK_LOCK)) return FALSE; break; case GREAT_POWER_ATTACK_FEAT: if (!HAVE_FEAT(ch, POWER_ATTACK_FEAT)) return FALSE; break; case PUNCH_FOCUS_FEAT: case CLUB_FOCUS_FEAT: case AXES_FOCUS_FEAT: case LONGS_FOCUS_FEAT: case SHORTS_FOCUS_FEAT: case NONSTANDART_FOCUS_FEAT: case BOTHHANDS_FOCUS_FEAT: case PICK_FOCUS_FEAT: case SPADES_FOCUS_FEAT: case BOWS_FOCUS_FEAT: if (!ch->get_skill((ubyte) feat_info[feat].affected[0].location)) return FALSE; for (i = PUNCH_FOCUS_FEAT; i <= BOWS_FOCUS_FEAT; i++) if (HAVE_FEAT(ch, i)) count++; if (count >= 2+GET_REMORT(ch)/6) return FALSE; break; case GREAT_AIMING_ATTACK_FEAT: if (!HAVE_FEAT(ch, AIMING_ATTACK_FEAT)) return FALSE; break; case DOUBLESHOT_FEAT: if (!HAVE_FEAT(ch, BOWS_FOCUS_FEAT) || ch->get_skill(SKILL_BOWS) < 40) return FALSE; break; case RUNE_USER_FEAT: if (!HAVE_FEAT(ch, RUNE_NEWBIE_FEAT)) return FALSE; break; case RUNE_MASTER_FEAT: if (!HAVE_FEAT(ch, RUNE_USER_FEAT)) return FALSE; break; case RUNE_ULTIMATE_FEAT: if (!HAVE_FEAT(ch, RUNE_MASTER_FEAT)) return FALSE; break; } return TRUE; }