int CChar::NPC_GetTrainMax( const CChar * pStudent, SKILL_TYPE Skill ) const { ADDTOCALLSTACK("CChar::NPC_GetTrainMax"); // What is the max I can train to ? int iMax; int iMaxAllowed; CVarDefCont * pValue = GetKey("OVERRIDE.TRAINSKILLMAXPERCENT",true); if ( pValue ) { iMax = static_cast<int>(IMULDIV( pValue->GetValNum(), Skill_GetBase(Skill), 100 )); } else { iMax = IMULDIV( g_Cfg.m_iTrainSkillPercent, Skill_GetBase(Skill), 100 ); } pValue = GetKey("OVERRIDE.TRAINSKILLMAX",true); if ( pValue ) { iMaxAllowed = static_cast<int>(pValue->GetValNum()); } else { iMaxAllowed = g_Cfg.m_iTrainSkillMax; } if ( iMax > iMaxAllowed ) return minimum(iMaxAllowed, pStudent->Skill_GetMax(Skill)); // Is this more that the student can take ? return minimum(iMax, pStudent->Skill_GetMax(Skill)); }
int CValueCurveDef::GetLinear( int iSkillPercent ) const { ADDTOCALLSTACK("CValueCurveDef::GetLinear"); // // ARGS: // iSkillPercent = 0 - 1000 = 0 to 100.0 percent // m_Rate[3] = the 3 advance rate control numbers, 100,50,0 skill levels // Acts as line segments. // RETURN: // raw chance value. size_t iSegSize; size_t iLoIdx; size_t iQty = m_aiValues.GetCount(); switch (iQty) { case 0: return( 0 ); // no values defined ! case 1: return( m_aiValues[0] ); case 2: iLoIdx = 0; iSegSize = 1000; break; case 3: // Do this the fastest. if ( iSkillPercent >= 500 ) { iLoIdx = 1; iSkillPercent -= 500; } else { iLoIdx = 0; } iSegSize = 500; break; default: // More iLoIdx = IMULDIV( iSkillPercent, iQty, 1000 ); iQty--; if ( iLoIdx >= iQty ) iLoIdx = iQty - 1; iSegSize = 1000 / iQty; iSkillPercent -= ( iLoIdx * iSegSize ); break; } int iLoVal = m_aiValues[iLoIdx]; int iHiVal = m_aiValues[iLoIdx + 1]; int iChance = iLoVal + IMULDIV( iHiVal - iLoVal, static_cast<int>(iSkillPercent), static_cast<int>(iSegSize) ); if ( iChance <= 0 ) return 0; // less than no chance ? return( iChance ); }
int CPointBase::StepLinePath( const CPointBase & ptSrc, int iSteps ) { ADDTOCALLSTACK("CPointBase::StepLinePath"); // Take x steps toward this point. int dx = m_x - ptSrc.m_x; int dy = m_y - ptSrc.m_y; int iDist2D = GetDist( ptSrc ); if ( ! iDist2D ) return 0; m_x = static_cast<short>(ptSrc.m_x + IMULDIV( iSteps, dx, iDist2D )); m_y = static_cast<short>(ptSrc.m_y + IMULDIV( iSteps, dy, iDist2D )); return( iDist2D ); }
int CResource::Calc_StealingItem( CChar * pCharThief, CItem * pItem, CChar * pCharMark ) { // Chance to steal and retrieve the item successfully. // weight of the item // heavier items should be more difficult. // thiefs skill/ dex // marks skill/dex // marks war mode ? // NOTE: // Items on the ground can always be stolen. chance of being seen is another matter. // RETURN: // 0-100 percent chance to hit on a d100 roll. // 0-100 percent difficulty against my SKILL_STEALING skill. ASSERT(pCharThief); ASSERT(pCharMark); int iDexMark = pCharMark->Stat_GetAdjusted(STAT_DEX); int iSkillMark = pCharMark->Skill_GetAdjusted( SKILL_STEALING ); int iWeightItem = pItem->GetWeight(); int iDifficulty = iDexMark/2 + (iSkillMark/5) + Calc_GetRandVal(iDexMark/2) + IMULDIV( iWeightItem, 4, WEIGHT_UNITS ); if ( pItem->IsItemEquipped()) { // This is REALLY HARD to do. iDifficulty += iDexMark/2 + pCharMark->Stat_GetAdjusted(STAT_INT); } if ( pCharThief->IsStatFlag( STATF_War )) // all keyed up. { iDifficulty += Calc_GetRandVal( iDexMark/2 ); } return( iDifficulty ); }
int CResource::Calc_CombatAttackSpeed( CChar * pChar, CItem * pWeapon ) { // Combat: Speed of the attack // based on dex and weight of the weapon. // ? my tactics ? // ? my skill with weapon ? // ? How much weight i'm carrying ? // RETURN: // Time in tenths of a sec. (for entire swing, not just time to hit) ASSERT(pChar); if ( pWeapon != NULL ) { CItemBase * pItemDef = dynamic_cast <CItemBase *> (pWeapon->Base_GetDef()); if ( pItemDef ) { BYTE speed = pItemDef->GetSpeed(); if ( speed ) { int iWaitTime = (TICK_PER_SEC * g_Cfg.m_iSpeedScaleFactor) / ( ( pChar->Stat_GetAdjusted(STAT_DEX) + 100 ) * speed ); return (iWaitTime > 5 ? iWaitTime : 5); } } } if ( pChar->m_pNPC && pChar->m_pNPC->m_Brain == NPCBRAIN_GUARD && m_fGuardsInstantKill ) return( 1 ); // Base speed is just your DEX range=40 to 0 int iWaitTime = IMULDIV( 100 - pChar->Stat_GetAdjusted(STAT_DEX), 40, 100 ); if ( iWaitTime < 5 ) // no-one needs to be this fast. iWaitTime = 5; else iWaitTime += 5; // Speed of the weapon as well effected by strength (minor). if ( pWeapon != NULL ) { DEBUG_CHECK( pWeapon->IsItemEquipped()); int iWeaponWait = (pWeapon->GetWeight() * 10 ) / ( 4 * WEIGHT_UNITS ); // tenths of a stone. if ( pWeapon->GetEquipLayer() == LAYER_HAND2 ) // 2 handed is slower { iWeaponWait += iWaitTime/2; } iWaitTime += iWeaponWait; } else { iWaitTime += 2; } return( iWaitTime ); }
INT64 Calc_GetRandLLVal( INT64 iqty ) { if ( iqty <= 0 ) return( 0 ); if ( iqty >= LLONG_MAX ) { return( IMULDIV( g_World.m_Rand.genrand64_int64(), (DWORD) iqty, LLONG_MAX )) ; } return( g_World.m_Rand.genrand64_int64() % iqty ); }
int Calc_GetRandVal( int iqty ) { if ( iqty <= 0 ) return( 0 ); if ( iqty >= INT_MAX ) { return( IMULDIV( g_World.m_Rand.randInt(), (DWORD) iqty, INT_MAX )) ; } return( g_World.m_Rand.randInt() % iqty ); }
bool CChar::NPC_CheckHirelingStatus() { ADDTOCALLSTACK("CChar::NPC_CheckHirelingStatus"); // Am i happy at the moment ? // If not then free myself. // // RETURN: // true = happy. if ( ! IsStatFlag( STATF_Pet )) return( true ); CCharBase * pCharDef = Char_GetDef(); int iFoodConsumeRate = g_Cfg.m_iRegenRate[STAT_FOOD]; unsigned int iWage = pCharDef->GetHireDayWage(); if ( ! iWage || ! iFoodConsumeRate ) return( true ); // I am hired for money not for food. unsigned int iPeriodWage = IMULDIV( iWage, iFoodConsumeRate, 24 * 60 * g_Cfg.m_iGameMinuteLength ); if ( iPeriodWage <= 0 ) iPeriodWage = 1; CItemContainer * pBank = GetBank(); if ( pBank->m_itEqBankBox.m_Check_Amount > iPeriodWage ) { pBank->m_itEqBankBox.m_Check_Amount -= iPeriodWage; } else { TCHAR* pszMsg = Str_GetTemp(); sprintf(pszMsg, g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_WAGE_COST ), iWage); Speak(pszMsg); CChar * pOwner = NPC_PetGetOwner(); if ( pOwner ) { Speak( g_Cfg.GetDefaultMsg( DEFMSG_NPC_PET_HIRE_TIMEUP ) ); CItem * pMemory = Memory_AddObjTypes( pOwner, MEMORY_SPEAK ); if ( pMemory ) pMemory->m_itEqMemory.m_Action = NPC_MEM_ACT_SPEAK_HIRE; NPC_PetDesert(); return false; } // Some sort of strange bug to get here. Memory_ClearTypes( MEMORY_IPET ); StatFlag_Clear( STATF_Pet ); } return( true ); }
LONG CItemVendable::GetVendorPrice( int iConvertFactor ) { ADDTOCALLSTACK("CItemVendable::GetVendorPrice"); // Player is buying/selling from a vendor. // ASSUME this item is on the vendor ! // Consider: (if not on a player vendor) // Quality of the item. // rareity of the item. // ARGS: // iConvertFactor will consider: // Vendors Karma. // Players Karma // -100 = reduce price by 100% (player selling to vendor?) // 0 = base price // +100 = increase price by 100% (vendor selling to player?) INT64 lPrice = m_price; if ( lPrice <= 0 ) // set on player vendor. { if ( lPrice == 0 ) // set a new randomized price for the item { CItemBase * pItemDef; if ( IsType( IT_DEED )) { // Deeds just represent the item they are deeding. pItemDef = CItemBase::FindItemBase(static_cast<ITEMID_TYPE>(RES_GET_INDEX(m_itDeed.m_Type))); if ( pItemDef == NULL ) return( 1 ); } else { pItemDef = Item_GetDef(); } lPrice = pItemDef->GetMakeValue(GetQuality()); m_price = static_cast<long>(-lPrice); } else { lPrice = -lPrice; } } lPrice += IMULDIV( lPrice, maximum(iConvertFactor, -100), 100 ); if (lPrice > LONG_MAX) return LONG_MAX; else if (lPrice <= 0) return 0; return static_cast<long>(lPrice); }
void CSector::SetDefaultWeatherChance() { ADDTOCALLSTACK("CSector::SetDefaultWeatherChance"); CPointMap pt = GetBasePoint(); BYTE iPercent = static_cast<BYTE>(IMULDIV( pt.m_y, 100, g_MapList.GetY(pt.m_map) )); // 100 = south if ( iPercent < 50 ) { // Anywhere north of the Britain Moongate is a good candidate for snow m_ColdChance = 1 + ( 49 - iPercent ) * 2; m_RainChance = 15; } else { // warmer down south m_ColdChance = 1; // rain more likely down south. m_RainChance = 15 + ( iPercent - 50 ) / 10; } }
size_t CContainer::ResourceConsumePart( const CResourceQtyArray * pResources, int iReplicationQty, int iDamagePercent, bool fTest, DWORD dwArg ) { ADDTOCALLSTACK("CContainer::ResourceConsumePart"); // Consume just some of the resources. // ARGS: // pResources = the resources i need to make 1 replication of this end product. // iDamagePercent = 0-100 // RETURN: // BadIndex = all needed items where present. // index of the item we did not have. if ( iDamagePercent <= 0 ) return pResources->BadIndex(); size_t iMissing = pResources->BadIndex(); size_t iQtyRes = pResources->GetCount(); for ( size_t i = 0; i < iQtyRes; i++ ) { int iResQty = static_cast<int>(pResources->GetAt(i).GetResQty()); if (iResQty <= 0) // not sure why this would be true continue; int iQtyTotal = ( iResQty * iReplicationQty ); if ( iQtyTotal <= 0 ) continue; iQtyTotal = IMULDIV( iQtyTotal, iDamagePercent, 100 ); if ( iQtyTotal <= 0 ) continue; RESOURCE_ID rid = pResources->GetAt(i).GetResourceID(); int iRet = ContentConsume( rid, iQtyTotal, fTest, dwArg ); if ( iRet ) { iMissing = i; } } return( iMissing ); }
int Calc_GetBellCurve( int iValDiff, int iVariance ) { // Produce a log curve. // // 50+ // | // | // | // 25| + // | // | + // | + // 0 --+--+--+--+------ // iVar iValDiff // // ARGS: // iValDiff = Given a value relative to 0 // 0 = 50.0% chance. // iVariance = the 25.0% point of the bell curve // RETURN: // (0-100.0) % chance at this iValDiff. // Chance gets smaller as Diff gets bigger. // EXAMPLE: // if ( iValDiff == iVariance ) return( 250 ) // if ( iValDiff == 0 ) return( 500 ); // if ( iVariance <= 0 ) // this really should not happen but just in case. return( 500 ); if ( iValDiff < 0 ) iValDiff = -iValDiff; int iChance = 500; while ( iValDiff > iVariance && iChance ) { iValDiff -= iVariance; iChance /= 2; // chance is halved for each Variance period. } return( iChance - IMULDIV( iChance/2, iValDiff, iVariance )); }
void CChar::Use_Drink( CItem * pItem ) { ADDTOCALLSTACK("CChar::Use_Drink"); // IT_POTION: // IT_DRINK: // IT_PITCHER: // IT_WATER_WASH: // IT_BOOZE: if ( !CanMove(pItem) ) { SysMessageDefault(DEFMSG_DRINK_CANTMOVE); return; } const CItemBase *pItemDef = pItem->Item_GetDef(); ITEMID_TYPE idbottle = static_cast<ITEMID_TYPE>(RES_GET_INDEX(pItemDef->m_ttDrink.m_idEmpty)); if ( pItem->IsType(IT_BOOZE) ) { // Beer wine and liquor. vary strength of effect. int iAlcohol = Calc_GetRandVal(4); CItem *pDrunkLayer = LayerFind(LAYER_FLAG_Drunk); if ( !pDrunkLayer ) pDrunkLayer = Spell_Effect_Create(SPELL_Liquor, LAYER_FLAG_Drunk, 0, 5 * TICK_PER_SEC, this); pDrunkLayer->m_itSpell.m_spellcharges += iAlcohol; if ( pDrunkLayer->m_itSpell.m_spellcharges > 60 ) pDrunkLayer->m_itSpell.m_spellcharges = 60; } if ( pItem->IsType(IT_POTION) ) { // Time limit on using potions. if ( LayerFind(LAYER_FLAG_PotionUsed) ) { SysMessageDefault(DEFMSG_DRINK_POTION_DELAY); return; } // Convey the effect of the potion. int iSkillQuality = pItem->m_itPotion.m_skillquality; if ( g_Cfg.m_iFeatureAOS & FEATURE_AOS_UPDATE_B ) { int iEnhance = static_cast<int>(GetDefNum("EnhancePotions", false)); if ( iEnhance ) iSkillQuality += IMULDIV(iSkillQuality, iEnhance, 100); } OnSpellEffect(static_cast<SPELL_TYPE>(RES_GET_INDEX(pItem->m_itPotion.m_Type)), this, iSkillQuality, pItem); // Give me the marker that i've used a potion. Spell_Effect_Create(SPELL_NONE, LAYER_FLAG_PotionUsed, iSkillQuality, 15 * TICK_PER_SEC, this); } if ( pItem->IsType(IT_DRINK) && IsSetOF(OF_DrinkIsFood) ) { short iRestore = 0; if ( pItem->m_itDrink.m_foodval ) iRestore = static_cast<short>(pItem->m_itDrink.m_foodval); else iRestore = static_cast<short>(pItem->Item_GetDef()->GetVolume()); if ( iRestore < 1 ) iRestore = 1; if ( Stat_GetVal(STAT_FOOD) >= Stat_GetMax(STAT_FOOD) ) { SysMessageDefault(DEFMSG_DRINK_FULL); return; } Stat_SetVal(STAT_FOOD, Stat_GetVal(STAT_FOOD) + iRestore); if ( pItem->m_itFood.m_poison_skill ) SetPoison(pItem->m_itFood.m_poison_skill * 10, 1 + (pItem->m_itFood.m_poison_skill / 50), this); } //Sound(sm_DrinkSounds[Calc_GetRandVal(COUNTOF(sm_DrinkSounds))]); UpdateAnimate(ANIM_EAT); pItem->ConsumeAmount(); // Create the empty bottle ? if ( idbottle != ITEMID_NOTHING ) ItemBounce(CItem::CreateScript(idbottle, this), false); }
bool CChar::Use_Eat( CItem * pItemFood, short iQty ) { ADDTOCALLSTACK("CChar::Use_Eat"); // What we can eat should depend on body type. // How much we can eat should depend on body size and current fullness. // // ??? monsters should be able to eat corpses / raw meat // IT_FOOD or IT_FOOD_RAW // NOTE: Some foods like apples are stackable ! if ( !CanMove(pItemFood) ) { SysMessageDefault(DEFMSG_FOOD_CANTMOVE); return false; } if ( Stat_GetMax(STAT_FOOD) == 0 ) { SysMessageDefault(DEFMSG_FOOD_CANTEAT); return false; } // Is this edible by me ? if ( !Food_CanEat(pItemFood) ) { SysMessageDefault(DEFMSG_FOOD_RCANTEAT); return false; } if ( Stat_GetVal(STAT_FOOD) >= Stat_GetMax(STAT_FOOD) ) { SysMessageDefault(DEFMSG_FOOD_CANTEATF); return false; } Use_EatQty(pItemFood, iQty); LPCTSTR pMsg; int index = IMULDIV(Stat_GetVal(STAT_FOOD), 5, Stat_GetMax(STAT_FOOD)); switch ( index ) { case 0: pMsg = g_Cfg.GetDefaultMsg(DEFMSG_FOOD_FULL_1); break; case 1: pMsg = g_Cfg.GetDefaultMsg(DEFMSG_FOOD_FULL_2); break; case 2: pMsg = g_Cfg.GetDefaultMsg(DEFMSG_FOOD_FULL_3); break; case 3: pMsg = g_Cfg.GetDefaultMsg(DEFMSG_FOOD_FULL_4); break; case 4: pMsg = g_Cfg.GetDefaultMsg(DEFMSG_FOOD_FULL_5); break; case 5: default: pMsg = g_Cfg.GetDefaultMsg(DEFMSG_FOOD_FULL_6); break; } SysMessage(pMsg); return true; }
bool CChar::Use_Repair( CItem * pItemArmor ) { ADDTOCALLSTACK("CChar::Use_Repair"); // Attempt to repair the item. // If it is repairable. if ( !pItemArmor || !pItemArmor->Armor_IsRepairable() ) { SysMessageDefault(DEFMSG_REPAIR_NOT); return false; } if ( pItemArmor->IsItemEquipped() ) { SysMessageDefault(DEFMSG_REPAIR_WORN); return false; } if ( !CanUse(pItemArmor, true) ) { SysMessageDefault(DEFMSG_REPAIR_REACH); return false; } // Quickly use arms lore skill, but don't gain any skill until later on int iArmsLoreDiff = Calc_GetRandVal(30); if ( !Skill_UseQuick(SKILL_ARMSLORE, iArmsLoreDiff, false) ) { // apply arms lore skillgain for failure Skill_Experience(SKILL_ARMSLORE, -iArmsLoreDiff); SysMessageDefault(DEFMSG_REPAIR_UNK); return false; } if ( pItemArmor->m_itArmor.m_Hits_Cur >= pItemArmor->m_itArmor.m_Hits_Max ) { SysMessageDefault(DEFMSG_REPAIR_FULL); return false; } m_Act_p = g_World.FindItemTypeNearby(GetTopPoint(), IT_ANVIL, 2, false); if ( !m_Act_p.IsValidPoint() ) { SysMessageDefault(DEFMSG_REPAIR_ANVIL); return false; } CItemBase *pItemDef = pItemArmor->Item_GetDef(); ASSERT(pItemDef); // Use up some raw materials to repair. int iTotalHits = pItemArmor->m_itArmor.m_Hits_Max; int iDamageHits = pItemArmor->m_itArmor.m_Hits_Max - pItemArmor->m_itArmor.m_Hits_Cur; int iDamagePercent = IMULDIV(100, iDamageHits, iTotalHits); size_t iMissing = ResourceConsumePart(&(pItemDef->m_BaseResources), 1, iDamagePercent / 2, true); if ( iMissing != pItemDef->m_BaseResources.BadIndex() ) { // Need this to repair. const CResourceDef *pCompDef = g_Cfg.ResourceGetDef(pItemDef->m_BaseResources.GetAt(iMissing).GetResourceID()); SysMessagef(g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_LACK_1), pCompDef ? pCompDef->GetName() : g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_LACK_2)); return false; } UpdateDir(m_Act_p); UpdateAnimate(ANIM_ATTACK_1H_SLASH); // quarter the skill to make it. // + more damaged items should be harder to repair. // higher the percentage damage the closer to the skills to make it. size_t iRes = pItemDef->m_SkillMake.FindResourceType(RES_SKILL); if ( iRes == pItemDef->m_SkillMake.BadIndex() ) return false; CResourceQty RetMainSkill = pItemDef->m_SkillMake[iRes]; int iSkillLevel = static_cast<int>(RetMainSkill.GetResQty()) / 10; int iDifficulty = IMULDIV(iSkillLevel, iDamagePercent, 100); if ( iDifficulty < iSkillLevel / 4 ) iDifficulty = iSkillLevel / 4; // apply arms lore skillgain now LPCTSTR pszText; Skill_Experience(SKILL_ARMSLORE, iArmsLoreDiff); bool fSuccess = Skill_UseQuick(static_cast<SKILL_TYPE>(RetMainSkill.GetResIndex()), iDifficulty); if ( fSuccess ) { pItemArmor->m_itArmor.m_Hits_Cur = static_cast<WORD>(iTotalHits); pszText = g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_1); } else { /***************************** // not sure if this is working! ******************************/ // Failure if ( !Calc_GetRandVal(6) ) { pszText = g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_2); pItemArmor->m_itArmor.m_Hits_Max--; pItemArmor->m_itArmor.m_Hits_Cur--; } else if ( !Calc_GetRandVal(3) ) { pszText = g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_3); pItemArmor->m_itArmor.m_Hits_Cur--; } else pszText = g_Cfg.GetDefaultMsg( DEFMSG_REPAIR_4 ); iDamagePercent = Calc_GetRandVal(iDamagePercent); // some random amount } ResourceConsumePart(&(pItemDef->m_BaseResources), 1, iDamagePercent / 2, false); if ( pItemArmor->m_itArmor.m_Hits_Cur <= 0 ) pszText = g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_5); TCHAR *pszMsg = Str_GetTemp(); sprintf(pszMsg, g_Cfg.GetDefaultMsg(DEFMSG_REPAIR_MSG), pszText, pItemArmor->GetName()); Emote(pszMsg); if ( pItemArmor->m_itArmor.m_Hits_Cur <= 0 ) pItemArmor->Delete(); else pItemArmor->UpdatePropertyFlag(AUTOTOOLTIP_FLAG_DURABILITY); return fSuccess; }
int CResource::Calc_CombatAttackSpeed( CChar * pChar, CItem * pWeapon ) { ADDTOCALLSTACK("CResource::Calc_CombatAttackSpeed"); // Calculate the swing speed value on chars // RETURN: // Time in tenths of a sec. (for entire swing, not just time to hit) ASSERT(pChar); if ( pChar->m_pNPC && pChar->m_pNPC->m_Brain == NPCBRAIN_GUARD && m_fGuardsInstantKill ) return 1; int iSwingSpeedIncrease = static_cast<int>(pChar->GetDefNum("INCREASESWINGSPEED", true)); int iBaseSpeed = 50; // Base speed = Wrestling speed. if ( pWeapon ) // If we have a weapon, base speed should match weapon's value. iBaseSpeed = pWeapon->GetSpeed(); switch ( g_Cfg.m_iCombatSpeedEra ) { case 0: // OLD (55r and lower) formula using DEX and: a)Speed if set on weapon or b)calculating delay from weapon's weight if speed not set (taking in count if weapon is 2h) { if ( pWeapon ) { if (iBaseSpeed) { int iWaitTime = (TICK_PER_SEC * g_Cfg.m_iSpeedScaleFactor) / ((pChar->Stat_GetAdjusted(STAT_DEX) + 100) * iBaseSpeed); return ( iWaitTime < 5 ? 5 : iWaitTime ); // Never less than 5 tenths, even removing the '5' limit it should never be less than 0 or CChar will get SetTimeout(-1) and stop attack. } } // Base speed is just your DEX range=40 to 0 int iWaitTime = IMULDIV(100 - pChar->Stat_GetAdjusted(STAT_DEX), 40, 100); if (iWaitTime < 5) // no-one needs to be this fast. iWaitTime = 5; else iWaitTime += 5; // Speed of the weapon as well effected by strength (minor). if ( pWeapon ) { int iWeaponWait = (pWeapon->GetWeight() * 10) / (4 * WEIGHT_UNITS); // tenths of a stone. if (pWeapon->GetEquipLayer() == LAYER_HAND2) // 2 handed is slower { iWeaponWait += iWaitTime / 2; } iWaitTime += iWeaponWait; } else iWaitTime += 2; return iWaitTime; } case 1: { // AOS formula (default m_iSpeedScaleFactor = 40000) int iSwingSpeed = (pChar->Stat_GetVal(STAT_DEX) + 100) * iBaseSpeed; iSwingSpeed = maximum(1, iSwingSpeed * (100 + iSwingSpeedIncrease) / 100); iSwingSpeed = ((g_Cfg.m_iSpeedScaleFactor * TICK_PER_SEC) / iSwingSpeed) / 2; if ( iSwingSpeed < 12 ) //1.25 iSwingSpeed = 12; return iSwingSpeed; } default: case 2: { // SE formula (default m_iSpeedScaleFactor = 80000) int iSwingSpeed = maximum(1, iBaseSpeed * (100 + iSwingSpeedIncrease) / 100); iSwingSpeed = (g_Cfg.m_iSpeedScaleFactor / ((pChar->Stat_GetVal(STAT_DEX) + 100) * iSwingSpeed)) - 2; // get speed in ticks of 0.25s each if ( iSwingSpeed < 5 ) iSwingSpeed = 5; iSwingSpeed = (iSwingSpeed * TICK_PER_SEC) / 4; // convert 0.25s ticks into ms return iSwingSpeed; } case 3: { // ML formula (doesn't use m_iSpeedScaleFactor and it's only compatible with ML speed format eg. 0.25 ~ 5.00 instead 0 ~ 50) int iSwingSpeed = ((iBaseSpeed * 4) - (pChar->Stat_GetVal(STAT_DEX) / 30)) * (100 / (100 + iSwingSpeedIncrease)); // get speed in ticks of 0.25s each if ( iSwingSpeed < 5 ) iSwingSpeed = 5; iSwingSpeed = (iSwingSpeed * TICK_PER_SEC) / 4; // convert 0.25s ticks into ms return iSwingSpeed; } } }