package com.minecolonies.coremod.entity.ai.basic;
public abstract class AbstractEntityAICrafting
<J extends AbstractJobCrafter<?, J>, B extends AbstractBuildingWorker>
extends AbstractEntityAIInteract<J, B>
{
/**
* Time the worker delays until the next hit.
*/
protected static final int HIT_DELAY = 20;
/**
* Increase this value to make the product creation progress way slower.
*/
public static final int PROGRESS_MULTIPLIER = 10;
/**
* Max level which should have an effect on the speed of the worker.
*/
protected static final int MAX_LEVEL = 50;
/**
* Times the product needs to be hit.
*/
private static final int HITTING_TIME = 3;
//当前制作中的请求
public IRequest<? extends PublicCrafting> currentRequest;
//当前制作中的配方
protected IRecipeStorage currentRecipeStorage;
/**
* The number of actions a crafting "success" is worth.
* By default, that's 1 action for 1 crafting success.
* Override this in your subclass to make crafting recipes worth more actions :-)
*
* @return The number of actions a crafting "success" is worth.
*/
protected int getActionRewardForCraftingSuccess()
{
return 1;
}
//状态机初始化
public AbstractEntityAICrafting(@NotNull final J job)
{
super(job);
super.registerTargets(
/*
* Check if tasks should be executed.
*/
new AITarget(IDLE, () -> START_WORKING, 1),
new AITarget(START_WORKING, this::decide, STANDARD_DELAY),
new AITarget(QUERY_ITEMS, this::queryItems, STANDARD_DELAY),
new AITarget(GET_RECIPE, this::getRecipe, STANDARD_DELAY),
new AITarget(CRAFT, this::craft, HIT_DELAY)
);
worker.setCanPickUpLoot(true);
}
//START_WORKING的状态装换函数, 决定做什么.
protected IAIState decide()
{
if (job.getTaskQueue().isEmpty())
{
return START_WORKING;
}
if (job.getCurrentTask() == null)
{
return START_WORKING;
}
if (walkToBuilding())
{
return START_WORKING;
}
if (job.getActionsDone() > 0)
{
// Wait to dump before continuing.
return getState();
}
//有请求,有配方,开始请求物品.
if (currentRequest != null && currentRecipeStorage != null)
{
return QUERY_ITEMS;
}
//有请求,没配方
return GET_RECIPE;
}
//GET_RECIPE的状态转换函数 , Query the IRecipeStorage of the first request in the queue.
protected IAIState getRecipe()
{
final IRequest<? extends PublicCrafting> currentTask = job.getCurrentTask();
if (currentTask == null)
{
return START_WORKING;
}
final IBuildingWorker buildingWorker = getOwnBuilding();
currentRecipeStorage = buildingWorker.getFirstFullFillableRecipe(stack -> stack.isItemEqual(currentTask.getRequest().getStack()), 1);
if (currentRecipeStorage == null)
{
job.finishRequest(false);
incrementActionsDone(getActionRewardForCraftingSuccess());
return START_WORKING;
}
currentRequest = currentTask;
//
job.setMaxCraftingCount(currentRequest.getRequest().getCount());
//当前有的数量
final int currentCount = InventoryUtils.getItemCountInItemHandler(worker.getInventoryCitizen(), stack -> stack.isItemEqual(currentRecipeStorage.getPrimaryOutput()));
//制作中的数量
final int inProgressCount = getExtendedOutputCount(currentRecipeStorage.getPrimaryOutput());
//每轮做出几个
final int countPerIteration = currentRecipeStorage.getPrimaryOutput().getCount();
//需要制作几次
final int doneOpsCount = currentCount / countPerIteration;
//制作中的次数
final int progressOpsCount = inProgressCount / countPerIteration;
//还需要制作多少次
final int remainingOpsCount = currentRequest.getRequest().getCount() - doneOpsCount - progressOpsCount;
//检测数量是否够制作
final List<ItemStorage> input = currentRecipeStorage.getCleanedInput();
for (final ItemStorage inputStorage : input)
{
if (InventoryUtils.getItemCountInProvider(getOwnBuilding(), itemStack -> itemStack.isItemEqual(inputStorage.getItemStack()))
+ InventoryUtils.getItemCountInItemHandler(worker.getInventoryCitizen(), itemStack -> itemStack.isItemEqual(inputStorage.getItemStack()))
< inputStorage.getAmount() * remainingOpsCount)
{
//
job.finishRequest(false);
incrementActionsDone(getActionRewardForCraftingSuccess());
return START_WORKING;
}
}
job.setCraftCounter(doneOpsCount);
return QUERY_ITEMS;
}
/**
* Get an extended output count that can be overriden.
* @param primaryOutput the type of output.
* @return the output count that should be added too.
*/
protected int getExtendedOutputCount(final ItemStack primaryOutput)
{
return 0;
}
@Override
public IAIState getStateAfterPickUp()
{
return GET_RECIPE;
}
/**
* Query the required items to take them in the inventory to craft.
*
* @return the next state to go to.
*/
private IAIState queryItems()
{
if (currentRecipeStorage == null)
{
return START_WORKING;
}
return checkForItems(currentRecipeStorage);
}
/**
* Check for all items of the required recipe.
*
* @param storage the recipe storage.
* @return the next state to go to.
*/
protected IAIState checkForItems(@NotNull final IRecipeStorage storage)
{
final int inProgressCount = getExtendedOutputCount(currentRecipeStorage.getPrimaryOutput());
final int countPerIteration = currentRecipeStorage.getPrimaryOutput().getCount();
final int progressOpsCount = inProgressCount / countPerIteration;
final List<ItemStorage> input = storage.getCleanedInput();
for (final ItemStorage inputStorage : input)
{
final Predicate<ItemStack> predicate = stack -> !ItemStackUtils.isEmpty(stack) && new Stack(stack).matches(inputStorage.getItemStack());
if (InventoryUtils.getItemCountInItemHandler(worker.getInventoryCitizen(), predicate) + ((job.getCraftCounter() + progressOpsCount ) * inputStorage.getAmount())
< inputStorage.getAmount() * job.getMaxCraftingCount())
{
if (InventoryUtils.hasItemInProvider(getOwnBuilding(), predicate))
{
needsCurrently = new Tuple<>(predicate, inputStorage.getAmount() * job.getMaxCraftingCount());
return GATHERING_REQUIRED_MATERIALS;
}
currentRecipeStorage = null;
currentRequest = null;
return GET_RECIPE;
}
}
return CRAFT;
}
/**
* The actual crafting logic.
*
* @return the next state to go to.
*/
protected IAIState craft()
{
if (currentRecipeStorage == null || job.getCurrentTask() == null)
{
return START_WORKING;
}
if (currentRequest == null && job.getCurrentTask() != null)
{
return GET_RECIPE;
}
if (walkToBuilding())
{
return getState();
}
job.setProgress(job.getProgress() + 1);
worker.setHeldItem(Hand.MAIN_HAND,
currentRecipeStorage.getCleanedInput().get(worker.getRandom().nextInt(currentRecipeStorage.getCleanedInput().size())).getItemStack().copy());
worker.setHeldItem(Hand.OFF_HAND, currentRecipeStorage.getPrimaryOutput().copy());
worker.getCitizenItemHandler().hitBlockWithToolInHand(getOwnBuilding().getPosition());
currentRequest = job.getCurrentTask();
if (currentRequest != null && (currentRequest.getState() == RequestState.CANCELLED || currentRequest.getState() == RequestState.FAILED))
{
currentRequest = null;
incrementActionsDone(getActionRewardForCraftingSuccess());
currentRecipeStorage = null;
return START_WORKING;
}
if (job.getProgress() >= getRequiredProgressForMakingRawMaterial())
{
final IAIState check = checkForItems(currentRecipeStorage);
if (check == CRAFT)
{
if (!currentRecipeStorage.fullFillRecipe(worker.getItemHandlerCitizen()))
{
currentRequest = null;
incrementActionsDone(getActionRewardForCraftingSuccess());
job.finishRequest(false);
resetValues();
return START_WORKING;
}
currentRequest.addDelivery(currentRecipeStorage.getPrimaryOutput());
job.setCraftCounter(job.getCraftCounter() + 1);
if (job.getCraftCounter() >= job.getMaxCraftingCount())
{
incrementActionsDone(getActionRewardForCraftingSuccess());
currentRecipeStorage = null;
resetValues();
if (inventoryNeedsDump())
{
if (job.getMaxCraftingCount() == 0 && job.getProgress() == 0 && job.getCraftCounter() == 0 && currentRequest != null)
{
job.finishRequest(true);
worker.getCitizenExperienceHandler().addExperience(currentRequest.getRequest().getCount() / 2.0);
}
}
}
else
{
job.setProgress(0);
return GET_RECIPE;
}
}
else
{
currentRequest = null;
job.finishRequest(false);
incrementActionsDoneAndDecSaturation();
resetValues();
}
return START_WORKING;
}
return getState();
}
/**
* Reset all the values.
*/
public void resetValues()
{
job.setMaxCraftingCount(0);
job.setProgress(0);
job.setCraftCounter(0);
worker.setHeldItem(Hand.MAIN_HAND, ItemStackUtils.EMPTY);
worker.setHeldItem(Hand.OFF_HAND, ItemStackUtils.EMPTY);
}
@Override
public IAIState afterDump()
{
if (job.getMaxCraftingCount() == 0 && job.getProgress() == 0 && job.getCraftCounter() == 0 && currentRequest != null)
{
// Fallback security blanket. Normally, the craft() method should have dealt with the request.
if (currentRequest.getState() == RequestState.IN_PROGRESS)
{
job.finishRequest(true);
}
currentRequest = null;
}
resetValues();
return super.afterDump();
}
@Override
protected int getActionsDoneUntilDumping()
{
return 1;
}
/**
* Get the required progress to execute a recipe.
*
* @return the amount of hits required.
*/
private int getRequiredProgressForMakingRawMaterial()
{
return PROGRESS_MULTIPLIER / Math.min(worker.getCitizenData().getJobModifier() + 1, MAX_LEVEL) * HITTING_TIME;
}
@Override
public boolean isAfterDumpPickupAllowed()
{
return currentRequest == null;
}
}
评论区