uint32 Mob::GetInstrumentMod(uint16 spell_id) const { if (GetClass() != BARD || spells[spell_id].IsDisciplineBuff) // Puretone is Singing but doesn't get any mod return 10; uint32 effectmod = 10; int effectmodcap = 0; bool nocap = false; if (RuleB(Character, UseSpellFileSongCap)) { effectmodcap = spells[spell_id].songcap / 10; // this looks a bit weird, but easiest way I could think to keep both systems working if (effectmodcap == 0) nocap = true; else effectmodcap += 10; } else { effectmodcap = RuleI(Character, BaseInstrumentSoftCap); } // this should never use spell modifiers... // if a spell grants better modifers, they are copied into the item mods // because the spells are supposed to act just like having the intrument. // item mods are in 10ths of percent increases // clickies (Symphony of Battle) that have a song skill don't get AA bonus for some reason // but clickies that are songs (selo's on Composers Greaves) do get AA mod as well switch (spells[spell_id].skill) { case SkillPercussionInstruments: if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0) effectmod = 10; else if (GetSkill(SkillPercussionInstruments) == 0) effectmod = 10; else if (itembonuses.percussionMod > spellbonuses.percussionMod) effectmod = itembonuses.percussionMod; else effectmod = spellbonuses.percussionMod; if (IsBardSong(spell_id)) effectmod += aabonuses.percussionMod; break; case SkillStringedInstruments: if (itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0) effectmod = 10; else if (GetSkill(SkillStringedInstruments) == 0) effectmod = 10; else if (itembonuses.stringedMod > spellbonuses.stringedMod) effectmod = itembonuses.stringedMod; else effectmod = spellbonuses.stringedMod; if (IsBardSong(spell_id)) effectmod += aabonuses.stringedMod; break; case SkillWindInstruments: if (itembonuses.windMod == 0 && spellbonuses.windMod == 0) effectmod = 10; else if (GetSkill(SkillWindInstruments) == 0) effectmod = 10; else if (itembonuses.windMod > spellbonuses.windMod) effectmod = itembonuses.windMod; else effectmod = spellbonuses.windMod; if (IsBardSong(spell_id)) effectmod += aabonuses.windMod; break; case SkillBrassInstruments: if (itembonuses.brassMod == 0 && spellbonuses.brassMod == 0) effectmod = 10; else if (GetSkill(SkillBrassInstruments) == 0) effectmod = 10; else if (itembonuses.brassMod > spellbonuses.brassMod) effectmod = itembonuses.brassMod; else effectmod = spellbonuses.brassMod; if (IsBardSong(spell_id)) effectmod += aabonuses.brassMod; break; case SkillSinging: if (itembonuses.singingMod == 0 && spellbonuses.singingMod == 0) effectmod = 10; else if (itembonuses.singingMod > spellbonuses.singingMod) effectmod = itembonuses.singingMod; else effectmod = spellbonuses.singingMod; if (IsBardSong(spell_id)) effectmod += aabonuses.singingMod + spellbonuses.Amplification; break; default: effectmod = 10; return effectmod; } if (!RuleB(Character, UseSpellFileSongCap)) effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap; if (effectmod < 10) effectmod = 10; if (!nocap && effectmod > effectmodcap) // if the cap is calculated to be 0 using new rules, no cap. effectmod = effectmodcap; Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", GetName(), spell_id, effectmod, effectmodcap); return effectmod; }
bool Character::HasSkill(uint32 skillTypeID) const { return(GetSkill(skillTypeID) != NULL); }
void Character::UpdateSkillQueue() { Client *c = m_factory.entity_list.FindCharacter( itemID() ); SkillRef currentTraining = GetSkillInTraining(); if( currentTraining ) { if( m_skillQueue.empty() || currentTraining->typeID() != m_skillQueue.front().typeID ) { // either queue is empty or skill with different typeID is in training ... // stop training: _log( ITEM__ERROR, "%s (%u): Stopping training of skill %s (%u).", itemName().c_str(), itemID(), currentTraining->itemName().c_str(), currentTraining->itemID() ); /* uint64 timeEndTrain = currentTraining->expiryTime(); if(timeEndTrain != 0) { double nextLevelSP = currentTraining->GetSPForLevel( currentTraining->skillLevel() + 1 ); double SPPerMinute = GetSPPerMin( currentTraining ); double minRemaining = (double)(timeEndTrain - Win32TimeNow()) / (double)Win32Time_Minute; currentTraining->Set_skillPoints( nextLevelSP - (minRemaining * SPPerMinute) ); } currentTraining->Clear_expiryTime(); */ EvilNumber timeEndTrain = currentTraining->GetAttribute(AttrExpiryTime); if (timeEndTrain != 0) { EvilNumber nextLevelSP = currentTraining->GetSPForLevel( currentTraining->GetAttribute(AttrSkillLevel) + 1 ); EvilNumber SPPerMinute = GetSPPerMin( currentTraining ); EvilNumber minRemaining = (timeEndTrain - EvilNumber(Win32TimeNow())) / (double)Win32Time_Minute; //currentTraining->Set_skillPoints( nextLevelSP - (minRemaining * SPPerMinute) ); currentTraining->SetAttribute(AttrSkillPoints, nextLevelSP - (minRemaining * SPPerMinute)); } currentTraining->SetAttribute(AttrExpiryTime, 0); currentTraining->MoveInto( *this, flagSkill, true ); if( c != NULL ) { OnSkillTrainingStopped osst; osst.itemID = currentTraining->itemID(); osst.endOfTraining = 0; PyTuple* tmp = osst.Encode(); c->QueueDestinyEvent( &tmp ); PySafeDecRef( tmp ); c->UpdateSkillTraining(); } // nothing currently in training currentTraining = SkillRef(); } } EvilNumber nextStartTime = EvilTimeNow(); while( !m_skillQueue.empty() ) { if( !currentTraining ) { // something should be trained, get desired skill uint32 skillTypeID = m_skillQueue.front().typeID; currentTraining = GetSkill( skillTypeID ); if( !currentTraining ) { _log( ITEM__ERROR, "%s (%u): Skill %u to train was not found.", itemName().c_str(), itemID(), skillTypeID ); break; } _log( ITEM__TRACE, "%s (%u): Starting training of skill %s (%u).", m_itemName.c_str(), m_itemID, currentTraining->itemName().c_str(), currentTraining->itemID() ); EvilNumber SPPerMinute = GetSPPerMin( currentTraining ); // double SPToNextLevel = currentTraining->GetSPForLevel( currentTraining->skillLevel() + 1 ) - currentTraining->skillPoints(); EvilNumber SPToNextLevel = currentTraining->GetSPForLevel( currentTraining->GetAttribute(AttrSkillLevel) + 1) - currentTraining->GetAttribute(AttrSkillPoints); //uint64 timeTraining = nextStartTime + Win32Time_Minute * SPToNextLevel / SPPerMinute; EvilNumber timeTraining = nextStartTime + EvilTime_Minute * SPToNextLevel / SPPerMinute; currentTraining->MoveInto( *this, flagSkillInTraining ); //currentTraining->Set_expiryTime( timeTraining ); currentTraining->SetAttribute(AttrExpiryTime, timeTraining); if( c != NULL ) { OnSkillStartTraining osst; osst.itemID = currentTraining->itemID(); osst.endOfTraining = timeTraining.get_int(); PyTuple* tmp = osst.Encode(); c->QueueDestinyEvent( &tmp ); PySafeDecRef( tmp ); c->UpdateSkillTraining(); } } //if( currentTraining->expiryTime() <= Win32TimeNow() ) if( currentTraining->GetAttribute(AttrExpiryTime) <= EvilTimeNow() ) { // training has been finished: _log( ITEM__ERROR, "%s (%u): Finishing training of skill %s (%u).", itemName().c_str(), itemID(), currentTraining->itemName().c_str(), currentTraining->itemID() ); //currentTraining->Set_skillLevel( currentTraining->skillLevel() + 1 ); //currentTraining->Set_skillPoints( currentTraining->GetSPForLevel( currentTraining->skillLevel() ) ); //nextStartTime = currentTraining->expiryTime(); //currentTraining->Clear_expiryTime(); currentTraining->SetAttribute(AttrSkillLevel, currentTraining->GetAttribute(AttrSkillLevel) + 1 ); currentTraining->SetAttribute(AttrSkillPoints, currentTraining->GetSPForLevel( currentTraining->GetAttribute(AttrSkillLevel) ) ); nextStartTime = currentTraining->GetAttribute(AttrExpiryTime); currentTraining->SetAttribute(AttrExpiryTime, 0); currentTraining->MoveInto( *this, flagSkill, true ); if( c != NULL ) { OnSkillTrained ost; ost.itemID = currentTraining->itemID(); PyTuple* tmp = ost.Encode(); c->QueueDestinyEvent( &tmp ); PySafeDecRef( tmp ); c->UpdateSkillTraining(); } // erase first element in skill queue m_skillQueue.erase( m_skillQueue.begin() ); // nothing currently in training currentTraining = SkillRef(); } // else the skill is in training ... else break; } // Re-Calculate total SP trained and store in internal variable: _CalculateTotalSPTrained(); // Save character and skill data: SaveCharacter(); SaveSkillQueue(); }
void Client::GoFish() { //TODO: generate a message if we're already fishing /*if (!fishing_timer.Check()) { //this isn't the right check, may need to add something to the Client class like 'bool is_fishing' Message_StringID(0, ALREADY_FISHING); //You are already fishing! return; }*/ fishing_timer.Disable(); //we're doing this a second time (1st in Client::Handle_OP_Fishing) to make sure that, between when we started fishing & now, we're still able to fish (in case we move, change equip, etc) if (!CanFish()) //if we can't fish here, we don't need to bother with the rest return; //multiple entries yeilds higher probability of dropping... uint32 common_fish_ids[MAX_COMMON_FISH_IDS] = { 1038, // Tattered Cloth Sandals 1038, // Tattered Cloth Sandals 1038, // Tattered Cloth Sandals 13019, // Fresh Fish 13076, // Fish Scales 13076, // Fish Scales 7007, // Rusty Dagger 7007, // Rusty Dagger 7007 // Rusty Dagger }; //success formula is not researched at all int fishing_skill = GetSkill(SkillFishing); //will take into account skill bonuses on pole & bait //make sure we still have a fishing pole on: int32 bslot = m_inv.HasItemByUse(ItemTypeFishingBait, 1, invWhereWorn|invWherePersonal); const ItemInst* Bait = nullptr; if(bslot != SLOT_INVALID) Bait = m_inv.GetItem(bslot); //if the bait isnt equipped, need to add its skill bonus if(bslot >= IDX_INV && Bait->GetItem()->SkillModType == SkillFishing) { fishing_skill += Bait->GetItem()->SkillModValue; } if (fishing_skill > 100) { fishing_skill = 100+((fishing_skill-100)/2); } if (MakeRandomInt(0,175) < fishing_skill) { uint32 food_id = 0; //25% chance to fish an item. if (MakeRandomInt(0, 399) <= fishing_skill ) { uint32 npc_id = 0; uint8 npc_chance = 0; food_id = database.GetZoneFishing(m_pp.zone_id, fishing_skill, npc_id, npc_chance); //check for add NPC if(npc_chance > 0 && npc_id) { if(npc_chance < MakeRandomInt(0, 99)) { const NPCType* tmp = database.GetNPCType(npc_id); if(tmp != nullptr) { NPC* npc = new NPC(tmp, nullptr, GetX()+3, GetY(), GetZ(), GetHeading(), FlyMode3); npc->AddLootTable(); npc->AddToHateList(this, 1, 0, false); //no help yelling entity_list.AddNPC(npc); Message(MT_Emote, "You fish up a little more than you bargained for..."); } } } } //consume bait, should we always consume bait on success? DeleteItemInInventory(bslot, 1, true); //do we need client update? if(food_id == 0) { int index = MakeRandomInt(0, MAX_COMMON_FISH_IDS-1); food_id = common_fish_ids[index]; } const Item_Struct* food_item = database.GetItem(food_id); Message_StringID(MT_Skills, FISHING_SUCCESS); ItemInst* inst = database.CreateItem(food_item, 1); if(inst != nullptr) { if(CheckLoreConflict(inst->GetItem())) { Message_StringID(0, DUP_LORE); safe_delete(inst); } else { PushItemOnCursor(*inst); SendItemPacket(SLOT_CURSOR,inst,ItemPacketSummonItem); if(RuleB(TaskSystem, EnableTaskSystem)) UpdateTasksForItem(ActivityFish, food_id); safe_delete(inst); inst = m_inv.GetItem(SLOT_CURSOR); } } std::vector<void*> args; args.push_back(inst); parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst != nullptr ? inst->GetItem()->ID : 0, &args); } else { //chance to use bait when you dont catch anything... if (MakeRandomInt(0, 4) == 1) { DeleteItemInInventory(bslot, 1, true); //do we need client update? Message_StringID(MT_Skills, FISHING_LOST_BAIT); //You lost your bait! } else { if (MakeRandomInt(0, 15) == 1) //give about a 1 in 15 chance to spill your beer. we could make this a rule, but it doesn't really seem worth it //TODO: check for & consume an alcoholic beverage from inventory when this triggers, and set it as a rule that's disabled by default Message_StringID(MT_Skills, FISHING_SPILL_BEER); //You spill your beer while bringing in your line. else Message_StringID(MT_Skills, FISHING_FAILED); //You didn't catch anything. } parse->EventPlayer(EVENT_FISH_FAILURE, this, "", 0); } //chance to break fishing pole... //this is potentially exploitable in that they can fish //and then swap out items in primary slot... too lazy to fix right now if (MakeRandomInt(0, 49) == 1) { Message_StringID(MT_Skills, FISHING_POLE_BROKE); //Your fishing pole broke! DeleteItemInInventory(13,0,true); } if(CheckIncreaseSkill(SkillFishing, nullptr, 5)) { if(title_manager.IsNewTradeSkillTitleAvailable(SkillFishing, GetRawSkill(SkillFishing))) NotifyNewTitlesAvailable(); } }
void Client::ForageItem(bool guarantee) { int skill_level = GetSkill(SkillForage); //be wary of the string ids in switch below when changing this. uint32 common_food_ids[MAX_COMMON_FOOD_IDS] = { 13046, // Fruit 13045, // Berries 13419, // Vegetables 13048, // Rabbit Meat 13047, // Roots 13044, // Pod Of Water 14905, // mushroom 13106 // Fishing Grubs }; // these may need to be fine tuned, I am just guessing here if (guarantee || MakeRandomInt(0,199) < skill_level) { uint32 foragedfood = 0; uint32 stringid = FORAGE_NOEAT; if (MakeRandomInt(0,99) <= 25) { foragedfood = database.GetZoneForage(m_pp.zone_id, skill_level); } //not an else in case theres no DB food if(foragedfood == 0) { uint8 index = 0; index = MakeRandomInt(0, MAX_COMMON_FOOD_IDS-1); foragedfood = common_food_ids[index]; } const Item_Struct* food_item = database.GetItem(foragedfood); if(!food_item) { LogFile->write(EQEMuLog::Error, "nullptr returned from database.GetItem in ClientForageItem"); return; } if(foragedfood == 13106) stringid = FORAGE_GRUBS; else switch(food_item->ItemType) { case ItemTypeFood: stringid = FORAGE_FOOD; break; case ItemTypeDrink: if(strstr(food_item->Name, "ater")) stringid = FORAGE_WATER; else stringid = FORAGE_DRINK; break; default: break; } Message_StringID(MT_Skills, stringid); ItemInst* inst = database.CreateItem(food_item, 1); if(inst != nullptr) { // check to make sure it isn't a foraged lore item if(CheckLoreConflict(inst->GetItem())) { Message_StringID(0, DUP_LORE); safe_delete(inst); } else { PushItemOnCursor(*inst); SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem); if(RuleB(TaskSystem, EnableTaskSystem)) UpdateTasksForItem(ActivityForage, foragedfood); safe_delete(inst); inst = m_inv.GetItem(SLOT_CURSOR); } } std::vector<void*> args; args.push_back(inst); parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst ? inst->GetItem()->ID : 0, &args); int ChanceSecondForage = aabonuses.ForageAdditionalItems + itembonuses.ForageAdditionalItems + spellbonuses.ForageAdditionalItems; if(!guarantee && MakeRandomInt(0,99) < ChanceSecondForage) { Message_StringID(MT_Skills, FORAGE_MASTERY); ForageItem(true); } } else { Message_StringID(MT_Skills, FORAGE_FAILED); parse->EventPlayer(EVENT_FORAGE_FAILURE, this, "", 0); } CheckIncreaseSkill(SkillForage, nullptr, 5); }
Chapter::Chapter(const int chapter_number) : m_ball_game_chapter{*this}, m_bye_text{}, m_consequence{}, m_chapter_number{chapter_number}, m_chapter_type{ChapterType::normal}, m_dice_game_chapter{*this}, m_fighting_chapter{FightingChapter(*this)}, m_game_lost_chapter{this}, m_game_won_chapter{this}, m_luck_chapter(*this), m_observer{nullptr}, m_options_chapter{}, m_pawn_shop_chapter(this), m_pill_game_chapter{*this}, m_shop_chapter{this}, m_skill_chapter{*this}, m_text{}, m_verbose{false} { if (m_verbose) { std::clog << __func__ << std::endl; } Helper h; if (m_verbose) { std::clog << __func__ << std::endl; } #ifdef USE_TEXT_FILES_FOR_INPUT const std::string filename{h.GetFilesFolder() + h.ToStr(chapter_number) + ".txt"}; if (!h.IsRegularFile(filename)) { std::stringstream msg; msg << __func__ << ": ERROR: File " << filename << " does not exist"; Helper().Cout(msg.str()); throw std::runtime_error(msg.str()); } const std::vector<std::string> lines{h.FileToVector(filename)}; #else const std::vector<std::string> lines(1,GetFile(h.ToStr(chapter_number))); #endif std::stringstream s; std::copy(std::begin(lines),std::end(lines),std::ostream_iterator<std::string>(s," ")); m_text = h.ReadText(s); m_chapter_type = ReadChapterType(s); switch (m_chapter_type) { case ChapterType::game_lost: return; case ChapterType::game_won: return; default: break; } while (!s.eof()) { const std::string str{h.ReadString(s)}; if (str.empty()) { break; } else if (str == "Bye" || str == "bye") { m_bye_text = h.ReadText(s); } else if (str == "Change" || str == "change") { m_consequence.Add(ParseConsequence(s)); } else if (str == "Escape" || str == "escape") { GetFighting().SetRoundsToEscape(h.ReadInt(s)); GetFighting().SetEscapeToChapter(h.ReadInt(s)); } else if (str == "Fight_both" || str == "fight_both") { GetFighting().SetFightSequentially(false); } else if (str == "Luck" || str == "luck") { assert(this->m_chapter_type == ChapterType::test_your_luck); const std::string luck_text{h.ReadText(s)}; assert(!luck_text.empty()); GetLuck().SetLuckText(luck_text); const std::string goto_str{h.ReadString(s)}; assert(goto_str == "goto"); const int luck_chapter{h.ReadInt(s)}; assert(luck_chapter > 1); GetLuck().SetLuckChapter(luck_chapter); } else if (str == "Monster" || str == "monster") { this->m_chapter_type = ChapterType::fight; const Monster monster{ParseMonster(s)}; GetFighting().AddMonster(monster); } else if (str == "Next_chapter" || str == "goto") { m_consequence.SetNextChapter(h.ReadInt(s)); } else if (str == "No_luck" || str == "no_luck") { assert(this->m_chapter_type == ChapterType::test_your_luck); //s << std::noskipws; //Obligatory //Parse(s,' '); //You expect a space after a word const std::string no_luck_text{h.ReadText(s)}; assert(!no_luck_text.empty()); GetLuck().SetNoLuckText(no_luck_text); const std::string then{h.ReadString(s)}; if (then == "change") { const Consequence no_luck_consequence{ParseConsequence(s)}; GetLuck().SetNoLuckConsequence(no_luck_consequence); const std::string goto_str{h.ReadString(s)}; assert(goto_str == "goto"); } else { assert(then == "goto"); } const int no_luck_chapter{h.ReadInt(s)}; assert(no_luck_chapter > 1); GetLuck().SetNoLuckChapter(no_luck_chapter); } else if (str == "No_skill" || str == "no_skill") { assert(this->m_chapter_type == ChapterType::test_your_skill); //Parse(s,' '); //You expect a space after a word const std::string no_skill_text{h.ReadText(s)}; assert(!no_skill_text.empty()); GetSkill().SetNoSkillText(no_skill_text); const std::string then_str{h.ReadString(s)}; Consequence consequence; if (then_str == "goto") { consequence.SetNextChapter(h.ReadInt(s)); } else if (then_str == "change") { consequence = ParseConsequence(s); //Also read goto const std::string goto_str{h.ReadString(s)}; assert(goto_str == "goto"); consequence.SetNextChapter(h.ReadInt(s)); } else { assert(!"Should not get here"); } GetSkill().SetNoSkillConsequence(consequence); } else if (str == "Option" || str == "option") { const std::string option_text{h.ReadText(s)}; const std::string t{h.ReadString(s)}; if (t == "if") { const Condition condition{ParseCondition(s)}; const std::string then_str{h.ReadString(s)}; Consequence consequence; if (then_str == "goto") { consequence.SetNextChapter(h.ReadInt(s)); } else if (then_str == "change") { consequence = ParseConsequence(s); //Also read goto const std::string goto_str{h.ReadString(s)}; assert(goto_str == "goto"); consequence.SetNextChapter(h.ReadInt(s)); } else { assert(!"Should not get here"); } Option option(option_text,consequence); option.SetCondition(condition); GetOptions().AddOption(option); } else if (t == "ifnot") { Condition condition; const std::string what{h.ReadString(s)}; if (IsItem(what)) { const Item item_not_needed{ToItem(what)}; condition.AddItemNotNeeded(item_not_needed); } else { std::cerr << "Unknown item " << what << " in chapter " << chapter_number << std::endl; assert(!"Should not get here"); } const std::string str_goto{h.ReadString(s)}; assert(str_goto == "goto"); Consequence consequence; consequence.SetNextChapter(h.ReadInt(s)); Option option(option_text,consequence); option.SetCondition(condition); GetOptions().AddOption(option); } else if (t == "goto") { Consequence consequence; consequence.SetNextChapter(h.ReadInt(s)); const Option option(option_text,consequence); GetOptions().AddOption(option); } else if (h.IsInt(t)) { std::clog << "WARNING: goto omitted in chapter " << chapter_number << std::endl; //If no goto, just parse the number Consequence consequence; consequence.SetNextChapter(h.ToInt(t)); const Option option(option_text,consequence); GetOptions().AddOption(option); } else { std::cerr << "Unknown option " << t << " in chapter " << chapter_number <<std::endl; assert(!"Should not get here"); } } else if (str == "Random_monsters" || str == "random_monsters") { std::vector<Monster> monsters{ParseMonsters(s)}; m_chapter_type = ChapterType::fight; const int which_monster_index{Dice::Get()->Throw() - 1}; assert(which_monster_index >= 0); assert(which_monster_index < static_cast<int>(monsters.size())); const Monster monster{monsters[which_monster_index]}; m_fighting_chapter.AddMonster(monster); } else if (str == "Sell_items" || str == "sell_items") { assert(this->m_chapter_type == ChapterType::pawn_shop); //m_chapter_type = ChapterType::pawn_shop; m_pawn_shop_chapter = ParsePawnShopChapter(s,this); } else if (str == "Shop_items" || str == "shop_items") { assert(this->m_chapter_type == ChapterType::shop); //m_chapter_type = ChapterType::shop; m_shop_chapter = ParseShopChapter(s,this); } else if (str == "Skill" || str == "skill") { assert(this->m_chapter_type == ChapterType::test_your_skill); this->m_chapter_type = ChapterType::test_your_skill; //Parse(s,' '); //You expect a space after a word const std::string skill_text{h.ReadText(s)}; assert(!skill_text.empty()); GetSkill().SetSkillText(skill_text); const std::string then_str{h.ReadString(s)}; Consequence consequence; if (then_str == "goto") { consequence.SetNextChapter(h.ReadInt(s)); } else if (then_str == "change") { consequence = ParseConsequence(s); //Also read goto const std::string goto_str{h.ReadString(s)}; assert(goto_str == "goto"); consequence.SetNextChapter(h.ReadInt(s)); } else { assert(!"Should not get here"); } GetSkill().SetSkillConsequence(consequence); } else { std::cerr << "Chapter cannot parse chapter " << chapter_number << '\n' << "Unknown string: " << str << '\n' ; assert(!"Should not get here"); } } }
void Chapter::Do(Character& character) const { if (m_verbose) { std::clog << __func__ << std::endl; } Helper h; assert(m_observer); character.SetObserver(m_observer); if (m_verbose) { std::clog << "The first text comes now" << std::endl; } ShowText("\n"); #ifndef NDEBUG ShowText("CHAPTER " + h.ToStr(GetChapterNumber()) + "\n"); #endif //Display the text line by line ShowText(m_text + "\n"); if (m_verbose) { std::clog << "The first text has been" << std::endl; } if (GetType() == ChapterType::game_lost) { m_game_lost_chapter.Do(character); assert(character.IsDead()); return; } else if (GetType() == ChapterType::game_won) { ShowText("\n"); m_game_won_chapter.Do(character); ShowText("\n"); return; } else if (GetType() == ChapterType::play_dice) { m_dice_game_chapter.Do(character); m_consequence.Apply(character); } else if (GetType() == ChapterType::play_ball) { m_ball_game_chapter.Do(character); m_consequence.Apply(character); } else if (GetType() == ChapterType::play_pill) { m_pill_game_chapter.Do(character); if (character.IsDead()) return; m_consequence.Apply(character); } //Options else if (!GetOptions().GetOptions().empty()) { if (GetOptions().GetValidOptions(character).empty()) { std::cerr << "ERROR: no valid options in chapter " << character.GetCurrentChapter() << std::endl << "Options:\n" ; assert(GetOptions().GetOptions().size() == 2); } while (1) { if (m_verbose) { std::clog << "Let the use choose a valid option" << std::endl; } auto options = GetOptions().GetValidOptions(character); //If there are options, add (1) showing inventory (2) eating provision (3) drinking potion if (options.size() > 1) { //Add to show the inventory options.push_back(CreateShowInventoryOption()); if (character.GetProvisions() > 0) { options.push_back(CreateEatProvisionOption()); } if (character.HasPotion()) { options.push_back(CreateDrinkPotionOption()); } } if (m_verbose) { std::clog << "Do the request" << std::endl; } assert(m_observer); const auto chosen = m_observer->RequestOption(options); if (m_verbose) { std::clog << "Done the request" << std::endl; } ShowText("\n"); if (chosen.GetConsequence().GetType() == ConsequenceType::show_inventory) { //Showing the inventory is trivial this->ShowText(character.ShowInventory()); continue; } if (chosen.GetConsequence().GetType() == ConsequenceType::eat_provision) { character.ChangeProvisions(-1); character.ChangeCondition(4); continue; } if (chosen.GetConsequence().GetType() == ConsequenceType::drink_potion) { character.DrinkPotion(); continue; } chosen.DoChoose(character); assert(m_consequence.GetNextChapter() == -1); //Only apply these consequences once m_consequence.Apply(character); break; } } else if (GetType() == ChapterType::fight) { const int n_chapters_before{static_cast<int>(character.GetChapters().size())}; m_fighting_chapter.Do(character); if (character.IsDead()) { m_game_lost_chapter.Do(character); return; } assert(m_consequence.GetNextChapter() > 0); const int n_chapters_after{static_cast<int>(character.GetChapters().size())}; if (n_chapters_after != n_chapters_before) { //Player has escaped //Check that there are no other consequences that need to be applied assert(m_consequence.GetChangeArrows() == 0); assert(m_consequence.GetChangeCondition() == 0); assert(m_consequence.GetChangeGold() == 0); assert(m_consequence.GetChangeLuck() == 0); assert(m_consequence.GetChangeProvisions() == 0); assert(m_consequence.GetChangeSkill() == 0); assert(m_consequence.GetItemsToAdd().empty()); assert(m_consequence.GetItemsToRemove().empty()); } else { m_consequence.Apply(character); } } else if (GetType() == ChapterType::test_your_luck) { GetLuck().Do(character); } else if (GetType() == ChapterType::test_your_skill) { GetSkill().Do(character); } else if (GetType() == ChapterType::shop) { GetShop().Do(character); m_consequence.Apply(character); } else if (GetType() == ChapterType::pawn_shop) { GetPawnShop().Do(character); m_consequence.Apply(character); } else if (GetType() == ChapterType::normal) { //Nothing m_consequence.Apply(character); } else { assert(!"Should not get here"); } if (character.IsDead()) { m_game_lost_chapter.Do(character); return; } ShowText(m_bye_text); }
int32 Client::CalcManaRegen(bool bCombat) { int regen = 0; auto level = GetLevel(); // so the new formulas break down with older skill caps where you don't have the skill until 4 or 8 // so for servers that want to use the old skill progression they can set this rule so they // will get at least 1 for standing and 2 for sitting. bool old = RuleB(Character, OldMinMana); if (!IsStarved()) { // client does some base regen for shrouds here if (IsSitting() || CanMedOnHorse()) { // kind of weird to do it here w/e // client does some base medding regen for shrouds here if (GetClass() != BARD) { auto skill = GetSkill(EQEmu::skills::SkillMeditate); if (skill > 0) { regen++; if (skill > 1) regen++; if (skill >= 15) regen += skill / 15; } } if (old) regen = std::max(regen, 2); } else if (old) { regen = std::max(regen, 1); } } if (level > 61) { regen++; if (level > 63) regen++; } regen += aabonuses.ManaRegen; // add in + 1 bonus for SE_CompleteHeal, but we don't do anything for it yet? int item_bonus = itembonuses.ManaRegen; // this is capped already int heroic_bonus = 0; switch (GetCasterClass()) { case 'W': heroic_bonus = GetHeroicWIS(); break; default: heroic_bonus = GetHeroicINT(); break; } item_bonus += heroic_bonus / 25; regen += item_bonus; if (level <= 70 && regen > 65) regen = 65; regen = regen * 100.0f * AreaManaRegen * 0.01f + 0.5f; if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) { auto max_mana = GetMaxMana(); int fast_regen = 6 * (max_mana / zone->newzone_data.FastRegenMana); if (regen < fast_regen) // weird, but what the client is doing regen = fast_regen; } regen += spellbonuses.ManaRegen; // TODO: live does this in buff tick return (regen * RuleI(Character, ManaRegenMultiplier) / 100); }
uint32 Mob::GetInstrumentMod(uint16 spell_id) const { if (GetClass() != BARD) return 10; uint32 effectmod = 10; int effectmodcap = RuleI(Character, BaseInstrumentSoftCap); //this should never use spell modifiers... //if a spell grants better modifers, they are copied into the item mods //because the spells are supposed to act just like having the intrument. //item mods are in 10ths of percent increases switch (spells[spell_id].skill) { case SkillPercussionInstruments: if(itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0) effectmod = 10; else if(GetSkill(SkillPercussionInstruments) == 0) effectmod = 10; else if(itembonuses.percussionMod > spellbonuses.percussionMod) effectmod = itembonuses.percussionMod; else effectmod = spellbonuses.percussionMod; effectmod += aabonuses.percussionMod; break; case SkillStringedInstruments: if(itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0) effectmod = 10; else if(GetSkill(SkillStringedInstruments) == 0) effectmod = 10; else if(itembonuses.stringedMod > spellbonuses.stringedMod) effectmod = itembonuses.stringedMod; else effectmod = spellbonuses.stringedMod; effectmod += aabonuses.stringedMod; break; case SkillWindInstruments: if(itembonuses.windMod == 0 && spellbonuses.windMod == 0) effectmod = 10; else if(GetSkill(SkillWindInstruments) == 0) effectmod = 10; else if(itembonuses.windMod > spellbonuses.windMod) effectmod = itembonuses.windMod; else effectmod = spellbonuses.windMod; effectmod += aabonuses.windMod; break; case SkillBrassInstruments: if(itembonuses.brassMod == 0 && spellbonuses.brassMod == 0) effectmod = 10; else if(GetSkill(SkillBrassInstruments) == 0) effectmod = 10; else if(itembonuses.brassMod > spellbonuses.brassMod) effectmod = itembonuses.brassMod; else effectmod = spellbonuses.brassMod; effectmod += aabonuses.brassMod; break; case SkillSinging: if(itembonuses.singingMod == 0 && spellbonuses.singingMod == 0) effectmod = 10; else if(itembonuses.singingMod > spellbonuses.singingMod) effectmod = itembonuses.singingMod; else effectmod = spellbonuses.singingMod; effectmod += aabonuses.singingMod + spellbonuses.Amplification; break; default: effectmod = 10; break; } effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap; if (effectmod < 10) effectmod = 10; if (effectmod > effectmodcap) effectmod = effectmodcap; Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", GetName(), spell_id, effectmod, effectmodcap); return effectmod; }
//初始化主角各动作的动画 bool Hero::init(){ if (!Sprite::init()){ return false; } //将包含主角所有动作帧的合图载入到帧缓存池中 //SpriteFrameCache::getInstance()->addSpriteFramesWithFile("hero.plist", "hero.png"); //设置主角初始帧 this->initWithSpriteFrameName("hero_stand00.png"); //创建站立动画,帧数8,帧率10 Animation* standAnimation = GameUtile::createNormalAction("hero_stand%02d.png", 8, 10); this->setStandAction(RepeatForever::create(Animate::create(standAnimation))); //创建行走动画,帧数8,帧率20 Animation* walkAnimation = GameUtile::createNormalAction("hero_walk%02d.png", 8, 14); this->setWalkAction(RepeatForever::create(Animate::create(walkAnimation))); Animation* jumpAnimation = GameUtile::createNormalAction("hero_jump%02d.png", 9, 20); this->setJumpAction(Sequence::create(Animate::create(jumpAnimation), CallFuncN::create(CC_CALLBACK_0(Hero::EnableMoveable, this)), NULL)); //Animation* dropAnimation = GameUtile::createNormalAction("hero_stand00.png", 1, 10); //this->setDropAction(RepeatForever::create(Animate::create(dropAnimation))); Animation* crouchAnimation = GameUtile::createNormalAction("hero_crouch%02d.png", 4, 20); this->setCrouchAction(Animate::create(crouchAnimation)); Animation* standupAnimation = GameUtile::createNormalAction("hero_standup%02d.png", 4, 20); this->setStandupAction(Sequence::create(Animate::create(standupAnimation), CallFuncN::create(CC_CALLBACK_0(Hero::EnableMoveable, this)), NULL)); Animation* attackAnimation = GameUtile::createNormalAction("hero_attack%02d.png", 12, 20); this->setAttackAction(Sequence::create(Animate::create(attackAnimation), CallFuncN::create(CC_CALLBACK_0(Hero::EnableMoveable, this)), NULL)); Animation* walkAttackAnimation = GameUtile::createNormalAction("hero_walkattack%02d.png", 6, 30); this->setWalkAttackAction(Sequence::create(Animate::create(walkAttackAnimation), CallFuncN::create(CC_CALLBACK_0(Hero::EnableMoveable, this)), NULL)); Animation* jumpAttackAnimation = GameUtile::createNormalAction("hero_jumpattack%02d.png", 4, 14); this->setJumpAttackAction(Sequence::create(Animate::create(jumpAttackAnimation), CallFuncN::create(CC_CALLBACK_0(Hero::EnableMoveable, this)), NULL)); Animation* hurtAnimation = GameUtile::createNormalAction("hero_hurt%02d.png", 2, 20); this->setHurtAction(Sequence::create(Animate::create(hurtAnimation), CallFuncN::create(CC_CALLBACK_0(Role::EndHurt, this)), NULL)); Animation* dieAnimation = GameUtile::createNormalAction("hero_die%02d.png", 5, 10); this->setHurtAction(Animate::create(dieAnimation)); //Add other actions here m_currentHp = 100; m_maxHp = 100; m_currentMp = 100; m_maxMp = 100; m_strenth = 20; m_defence = 20; m_moveVelocity = 5; m_bodyBox = GameUtile::createBoundingBox(Vec2(32, 16), getContentSize() - Size(64, 28)); m_attackBox = GameUtile::createBoundingBox(Vec2(80, 20), Size(80, getContentSize().height + 20)); m_talkingBox = GameUtile::createBoundingBox(Vec2(-400, -50), Size(800, getContentSize().height + 100)); GetTool(TOOL_HPBOTTLE); GetSkill(SKILL_THROW_FIRE); //设置初始速度 this->setVelocity(Vec2(0, 0)); //set other properties here return true; }
void Window_Skill::UpdateHelp() { help_window->SetText(GetSkill() == NULL ? "" : GetSkill()->description); }
//returns true on success bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if(spec == nullptr) return(false); uint16 user_skill = GetSkill(spec->tradeskill); float chance = 0.0; float skillup_modifier = 0.0; int16 thirdstat = 0; int16 stat_modifier = 15; uint16 success_modifier = 0; // Rework based on the info on eqtraders.com // http://mboards.eqtraders.com/eq/showthread.php?t=22246 // 09/10/2006 v0.1 (eq4me) // 09/11/2006 v0.2 (eq4me) // Todo: // Implementing AAs // Success modifiers based on recipes // Skillup modifiers based on the rarity of the ingredients // Some tradeskills are more eqal then others. ;-) // If you want to customize the stage1 success rate do it here. // Remember: skillup_modifier is (float). Lower is better switch(spec->tradeskill) { case SkillFletching: skillup_modifier = RuleI(Character, TradeskillUpFletching); break; case SkillAlchemy: skillup_modifier = RuleI(Character, TradeskillUpAlchemy); break; case SkillJewelryMaking: skillup_modifier = RuleI(Character, TradeskillUpJewelcrafting); break; case SkillPottery: skillup_modifier = RuleI(Character, TradeskillUpPottery); break; case SkillBaking: skillup_modifier = RuleI(Character, TradeskillUpBaking); break; case SkillBrewing: skillup_modifier = RuleI(Character, TradeskillUpBrewing); break; case SkillBlacksmithing: skillup_modifier = RuleI(Character, TradeskillUpBlacksmithing); break; case SkillResearch: skillup_modifier = RuleI(Character, TradeskillUpResearch); break; case SkillMakePoison: skillup_modifier = RuleI(Character, TradeskillUpMakePoison); break; case SkillTinkering: skillup_modifier = RuleI(Character, TradeskillUpTinkering); break; default: skillup_modifier = 2; break; } // Some tradeskills take the higher of one additional stat beside INT and WIS // to determine the skillup rate. Additionally these tradeskills do not have an // -15 modifier on their statbonus. if (spec->tradeskill == SkillFletching || spec->tradeskill == SkillMakePoison) { thirdstat = GetDEX(); stat_modifier = 0; } else if (spec->tradeskill == SkillBlacksmithing) { thirdstat = GetSTR(); stat_modifier = 0; } int16 higher_from_int_wis = (GetINT() > GetWIS()) ? GetINT() : GetWIS(); int16 bonusstat = (higher_from_int_wis > thirdstat) ? higher_from_int_wis : thirdstat; std::vector< std::pair<uint32,uint8> >::iterator itr; //calculate the base success chance // For trivials over 68 the chance is (skill - 0.75*trivial) +51.5 // For trivial up to 68 the chance is (skill - trivial) + 66 if (spec->trivial >= 68) { chance = (user_skill - (0.75*spec->trivial)) + 51.5; } else { chance = (user_skill - spec->trivial) + 66; } int16 over_trivial = (int16)GetRawSkill(spec->tradeskill) - (int16)spec->trivial; //handle caps if(spec->nofail) { chance = 100; //cannot fail. Log.Out(Logs::Detail, Logs::Tradeskills, "...This combine cannot fail."); } else if(over_trivial >= 0) { // At reaching trivial the chance goes to 95% going up an additional // percent for every 40 skillpoints above the trivial. // The success rate is not modified through stats. // Mastery AAs are unaccounted for so far. // chance_AA = chance + ((100 - chance) * mastery_modifier) // But the 95% limit with an additional 1% for every 40 skill points // above critical still stands. // Mastery modifier is: 10%/25%/50% for rank one/two/three chance = 95.0f + (float(user_skill - spec->trivial) / 40.0f); Message_StringID(MT_Emote, TRADESKILL_TRIVIAL); } else if(chance < 5) { // Minimum chance is always 5 chance = 5; } else if(chance > 95) { //cap is 95, shouldent reach this before trivial, but just in case. chance = 95; } Log.Out(Logs::Detail, Logs::Tradeskills, "...Current skill: %d , Trivial: %d , Success chance: %f percent", user_skill , spec->trivial , chance); Log.Out(Logs::Detail, Logs::Tradeskills, "...Bonusstat: %d , INT: %d , WIS: %d , DEX: %d , STR: %d", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR()); float res = zone->random.Real(0, 99); int aa_chance = 0; aa_chance = spellbonuses.ReduceTradeskillFail[spec->tradeskill] + itembonuses.ReduceTradeskillFail[spec->tradeskill] + aabonuses.ReduceTradeskillFail[spec->tradeskill]; const Item_Struct* item = nullptr; chance = mod_tradeskill_chance(chance, spec); if (((spec->tradeskill==75) || GetGM() || (chance > res)) || zone->random.Roll(aa_chance)) { success_modifier = 1; if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); Message_StringID(4, TRADESKILL_SUCCEED, spec->name.c_str()); Log.Out(Logs::Detail, Logs::Tradeskills, "Tradeskill success"); itr = spec->onsuccess.begin(); while(itr != spec->onsuccess.end() && !spec->quest) { //should we check this crap? SummonItem(itr->first, itr->second); item = database.GetItem(itr->first); if (this->GetGroup()) { entity_list.MessageGroup(this, true, MT_Skills, "%s has successfully fashioned %s!", GetName(), item->Name); } /* QS: Player_Log_Trade_Skill_Events */ if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){ std::string event_desc = StringFormat("Success :: fashioned recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID()); QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc); } if(RuleB(TaskSystem, EnableTaskSystem)) UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second); ++itr; } return(true); } /* Tradeskill Fail */ else { success_modifier = 2; // Halves the chance if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); Message_StringID(MT_Emote,TRADESKILL_FAILED); Log.Out(Logs::Detail, Logs::Tradeskills, "Tradeskill failed"); if (this->GetGroup()) { entity_list.MessageGroup(this,true,MT_Skills,"%s was unsuccessful in %s tradeskill attempt.",GetName(),this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its"); } /* QS: Player_Log_Trade_Skill_Events */ if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){ std::string event_desc = StringFormat("Failed :: recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID()); QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc); } itr = spec->onfail.begin(); while(itr != spec->onfail.end()) { //should we check these arguments? SummonItem(itr->first, itr->second); ++itr; } /* Salvage Item rolls */ // Rolls on each item, is possible to return everything int SalvageChance = aabonuses.SalvageChance + itembonuses.SalvageChance + spellbonuses.SalvageChance; // Skip check if not a normal TS or if a quest recipe these should be nofail, but check amyways if(SalvageChance && spec->tradeskill != 75 && !spec->quest) { itr = spec->salvage.begin(); uint8 sc = 0; while(itr != spec->salvage.end()) { for(sc = 0; sc < itr->second; sc++) if(zone->random.Roll(SalvageChance)) SummonItem(itr->first, 1); ++itr; } } } return(false); }
// // 외부에서 UseSkill을 명령할땐 이것으로 호출하자. // sutType : 스킬을 사용할때 스킬큐에서 연타로 사용한건가 일반적인 사용을 한건가.c // int CMover::CMD_SetUseSkill( OBJID idTarget, int nSkillIdx, SKILLUSETYPE sutType ) { m_oaCmd = OBJACT_NONE; TRACE( "CMD_SetUseSkill( " ); if( m_pActMover->IsFly() ) return 0; // 비행중엔 스킬사용 금지. if( m_pActMover->IsActAttack() ) return 0; if( m_pActMover->IsActJump() ) return 0; // 점프중엔 사용금지. if( m_pActMover->GetState() & OBJSTA_DMG_FLY_ALL ) return 0; // 데미지 플라이중엔 스킬사용금지. if( IsDie() ) return 0; // 죽었을때 사용금지. LPSKILL pSkill = GetSkill( 0, nSkillIdx ); // this가 가진 스킬중 nIdx에 해당하는 스킬을 꺼낸다. if( pSkill == NULL ) { Error( "CMD_SetUseSkill : %s skill(%d) not found", m_szName, nSkillIdx ); return 0; // } ItemProp* pSkillProp = pSkill->GetProp(); if( pSkillProp == NULL ) // JobSkill 리스트에서 꺼낸 스킬의 프로퍼티를 꺼냄. { Error( "CMD_SetUseSkill : %s. skill(%d) property not found", m_szName, pSkill->dwSkill ); return 0; // } if( IsPlayer() && IsStateMode( STATE_BASEMOTION_MODE ) ) // 시전중(준비시간)일땐 사용금지. { #ifdef __CLIENT g_DPlay.SendStateModeCancel( STATE_BASEMOTION_MODE, STATEMODE_BASEMOTION_CANCEL ); #endif return 0; } // 도달범위 - 얼마나 가까이 근접해야하는가. 미터단위 float fArrivalRange = 0.0f; fArrivalRange = GetAttackRange( pSkillProp->dwAttackRange ); switch( pSkillProp->dwUseChance ) { case WUI_NOW: // 타겟팅과 상관없이 자기자신에게 쓰는 방식. idTarget = GetId(); break; case WUI_TARGETINGOBJ: // 셀렉트 되어 있는 타겟에게 사용. { #ifdef __CLIENT CObj *pFocusObj = GetWorld()->GetObjFocus(); if( pFocusObj && pFocusObj->GetType() == OT_MOVER ) idTarget = ((CMover*)pFocusObj)->GetId(); #else if( IsPlayer() ) idTarget = ((CUser *)this)->m_idSetTarget; #endif // __CLIENT } break; #ifdef __CLIENT case WUI_TARGETCURSORPTZ: { idTarget = GetId(); CRect rect; D3DXVECTOR3 vPos; CWndWorld* pWndWorld; pWndWorld = (CWndWorld*)g_WndMng.GetWndBase( APP_WORLD ); rect = pWndWorld->GetClientRect(); if( GetWorld()->ClientPointToVector( NULL, rect, pWndWorld->GetMousePoint(), &GetWorld()->m_matProj, &GetWorld()->GetCamera()->m_matView, &vPos, TRUE ) ) { #ifdef __SKILL0517 AddSkillProp* pAddSkillProp = prj.GetAddSkillProp( pSkillProp->dwSubDefine, GetSkillLevel( pSkill ) ); // UseSkill에서 사용한 스킬의 프로퍼티 꺼냄 #else // __SKILL0517 AddSkillProp* pAddSkillProp = prj.GetAddSkillProp( pSkillProp->dwSubDefine, pSkill->dwLevel ); // UseSkill에서 사용한 스킬의 프로퍼티 꺼냄 #endif // __SKILL0517 if( pAddSkillProp == NULL ) { Error( "CMover::OnMagicSkill : %s. add스킬(%d)의 프로퍼티가 없다.", m_szName, nSkillIdx ); return 0; // property not found } FLOAT fDist; FLOAT fMaxDistSq; D3DXVECTOR3 vDist; fMaxDistSq = (float)pAddSkillProp->dwSkillRange; fMaxDistSq *= fMaxDistSq; vDist = vPos - GetPos(); fDist = D3DXVec3LengthSq( &vDist ); SetAngle( GetDegree(vPos, GetPos()) ); // 목표쪽으로 몸을 돌림. // 텔레포트 할 위치가 멀경우 현제 스킬에 해당하는 거리로 바꿔준다 if( fDist > fMaxDistSq ) { FLOAT fLength; D3DXVECTOR3 vDirNor; D3DXVec3Normalize( &vDirNor, &vDist ); fLength = (float)pAddSkillProp->dwSkillRange; float y = vPos.y; vPos = GetPos() + (vDirNor * fLength); vPos.y = y; // 스킬에 해당하는 거리로 바꾼곳이 못가는 지역이라면 갈수 있는 지역을 검사한다. int nAttr = GetWorld()->GetHeightAttribute( vPos.x, vPos.z ); if( nAttr != HATTR_NONE ) { while( nAttr != HATTR_NONE ) { if( nAttr == HATTR_NOFLY ) break; fLength -= 1.0f; // 1미터씩 줄여가며 계산한다. vPos = GetPos() + (vDirNor * fLength); nAttr = GetWorld()->GetHeightAttribute( vPos.x, vPos.z ); // 캐릭터의 앞 뒤로 이동불가 일 경우 뒷쪽이 이동불가 해제 될때 까지 계속 계산하여 이동시킴 // 그러므로 텔레포트 스킬 범위를 넘어설 경우 원래 자리로 텔레포트 하도록 처리 D3DXVECTOR3 vTemp = vPos - GetPos(); float fTemp = D3DXVec3LengthSq( &vTemp ); if(fTemp > fMaxDistSq) { vPos = GetPos(); break; } } // 한번더 줄여줌 fLength -= 1.0f; vPos = GetPos() + (vDirNor * fLength); // 줄인 곳이 이동불가 지역일 수 있다. nAttr = GetWorld()->GetHeightAttribute( vPos.x, vPos.z ); if( nAttr != HATTR_NONE ) { vPos = GetPos(); } } } else // 텔레포트 할 위치가 해당스킬 거리보다 작을경우 { int nAttr = GetWorld()->GetHeightAttribute( vPos.x, vPos.z ); FLOAT fLength; D3DXVECTOR3 vDirNor; D3DXVec3Normalize( &vDirNor, &vDist ); fLength = 0.0f; while( nAttr != HATTR_NONE ) { if( nAttr == HATTR_NOFLY ) break; fLength -= 1.0f; vPos = GetPos() + (vDirNor * fLength); nAttr = GetWorld()->GetHeightAttribute( vPos.x, vPos.z ); // 캐릭터의 앞 뒤로 이동불가 일 경우 뒷쪽이 이동불가 해제 될때 까지 계속 계산하여 이동시킴 // 그러므로 텔레포트 스킬 범위를 넘어설 경우 원래 자리로 텔레포트 하도록 처리 D3DXVECTOR3 vTemp = vPos - GetPos(); float fTemp = D3DXVec3LengthSq( &vTemp ); if(fTemp > fMaxDistSq) { vPos = GetPos(); break; } } } if( IsActiveMover() && g_eLocal.GetState( EVE_SCHOOL ) ) // 학교이벤섭이면. { D3DXVECTOR3 v1, v2; v1 = GetPos(); v1.y += 0.1f; v2 = vPos; v2.y += 0.1f; if( GetWorld()->IntersectObjLine( NULL, v1, v2, FALSE, FALSE ) ) // 텔레포트는 라인체크함. { g_WndMng.PutString( prj.GetText(TID_GAME_NOMOVING), NULL, prj.GetTextColor(TID_GAME_NOMOVING) ); g_WndMng.m_pWndWorld->SetNextSkill( NEXTSKILL_NONE ); return 0; } } pWndWorld->m_vTelePos = vPos; } else { g_WndMng.m_pWndWorld->SetNextSkill( NEXTSKILL_NONE ); g_WndMng.PutString( prj.GetText(TID_GAME_NOMOVING), NULL, prj.GetTextColor(TID_GAME_NOMOVING) ); return 0; } } break; #endif // __CLIENT } // 타인에게 쓰는경우에만 검사... if( idTarget != GetId() ) { CMover *pTarget = prj.GetMover( idTarget ); if( IsValidObj(pTarget) ) { if( pSkillProp->nEvildoing < 0 ) // 나쁜 스킬은 if( IsAttackAble(pTarget) == FALSE ) // 공격허용이 되지 않으면 사용할 수 없음. return 0; if( pSkill->dwSkill == SI_ASS_HEAL_RESURRECTION ) // 부활을 사용했을때 { if( pTarget->IsNPC() || pTarget->IsDie() == FALSE ) // 상대가 NPC거나 상대가 죽어있지 않다면 취소 return 0; } else { if( pTarget->IsDie() ) // 부활이 아닌 스킬을 사용했을때 상대가 죽어있으면 사용안됨. return 0; } } } // 타겟 근접 방식. switch( pSkillProp->dwExeTarget ) { case EXT_SELFCHGPARAMET: // 시전자 자신에게 사용하는 종류 idTarget = GetId(); // 타겟을 자기자신으로 설정. SetDestObj( idTarget, fArrivalRange, TRUE ); // 이동할 목표물을 idTarget으로 설정. << 이게 왜 필요하지? ㅡ.ㅡ? break; case EXT_MAGICATKSHOT: case EXT_MAGICATK: // 원거리에서 마법으로 타겟을 공격 case EXT_MAGICSHOT: if( idTarget == NULL_ID ) return 0; // 타겟이 없거나 유효하지 않으면 실패. SetDestObj( idTarget, fArrivalRange, TRUE ); // 이동할 목표물을 idTarget으로 설정. 다가가는 범위는 fArrivalRange값으로.. break; case EXT_ANOTHERWITH: // 나 혹은 다른사람에게 시전 if( idTarget == NULL_ID ) // 타겟이 잡혀있지 않으면 idTarget = GetId(); // 자신을 타겟으로 잡음 SetDestObj( idTarget, fArrivalRange, TRUE ); // 이동할 목표물을 idTarget으로 설정. break; case EXT_AROUNDATK: // 내 주위적들을 대상. idTarget = GetId(); // 타겟을 자기자신으로 설정. SetDestObj( idTarget, fArrivalRange, TRUE ); // 이동할 목표물을 idTarget으로 설정. break; case EXT_OBJCHGPARAMET: // 타인에게 사용 default: // 그외는 모두 근접하자. if( idTarget == NULL_ID ) return 0; // 타겟이 없거나 유효하지 않으면 실패. SetDestObj( idTarget, fArrivalRange, TRUE ); // 이동할 목표물을 idTarget으로 설정. break; } ClearActParam(); SetCmd( OBJACT_USESKILL, nSkillIdx, idTarget, sutType ); // 사정거리가 되었을때 실행할 명령 셋팅. TRACE( "\n)CMD_SetUseSkill\n" ); return 1; }
//returns true on success bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if(spec == NULL) return(false); uint16 user_skill = GetSkill(spec->tradeskill); float chance = 0.0; float skillup_modifier = 0.0; int16 thirdstat = 0; int16 stat_modifier = 15; uint16 success_modifier = 0; // Rework based on the info on eqtraders.com // http://mboards.eqtraders.com/eq/showthread.php?t=22246 // 09/10/2006 v0.1 (eq4me) // 09/11/2006 v0.2 (eq4me) // Todo: // Implementing AAs // Success modifiers based on recipes // Skillup modifiers based on the rarity of the ingredients // Some tradeskills are more eqal then others. ;-) // If you want to customize the stage1 success rate do it here. // Remember: skillup_modifier is (float). Lower is better switch(spec->tradeskill) { case FLETCHING: case ALCHEMY: case JEWELRY_MAKING: case POTTERY: skillup_modifier = 4; break; case BAKING: case BREWING: skillup_modifier = 3; break; case RESEARCH: skillup_modifier = 1; break; default: skillup_modifier = 2; break; } // Some tradeskills take the higher of one additional stat beside INT and WIS // to determine the skillup rate. Additionally these tradeskills do not have an // -15 modifier on their statbonus. if (spec->tradeskill == FLETCHING || spec->tradeskill == MAKE_POISON) { thirdstat = GetDEX(); stat_modifier = 0; } else if (spec->tradeskill == BLACKSMITHING) { thirdstat = GetSTR(); stat_modifier = 0; } int16 higher_from_int_wis = (GetINT() > GetWIS()) ? GetINT() : GetWIS(); int16 bonusstat = (higher_from_int_wis > thirdstat) ? higher_from_int_wis : thirdstat; vector< pair<uint32,uint8> >::iterator itr; //calculate the base success chance // For trivials over 68 the chance is (skill - 0.75*trivial) +51.5 // For trivial up to 68 the chance is (skill - trivial) + 66 if (spec->trivial >= 68) { chance = (user_skill - (0.75*spec->trivial)) + 51.5; } else { chance = (user_skill - spec->trivial) + 66; } int16 over_trivial = (int16)GetRawSkill(spec->tradeskill) - (int16)spec->trivial; //handle caps if(spec->nofail) { chance = 100; //cannot fail. _log(TRADESKILLS__TRACE, "...This combine cannot fail."); } else if(over_trivial >= 0) { // At reaching trivial the chance goes to 95% going up an additional // percent for every 40 skillpoints above the trivial. // The success rate is not modified through stats. // Mastery AAs are unaccounted for so far. // chance_AA = chance + ((100 - chance) * mastery_modifier) // But the 95% limit with an additional 1% for every 40 skill points // above critical still stands. // Mastery modifier is: 10%/25%/50% for rank one/two/three chance = 95.0f + (float(user_skill - spec->trivial) / 40.0f); Message_StringID(MT_Emote, TRADESKILL_TRIVIAL); } else if(chance < 5) { // Minimum chance is always 5 chance = 5; } else if(chance > 95) { //cap is 95, shouldent reach this before trivial, but just in case. chance = 95; } _log(TRADESKILLS__TRACE, "...Current skill: %d , Trivial: %d , Success chance: %f percent", user_skill , spec->trivial , chance); _log(TRADESKILLS__TRACE, "...Bonusstat: %d , INT: %d , WIS: %d , DEX: %d , STR: %d", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR()); float res = MakeRandomFloat(0, 99); int AAChance = 0; //AA modifiers //can we do this with nested switches? if(spec->tradeskill == ALCHEMY){ switch(GetAA(aaAlchemyMastery)){ case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if(spec->tradeskill == JEWELRY_MAKING){ switch(GetAA(aaJewelCraftMastery)){ case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } const Item_Struct* item = NULL; if (spec->tradeskill == BLACKSMITHING) { switch(GetAA(aaBlacksmithingMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == BAKING) { switch(GetAA(aaBakingMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == BREWING) { switch(GetAA(aaBrewingMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == FLETCHING) { switch(GetAA(aaFletchingMastery2)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == POTTERY) { switch(GetAA(aaPotteryMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == TAILORING) { switch(GetAA(aaTailoringMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == RESEARCH) { switch(GetAA(aaArcaneTongues)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){ success_modifier = 1; if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); Message_StringID(4,TRADESKILL_SUCCEED,spec->name.c_str()); _log(TRADESKILLS__TRACE, "Tradeskill success"); itr = spec->onsuccess.begin(); while(itr != spec->onsuccess.end() && !spec->quest) { //should we check this crap? SummonItem(itr->first, itr->second); item = database.GetItem(itr->first); if (this->GetGroup()) { entity_list.MessageGroup(this,true,MT_Skills,"%s has successfully fashioned %s!",GetName(),item->Name); } if(RuleB(TaskSystem, EnableTaskSystem)) UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second); itr++; } return(true); } else { success_modifier = 2; // Halves the chance if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); Message_StringID(MT_Emote,TRADESKILL_FAILED); _log(TRADESKILLS__TRACE, "Tradeskill failed"); if (this->GetGroup()) { entity_list.MessageGroup(this,true,MT_Skills,"%s was unsuccessful in %s tradeskill attempt.",GetName(),this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its"); } itr = spec->onfail.begin(); while(itr != spec->onfail.end()) { //should we check these arguments? SummonItem(itr->first, itr->second); itr++; } } return(false); }
void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid) { char errbuf[MYSQL_ERRMSG_SIZE]; MYSQL_RES *result; MYSQL_ROW row; if (!database.RunQuery(query, qlen, errbuf, &result)) { LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query, errbuf); return; } uint8 qcount = 0; qcount = mysql_num_rows(result); if(qcount < 1) { //search gave no results... not an error return; } if(mysql_num_fields(result) != 6) { LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query); return; } uint8 r; for(r = 0; r < qcount; r++) { row = mysql_fetch_row(result); if(row == NULL || row[0] == NULL || row[1] == NULL || row[2] == NULL || row[3] == NULL || row[5] == NULL) continue; uint32 recipe = (uint32)atoi(row[0]); const char *name = row[1]; uint32 trivial = (uint32) atoi(row[2]); uint32 comp_count = (uint32) atoi(row[3]); uint32 tradeskill = (uint16) atoi(row[5]); // Skip the recipes that exceed the threshold in skill difference // Recipes that have either been made before or were // explicitly learned are excempt from that limit if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)) { if (((int32)trivial - (int32)GetSkill((SkillType)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff) && row[4] == NULL) { continue; } } EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct)); RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer; reply->object_type = objtype; reply->some_id = someid; reply->component_count = comp_count; reply->recipe_id = recipe; reply->trivial = trivial; strn0cpy(reply->recipe_name, name, sizeof(reply->recipe_name)); FastQueuePacket(&outapp); } mysql_free_result(result); }
uint16 Mob::GetInstrumentMod(uint16 spell_id) const { if(GetClass() != BARD) return(10); uint16 effectmod = 10; //this should never use spell modifiers... //if a spell grants better modifers, they are copied into the item mods //because the spells are supposed to act just like having the intrument. //item mods are in 10ths of percent increases switch(spells[spell_id].skill) { case PERCUSSION_INSTRUMENTS: if(itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0) effectmod = 10; else if(GetSkill(PERCUSSION_INSTRUMENTS) == 0) effectmod = 10; else if(itembonuses.percussionMod > spellbonuses.percussionMod) effectmod = itembonuses.percussionMod; else effectmod = spellbonuses.percussionMod; break; case STRINGED_INSTRUMENTS: if(itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0) effectmod = 10; else if(GetSkill(STRINGED_INSTRUMENTS) == 0) effectmod = 10; else if(itembonuses.stringedMod > spellbonuses.stringedMod) effectmod = itembonuses.stringedMod; else effectmod = spellbonuses.stringedMod; break; case WIND_INSTRUMENTS: if(itembonuses.windMod == 0 && spellbonuses.windMod == 0) effectmod = 10; else if(GetSkill(WIND_INSTRUMENTS) == 0) effectmod = 10; else if(itembonuses.windMod > spellbonuses.windMod) effectmod = itembonuses.windMod; else effectmod = spellbonuses.windMod; break; case BRASS_INSTRUMENTS: if(itembonuses.brassMod == 0 && spellbonuses.brassMod == 0) effectmod = 10; else if(GetSkill(BRASS_INSTRUMENTS) == 0) effectmod = 10; else if(itembonuses.brassMod > spellbonuses.brassMod) effectmod = itembonuses.brassMod; else effectmod = spellbonuses.brassMod; break; case SINGING: if(itembonuses.singingMod == 0 && spellbonuses.singingMod == 0) effectmod = 10; else if(itembonuses.singingMod > spellbonuses.singingMod) effectmod = itembonuses.singingMod; else effectmod = spellbonuses.singingMod; break; default: effectmod = 10; break; } if(spells[spell_id].skill == SINGING) { effectmod += 2*GetAA(aaSingingMastery); effectmod += 2*GetAA(aaImprovedSingingMastery); } else { effectmod += 2*GetAA(aaInstrumentMastery); effectmod += 2*GetAA(aaImprovedInstrumentMastery); } effectmod += 2*GetAA(aaAyonaesTutelage); //singing & instruments effectmod += 2*GetAA(aaEchoofTaelosia); //singing & instruments if(effectmod < 10) effectmod = 10; _log(SPELLS__BARDS, "%s::GetInstrumentMod() spell=%d mod=%d\n", GetName(), spell_id, effectmod); return(effectmod); }