WIP Some battle logic has been implemented

This commit is contained in:
Stephen Seo 2018-09-05 15:54:06 +09:00
parent c8ce401e4d
commit 2aec173e80
5 changed files with 335 additions and 13 deletions

View 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;

View 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;
}
/**

View 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
@ -301,6 +326,31 @@ 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

View 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;
}

View file

@ -10,7 +10,14 @@
<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. -->