/**
Copyright (C) <2015> <coolAlias>
This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such,
you can redistribute it and/or modify it under the terms of the GNU
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package zeldaswordskills.entity.player.quests;
import java.util.Random;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.IChatComponent;
import zeldaswordskills.ZSSMain;
import zeldaswordskills.network.PacketDispatcher;
import zeldaswordskills.network.client.SyncQuestPacket;
public abstract class QuestBase implements IQuest
{
/** Dummy quest handler */
public static final IQuestHandler DEFAULT_QUEST_HANDLER = (new IQuestHandler() {
@Override
public void onQuestBegun(IQuest quest, EntityPlayer player) {}
@Override
public void onQuestChanged(IQuest quest, EntityPlayer player) {}
@Override
public void onQuestCompleted(IQuest quest, EntityPlayer player) {}
});
/** Bit flag (bit 6, i.e. 64) set when the quest has begun */
protected static final int FLAG_BEGIN = 64;
/** Bit flag (bit 7, i.e. 128) set when the quest is complete */
protected static final int FLAG_COMPLETE = 128;
/** RNG used by quests */
protected static final Random rand = new Random();
/**
* Combined total of all quest flags which have been set; each flag should be a
* specific bit - used bits are {@link #FLAG_BEGIN} and {@link #FLAG_COMPLETE}
*/
protected int flag;
/**
* Returns true if the flag is set; flags can be checked in combination by adding them together
*/
protected boolean isset(int flag) {
return (this.flag & flag) == flag;
}
/**
* Sets the flag; flags can be set in combination by adding them together
*/
protected void set(int flag) {
this.flag |= flag;
}
/**
* Unsets the flag; flags can be unset in combination by adding them together
*/
protected void unset(int flag) {
this.flag &= ~flag;
}
@Override
public boolean canBegin(EntityPlayer player) {
return !isset(FLAG_BEGIN);
}
@Override
public boolean begin(EntityPlayer player, Object... data) {
if (canBegin(player) && onBegin(player, data)) {
set(FLAG_BEGIN);
return true;
}
return false;
}
/**
* Called from the default {@link #begin} implementation after {@link #canBegin}
* has already returned true; this is usually a good time to send chat messages, etc.
* @param data any extra data from {@link #begin}
* @return true to allow the quest to begin, or false to abort
*/
protected abstract boolean onBegin(EntityPlayer player, Object... data);
@Override
public boolean hasBegun(EntityPlayer player) {
return isset(FLAG_BEGIN);
}
@Override
public boolean canComplete(EntityPlayer player) {
return !isComplete(player);
}
@Override
public boolean complete(EntityPlayer player, Object... data) {
if (canComplete(player) && onComplete(player, data)) {
set(FLAG_COMPLETE);
return true;
}
return false;
}
/**
* Called from the default {@link #complete} implementation after {@link #canComplete}
* has already returned true; this is usually a good time to give quest rewards, etc.
* @param data any extra data from {@link #complete}
* @return true to allow the quest to complete, or false to abort
*/
protected abstract boolean onComplete(EntityPlayer player, Object... data);
@Override
public boolean isComplete(EntityPlayer player) {
return isset(FLAG_COMPLETE);
}
@Override
public boolean requiresSync() {
return false;
}
@Override
public void writeToNBT(NBTTagCompound compound) {
compound.setInteger("questFlag", flag);
}
@Override
public void readFromNBT(NBTTagCompound compound) {
flag = compound.getInteger("questFlag");
}
/**
* Writes the quest to NBT, guaranteeing that the fully qualified class name is included
*/
public static NBTTagCompound saveToNBT(IQuest quest) {
NBTTagCompound compound = new NBTTagCompound();
quest.writeToNBT(compound);
compound.setString("QuestClass", quest.getClass().getName());
return compound;
}
/**
* Reconstructs the IQuest instance from the NBTTagCompound, assuming the
* fully qualified class name is stored using the key "QuestClass"
*/
public static IQuest loadFromNBT(NBTTagCompound compound) {
try {
Class<?> clazz = Class.forName(compound.getString("QuestClass"));
Object o = clazz.newInstance();
if (o instanceof IQuest) {
((IQuest) o).readFromNBT(compound);
return (IQuest) o;
} else {
ZSSMain.logger.warn("Failed to load quest from NBT: " + compound);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
/**
* Returns a new instance of the given quest class, or null if there was an exception
*/
public static IQuest getQuestInstance(Class<? extends IQuest> clazz) {
try {
return clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
/**
* Returns true if the given quest made progress and/or no further interactions should occur.
* @param player
* @param quest Quest instance to check for progress
* @param handler IQuestHandler will have its methods called at appropriate times; pass {@link #DEFAULT_QUEST_HANDLER} if none available
* @param data Optional arguments passed to the various {@code IQuest} methods, e.g. {@link IQuest#begin begin} and {@link IQuest#complete complete}
* @return True if something happened, i.e. the calling interaction should be canceled
*/
public static boolean checkQuestProgress(EntityPlayer player, IQuest quest, IQuestHandler handler, Object... data) {
boolean changed = false;
if (quest.isComplete(player)) {
// do nothing if quest is already complete
} else if (quest.canBegin(player)) {
if (quest.begin(player, data)) {
handler.onQuestBegun(quest, player);
changed = true;
}
} else if (quest.canComplete(player)) {
if (quest.complete(player, data)) {
handler.onQuestCompleted(quest, player);
changed = true;
}
} else if (quest.hasBegun(player) && quest.update(player, data)) {
handler.onQuestChanged(quest, player);
changed = true;
}
if (!changed) { // check for hint
IChatComponent hint = quest.getHint(player, data);
if (hint != null) { // result may be different on client vs. server due to Random
if (!player.worldObj.isRemote && !hint.getUnformattedText().equals("")) {
player.addChatMessage(hint);
}
return true;
}
}
if (changed && quest.requiresSync() && player instanceof EntityPlayerMP) {
PacketDispatcher.sendTo(new SyncQuestPacket(quest), (EntityPlayerMP) player);
}
return changed;
}
}