diff --git a/src/main/java/com/seodisparate/TurnBasedMinecraft/TurnBasedMinecraftMod.java b/src/main/java/com/seodisparate/TurnBasedMinecraft/TurnBasedMinecraftMod.java index e4a3a04..7089755 100644 --- a/src/main/java/com/seodisparate/TurnBasedMinecraft/TurnBasedMinecraftMod.java +++ b/src/main/java/com/seodisparate/TurnBasedMinecraft/TurnBasedMinecraftMod.java @@ -39,7 +39,7 @@ public class TurnBasedMinecraftMod private static BattleManager battleManager; private static int packetHandlerID = 0; public static Entity attackingEntity; - private static Config config; + public static Config config; public static Battle currentBattle; diff --git a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Battle.java b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Battle.java index 78cf6e8..526f5ae 100644 --- a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Battle.java +++ b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Battle.java @@ -7,6 +7,11 @@ import java.util.Collection; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; +import java.util.PriorityQueue; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; import com.seodisparate.TurnBasedMinecraft.TurnBasedMinecraftMod; import com.seodisparate.TurnBasedMinecraft.common.networking.PacketBattleInfo; @@ -21,12 +26,13 @@ public class Battle private final int id; private Map sideA; private Map sideB; - private Map players; + private Map players; + private PriorityQueue turnOrderQueue; private Instant lastUpdated; private State state; - private int playerCount; - private int undecidedCount; + private AtomicInteger playerCount; + private AtomicInteger undecidedCount; private Duration timer; public enum State @@ -76,17 +82,26 @@ public class Battle this.id = id; this.sideA = new Hashtable(); this.sideB = new Hashtable(); - players = new HashMap(); - playerCount = 0; + players = new Hashtable(); + turnOrderQueue = new PriorityQueue(new Combatant.CombatantComparator()); + playerCount = new AtomicInteger(0); + undecidedCount = new AtomicInteger(0); if(sideA != null) { for(Entity e : sideA) { - this.sideA.put(e.getEntityId(), new Combatant(e)); + EntityInfo entityInfo = TurnBasedMinecraftMod.config.getMatchingEntityInfo(e); + if(entityInfo == null && !(e instanceof EntityPlayer)) + { + continue; + } + Combatant newCombatant = new Combatant(e, entityInfo); + this.sideA.put(e.getEntityId(), newCombatant); if(e instanceof EntityPlayer) { - ++playerCount; - players.put(e.getEntityId(), (EntityPlayer)e); + newCombatant.recalcSpeedOnCompare = true; + playerCount.incrementAndGet(); + players.put(e.getEntityId(), newCombatant); } } } @@ -94,18 +109,25 @@ public class Battle { for(Entity e : sideB) { - this.sideB.put(e.getEntityId(), new Combatant(e)); + EntityInfo entityInfo = TurnBasedMinecraftMod.config.getMatchingEntityInfo(e); + if(entityInfo == null && !(e instanceof EntityPlayer)) + { + continue; + } + Combatant newCombatant = new Combatant(e, entityInfo); + this.sideB.put(e.getEntityId(), newCombatant); if(e instanceof EntityPlayer) { - ++playerCount; - players.put(e.getEntityId(), (EntityPlayer)e); + newCombatant.recalcSpeedOnCompare = true; + playerCount.incrementAndGet(); + players.put(e.getEntityId(), newCombatant); } } } lastUpdated = null; state = State.DECISION; - undecidedCount = playerCount; + undecidedCount.set(playerCount.get()); timer = TurnBasedMinecraftMod.BattleDecisionTime; } @@ -126,28 +148,42 @@ public class Battle public void addCombatantToSideA(Entity e) { - sideA.put(e.getEntityId(), new Combatant(e)); + EntityInfo entityInfo = TurnBasedMinecraftMod.config.getMatchingEntityInfo(e); + if(entityInfo == null && !(e instanceof EntityPlayer)) + { + return; + } + Combatant newCombatant = new Combatant(e, entityInfo); + sideA.put(e.getEntityId(), newCombatant); if(e instanceof EntityPlayer) { - ++playerCount; - players.put(e.getEntityId(), (EntityPlayer)e); + newCombatant.recalcSpeedOnCompare = true; + playerCount.incrementAndGet(); + players.put(e.getEntityId(), newCombatant); if(state == State.DECISION) { - ++undecidedCount; + undecidedCount.incrementAndGet(); } } } public void addCombatantToSideB(Entity e) { - sideB.put(e.getEntityId(), new Combatant(e)); + EntityInfo entityInfo = TurnBasedMinecraftMod.config.getMatchingEntityInfo(e); + if(entityInfo == null && !(e instanceof EntityPlayer)) + { + return; + } + Combatant newCombatant = new Combatant(e, entityInfo); + sideB.put(e.getEntityId(), newCombatant); if(e instanceof EntityPlayer) { - ++playerCount; - players.put(e.getEntityId(), (EntityPlayer)e); + newCombatant.recalcSpeedOnCompare = true; + playerCount.incrementAndGet(); + players.put(e.getEntityId(), newCombatant); if(state == State.DECISION) { - ++undecidedCount; + undecidedCount.incrementAndGet(); } } } @@ -157,8 +193,8 @@ public class Battle sideA.clear(); sideB.clear(); players.clear(); - playerCount = 0; - undecidedCount = 0; + playerCount.set(0); + undecidedCount.set(0); } public Collection getSideA() @@ -201,27 +237,20 @@ public class Battle return combatant; } - public void setDecision(int entityID, Decision decision) + public void setDecision(int entityID, Decision decision, int targetEntityID) { if(state != State.DECISION) { return; } - Combatant combatant = sideA.get(entityID); + Combatant combatant = players.get(entityID); if(combatant == null) { - combatant = sideB.get(entityID); - if(combatant == null) - { - return; - } - } - - if(combatant.entity instanceof EntityPlayer) - { - combatant.decision = decision; - --undecidedCount; + return; } + combatant.decision = decision; + combatant.targetEntityID = targetEntityID; + undecidedCount.decrementAndGet(); } public State getState() @@ -236,9 +265,9 @@ public class Battle return; } PacketBattleInfo infoPacket = new PacketBattleInfo(getSideAIDs(), getSideBIDs()); - for(EntityPlayer p : players.values()) + for(Combatant p : players.values()) { - PacketHandler.INSTANCE.sendTo(infoPacket, (EntityPlayerMP)p); + PacketHandler.INSTANCE.sendTo(infoPacket, (EntityPlayerMP)p.entity); } } @@ -263,15 +292,29 @@ public class Battle { case DECISION: timer = timer.minus(dt); - if(timer.isNegative() || timer.isZero() || undecidedCount <= 0) + if(timer.isNegative() || timer.isZero() || undecidedCount.get() <= 0) { state = State.ATTACK; timer = TurnBasedMinecraftMod.BattleDecisionTime; + turnOrderQueue.clear(); + for(Combatant c : sideA.values()) + { + turnOrderQueue.add(c); + } + for(Combatant c : sideB.values()) + { + turnOrderQueue.add(c); + } update(Duration.ZERO); } break; case ATTACK: - // TODO + Combatant next = turnOrderQueue.poll(); + while(next != null) + { + // TODO attack per entity here + next = turnOrderQueue.poll(); + } break; case HEALTH_CHECK: // TODO diff --git a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/BattleManager.java b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/BattleManager.java index 40dcd12..18ddcbd 100644 --- a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/BattleManager.java +++ b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/BattleManager.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.Hashtable; import java.util.Map; +import com.seodisparate.TurnBasedMinecraft.TurnBasedMinecraftMod; import com.seodisparate.TurnBasedMinecraft.common.networking.PacketBattleEntered; import com.seodisparate.TurnBasedMinecraft.common.networking.PacketHandler; @@ -36,6 +37,12 @@ public class BattleManager */ public boolean checkAttack(final LivingAttackEvent event) { + // verify that both entities are EntityPlayer or has a corresponding EntityInfo + if(!(event.getEntity() instanceof EntityPlayer || TurnBasedMinecraftMod.config.getEntityInfoReference(event.getEntity().getClass().getName()) != null) + || !(event.getSource().getTrueSource() instanceof EntityPlayer || TurnBasedMinecraftMod.config.getEntityInfoReference(event.getSource().getTrueSource().getClass().getName()) != null)) + { + return false; + } // check if one is in battle Entity inBattle = null; Entity notInBattle = null; diff --git a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Combatant.java b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Combatant.java index e32bb92..a25db37 100644 --- a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Combatant.java +++ b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Combatant.java @@ -1,31 +1,126 @@ package com.seodisparate.TurnBasedMinecraft.common; +import java.util.Comparator; + +import com.seodisparate.TurnBasedMinecraft.TurnBasedMinecraftMod; + import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.potion.PotionEffect; public class Combatant { public Entity entity; public Battle.Decision decision; public int itemToUse; - public float speed; + public EntityInfo entityInfo; + public boolean recalcSpeedOnCompare; + public int targetEntityID; public Combatant() { decision = Battle.Decision.UNDECIDED; - speed = 0.5f; + recalcSpeedOnCompare = false; } - public Combatant(Entity e) + public Combatant(Entity e, EntityInfo entityInfo) { entity = e; decision = Battle.Decision.UNDECIDED; - speed = 0.5f; + this.entityInfo = entityInfo; + recalcSpeedOnCompare = false; } - public Combatant(Entity e, float speed) + /** + * Provided in reverse order of speed because PriorityQueue has least first. + */ + public static class CombatantComparator implements Comparator { - entity = e; - decision = Battle.Decision.UNDECIDED; - this.speed = speed; + @Override + public int compare(Combatant c0, Combatant c1) + { + if(c0.entity instanceof EntityPlayer && c0.recalcSpeedOnCompare) + { + EntityLivingBase c0Entity = (EntityLivingBase)c0.entity; + boolean isHaste = false; + boolean isSlow = false; + for(PotionEffect e : c0Entity.getActivePotionEffects()) + { + if(e.getEffectName().equals(MobEffects.HASTE.getName())) + { + isHaste = true; + } + else if(e.getEffectName().equals(MobEffects.SLOWNESS.getName())) + { + isSlow = true; + } + } + if(c0.entityInfo == null) + { + c0.entityInfo = new EntityInfo(); + } + if(isHaste && !isSlow) + { + c0.entityInfo.speed = TurnBasedMinecraftMod.config.getPlayerHasteSpeed(); + } + else if(isSlow && !isHaste) + { + c0.entityInfo.speed = TurnBasedMinecraftMod.config.getPlayerSlowSpeed(); + } + else + { + c0.entityInfo.speed = TurnBasedMinecraftMod.config.getPlayerSpeed(); + } + } + + if(c1.entity instanceof EntityPlayer && c1.recalcSpeedOnCompare) + { + EntityLivingBase c1Entity = (EntityLivingBase)c1.entity; + boolean isHaste = false; + boolean isSlow = false; + for(PotionEffect e : c1Entity.getActivePotionEffects()) + { + if(e.getEffectName().equals(MobEffects.HASTE.getName())) + { + isHaste = true; + } + else if(e.getEffectName().equals(MobEffects.SLOWNESS.getName())) + { + isSlow = true; + } + } + if(c1.entityInfo == null) + { + c1.entityInfo = new EntityInfo(); + } + if(isHaste && !isSlow) + { + c1.entityInfo.speed = TurnBasedMinecraftMod.config.getPlayerHasteSpeed(); + } + else if(isSlow && !isHaste) + { + c1.entityInfo.speed = TurnBasedMinecraftMod.config.getPlayerSlowSpeed(); + } + else + { + c1.entityInfo.speed = TurnBasedMinecraftMod.config.getPlayerSpeed(); + } + } + + if(c0.entityInfo.speed > c1.entityInfo.speed) + { + return -1; + } + else if(c0.entityInfo.speed < c1.entityInfo.speed) + { + return 1; + } + else + { + return 0; + } + } } } diff --git a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Config.java b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Config.java index 1d8897f..1e24996 100644 --- a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Config.java +++ b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/Config.java @@ -24,9 +24,12 @@ import com.seodisparate.TurnBasedMinecraft.common.EntityInfo.Category; public class Config { - private Map entityInfoMap; + private Map entityInfoMap; private Set ignoreBattleTypes; private Logger logger; + private int playerSpeed; + private int playerHasteSpeed; + private int playerSlowSpeed; private enum ConfigParseResult { @@ -36,7 +39,7 @@ public class Config public Config(Logger logger) { - entityInfoMap = new HashMap(); + entityInfoMap = new HashMap(); ignoreBattleTypes = new HashSet(); this.logger = logger; @@ -65,6 +68,7 @@ public class Config ConfigParseResult result = parseConfig(configFile); if(result == ConfigParseResult.IS_OLD) { + logger.warn("Config file " + TurnBasedMinecraftMod.CONFIG_FILENAME + " is older version, renaming..."); moveOldConfig(); writeConfig(); ConfigParseResult resultSecond = parseConfig(configFile); @@ -109,8 +113,10 @@ public class Config File configFile = new File(TurnBasedMinecraftMod.CONFIG_FILE_PATH); if(configFile.exists()) { - configFile.renameTo(new File(TurnBasedMinecraftMod.CONFIG_DIRECTORY + "_" - + DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(Instant.now()))); + configFile.renameTo(new File(TurnBasedMinecraftMod.CONFIG_DIRECTORY + + "TBM_Config_" + + DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(Instant.now()) + + ".xml")); } } @@ -149,6 +155,28 @@ public class Config } } while(!(xmlReader.isEndElement() && xmlReader.getLocalName().equals("IgnoreBattleTypes"))); } + else if(xmlReader.getLocalName().equals("PlayerStats")) + { + do + { + xmlReader.next(); + if(xmlReader.isStartElement()) + { + if(xmlReader.getLocalName().equals("Speed")) + { + playerSpeed = Integer.parseInt(xmlReader.getElementText()); + } + else if(xmlReader.getLocalName().equals("HasteSpeed")) + { + playerHasteSpeed = Integer.parseInt(xmlReader.getElementText()); + } + else if(xmlReader.getLocalName().equals("SlowSpeed")) + { + playerSlowSpeed = Integer.parseInt(xmlReader.getElementText()); + } + } + } while(!(xmlReader.isEndElement() && xmlReader.getLocalName().equals("PlayerStats"))); + } else if(xmlReader.getLocalName().equals("EntityStats")) { do @@ -179,6 +207,10 @@ public class Config { eInfo.attackProbability = Integer.parseInt(xmlReader.getAttributeValue(i)); } + else if(xmlReader.getAttributeLocalName(i).equals("Variance")) + { + eInfo.attackVariance = Integer.parseInt(xmlReader.getAttributeValue(i)); + } } eInfo.attackPower = Integer.parseInt(xmlReader.getElementText()); } @@ -230,9 +262,20 @@ public class Config } } while(!(xmlReader.isEndElement() && xmlReader.getLocalName().equals("Conflicts"))); } + else if(xmlReader.getLocalName().equals("IgnoreBattle")) + { + if(xmlReader.getElementText().toLowerCase().equals("true")) + { + eInfo.ignoreBattle = true; + } + } + else if(xmlReader.getLocalName().equals("Speed")) + { + eInfo.speed = Integer.parseInt(xmlReader.getElementText()); + } } } while(!(xmlReader.isEndElement() && xmlReader.getLocalName().equals(classType))); - entityInfoMap.put(eInfo.classType, eInfo); + entityInfoMap.put(eInfo.classType.getName(), eInfo); } } while(!(xmlReader.isEndElement() && xmlReader.getLocalName().equals("EntityStats"))); } @@ -242,4 +285,51 @@ public class Config fis.close(); return ConfigParseResult.SUCCESS; } + + public int getPlayerSpeed() + { + return playerSpeed; + } + + public int getPlayerHasteSpeed() + { + return playerHasteSpeed; + } + + public int getPlayerSlowSpeed() + { + return playerSlowSpeed; + } + + /** + * Returns a clone of an EntityInfo (to prevent editing it). + * @param classFullName + * @return a clone of the stored EntityInfo or null if invalid String + */ + public EntityInfo getEntityInfo(String classFullName) + { + return entityInfoMap.get(classFullName).clone(); + } + + protected EntityInfo getEntityInfoReference(String classFullName) + { + return entityInfoMap.get(classFullName); + } + + protected EntityInfo getMatchingEntityInfo(Object entity) + { + EntityInfo matching = entityInfoMap.get(entity.getClass().getName()); + if(matching.classType.isInstance(entity)) + { + for(Class c : matching.conflictingTypes) + { + if(c.isInstance(entity)) + { + return entityInfoMap.get(c.getName()); + } + } + return matching; + } + return null; + } } diff --git a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/EntityInfo.java b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/EntityInfo.java index a068575..e4a6ae2 100644 --- a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/EntityInfo.java +++ b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/EntityInfo.java @@ -1,19 +1,23 @@ package com.seodisparate.TurnBasedMinecraft.common; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; public class EntityInfo { public Class classType; public List conflictingTypes; + public boolean ignoreBattle; public int attackPower; public int attackProbability; + public int attackVariance; public Effect attackEffect; public int attackEffectProbability; public int defenseDamage; public int defenseDamageProbability; public int evasion; + public int speed; public Category category; public enum Category @@ -193,13 +197,16 @@ public class EntityInfo { classType = null; conflictingTypes = new ArrayList(); + ignoreBattle = false; attackPower = 0; attackProbability = 70; + attackVariance = 0; attackEffect = Effect.UNKNOWN; attackEffectProbability = 50; defenseDamage = 0; defenseDamageProbability = 0; evasion = 15; + speed = 50; category = Category.UNKNOWN; } @@ -212,13 +219,16 @@ public class EntityInfo { newEntityInfo.conflictingTypes.add(c); } + newEntityInfo.ignoreBattle = ignoreBattle; newEntityInfo.attackPower = attackPower; newEntityInfo.attackProbability = attackProbability; + newEntityInfo.attackVariance = attackVariance; newEntityInfo.attackEffect = attackEffect; newEntityInfo.attackEffectProbability = attackEffectProbability; newEntityInfo.defenseDamage = defenseDamage; newEntityInfo.defenseDamageProbability = defenseDamageProbability; newEntityInfo.evasion = evasion; + newEntityInfo.speed = speed; newEntityInfo.category = category; return newEntityInfo; } diff --git a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/networking/PacketBattleDecision.java b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/networking/PacketBattleDecision.java index 71ca925..6e3d622 100644 --- a/src/main/java/com/seodisparate/TurnBasedMinecraft/common/networking/PacketBattleDecision.java +++ b/src/main/java/com/seodisparate/TurnBasedMinecraft/common/networking/PacketBattleDecision.java @@ -14,13 +14,15 @@ public class PacketBattleDecision implements IMessage { private int battleID; private Battle.Decision decision; + private int targetEntityID; public PacketBattleDecision() {} - public PacketBattleDecision(int battleID, Battle.Decision decision) + public PacketBattleDecision(int battleID, Battle.Decision decision, int targetEntityID) { this.battleID = battleID; this.decision = decision; + this.targetEntityID = targetEntityID; } @Override @@ -28,6 +30,7 @@ public class PacketBattleDecision implements IMessage { battleID = buf.readInt(); decision = Decision.valueOf(buf.readInt()); + targetEntityID = buf.readInt(); } @Override @@ -35,6 +38,7 @@ public class PacketBattleDecision implements IMessage { buf.writeInt(battleID); buf.writeInt(decision.getValue()); + buf.writeInt(targetEntityID); } public static class HandleBattleDecision implements IMessageHandler @@ -46,7 +50,7 @@ public class PacketBattleDecision implements IMessage if(b != null) { EntityPlayerMP player = ctx.getServerHandler().player; - b.setDecision(player.getEntityId(), message.decision); + b.setDecision(player.getEntityId(), message.decision, message.targetEntityID); } return null; } diff --git a/src/main/resources/TBM_Config.xml b/src/main/resources/TBM_Config.xml index 937660f..3dc684e 100644 --- a/src/main/resources/TBM_Config.xml +++ b/src/main/resources/TBM_Config.xml @@ -1,116 +1,152 @@ + 1 + + + 50 + 80 + 20 + + + + + + + + + + 5 fire 5 monster + 45 2 poison 35 monster + 75 true 15 5 monster + 25 8 2 25 monster + 45 7 40 monster + 70 2 40 monster + 35 6 35 monster + 35 true 13 35 monster + 60 11 2 monster + 45 6 2 25 monster + 50 3 hunger 5 monster + 25 14 5 monster + 45 3 12 monster + 35 8 10 monster + 50 6 5 animal + 35 4 15 monster + 10 1 37 monster + 35 3 13 monster + 30 2 10 monster + 30 0 5 passive + 60 2 @@ -119,33 +155,39 @@ monster + 70 3 slow 13 monster + 30 9 30 monster + 80 13 10 monster + 35 5 8 monster + 35 8 wither 7 monster + 65 3 @@ -155,21 +197,25 @@ 5 monster + 25 3 5 monster + 25 0 35 passive + 75 0 10 passive + 35 0 @@ -178,92 +224,110 @@ passive + 20 0 10 passive + 65 0 10 passive + 65 1 10 passive + 50 0 1 passive + 20 0 10 passive + 50 1 10 passive + 75 0 35 passive + 70 0 10 passive + 30 0 40 passive + 75 0 5 passive + 30 0 5 passive + 65 0 15 passive + 40 0 5 passive + 35 4 20 animal + 70 0 8 passive + 65 10 27 boss + 63 8 20 wither boss + 68 \ No newline at end of file