// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
// nel
#include "nel/misc/string_conversion.h"
#include "nel/misc/i18n.h"
#include "rm_family.h"
using namespace std;
using namespace NLMISC;
namespace RM_FABER_TYPE
{
// The conversion table
const CStringConversion::CPair stringTable [] =
{
{ "MpL", MPL },
{ "MpH", MPH },
{ "MpP", MPP },
{ "MpM", MPM },
{ "MpG", MPG },
{ "MpC", MPC },
{ "MpGA", MPGA },
{ "MpPE", MPPE },
{ "MpCA", MPCA },
{ "MpE", MPE },
{ "MpEN", MPEN },
{ "MpPR", MPPR },
{ "MpCR", MPCR },
{ "MpRI", MPRI },
{ "MpRE", MPRE },
{ "MpAT", MPAT },
{ "MpSU", MPSU },
{ "MpED", MPED },
{ "MpBT", MPBT },
{ "MpPES", MPPES },
{ "MpSH", MPSH },
{ "MpTK", MPTK },
{ "MpJH", MPJH },
{ "MpCF", MPCF },
{ "MpVE", MPVE },
{ "MpMF", MPMF }
};
std::string TypeToSheetEntry[]=
{
"A MpL (Blade)",
"B MpH (Hammer)",
"C MpP (Point)",
"D MpM (Shaft)",
"E MpG (Grip)",
"F MpC (Counterweight)",
"G MpGA (Trigger)",
"H MpPE (Firing pin)",
"I MpCA (Barrel)",
"J MpE (Explosive)",
"K MpEN (Ammo jacket)",
"L MpPR (Ammo bullet)",
"M MpCR (Armor shell)",
"N MpRI (Armor interior coating)",
"O MpRE (Armor interieur stuffing)",
"P MpAT (Armor clip)",
"Q MpSU (Jewel stone support)",
"R MpED (Jewel stone)",
"S MpBT (Blacksmith tool)",
"T MpPES (Pestle tool)",
"U MpSH (Sharpener tool)",
"V MpTK (Tunneling Knife)",
"W MpJH (Jewelry hammer)",
"X MpCF (Campfire)",
"Y MpVE (Clothes)",
"Z MpMF (Magic Focus)",
};
CStringConversion conversion(stringTable, sizeof(stringTable) / sizeof(stringTable[0]), Unknown);
// convert type id to type name string
const std::string& toString( TRMFType faber_type )
{
return conversion.toString(faber_type);
}
// convert type name to type enum value
TRMFType toFaberType( const std::string& str )
{
return conversion.fromString(str);
}
const std::string &faberTypeToSheetEntry(TRMFType type)
{
nlctassert( (sizeof(TypeToSheetEntry)/sizeof(TypeToSheetEntry[0])) == NUM_FABER_TYPE );
if(type>=NUM_FABER_TYPE)
{
static std::string empty;
return empty;
}
return TypeToSheetEntry[type];
}
/// Client: use the CI18N
const ucstring& toLocalString( TRMFType e )
{
return CI18N::get("mpft" + toString(e));
}
std::string toIconDefineString( TRMFType e )
{
return string("item_part_icon_") + RM_FABER_TYPE::toString(e);
}
};
namespace RM_FAMILY
{
/// Get the Localized UCString
const ucstring& toLocalString( TRMFamily e )
{
return CI18N::get("mpfam" + toString(e));
}
};
namespace RM_GROUP
{
/// Get the Localized UCString
const ucstring& toLocalString( TRMGroup e )
{
return CI18N::get("mpgroup" + toString(e));
}
};
namespace RM_FABER_PROPERTY
{
/// Get the Localized UCString
const ucstring& toLocalString( TRMFProperty e )
{
return CI18N::get("mpprop" + toString(e));
}
};
// With which quality the RawMaterial can be used for craft
namespace RM_FABER_QUALITY
{
// The conversion table
const CStringConversion::CPair stringTable [] =
{
{ "Slightly", SLIGHTLY },
{ "Moderately", MODERATELY },
{ "Quite", QUITE },
{ "Extremely", EXTREMELY }
};
CStringConversion conversion(stringTable, sizeof(stringTable) / sizeof(stringTable[0]), Unknown);
// convert type id to type name string
const std::string& toString( TFaberQuality fq )
{
return conversion.toString(fq);
}
// convert type name to type enum value
TFaberQuality toFaberQuality( const std::string& str )
{
return conversion.fromString(str);
}
/// Client: use the CI18N
const ucstring& toLocalString( TFaberQuality e )
{
return CI18N::get("mpfq" + toString(e));
}
}
namespace RM_COLOR
{
const std::string ColorTable[]=
{
"Red",
"Beige",
"Green",
"Turquoise",
"Blue",
"Purple",
"White",
"Black",
};
const std::string UnknownColor= "Unknown";
const std::string& toString( sint value )
{
nlctassert(sizeof(ColorTable)/sizeof(ColorTable[0]) == NumColors);
if( validColor(value) )
return ColorTable[value];
else
return UnknownColor;
}
/// Get the Localized UCString
const ucstring& toLocalString( sint value )
{
return CI18N::get("mpcol" + toString(value));
}
};//RM_COLOR
namespace RM_FABER_STAT_TYPE
{
// The conversion table
const CStringConversion::CPair stringTable [] =
{
{ "Durability", Durability },
{ "Weight", Weight },
{ "SapLoad", SapLoad },
{ "DMG", DMG },
{ "Speed", Speed },
{ "Range", Range },
{ "DodgeModifier", DodgeModifier },
{ "ParryModifier", ParryModifier },
{ "AdversaryDodgeModifier", AdversaryDodgeModifier },
{ "AdversaryParryModifier", AdversaryParryModifier },
{ "ProtectionFactor", ProtectionFactor },
{ "MaxSlashingProtection", MaxSlashingProtection },
{ "MaxBluntProtection", MaxBluntProtection },
{ "MaxPiercingProtection", MaxPiercingProtection },
{ "AcidProtection", AcidProtection },
{ "ColdProtection", ColdProtection },
{ "FireProtection", FireProtection },
{ "RotProtection", RotProtection },
{ "ShockWaveProtection", ShockWaveProtection },
{ "PoisonProtection", PoisonProtection },
{ "ElectricityProtection", ElectricityProtection },
{ "DesertResistance", DesertResistance },
{ "ForestResistance", ForestResistance },
{ "LacustreResistance", LacustreResistance },
{ "JungleResistance", JungleResistance },
{ "PrimaryRootResistance", PrimaryRootResistance },
{ "ElementalCastingTimeFactor", ElementalCastingTimeFactor },
{ "ElementalPowerFactor", ElementalPowerFactor },
{ "OffensiveAfflictionCastingTimeFactor", OffensiveAfflictionCastingTimeFactor },
{ "OffensiveAfflictionPowerFactor", OffensiveAfflictionPowerFactor },
{ "DefensiveAfflictionCastingTimeFactor", DefensiveAfflictionCastingTimeFactor },
{ "DefensiveAfflictionPowerFactor", DefensiveAfflictionPowerFactor },
{ "HealCastingTimeFactor", HealCastingTimeFactor },
{ "HealPowerFactor", HealPowerFactor },
};
CStringConversion conversion(stringTable, sizeof(stringTable) / sizeof(stringTable[0]), Unknown);
const std::string& toString( TRMStatType stats )
{
// must change the convsersion table
nlctassert(NumRMStatType == sizeof(stringTable)/sizeof(stringTable[0]));
return conversion.toString(stats);
}
const ucstring& toLocalString( TRMStatType stats )
{
// must change en.uxt
nlctassert(NumRMStatType == sizeof(stringTable)/sizeof(stringTable[0]));
return CI18N::get("mpstat" + NLMISC::toString((uint)stats));
}
// Array saying for wich item part built, what stat is usefull
class CItemPartToStat
{
public:
bool StatRelevant[RM_FABER_TYPE::NUM_FABER_TYPE * NumRMStatType];
CItemPartToStat()
{
memset(StatRelevant, 0, RM_FABER_TYPE::NUM_FABER_TYPE*NumRMStatType*sizeof(bool));
}
void setStatLine(RM_FABER_TYPE::TRMFType ft, const uint8 vals[NumRMStatType])
{
nlassert(sizeof(uint8)==sizeof(bool));
nlassert(ft=RM_FABER_TYPE::NUM_FABER_TYPE || fs>=NumRMStatType)
return false;
// build
static bool init= false;
if(!init)
{
init= true;
// Hardcoded array
// Dur Wgt Sap Dmg Spd Rng Dog Par ADo APa Prt Slh Bln Prc Apt CPt FPt RPt SPt PPt EPt DRt FRt LRt JRt PRt DAC DAP DHC DHP OAC OAP OEC OEP
const uint8 mpL[]= {1 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Blade
const uint8 mpH[]= {1 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Hammer
const uint8 mpP[]= {1 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Point
const uint8 mpM[]= {1 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Shaft
const uint8 mpG[]= {1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Grip
const uint8 mpC[]= {1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Counterweight
const uint8 mpGA[]= {1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Trigger
const uint8 mpPE[]= {1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Firing pin
const uint8 mpCA[]= {1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Barrel
const uint8 mpE[]= {1 ,1 ,0 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Explosive
const uint8 mpEN[]= {1 ,1 ,0 ,0 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Ammo jacket
const uint8 mpPR[]= {1 ,1 ,0 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Ammo bullet
const uint8 mpCR[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Armor shell
const uint8 mpRI[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Lining
const uint8 mpRE[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Stuffing
const uint8 mpAT[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Armor clip
const uint8 mpSU[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Jewel stone support
const uint8 mpED[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Jewel stone
const uint8 mpBT[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Blacksmith tool
const uint8 mpPES[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Pestle tool
const uint8 mpSH[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Sharpener tool
const uint8 mpTK[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // TunnelingKnife
const uint8 mpJH[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Jewelry hammer
const uint8 mpCF[]= {1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Campfire
const uint8 mpVE[]= {1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; // Clothes
const uint8 mpMF[]= {1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 }; // Magic Focus
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPL, mpL);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPH, mpH);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPP, mpP);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPM, mpM);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPG, mpG);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPC, mpC);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPGA, mpGA);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPPE, mpPE);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPCA, mpCA);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPE, mpE);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPEN, mpEN);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPPR, mpPR);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPCR, mpCR);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPRI, mpRI);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPRE, mpRE);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPAT, mpAT);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPSU, mpSU);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPED, mpED);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPBT, mpBT);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPPES, mpPES);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPSH, mpSH);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPTK, mpTK);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPJH, mpJH);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPCF, mpCF);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPVE, mpVE);
ItemPartToStat.setStatLine(RM_FABER_TYPE::MPMF, mpMF);
}
// get
return ItemPartToStat.StatRelevant[ft*NumRMStatType + fs];
}
// ***************************************************************************
// GamePlay tuning
const float StretchStatMinDeltaWanted= 0.3f;
const float StretchStatMaxDeltaFactor= 2.0f;
const float StretchStatBonusDeltaThreshold= 0.35f; // if the player get +35% or more in one stat
const float StretchStatBonusValue= 0.1f; // then +10%!
// stretch the item stats
void stretchItemStats(float array[NumRMStatType], uint64 statBitField, bool addBonusRule)
{
uint i;
// *** ensure 0-1.
for(i=0;imaxStat)
{
maxStat= array[i];
bestStat= i;
}
}
}
// mean, and difference max-mean
float meanStat= 0;
if(numStatUsed)
meanStat= energy / numStatUsed;
float maxDelta= maxStat-meanStat;
// if all stats are equals, no-op
if(maxDelta==0.f)
return;
// *** if the player has succed in applying a nearly perfect bonus for its best stat (eg:+40%), add an additional bonus
float bestStatBonusValue= 0.f;
// also, add this bonus only if addBonusRule
if(addBonusRule && maxDelta>=StretchStatBonusDeltaThreshold)
{
bestStatBonusValue= StretchStatBonusValue;
}
// *** stretch the maxDelta so it reaches at least StretchStatMinDeltaWanted (eg: +30%)
if(maxDelta < StretchStatMinDeltaWanted)
{
float stretchFactor= StretchStatMinDeltaWanted / maxDelta;
stretchFactor= min(stretchFactor, StretchStatMaxDeltaFactor);
// For all stats, stretch the delta
float newEnergy=0;
float numStatNot0= 0;
float numStatNot1= 0;
for(i=0;i0.f)
numStatNot0++;
if(array[i]<1.f)
numStatNot1++;
}
}
/// loss or gain of energy because of Clamp 0 or 1 ?
float deltaEnergy = newEnergy - energy;
// while more than 0.1% of energy loss or gained (float precision...)
uint nbPass=0;
while(fabs(deltaEnergy)>(0.001f*numStatUsed))
{
// redistribute delta among all stats not clamped
float deltaStat;
// if we gain too much energy because of ClampTo0, decrement Not0 Stats.
if(deltaEnergy>0)
deltaStat= -deltaEnergy/numStatNot0;
// if we loss too much energy because of ClampTo1, increment Not1 Stats.
else
deltaStat= -deltaEnergy/numStatNot1;
// redistribute all stats, to get correct energy
newEnergy=0;
numStatNot0= 0;
numStatNot1= 0;
for(i=0;i0.f)
numStatNot0++;
if(array[i]<1.f)
numStatNot1++;
}
}
// This pass may have regenerate clamp problems.
deltaEnergy = newEnergy - energy;
// this cannot happen, because in the worst case, all stats are clamped to 0 or 1.
nbPass++;
nlassert(nbPass<=NumRMStatType);
}
}
// **** add the best stat bonus value (at the end)
if(bestStatBonusValue)
{
array[bestStat]+= bestStatBonusValue;
clamp(array[bestStat], 0.f, 1.f);
}
}
// ***************************************************************************
// method used by getStatFinalValidity, for both magic protection and magic resist
uint64 filterStatValidity3BestOne(const float array[NumRMStatType], uint64 bfIn, uint statStart, uint statEnd)
{
uint64 ret= bfIn;
// init 3 bests
const uint NumBests= 3;
sint bestStat[NumBests];
float bestValues[NumBests];
for(uint i=0;i bestValues[0] )
{
bestStat[2]= bestStat[1];
bestValues[2]= bestValues[1];
bestStat[1]= bestStat[0];
bestValues[1]= bestValues[0];
bestStat[0]= i;
bestValues[0]= val;
}
else if( val > bestValues[1] )
{
bestStat[2]= bestStat[1];
bestValues[2]= bestValues[1];
bestStat[1]= i;
bestValues[1]= val;
}
else if( val > bestValues[2] )
{
bestStat[2]= i;
bestValues[2]= val;
}
}
}
// then keep only bits that are sets
for(uint i=statStart;i=startMResist && fs=startMProt && fs