]> git.seodisparate.com - TurnBasedMinecraftMod/commitdiff
WIP Some battle logic has been implemented
authorStephen Seo <seo.disparate@gmail.com>
Wed, 5 Sep 2018 06:54:06 +0000 (15:54 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 5 Sep 2018 06:54:06 +0000 (15:54 +0900)
src/main/java/com/seodisparate/TurnBasedMinecraft/common/Battle.java
src/main/java/com/seodisparate/TurnBasedMinecraft/common/Combatant.java
src/main/java/com/seodisparate/TurnBasedMinecraft/common/Config.java
src/main/java/com/seodisparate/TurnBasedMinecraft/common/networking/PacketBattleDecision.java
src/main/resources/TBM_Config.xml

index 526f5ae9663119f38801f252db7aac06b0fefa10..3c2d0688f9921df2064a530e2ba4793ad75c3630 100644 (file)
@@ -18,8 +18,10 @@ import com.seodisparate.TurnBasedMinecraft.common.networking.PacketBattleInfo;
 import com.seodisparate.TurnBasedMinecraft.common.networking.PacketHandler;
 
 import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLivingBase;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.util.DamageSource;
 
 public class Battle
 {
@@ -38,7 +40,7 @@ public class Battle
     public enum State
     {
         DECISION,
-        ATTACK,
+        ACTION,
         HEALTH_CHECK
     }
     
@@ -96,6 +98,7 @@ public class Battle
                     continue;
                 }
                 Combatant newCombatant = new Combatant(e, entityInfo);
+                newCombatant.isSideA = true;
                 this.sideA.put(e.getEntityId(), newCombatant);
                 if(e instanceof EntityPlayer)
                 {
@@ -115,6 +118,7 @@ public class Battle
                     continue;
                 }
                 Combatant newCombatant = new Combatant(e, entityInfo);
+                newCombatant.isSideA = false;
                 this.sideB.put(e.getEntityId(), newCombatant);
                 if(e instanceof EntityPlayer)
                 {
@@ -154,6 +158,7 @@ public class Battle
             return;
         }
         Combatant newCombatant = new Combatant(e, entityInfo);
+        newCombatant.isSideA = true;
         sideA.put(e.getEntityId(), newCombatant);
         if(e instanceof EntityPlayer)
         {
@@ -175,6 +180,7 @@ public class Battle
             return;
         }
         Combatant newCombatant = new Combatant(e, entityInfo);
+        newCombatant.isSideA = false;
         sideB.put(e.getEntityId(), newCombatant);
         if(e instanceof EntityPlayer)
         {
@@ -237,19 +243,26 @@ public class Battle
         return combatant;
     }
     
-    public void setDecision(int entityID, Decision decision, int targetEntityID)
+    public void setDecision(int entityID, Decision decision, int targetIDOrItemID)
     {
         if(state != State.DECISION)
         {
             return;
         }
         Combatant combatant = players.get(entityID);
-        if(combatant == null)
+        if(combatant == null || combatant.decision != Decision.UNDECIDED)
         {
             return;
         }
         combatant.decision = decision;
-        combatant.targetEntityID = targetEntityID;
+        if(decision == Decision.ATTACK)
+        {
+            combatant.targetEntityID = targetIDOrItemID;
+        }
+        else if(decision == Decision.USE_ITEM)
+        {
+            combatant.itemToUse = targetIDOrItemID;
+        }
         undecidedCount.decrementAndGet();
     }
     
@@ -294,7 +307,7 @@ public class Battle
             timer = timer.minus(dt);
             if(timer.isNegative() || timer.isZero() || undecidedCount.get() <= 0)
             {
-                state = State.ATTACK;
+                state = State.ACTION;
                 timer = TurnBasedMinecraftMod.BattleDecisionTime;
                 turnOrderQueue.clear();
                 for(Combatant c : sideA.values())
@@ -306,16 +319,264 @@ public class Battle
                     turnOrderQueue.add(c);
                 }
                 update(Duration.ZERO);
+                // TODO assign decisions to non-players
             }
             break;
