For reference, this is the part of CalculateNextWorkRequired() that you're trying to replace:
// Limit adjustment step
// this section is left unchanged I am just pasting it here for reference
int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
if (nActualTimespan < params.nPowTargetTimespan/4)
nActualTimespan = params.nPowTargetTimespan/4;
if (nActualTimespan > params.nPowTargetTimespan*4)
nActualTimespan = params.nPowTargetTimespan*4;
// Retarget
// this is the actual section you must change
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
arith_uint256 bnNew;
bnNew.SetCompact(pindexLast->nBits);
bnNew *= nActualTimespan;
bnNew /= params.nPowTargetTimespan;
You probably already know this but nPowTargetTimespan is the epoch interval (two weeks if you used Bitcoin's chainparams) and n_actualTimeSpan takes the difference between the first and last block times in the epoch and powLimit is the biggest target possible for the smallest difficulty.
Before this snippet there's an If condition that checks if retargeting has been disabled, and I don't think you should change that. After this snippet is a limit check that caps the target to the maximum number powLimit.
If your chain is having a drought of blocks mined then there are two specific cases to worry about: one when there is only one block mined during the epoch (
pindexLast->GetBlockTime() == nFirstBlockTime and nActualTimeSpan = 0), and when there are no blocks mined during the epoch (all three variables above are undefined or at least have some dummy default value).
Then the target is multiplied by actualTimeSpan/bPowTargetTimespan (that's 2 weeks in bitcoin, unless you changed it in your chainparams).
I suggest you make a special case when there are zero mined blocks in the interval, to automatically decays the difficulty by some percentage factor. Then if if the chain becomes dormant by nobody mining blocks to it in multiple epoches, the difficulty will get progressively lower until it is restored to easier levels. Maybe even put it in your chainparams if you want to play around with it on your network. In other words, change it to something like this:
// Retarget
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
arith_uint256 bnNew;
bnNew.SetCompact(pindexLast->nBits);
int64_t nLastBlockTime = pIndexLast->GetBlockTime();
if (nLastBlockTime == nFirstBlockTime) {
// These parameter don't exist, I made them up and
// you have to define it in params.h and chainparams.cpp
bnNew *= params.droughtAdjustmentNumerator;
bnNew /= params.droughtAdjustmentDenominator;
}
else {
bnNew *= nActualTimespan;
bnNew /= params.nPowTargetTimespan;
}
// ...
Because the adjustment has to be done with integers and not floating-point numbers, two additional parameters are needed to simulate a percentage adjustment.