/*******************************************************************************
* Copyright 2014 Tobias Welther
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package de.tobiyas.racesandclasses.playermanagement.spellmanagement;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import de.tobiyas.racesandclasses.RacesAndClasses;
import de.tobiyas.racesandclasses.datacontainer.traitholdercontainer.AbstractTraitHolder;
import de.tobiyas.racesandclasses.datacontainer.traitholdercontainer.TraitHolderCombinder;
import de.tobiyas.racesandclasses.eventprocessing.eventresolvage.resolvers.WorldResolver;
import de.tobiyas.racesandclasses.playermanagement.player.RaCPlayer;
import de.tobiyas.racesandclasses.playermanagement.spellmanagement.mana.ManaManager;
import de.tobiyas.racesandclasses.playermanagement.spellmanagement.mana.impl.OwnManaManager;
import de.tobiyas.racesandclasses.traitcontainer.interfaces.AbstractBasicTrait;
import de.tobiyas.racesandclasses.traitcontainer.interfaces.markerinterfaces.MagicSpellTrait;
import de.tobiyas.racesandclasses.traitcontainer.interfaces.markerinterfaces.Trait;
import de.tobiyas.racesandclasses.traitcontainer.interfaces.markerinterfaces.TraitWithCost;
import de.tobiyas.racesandclasses.traitcontainer.interfaces.markerinterfaces.TraitWithRestrictions;
import de.tobiyas.racesandclasses.util.items.WandItem;
public class PlayerSpellManager {
/**
* Last time an Event was triggered
*/
private long lastEventTime = System.currentTimeMillis();
/**
* The Player this container belongs.
*/
private final RaCPlayer player;
/**
* The {@link OwnManaManager} that the player has.
*/
protected final ManaManager manaManager;
/**
* The Spell list of the Player.
*/
protected final RotatableList<MagicSpellTrait> spellList;
/**
* Creates a SpellManager with a containing {@link OwnManaManager}.
*
* @param player to create with
*/
public PlayerSpellManager(RaCPlayer player) {
this.player = player;
this.manaManager = RacesAndClasses.getPlugin().getConfigManager().getGeneralConfig().getConfig_manaManagerType().generate(player);
this.spellList = new RotatableList<MagicSpellTrait>();
}
/**
* Rescans the Player for changed Races and Classes to update the Mana
* and the Spells he can cast.
*/
public void rescan(){
MagicSpellTrait current = getCurrentSpell();
spellRescan();
manaManager.rescanPlayer();
if(current != null) selectSpell(current.getDisplayName());
}
/**
* Selects the Spell by name if possible.
*
* @param displayName to select.
*/
private void selectSpell(String displayName) {
for(int i = 0; i < spellList.size(); i++){
MagicSpellTrait next = spellList.next();
if(next instanceof TraitWithRestrictions){
boolean canUse = ((TraitWithRestrictions) next).isInLevelRange(player.getLevelManager().getCurrentLevel());
if(!canUse) continue;
if(next.getDisplayName().equalsIgnoreCase(displayName)) return;
}
}
}
/**
* Rescans the Spells the player can cast
*/
private void spellRescan(){
List<MagicSpellTrait> spellList = new LinkedList<MagicSpellTrait>();
if(player == null || !player.isOnline() ||WorldResolver.isOnDisabledWorld(player.getPlayer())){
this.spellList.setEntries(spellList);
return;
}
//Obtain all spells:
Set<Trait> traits = TraitHolderCombinder.getSkillTreeReducedTraitsOfPlayer(player);
for(Trait trait : traits){
if(trait instanceof MagicSpellTrait){
spellList.add((MagicSpellTrait) trait);
}
}
Collections.sort(spellList);
this.spellList.setEntries(spellList);
}
/**
* Changes to the next Spell in the List.
*
* @return the next Spell.
*/
public MagicSpellTrait changeToNextSpell(){
if(spellList.size() == 0) return null;
if(System.currentTimeMillis() - lastEventTime < 100) return null;
lastEventTime = System.currentTimeMillis();
for(int i = 0; i < spellList.size(); i++){
MagicSpellTrait next = spellList.next();
if(next instanceof TraitWithRestrictions){
boolean canUse = ((TraitWithRestrictions) next).isInLevelRange(player.getLevelManager().getCurrentLevel());
if(canUse) return next;
}else{
return next;
}
}
return null;
}
/**
* Changes to the Previous Spell in the List.
*
* @return the next Spell.
*/
public TraitWithCost changeToPrevSpell() {
if(spellList.size() == 0) return null;
if(System.currentTimeMillis() - lastEventTime < 100) return null;
lastEventTime = System.currentTimeMillis();
for(int i = 0; i < spellList.size(); i++){
TraitWithCost next = spellList.previous();
if(next instanceof TraitWithRestrictions){
boolean canUse = ((TraitWithRestrictions) next).isInLevelRange(player.getLevelManager().getCurrentLevel());
if(canUse) return next;
}else{
return next;
}
}
return null;
}
/**
* Returns all Spells available.
*
* @return all spells available.
*/
public List<MagicSpellTrait> getAllSpells(){
List<MagicSpellTrait> spells = this.spellList.getAllEntries();
java.util.Iterator<MagicSpellTrait> it = spells.iterator();
while(it.hasNext()){
if(!it.next().isInLevelRange(player.getLevelManager().getCurrentLevel())) {
it.remove();
}
}
return spells;
}
/**
* Returns the {@link OwnManaManager} to check Spell casting or
* Mana Capabilities.
*
* @return the OwnManaManager of the Player
*/
public ManaManager getManaManager(){
return manaManager;
}
/**
* Returns the current Spell.
*
* If no spells are present, null is returned.
*
* @return the current spell.
*/
public MagicSpellTrait getCurrentSpell() {
MagicSpellTrait current = spellList.currentEntry();
if(current == null) return changeToNextSpell();
if(current instanceof TraitWithRestrictions){
boolean canUse = ((TraitWithRestrictions) current).isInLevelRange(player.getLevelManager().getCurrentLevel());
if(!canUse) return changeToNextSpell();
}
return current;
}
/**
* Ticks the Container.
*/
public void tick(){
manaManager.tick();
}
/**
* Checks if the player can cast the spell
* @param type to check
* @param cost to check
* @param castMaterial to check (may be null if type != ITEM)
*
* @return true if he can, false if not
*/
public boolean canCastSpell(TraitWithCost trait){
double cost = trait.getCost(player);
switch(trait.getCostType()){
case HEALTH: return player.getHealth() > cost;
case MANA: return getManaManager().hasEnoughMana(cost);
case ITEM: return playerHasItem(trait.getCastMaterialType(player), trait.getCastMaterialDamage(player), trait.getCastMaterialName(player), (int)cost);
case HUNGER : return player.getPlayer().getFoodLevel() >= cost;
case EXP : return player.getLevelManager().canRemove((int)cost);
default: return false;
}
}
/**
* Checks if the player has the Item.
* @param material to check
* @param damage to check
* @param name to check
* @return true if has.
*/
private boolean playerHasItem(Material material, short damage, String name, int amount){
if(!player.isOnline()) return false;
if(name != null) name = ChatColor.stripColor(name);
Player player = this.player.getPlayer();
PlayerInventory inv = player.getInventory();
//Check for items:
for(ItemStack item : inv){
if(item == null) continue;
//Check if item is correct:
if(item.getType() != material) continue;
if(item.getDurability() != damage) continue;
//Check for name.
if(name != null && !name.isEmpty()){
if(!item.getItemMeta().hasDisplayName()) continue;
String itemName = item.getItemMeta().getDisplayName();
itemName = ChatColor.stripColor(itemName);
if(!itemName.equalsIgnoreCase(name)) continue;
}
//If done, check!
amount -= item.getAmount();
if(amount <= 0) return true;
}
return false;
}
/**
* Removes the Item.
* @param material to check
* @param damage to check
* @param name to check
* @param amount to remove
*/
private void removeItem(Material material, short damage, String name, int amount){
if(!player.isOnline()) return;
if(name != null) name = ChatColor.stripColor(name);
Player player = this.player.getPlayer();
PlayerInventory inv = player.getInventory();
//Check for items:
for(int i = 0; i < inv.getSize(); i++){
ItemStack item = inv.getItem(i);
if(item == null) continue;
//Check if item is correct:
if(item.getType() != material) continue;
if(item.getDurability() != damage) continue;
//Check for name.
if(name != null && !name.isEmpty()){
if(!item.getItemMeta().hasDisplayName()) continue;
String itemName = item.getItemMeta().getDisplayName();
itemName = ChatColor.stripColor(itemName);
if(!itemName.equalsIgnoreCase(name)) continue;
}
//If done, remove!
int newAmount = item.getAmount() - amount;
item.setAmount(newAmount);
inv.setItem(i, item);
amount = -newAmount;
if(amount <= 0) return;
}
}
/**
* Removes the spell cost from the player
*
* @param trait to remove the cost from
*/
public void removeCost(TraitWithCost trait) {
double cost = trait.getCost(player);
switch(trait.getCostType()){
case HEALTH: player.getHealthManager().damage(cost);break;
case MANA: getManaManager().drownMana(cost); break;
case ITEM: removeItem(trait.getCastMaterialType(player), trait.getCastMaterialDamage(player), trait.getCastMaterialName(player), (int)cost); break;
case HUNGER:
int oldFoodLevel = player.getPlayer().getFoodLevel();
int newFoodLevel = (int) (oldFoodLevel - cost);
player.getPlayer().setFoodLevel(newFoodLevel < 0 ? 0 : newFoodLevel);
case EXP:
player.getLevelManager().removeExp((int) cost);
}
}
/**
* Returns the amount of spells the SpellManager contains.
*
* @return
*/
public int getSpellAmount() {
return spellList.size();
}
/**
* Changes the current spell to the one posted.
*
* @param spellName to set. This is the display name!!!
*
* @return true if it worked, false otherwise.
*/
public boolean changeToSpell(String spellName) {
if(getSpellAmount() == 0) return false;
for(int i = 0; i < spellList.size(); i++){
TraitWithCost spell = spellList.currentEntry();
if(spell instanceof AbstractBasicTrait){
//first check Display name
String name = ((AbstractBasicTrait) spell).getDisplayName();
if(name.equalsIgnoreCase(spellName)){
return true;
}
//second check Trait Name.
name = ((AbstractBasicTrait) spell).getName();
if(name.equalsIgnoreCase(spellName)){
return true;
}
}
spellList.next();
}
return false;
}
/**
* Tries to cast the current spell.
*
* @return true if worked, false otherwise.
*/
public boolean tryCastCurrentSpell() {
TraitWithCost currentSpell = spellList.currentEntry();
if(currentSpell == null) return false;
RacesAndClasses plugin = RacesAndClasses.getPlugin();
@SuppressWarnings("deprecation")
List<Block> blocks = player.getPlayer().getLineOfSight((HashSet<Byte>)null, 100);
Block lookingAt = blocks.iterator().next();
ItemStack wandItem = new ItemStack(plugin.getConfigManager().getGeneralConfig().getConfig_itemForMagic().generateItem());
plugin.fireEventIntern(new PlayerInteractEvent(player.getPlayer(), Action.LEFT_CLICK_AIR, wandItem,
lookingAt, BlockFace.UP));
return true;
}
/**
* Checks if the item passed is a Wand.
* @param item to check against
* @return true if the item passed is a wand for the player
*/
public boolean isWandItem(ItemStack item){
if(player == null) return false;
if(item == null) return false;
RacesAndClasses plugin = RacesAndClasses.getPlugin();
Set<WandItem> wands = new HashSet<>();
wands.add(plugin.getConfigManager().getGeneralConfig().getConfig_itemForMagic());
AbstractTraitHolder classHolder = player.getclass();
if(classHolder != null){
wands.addAll(classHolder.getAdditionalWandMaterials());
}
AbstractTraitHolder raceHolder = player.getRace();
if(raceHolder != null){
wands.addAll(raceHolder.getAdditionalWandMaterials());
}
for(WandItem wand : wands) if(wand.isItem(item)) return true;
return false;
}
}