-        case ATTACK:
+        case ACTION:
+        {
             Combatant next = turnOrderQueue.poll();
             while(next != null)
             {
-                // TODO attack per entity here
+                if(!next.entity.isEntityAlive())
+                {
+                    next = turnOrderQueue.poll();
+                    continue;
+                }
+                switch(next.decision)
+                {
+                case UNDECIDED:
+                    next = turnOrderQueue.poll();
+                    continue;
+                case ATTACK:
+                    Combatant target = null;
+                    if(next.entity instanceof EntityPlayer)
+                    {
+                        if(next.isSideA)
+                        {
+                            target = sideB.get(next.targetEntityID);
+                        }
+                        else
+                        {
+                            target = sideA.get(next.targetEntityID);
+                        }
+                        if(target == null || !target.entity.isEntityAlive())
+                        {
+                            next = turnOrderQueue.poll();
+                            continue;
+                        }
+                        int hitChance = TurnBasedMinecraftMod.config.getPlayerAttackProbability();
+                        if(target.entity instanceof EntityPlayer)
+                        {
+                            hitChance -= TurnBasedMinecraftMod.config.getPlayerEvasion();
+                        }
+                        else
+                        {
+                            hitChance -= target.entityInfo.evasion;
+                        }
+                        if((int)(Math.random() * 100) < hitChance)
+                        {
+                            if(target.remainingDefenses <= 0)
+                            {
+                                // attack
+                                // TODO damage via bow and arrow
+                                TurnBasedMinecraftMod.attackingEntity = next.entity;
+                                ((EntityPlayer)next.entity).attackTargetEntityWithCurrentItem(target.entity);
+                                TurnBasedMinecraftMod.attackingEntity = null;
+                                if(!(target.entity instanceof EntityPlayer) && target.entityInfo.defenseDamage > 0)
+                                {
+                                    if((int)(Math.random() * 100) < target.entityInfo.defenseDamageProbability)
+                                    {
+                                        // defense damage
+                                        DamageSource defenseDamageSource = DamageSource.causeMobDamage((EntityLivingBase)target.entity);
+                                        TurnBasedMinecraftMod.attackingEntity = target.entity;
+                                        next.entity.attackEntityFrom(defenseDamageSource, target.entityInfo.defenseDamage);
+                                        TurnBasedMinecraftMod.attackingEntity = null;
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                // blocked
+                                --target.remainingDefenses;
+                            }
+                        }
+                        else
+                        {
+                            // miss
+                        }
+                    }
+                    else
+                    {
+                        if(next.isSideA)
+                        {
+                            int randomTargetIndex = (int)(Math.random() * sideB.size());
+                            for(Combatant c : sideB.values())
+                            {
+                                if(randomTargetIndex-- == 0)
+                                {
+                                    target = c;
+                                    break;
+                                }
+                            }
+                        }
+                        else
+                        {
+                            int randomTargetIndex = (int)(Math.random() * sideA.size());
+                            for(Combatant c : sideA.values())
+                            {
+                                if(randomTargetIndex-- == 0)
+                                {
+                                    target = c;
+                                    break;
+                                }
+                            }
+                        }
+                        if(target == null || !target.entity.isEntityAlive())
+                        {
+                            next = turnOrderQueue.poll();
+                            continue;
+                        }
+                        int hitChance = next.entityInfo.attackProbability;
+                        if(target.entity instanceof EntityPlayer)
+                        {
+                            hitChance -= TurnBasedMinecraftMod.config.getPlayerEvasion();
+                        }
+                        else
+                        {
+                            hitChance -= target.entityInfo.evasion;
+                        }
+                        if((int)(Math.random() * 100) < hitChance)
+                        {
+                            if(target.remainingDefenses <= 0)
+                            {
+                                DamageSource damageSource = DamageSource.causeMobDamage((EntityLivingBase)next.entity);
+                                int damageAmount = next.entityInfo.attackPower;
+                                if(next.entityInfo.attackVariance > 0)
+                                {
+                                    damageAmount += (int)(Math.random() * (next.entityInfo.attackVariance * 2 + 1)) - next.entityInfo.attackVariance;
+                                }
+                                // attack
+                                TurnBasedMinecraftMod.attackingEntity = next.entity;
+                                target.entity.attackEntityFrom(damageSource, next.entityInfo.attackPower);
+                                TurnBasedMinecraftMod.attackingEntity = null;
+                                if(!(target.entity instanceof EntityPlayer) && target.entityInfo.defenseDamage > 0)
+                                {
+                                    if((int)(Math.random() * 100) < target.entityInfo.defenseDamageProbability)
+                                    {
+                                        // defense damage
+                                        DamageSource defenseDamageSource = DamageSource.causeMobDamage((EntityLivingBase)target.entity);
+                                        TurnBasedMinecraftMod.attackingEntity = target.entity;
+                                        next.entity.attackEntityFrom(defenseDamageSource, target.entityInfo.defenseDamage);
+                                        TurnBasedMinecraftMod.attackingEntity = null;
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                // blocked
+                                --target.remainingDefenses;
+                            }
+                        }
+                        else
+                        {
+                            // miss
+                        }
+                    }
+                    break;
+                case DEFEND:
+                    next.remainingDefenses = TurnBasedMinecraftMod.config.getDefenseDuration();
+                    break;
+                case FLEE:
+                    int fastestEnemySpeed = 0;
+                    if(next.isSideA)
+                    {
+                        for(Combatant c : sideB.values())
+                        {
+                            if(c.entity instanceof EntityPlayer)
+                            {
+                                if(TurnBasedMinecraftMod.config.getPlayerSpeed() > fastestEnemySpeed)
+                                {
+                                    fastestEnemySpeed = TurnBasedMinecraftMod.config.getPlayerSpeed();
+                                }
+                            }
+                            else
+                            {
+                                if(c.entityInfo.speed > fastestEnemySpeed)
+                                {
+                                    fastestEnemySpeed = c.entityInfo.speed;
+                                }
+                            }
+                        }
+                    }
+                    else
+                    {
+                        for(Combatant c : sideA.values())
+                        {
+                            if(c.entity instanceof EntityPlayer)
+                            {
+                                if(TurnBasedMinecraftMod.config.getPlayerSpeed() > fastestEnemySpeed)
+                                {
+                                    fastestEnemySpeed = TurnBasedMinecraftMod.config.getPlayerSpeed();
+                                }
+                            }
+                            else
+                            {
+                                if(c.entityInfo.speed > fastestEnemySpeed)
+                                {
+                                    fastestEnemySpeed = c.entityInfo.speed;
+                                }
+                            }
+                        }
+                    }
+                    int fleeProbability = 0;
+                    if(next.entity instanceof EntityPlayer)
+                    {
+                        if(fastestEnemySpeed >= TurnBasedMinecraftMod.config.getPlayerSpeed())
+                        {
+                            fleeProbability = TurnBasedMinecraftMod.config.getFleeBadProbability();
+                        }
+                        else
+                        {
+                            fleeProbability = TurnBasedMinecraftMod.config.getFleeGoodProbability();
+                        }
+                    }
+                    else
+                    {
+                        if(fastestEnemySpeed >= next.entityInfo.speed)
+                        {
+                            fleeProbability = TurnBasedMinecraftMod.config.getFleeBadProbability();
+                        }
+                        else
+                        {
+                            fleeProbability = TurnBasedMinecraftMod.config.getFleeGoodProbability();
+                        }
+                    }
+                    if((int)(Math.random() * 100) < fleeProbability)
+                    {
+                        // flee success
+                        if(next.isSideA)
+                        {
+                            sideA.remove(next.entity.getEntityId());
+                        }
+                        else
+                        {
+                            sideB.remove(next.entity.getEntityId());
+                        }
+                        if(next.entity instanceof EntityPlayer)
+                        {
+                            players.remove(next.entity.getEntityId());
+                            playerCount.decrementAndGet();
+                            // TODO notify player exited battle
+                        }
+                    }
+                    break;
+                case USE_ITEM:
+                    break;
+                }
                 next = turnOrderQueue.poll();
             }
+            for(Combatant c : sideA.values())
+            {
+                c.decision = Decision.UNDECIDED;
+            }
+            for(Combatant c : sideB.values())
+            {
+                c.decision = Decision.UNDECIDED;
+            }
+            state = State.HEALTH_CHECK;
+            update(Duration.ZERO);
             break;
+        }
         case HEALTH_CHECK:
             // TODO
             break;
index a25db37413748cd7f7840c865803d8018dbb3d4a..efce8ba23062eb7c15e77459e727f62413d6c059 100644 (file)
@@ -18,11 +18,14 @@ public class Combatant
     public EntityInfo entityInfo;
     public boolean recalcSpeedOnCompare;
     public int targetEntityID;
+    public boolean isSideA;
+    public int remainingDefenses;
     
     public Combatant()
     {
         decision = Battle.Decision.UNDECIDED;
         recalcSpeedOnCompare = false;
+        remainingDefenses = 0;
     }
     
     public Combatant(Entity e, EntityInfo entityInfo)
@@ -31,6 +34,7 @@ public class Combatant
         decision = Battle.Decision.UNDECIDED;
         this.entityInfo = entityInfo;
         recalcSpeedOnCompare = false;
+        remainingDefenses = 0;
     }
     
     /**
index 1e24996369251c26d0048145971ea03e9edaa60f..1eb2325765e1424b6e37f1490341caf8591b1b41 100644 (file)
@@ -30,6 +30,11 @@ public class Config
     private int playerSpeed;
     private int playerHasteSpeed;
     private int playerSlowSpeed;
+    private int playerAttackProbability = 100;
+    private int playerEvasion = 10;
+    private int defenseDuration = 1;
+    private int fleeGoodProbability = 90;
+    private int fleeBadProbability = 40;
     
     private enum ConfigParseResult
     {
@@ -174,9 +179,29 @@ public class Config
                             {
                                 playerSlowSpeed = Integer.parseInt(xmlReader.getElementText());
                             }
+                            else if(xmlReader.getLocalName().equals("AttackProbability"))
+                            {
+                                playerAttackProbability = Integer.parseInt(xmlReader.getElementText());
+                            }
+                            else if(xmlReader.getLocalName().equals("Evasion"))
+                            {
+                                playerEvasion = Integer.parseInt(xmlReader.getElementText());
+                            }
                         }
                     } while(!(xmlReader.isEndElement() && xmlReader.getLocalName().equals("PlayerStats")));
                 }
+                else if(xmlReader.getLocalName().equals("DefenseDuration"))
+                {
+                    defenseDuration = Integer.parseInt(xmlReader.getElementText());
+                }
+                else if(xmlReader.getLocalName().equals("FleeGoodProbability"))
+                {
+                    fleeGoodProbability = Integer.parseInt(xmlReader.getElementText());
+                }
+                else if(xmlReader.getLocalName().equals("FleeBadProbability"))
+                {
+                    fleeBadProbability = Integer.parseInt(xmlReader.getElementText());
+                }
                 else if(xmlReader.getLocalName().equals("EntityStats"))
                 {
                     do
@@ -300,7 +325,32 @@ public class Config
     {
         return playerSlowSpeed;
     }
+    
+    public int getPlayerAttackProbability()
+    {
+        return playerAttackProbability;
+    }
 
+    public int getPlayerEvasion()
+    {
+        return playerEvasion;
+    }
+    
+    public int getDefenseDuration()
+    {
+        return defenseDuration;
+    }
+    
+    public int getFleeGoodProbability()
+    {
+        return fleeGoodProbability;
+    }
+    
+    public int getFleeBadProbability()
+    {
+        return fleeBadProbability;
+    }
+    
     /**
      * Returns a clone of an EntityInfo (to prevent editing it).
      * @param classFullName
index 6e3d622a2449a477a566e94b6bd1cc282a4c0c93..a2738d224572e36a163dacfb7ba47b29c5d2bd13 100644 (file)
@@ -14,15 +14,15 @@ public class PacketBattleDecision implements IMessage
 {
     private int battleID;
     private Battle.Decision decision;
-    private int targetEntityID;
+    private int targetIDOrItemID;
     
     public PacketBattleDecision() {}
     
-    public PacketBattleDecision(int battleID, Battle.Decision decision, int targetEntityID)
+    public PacketBattleDecision(int battleID, Battle.Decision decision, int targetIDOrItemID)
     {
         this.battleID = battleID;
         this.decision = decision;
-        this.targetEntityID = targetEntityID;
+        this.targetIDOrItemID = targetIDOrItemID;
     }
 
     @Override
@@ -30,7 +30,7 @@ public class PacketBattleDecision implements IMessage
     {
         battleID = buf.readInt();
         decision = Decision.valueOf(buf.readInt());
-        targetEntityID = buf.readInt();
+        targetIDOrItemID = buf.readInt();
     }
 
     @Override
@@ -38,7 +38,7 @@ public class PacketBattleDecision implements IMessage
     {
         buf.writeInt(battleID);
         buf.writeInt(decision.getValue());
-        buf.writeInt(targetEntityID);
+        buf.writeInt(targetIDOrItemID);
     }
 
     public static class HandleBattleDecision implements IMessageHandler<PacketBattleDecision, IMessage>
@@ -50,7 +50,7 @@ public class PacketBattleDecision implements IMessage
             if(b != null)
             {
                 EntityPlayerMP player = ctx.getServerHandler().player;
-                b.setDecision(player.getEntityId(), message.decision, message.targetEntityID);
+                b.setDecision(player.getEntityId(), message.decision, message.targetIDOrItemID);
             }
             return null;
         }
index 3dc684e0d6c00ce22a6a4f206047ff5cb8c8cb71..fedad3917a3e3e013e47ea9478176f29c7b6d99f 100644 (file)
                <Speed>50</Speed>
                <HasteSpeed>80</HasteSpeed>
                <SlowSpeed>20</SlowSpeed>
+               <AttackProbability>90</AttackProbability>
+               <Evasion>10</Evasion>
        </PlayerStats>
+       <!-- Determines how many attacks a "defense" move can block before that entity's next turn. -->
+       <DefenseDuration>1</DefenseDuration>
+       <!-- Probability of escaping battle. If entity's speed is greater than the enemy team's speediest entity, then good probability is used. -->
+       <FleeGoodProbability>90</FleeGoodProbability>
+       <FleeBadProbability>40</FleeBadProbability>
        <!-- Battle stats for entities should be specified here. If an entity is not listed it cannot enter battle. -->
        <EntityStats>
                <!-- AttackPower: How much damage an entity does per attack. Usually has a "Probability" attribute between 0 and 100. Also may have a "Variance" attribute that varies the attack power by the specified amount randomly. -->