Thread: Basic combat base

Results 1 to 3 of 3
  1. #1 Basic combat base 
    Respected Member


    George's Avatar
    Join Date
    Mar 2009
    Posts
    7,099
    Thanks given
    2,226
    Thanks received
    3,146
    Rep Power
    5000
    Been writing a basic base for a combat system that I might use in a framework.

    This is by no means done and something can be approached differently and more efficiently. I've been working on this combat base for aprox 3 hours now but kinda ran dead into it. Thought i'd release the base here, as stated already it's by no means superior but it is a different approach to what i've seen over the years, so someone might benefit from it.

    NOTE: THERE'S ALOT OF CLASSES

    Code:
    package com.game.character.combat;
    
    import java.util.Objects;
    import com.game.character.Character;
    import com.game.character.combat.impl.MagicCombatAction;
    import com.game.character.combat.impl.MeleeCombatAction;
    import com.game.character.combat.impl.RangedCombatAction;
    
    /**
     * 
     * @author kronn
     *
     *         Representive of combat management
     *
     */
    
    public class Combat {
    
    	/**
    	 * The character
    	 */
    	private final Character character;
    
    	/**
    	 * Combat constructor
    	 * 
    	 * @param character
    	 *            the character
    	 */
    	public Combat(final Character character) {
    		this.character = character;
    	}
    
    	/**
    	 * Executes the combat action
    	 */
    	public void execute() {
    
    		final CombatBuilder bldr = character.getCombatBuilder();
    
    		if (character.getHitpoints() <= 0 || bldr.getVictim().getHitpoints() <= 0) {
    			reset();
    			return;
    		}
    
    		bldr.setAttackTimer(5);
    
    		CombatAction combat_action = null;
    
    		if (bldr.getAttackType() == AttackType.MELEE) {
    			combat_action = new MeleeCombatAction();
    		} else if (bldr.getAttackType() == AttackType.RANGED) {
    			combat_action = new RangedCombatAction(1);
    		} else if (bldr.getAttackType() == AttackType.MAGIC) {
    			combat_action = new MagicCombatAction(1);
    		}
    
    		if (Objects.isNull(combat_action)) {
    			reset();
    			return;
    		}
    
    		bldr.getContainer().getInitAnimation().ifPresent(bldr.getContainer().getCharacter()::playAnimation);
    		bldr.getContainer().getInitGraphic().ifPresent(bldr.getContainer().getCharacter()::playGraphics);
    
    		combat_action.execute(bldr.getContainer());
    	}
    
    	/**
    	 * Resets the character's combat
    	 */
    	private void reset() {
    		final CombatBuilder bldr = new CombatBuilder();
    		if (bldr != null) {
    			bldr.setAttackType(null);
    			bldr.setVictim(null);
    			bldr.setAttackTimer(-1);
    		}
    		character.setInteractingCharacter(null);
    	}
    
    	/**
    	 * The combat sequence
    	 */
    	public void sequence() {
    
    		if (character.getCombatBuilder().getAttackTimer() > 0) {
    			character.getCombatBuilder().decrementAttackTimer();
    		}
    
    		if (character.getCombatBuilder().getAttackTimer() <= 0 && character.getCombatBuilder().getVictim() != null) {
    			execute();
    		}
    	}
    }
    Code:
    package com.game.character.combat;
    
    import com.game.character.Character;
    
    /**
     * 
     * @author kronn
     *
     *         Representive of the combat container
     *
     */
    
    public class CombatBuilder {
    
    	/**
    	 * The character's attack timer
    	 */
    	private int attackTimer;
    
    	/**
    	 * The character's victim
    	 */
    	private Character victim;
    
    	/**
    	 * The character's attack type
    	 */
    	private AttackType attackType;
    
    	/**
    	 * The character's combat container
    	 */
    	private CombatActionContainer container;
    
    	/**
    	 * @return the attackTimer
    	 */
    	public int getAttackTimer() {
    		return attackTimer;
    	}
    
    	/**
    	 * @param attackTimer
    	 *            the attackTimer to set
    	 */
    	public void setAttackTimer(int attackTimer) {
    		this.attackTimer = attackTimer;
    	}
    
    	/**
    	 * Decrements the attack timer
    	 */
    	public void decrementAttackTimer() {
    		this.attackTimer--;
    	}
    
    	/**
    	 * @return the victim
    	 */
    	public Character getVictim() {
    		return victim;
    	}
    
    	/**
    	 * @param victim
    	 *            the victim to set
    	 */
    	public void setVictim(Character victim) {
    		this.victim = victim;
    	}
    
    	/**
    	 * @return the attackType
    	 */
    	public AttackType getAttackType() {
    		return attackType;
    	}
    
    	/**
    	 * @param attackType
    	 *            the attackType to set
    	 */
    	public void setAttackType(AttackType attackType) {
    		this.attackType = attackType;
    	}
    
    	/**
    	 * @return the container
    	 */
    	public CombatActionContainer getContainer() {
    		return container;
    	}
    
    	/**
    	 * @param container
    	 *            the container to set
    	 */
    	public void setContainer(CombatActionContainer container) {
    		this.container = container;
    	}
    
    }
    Code:
    package com.game.character.combat;
    
    import java.util.Optional;
    
    import com.game.character.Animation;
    import com.game.character.Character;
    import com.game.character.Graphic;
    
    /**
     * 
     * @author kronn
     *
     *         Representive of combat action container
     *
     */
    
    public class CombatActionContainer {
    
    	/**
    	 * @param character
    	 * @param victim
    	 * @param initAnimation
    	 * @param initGraphic
    	 * @param projectileId
    	 * @param finishGraphic
    	 */
    	public CombatActionContainer(Character character, Character victim, Optional<Animation> initAnimation,
    			Optional<Graphic> initGraphic, Optional<Integer> projectileId, final Optional<Graphic> finishGraphic) {
    		this.character = character;
    		this.victim = victim;
    		this.initAnimation = initAnimation;
    		this.initGraphic = initGraphic;
    		this.projectileId = projectileId;
    		this.finishGraphic = finishGraphic;
    	}
    
    	/**
    	 * The character
    	 */
    	private final Character character;
    
    	/**
    	 * The {@link #character}'s victim
    	 */
    	private final Character victim;
    
    	/**
    	 * The action's init animation
    	 */
    	private final Optional<Animation> initAnimation;
    
    	/**
    	 * The action's initiation graphic
    	 */
    	private final Optional<Graphic> initGraphic;
    
    	/**
    	 * The action's projectile id
    	 */
    	private final Optional<Integer> projectileId;
    
    	/**
    	 * The action's finish graphic
    	 */
    	private final Optional<Graphic> finishGraphic;
    
    	/**
    	 * @return the character
    	 */
    	public Character getCharacter() {
    		return character;
    	}
    
    	/**
    	 * @return the victim
    	 */
    	public Character getVictim() {
    		return victim;
    	}
    
    	/**
    	 * @return the initAnimation
    	 */
    	public Optional<Animation> getInitAnimation() {
    		return initAnimation;
    	}
    
    	/**
    	 * @return the initGraphic
    	 */
    	public Optional<Graphic> getInitGraphic() {
    		return initGraphic;
    	}
    
    	/**
    	 * @return the projectileId
    	 */
    	public Optional<Integer> getProjectileId() {
    		return projectileId;
    	}
    
    	/**
    	 * @return the finishGraphic
    	 */
    	public Optional<Graphic> getFinishGraphic() {
    		return finishGraphic;
    	}
    }
    Code:
    package com.game.character.combat;
    
    import com.game.character.player.Player;
    
    /**
     * 
     * @author kronn
     *
     *         An abstract class that represents combat actions
     *
     */
    
    public abstract class CombatAction {
    
    	/**
    	 * Execution of the combat action
    	 * 
    	 * @param attacker
    	 *            the attacker
    	 * @param victim
    	 *            the victim
    	 */
    	public abstract void execute(CombatActionContainer container);
    
    	/**
    	 * Adds experience to the player character
    	 * 
    	 * @param character
    	 *            the character
    	 * @param damage
    	 *            the damage
    	 */
    	public abstract void addExperience(final Player player, final int damage);
    
    }
    Code:
    package com.game.character.combat;
    
    /**
     * 
     * @author kronn
     *
     *         An enum representive of various attack types
     *
     */
    
    public enum AttackType {
    
    	MELEE, RANGED, MAGIC
    
    }
    Code:
    package com.game.character.combat.damage;
    
    import java.util.Optional;
    
    import com.game.character.combat.AttackType;
    
    /**
     * 
     * @author kronn
     *
     *         Representive of damage
     *
     */
    
    public class Damage {
    
    	/**
    	 * @param count
    	 * @param type
    	 * @param cycles
    	 * @param damageEffect
    	 */
    	public Damage(int count, AttackType type, int cycles, Optional<DamageEffect> damageEffect) {
    		this.count = count;
    		this.type = type;
    		this.cycles = cycles;
    		this.damageEffect = damageEffect;
    	}
    
    	/**
    	 * The count of the damage
    	 */
    	private int count;
    
    	/**
    	 * The type of damage
    	 */
    	private AttackType type;
    
    	/**
    	 * The amount of cycles before the damage appears to the character
    	 */
    	private int cycles;
    
    	/**
    	 * The damage action
    	 */
    	private Optional<DamageEffect> damageEffect;
    
    	/**
    	 * @return the count
    	 */
    	public int getCount() {
    		return count;
    	}
    
    	/**
    	 * @return the type
    	 */
    	public AttackType getType() {
    		return type;
    	}
    
    	/**
    	 * @return the cycles
    	 */
    	public int getCycles() {
    		return cycles;
    	}
    
    	/**
    	 * @return the damageEffect
    	 */
    	public Optional<DamageEffect> getDamageAction() {
    		return damageEffect;
    	}
    
    	/**
    	 * @param count
    	 *            the count to set
    	 */
    	public void setCount(int count) {
    		this.count = count;
    	}
    
    	/**
    	 * @param type
    	 *            the type to set
    	 */
    	public void setType(AttackType type) {
    		this.type = type;
    	}
    
    	/**
    	 * @param cycles
    	 *            the cycles to set
    	 */
    	public void setCycles(int cycles) {
    		this.cycles = cycles;
    	}
    
    }
    Code:
    package com.game.character.combat.damage;
    
    import com.game.character.Character;
    
    /**
     * 
     * @author kronn
     *
     *         An abstract class representive of damage effects
     *
     */
    
    public abstract class DamageEffect {
    
    	public abstract void execute(final Character character);
    
    }
    Code:
    package com.game.character.combat.damage;
    
    /**
     * 
     * @author kronn
     *
     *         An enum representive of various hitsplat icons
     *
     */
    
    public enum HitSplatIcon {
    
    	BLOCK((byte) 0), HIT((byte) 1), POISON((byte) 2), DISEASE((byte) 3);
    
    	/**
    	 * @param clientImage
    	 */
    	private HitSplatIcon(byte clientImage) {
    		this.clientImage = clientImage;
    	}
    
    	/**
    	 * @return the clientImage
    	 */
    	public byte getClientImage() {
    		return clientImage;
    	}
    
    	/**
    	 * The id for the client image
    	 */
    	private final byte clientImage;
    
    }
    Code:
    package com.game.character.combat.damage;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.LinkedList;
    import java.util.Queue;
    
    import com.game.character.Character;
    import com.game.character.UpdateFlags.UpdateFlag;
    
    /**
     * 
     * @author kronn
     *
     *         Representive of a character's damage queue
     *
     */
    
    public class DamageQueue {
    
    	/**
    	 * The maximum amount of damage objects the damage queue can hold per cycle
    	 */
    	private static final int MAXIMUM_DAMAGE_PER_CYCLE = 4;
    
    	/**
    	 * The character
    	 */
    	private final Character character;
    
    	/**
    	 * Damage Queue constructor
    	 * 
    	 * @param character
    	 *            the character
    	 */
    	public DamageQueue(final Character character) {
    		this.character = character;
    	}
    
    	/**
    	 * The character's damage queue
    	 */
    	private final Queue<Damage> damageQueue = new LinkedList<>();
    
    	/**
    	 * Submits a new damage object to the damage queue
    	 * 
    	 * @param damage
    	 *            the damage object
    	 */
    	public void submit(final Damage damage) {
    		damageQueue.add(damage);
    	}
    
    	/**
    	 * Polls damage objects from the damage queue
    	 */
    	public void poll() {
    		if (damageQueue.size() <= 0) {
    			return;
    		}
    		Collection<Damage> dmg_col = new ArrayList<Damage>();
    		int loop_count = damageQueue.size() > MAXIMUM_DAMAGE_PER_CYCLE ? MAXIMUM_DAMAGE_PER_CYCLE : damageQueue.size();
    		for (int index = 0; index < loop_count; index++) {
    			final Damage damage = damageQueue.poll();
    			if (damage != null) {
    				if (damage.getCycles() > 0) {
    					damage.setCycles(damage.getCycles() - 1);
    					dmg_col.add(damage);
    				} else if (damage.getCycles() <= 0) {
    					execute(damage);
    				}
    			}
    		}
    		if (dmg_col.size() > 0) {
    			damageQueue.addAll(dmg_col);
    		}
    	}
    
    	/**
    	 * Executes the incoming damage
    	 * 
    	 * @param damage
    	 *            the damage object
    	 */
    	public void execute(final Damage damage) {
    
    		if (character.getHitpoints() <= 0) {
    			return;
    		}
    
    		HitSplatIcon icon = null;
    
    		if (damage.getCount() <= 0) {
    			icon = HitSplatIcon.BLOCK;
    		} else if (damage.getCount() > 0) {
    			icon = HitSplatIcon.HIT;
    		}
    
    		if (!character.getUpdateFlags().get(UpdateFlag.HIT)) {
    			character.setHitMaskDamage(damage.getCount());
    			character.setHitIcon(icon);
    			character.getUpdateFlags().flag(UpdateFlag.HIT);
    		} else if (!character.getUpdateFlags().get(UpdateFlag.HIT_2)) {
    			character.setSecondaryHitMaskDamage(damage.getCount());
    			character.setSecondaryHitIcon(icon);
    			character.getUpdateFlags().flag(UpdateFlag.HIT_2);
    		}
    
    		if (!damage.getEffects().isEmpty()) {
    			damage.getEffects().forEach(effect -> effect.execute(character));
    		}
    
    		character.setHitpoints(character.getHitpoints() - damage.getCount());
    
    		if (character.getHitpoints() < 0) {
    			character.setHitpoints(0);
    		}
    
    	}
    }
    Code:
    package com.game.character.combat.damage.effects;
    
    import java.util.ArrayList;
    
    import com.game.character.Character;
    import com.game.character.combat.AttackType;
    import com.game.character.combat.damage.Damage;
    import com.game.character.combat.damage.DamageEffect;
    import com.game.character.player.Player;
    
    /**
     * 
     * @author kronn
     *
     *	Representive of ring effects
     *
     */
    
    public class RingEffect extends DamageEffect {
    
    	/**
    	 * The id of the ring
    	 */
    	private final Rings ring;
    
    	/**
    	 * The victim of the character
    	 */
    	private final Character victim;
    
    	/**
    	 * The amount of damage
    	 */
    	private final int damage;
    
    	/**
    	 * RingEffect constructor
    	 * 
    	 * @param id
    	 *            the id of the ring
    	 */
    	public RingEffect(final Rings ring, final Character victim, final int damage) {
    		this.ring = ring;
    		this.victim = victim;
    		this.damage = damage;
    	}
    
    	@Override
    	public void execute(Character character) {
    
    		if (character instanceof Player) {
    			final Player player = (Player) character;
    			if (player != null) {
    				if (player.getEquipment().contains(ring.getId())) {
    					if (ring == Rings.RING_OF_RECOIL) {
    						final int rec_damage = (int) (damage * .25);
    						victim.getDamageQueue().submit(new Damage(rec_damage, AttackType.NON_SPECIFIED, 1, new ArrayList<>()));
    					}
    				}
    			}
    		}
    
    	}
    
    	public enum Rings {
    
    		RING_OF_RECOIL(2550), RING_OF_LIFE(2570);
    
    		/**
    		 * @param id
    		 */
    		private Rings(int id) {
    			this.id = id;
    		}
    
    		/**
    		 * @return the id
    		 */
    		public int getId() {
    			return id;
    		}
    
    		/**
    		 * The id of the ring
    		 */
    		private final int id;
    	}
    }

    Example of combat action implementation
    Code:
    package com.game.character.combat.impl;
    
    import java.util.Arrays;
    
    import com.game.character.Character;
    import com.game.character.combat.AttackType;
    import com.game.character.combat.CombatAction;
    import com.game.character.combat.CombatActionContainer;
    import com.game.character.combat.damage.Damage;
    import com.game.character.combat.damage.DamageEffect;
    import com.game.character.combat.damage.effects.RingEffect;
    import com.game.character.combat.damage.effects.RingEffect.Rings;
    import com.game.character.player.Player;
    
    public class RangedCombatAction extends CombatAction {
    
    	/**
    	 * The distance to the victim
    	 */
    	private final int distanceToVictim;
    
    	/**
    	 * RangedCombatAction constructor
    	 * 
    	 * @param distanceToVictim
    	 *            the distance
    	 */
    	public RangedCombatAction(final int distanceToVictim) {
    		this.distanceToVictim = distanceToVictim;
    	}
    
    	@Override
    	public void execute(CombatActionContainer container) {
    
    		if (container.getProjectileId().isPresent()) {
    			final int id = container.getProjectileId().get();
    		}
    
    		int dmg = 10;
    		final Damage damage = new Damage(dmg, AttackType.RANGED, getHitDelay(distanceToVictim),
    				Arrays.asList(new RingEffect(Rings.RING_OF_RECOIL, container.getVictim(), dmg)));
    
    		if (damage != null) {
    			container.getVictim().getDamageQueue().submit(damage);
    		}
    
    		container.getFinishGraphic().ifPresent(container.getVictim()::playGraphics);
    
    		if (container.getCharacter() instanceof Player) {
    			addExperience((Player) container.getCharacter(), damage.getCount());
    		}
    	}
    
    	@Override
    	public void addExperience(Player player, int damage) {
    		// TODO add experience
    
    	}
    
    	/**
    	 * Damage effects
    	 */
    	private final DamageEffect effects = new DamageEffect() {
    
    		@Override
    		public void execute(Character character) {
    			// TODO damage effects
    		}
    
    	};
    
    	/**
    	 * Gets the hit delay
    	 * 
    	 * @param distanceToVictim
    	 *            the distance to the attackers victim
    	 * @return the hit delay
    	 */
    	private final int getHitDelay(final int distanceToVictim) {
    		switch (distanceToVictim) {
    		case 1:
    		case 2:
    			return 2;
    
    		case 3:
    		case 4:
    		case 5:
    		case 6:
    		case 7:
    		case 8:
    			return 3;
    
    		case 9:
    		case 10:
    			return 4;
    		}
    		return 1;
    	}
    }
    ITT: We don't messure cocks, if you got some constructive critism, feel free to post it.
    Attached image

    Spoiler for Spoilers!:
    Attached image
    Attached image
    Attached image
    Attached image
    Reply With Quote  
     

  2. Thankful users:


  3. #2  
    Registered Member
    hc747's Avatar
    Join Date
    Dec 2013
    Age
    26
    Posts
    1,474
    Thanks given
    3,312
    Thanks received
    691
    Rep Power
    1098
    The bldr object will never be null, and isn't actually associated with the character - you're checking if a local variable is null right after creating it.
    Code:
    /**
     * Resets the character's combat
     */
    private void reset() {
        final CombatBuilder bldr = new CombatBuilder();
        if (bldr != null) {
            bldr.setAttackType(null);
            bldr.setVictim(null);
            bldr.setAttackTimer(-1);
        }
        character.setInteractingCharacter(null);
    }
    Damage effect and combat action should be interfaces (they track no local state and depend upon implementation of their behaviour in sub-classes).
    Does combat builder actually build a combat object??
    Reply With Quote  
     

  4. Thankful users:


  5. #3  
    Registered Member

    Join Date
    Dec 2012
    Posts
    2,999
    Thanks given
    894
    Thanks received
    921
    Rep Power
    2555
    why not use a session-based design?
    Attached image
    Reply With Quote  
     


Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Replies: 43
    Last Post: 01-17-2014, 04:47 PM
  2. Basic NPC combat base Palidino base [554]
    By bracket in forum Tutorials
    Replies: 7
    Last Post: 12-18-2010, 12:07 AM
  3. [508]basic mage/player combat base
    By lilfuzzy420 in forum Snippets
    Replies: 2
    Last Post: 12-10-2010, 04:53 AM
  4. NPC Combat Base
    By Vastiko in forum Show-off
    Replies: 2
    Last Post: 11-27-2008, 09:43 AM
  5. Combat base
    By Pkitten in forum Tutorials
    Replies: 31
    Last Post: 07-10-2008, 11:36 PM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •