package com.aionemu.gameserver.services.toypet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import org.apache.commons.lang3.ArrayUtils;
import com.aionemu.commons.utils.Rnd;
import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.model.templates.pet.PetFeedResult;
import com.aionemu.gameserver.model.templates.pet.PetFlavour;
import com.aionemu.gameserver.model.templates.pet.PetRewards;
/**
* @author Rolandas
*/
/**
* <b>Current pre-calculated values multiplied by 4; in packet 14 bits. Max value: 17600 / 4 is 13 bits; feed points as in retail packets.</b><br>
* static final byte[][] pointValues = new byte[][] {<br>
* // 10 25 40 50 100 200 -- feed max count<br>
* { 0, 0, 0, 0, 0, 0 }, // level 1~5 items (feed points 0)<br>
* { 80, 200, 320, 400, 800, 1600 }, // level 6~10 items (feed points 8)<br>
* { 160, 400, 640, 800, 1600, 3200 }, // level 11~15 items (feed points 16)<br>
* { 240, 600, 960, 1200, 2400, 4800 }, // level 16~20 items (feed points 24)<br>
* { 320, 800, 1280, 1600, 3200, 6400 }, // level 21~25 items (feed points 32)<br>
* { 400, 1000, 1600, 2000, 4000, 8000 }, // level 26~30 items (feed points 40)<br>
* { 480, 1200, 1920, 2400, 4800, 9600 }, // level 31~35 items (feed points 48)<br>
* { 560, 1400, 2240, 2800, 5600, 11200 }, // level 36~40 items (feed points 56)<br>
* { 640, 1600, 2560, 3200, 6400, 12800 }, // level 41~45 items (feed points 64)<br>
* { 720, 1800, 2880, 3600, 7200, 14400 }, // level 46~50 items (feed points 72)<br>
* { 800, 2000, 3200, 4000, 8000, 16000 }, // level 51~55 items (feed points 80)<br>
* { 880, 2200, 3520, 4400, 8800, 17600 } // level 56~60 items (feed points 88)<br>
* };
*/
public final class PetFeedCalculator {
static byte ITEM_MAX_LEVEL = 60;
static final short[] fullCounts;
static final byte[] itemLevels;
static final int[][] pointValues;
static {
TreeSet<Short> counts = new TreeSet<>();
for (PetFlavour flavour : DataManager.PET_FEED_DATA.getPetFlavours()) {
if (flavour.getFullCount() > 0)
counts.add((short) (flavour.getFullCount() & 0xFFFF));
}
fullCounts = new short[counts.size()];
int i = 0;
Iterator<Short> countIter = counts.iterator();
while (countIter.hasNext())
fullCounts[i++] = countIter.next();
itemLevels = new byte[ITEM_MAX_LEVEL / 5];
itemLevels[0] = 5;
for (int j = 1; j < itemLevels.length; j++)
itemLevels[j] = (byte) (itemLevels[j - 1] + 5);
pointValues = new int[itemLevels.length][fullCounts.length];
calculate();
}
/**
* Calculate point values for each item levels and each max feed count
*/
static void calculate() {
for (byte levelByte : itemLevels) {
short level = (short) (levelByte & 0xFF);
if (level < 10)
continue;
int countIndex = 0;
for (short countByte : fullCounts) {
short count = (short) (countByte & 0xFF);
int finalLevel = level;
if (finalLevel % 5 == 0)
finalLevel--;
int pointLevel = itemLevels[finalLevel / 5];
int feedPoints = Math.max(0, pointLevel - 5) / 5 * 8;
pointValues[finalLevel / 5][countIndex++] = getPoints(feedPoints, count);
}
}
}
/**
* Formula to calculate pointValues array
*
* @param feedPoints
* - feed points for item
* @param maxFeedCount
* - max feeding count
* @return byte increment count after all items are fed
*/
static int getPoints(int feedPoints, int maxFeedCount) {
int points = 0;
int state = 0;
int consumed = 0;
while (consumed < maxFeedCount) {
boolean needSwitch = false;
int oldPoints = points;
if ((state == 0 && consumed > maxFeedCount * 0.5f) || (state == 1 && consumed > maxFeedCount * 0.8f)
|| (state == 2 && consumed > maxFeedCount * 1.05)) {
needSwitch = true;
}
points += feedPoints;
if (needSwitch) {
state++;
if (state == 1 && consumed <= 0.487f * maxFeedCount || state == 2 && consumed <= 0.78f * maxFeedCount) {
state--;
points = oldPoints;
}
}
consumed++;
}
return points;
}
public static void updatePetFeedProgress(PetFeedProgress progress, int itemLevel, int maxFeedCount) {
PetHungryLevel currHungryLevel = progress.getHungryLevel();
if (progress.isLovedFeeded()) { // loved food
if (progress.getLovedFoodRemaining() == 0)
return;
progress.setHungryLevel(PetHungryLevel.FULL);
progress.incrementCount(true);
return;
}
int oldPoints = progress.getTotalPoints();
boolean needSwitch = false;
if ((currHungryLevel == PetHungryLevel.HUNGRY && progress.getRegularCount() > maxFeedCount * 0.5f)
|| (currHungryLevel == PetHungryLevel.CONTENT && progress.getRegularCount() > maxFeedCount * 0.8f)
|| (currHungryLevel == PetHungryLevel.SEMIFULL && progress.getRegularCount() > maxFeedCount * 1.05)) {
// forcefully switch level
needSwitch = true;
} else {
int finalLevel = itemLevel;
if (finalLevel % 5 == 0)
finalLevel--;
byte pointLevel = itemLevels[finalLevel / 5];
byte pointsEarned = (byte) (Math.max(0, pointLevel - 5) / 5 * 8);
int feedProgress = progress.getTotalPoints() + pointsEarned;
progress.setTotalPoints(feedProgress);
}
if (needSwitch) {
// just a prevention to not switch level
PetHungryLevel nextLevel = progress.getHungryLevel().getNextValue();
if (nextLevel == PetHungryLevel.CONTENT && progress.getRegularCount() <= 0.487f * maxFeedCount || nextLevel == PetHungryLevel.SEMIFULL
&& progress.getRegularCount() <= 0.78f * maxFeedCount) {
progress.setTotalPoints(oldPoints);
} else {
progress.setHungryLevel(nextLevel);
}
}
progress.incrementCount(false);
}
public static PetFeedResult getReward(int fullCount, PetRewards rewardGroup, PetFeedProgress progress, int playerLevel) {
if (progress.getHungryLevel() != PetHungryLevel.FULL || rewardGroup.getResults().size() == 0)
return null;
int pointsIndex = ArrayUtils.indexOf(fullCounts, (short) fullCount);
if (pointsIndex == ArrayUtils.INDEX_NOT_FOUND)
return null;
if (progress.isLovedFeeded()) { // for cash feed
if (rewardGroup.getResults().size() == 1)
return rewardGroup.getResults().get(0);
List<PetFeedResult> validRewards = new ArrayList<>();
int maxLevel = 0;
for (PetFeedResult result : rewardGroup.getResults()) {
int resultLevel = DataManager.ITEM_DATA.getItemTemplate(result.getItem()).getLevel();
if (resultLevel > playerLevel)
continue;
if (resultLevel > maxLevel) {
maxLevel = resultLevel;
validRewards.clear();
}
validRewards.add(result);
}
return Rnd.get(validRewards);
}
int rewardIndex = 0;
int totalRewards = rewardGroup.getResults().size();
for (int row = 1; row < pointValues.length; row++) {
int[] points = pointValues[row];
if (points[pointsIndex] <= progress.getTotalPoints()) {
rewardIndex = Math.round((float) totalRewards / (pointValues.length - 1) * row) - 1;
}
}
// Fix rounding discrepancy
if (rewardIndex < 0)
rewardIndex = 0;
else if (rewardIndex > rewardGroup.getResults().size() - 1)
rewardIndex = rewardGroup.getResults().size() - 1;
return rewardGroup.getResults().get(rewardIndex);
}
}