Thread: [Concept] Object Oriented, event driven combat system

Page 1 of 2 12 LastLast
Results 1 to 10 of 11
  1. #1 [Concept] Object Oriented, event driven combat system 
    Registered Member
    Join Date
    Aug 2010
    Posts
    9
    Thanks given
    0
    Thanks received
    1
    Rep Power
    0
    This is a concept of an object oriented, event driven combat system that, once finished, should be applicable to most/all private servers but it's just a framework for now. It's far from finished and there's still some things I need to work out, but so far it looks promising.

    Pros to an event driven combat system:
    • Less CPU spent to cycle through all unnecessary bulk code related to combat
    • Better organization
    • Specific behaviour can be dealt with more approperiately because of abstraction and OO techniques


    Cons to an event driven combat system:
    • Potentially uses more memory
    • Has some overhead/boilerplate code
    • Relies on good use of an event manager


    On with the explanation:

    At the core of the system is the CombatListener class. This class allows for events to be triggered during specific processes of the combat system. It is because of this class that this system does not have code laying around that shouldn't be there in the first place. Also, because of its design, it lets you modularly generate which actions should occur in which scenarios. Previously, when dealing with actions that should be triggered when a player dies, you would have to cram all the logic inside a single method that deals with the player's death. Should the items be dropped based on the player's context? Who is considered the killer of said player? Do we give points to another player for killing said player? All of this logic is only cycled through if that logic is dealt with in a CombatListener that is attributed to the players involved and therefore these logic blocks are only ever processed if they are relevant to the current context.

    The biggest plus of this design should become apparent when doing combat calculations. Before, you would have to check each and every single possible scenario when a player attacks. Is he using prayer bonusses? Is he wearing a full barrows set? Is he using a berserker necklace in combination with an obsidian weapon? Instead of having a block of code to check for these statements (that is cycled through every time the player attacks, despite being completely irrelevant to the context incase a player is not benefiting from any of the aforementioned bonusses), the system only checks to see if bonusses should be applied incase they are relevant (A check is still required; a magic boosting prayer will only have its logic block checked if it's active, but that doesn't mean that it will trigger if the player is using melee).

    Code:
    package rs2.combat;
    
    import rs2.combat.hit.Hit;
    
    public class CombatListener {
    	
    	public void buffAsAttacker(Hit hit) {
    		
    	}
    	
    	public void buffAsDefender(Hit hit) {
    		
    	}
    	
    	public void modifyAsAttacker(Hit hit) {
    		
    	}
    	
    	public void modifyAsDefender(Hit hit) {
    		
    	}
    	
    	public void onHitAsAttacker(Hit hit) {
    		
    	}
    	
    	public void onHitAsDefender(Hit hit) {
    		
    	}
    	
    	public void onDeath(Hit hit) {
    		
    	}
    
    }
    Here's an example of how the CombatListener would look for the Protect From Melee prayer:
    Code:
    		CombatListener ProtectFromMelee = new CombatListener() {
    			@Override
    			public void modifyAsDefender(Hit hit) {
    				if (hit.getType() == HitType.CRUSH || hit.getType() == HitType.SLASH || hit.getType() == HitType.STAB) {
    					if (hit.getAttacker().isPlayer()) {
    						hit.setDamage((int)(hit.getDamage()*0.6));
    					} else {
    						hit.setDamage(0);
    					}
    				}
    			}
    		};
    Here's another example for the Vengeance spell.
    Code:
    		CombatListener vengeance = new CombatListener() {
    			@Override
    			public void onHitAsDefender(Hit hit) {
    				if (hit.getType() != HitType.POISON && hit.getType() != HitType.TRUE_DAMAGE) {
    					if (hit.getAttacker() != null) {
    						TrueHit veng = new TrueHit(hit.getAttacker(), (int)(hit.getDamage()*0.75));
    						Player def = (Player)hit.getDefender();
    						def.sendMessage("Taste Vengeance!");
    						hit.getDefender().removeCombatListener("vengeance");
    						veng.process();
    					}
    				}
    			}
    		};
    At this point I should probably also explain when these fields get called. The main protocol of hit-generation is as follows:
    1. Determine attacker and defender (not shown)
    2. Determine the type of hit this is going to be
    3. Determine the accuracy of attacker and his max hit
    4. Determine the defensive stats of the defender
    5. Buff the attacker's accuracy/max hit by cycling through his listeners -> buffAsAttacker()
    6. Buff the defender's ratings by cycling through his listeners -> buffAsDefender()
    7. Use the accuracy values to determine whether or not this hit will be successful
    8. Incase the hit goes through, determine the amount of damage to deal, based on the max hit
    9. Allow the attacker to "modify" the hit (after calculations) -> modifyAsAttacker()
    10. Allow the defender to "modify" the hit -> modifyAsDefender()
    11. Apply the hit once its delay has run out
    12. Apply any special effects associated with this hit
    13. Deal with any events triggered after dealing damage -> onHitAsAttacker()
    14. Deal with any event triggered after receiving damage -> onHitAsDefender()

    (if you have any questions about this protocol, ask away)


    As you might be able to tell, the Hit class also plays a significant role in this design. The Hit class encapsulates several core details of a hit; who is attacking, who is defending, how much damage the system eventually decided to hit and the type of the hit. For now, these are the possible hit types:

    Code:
    package rs2.combat.hit;
    
    public enum HitType {
    	
    	STAB,
    	SLASH,
    	CRUSH,
    	MAGIC,
    	RANGED,
    	POISON,
    	TRUE_DAMAGE,
    	DRAGONFIRE
    
    }
    One important thing to note is that one weapon can have different Hit-subtypes associated with it. A scimitar, for example, has 4 different attack styles, each of which can have their own Hit class associated with them (this allows us to increase defensive stats when using the defensive stance, increase strength when using the agressive stance, etc). Special Attacks are also entirely different Hit implementations because of the extra behaviour that they usually bring with them.

    While there's a lot of different mechanics involved with all the different types and subtypes of hits that exist in Runescape, the core functionality remains the same. A hit is dealt to a defender by an attacker (or none incase of "system damage" or "true damage"). The Hit class serves the very purpose of embodying this core functionality:

    Code:
    package rs2.combat.hit;
    
    import rs2.combat.Attackable;
    
    public abstract class Hit {
    	
    	private HitType type;
    	
    	private Attackable attacker;
    	
    	private Attackable defender;
    	
    	private int damage;
    	
    	public int getDamage() {
    		return damage;
    	}
    	
    	public void setDamage(int dmg) {
    		this.damage = dmg;
    	}
    	
    	public Attackable getAttacker() {
    		return attacker;
    	}
    	
    	public Attackable getDefender() {
    		return defender;
    	}
    	
    	public void setAttacker(Attackable attacker) {
    		this.attacker = attacker;
    	}
    	
    	public void setDefender(Attackable defender) {
    		this.defender = defender;
    	}
    
    	public HitType getType() {
    		return type;
    	}
    	
    	public void setType(HitType type) {
    		this.type = type;
    	}
    	
    	public void process() {
    		defender.dealDamage(this);
    		applySpecialEffects();
    	}
    	
    	public boolean isValid() {
    		return true;
    	}
    	
    	public void applySpecialEffects() {
    		
    	}
    }
    You may notice the fields isValid(), applySpecialEffects() and process(). These fields are there to provide access to more complex mechanics at a lower level.

    isValid(), for example, is there to determine whether or not player can actually use their weapon (can't fire all ammo with all ranged weapons, does the player have the required runes for a spell, etc). This field has nothing to do with matters such as wilderness levels or distance between the target and us because these are matters that are have been validated before we can apply a hit.

    applySpecialEffects() deals with events that are specific to a spell or a special attack. For example, the extra magic damage dealt by the Saradomin Sword's special attack would be dealt with here. The Dragon Scimitar special attack is another example. Special attack effects that give you increased accuracy and damage are dealt with in the hit-generation process in the that hit's class. The AoE damage and freeze effects of ice spells for example, are also dealt with in this method.

    The final field is the process() field. When a hit is generated, it isn't necessarily triggered. Some attacks (magic and ranged particularly) have a delay before they hit the target. When a hit is registered, it is passed on to the event system which, once ready, triggers the hit to occur. This is the main use of the process(). Attacks that have no delay should still be passed on to the event system for consistency.

    Let's look at an implementation of this class. Here's how a dragonfire hit would look like:
    Code:
    package rs2.combat.hit.impl;
    
    import rs2.combat.Attackable;
    import rs2.combat.CombatListener;
    import rs2.combat.CombatUtility;
    import rs2.combat.hit.Hit;
    import rs2.combat.hit.HitType;
    
    public class DragonfireHit extends Hit {
    	
    	public static final int ORIGINAL_MAX_HIT = 60;
    	public static final int REDUCED_MAX_HIT = 3;
    	
    	private boolean usingShield;
    	private boolean potionEffect;
    	private boolean usingPrayer;
    	
    	public DragonfireHit(Attackable defender) {
    		setDefender(defender);
    		setType(HitType.DRAGONFIRE);
    		int max = ORIGINAL_MAX_HIT;
    		CombatListener[] cbls = defender.getListeners();
    		for (CombatListener cbl : cbls) {
    			cbl.buffAsDefender(this);
    		}
    		if (usingPrayer) {
    			max = REDUCED_MAX_HIT;
    		}
    		if (usingShield) {
    			max = REDUCED_MAX_HIT;
    			if (potionEffect) {
    				max = 0;
    			}
    		}
    		setDamage(CombatUtility.getRandomHit(max));
    	}
    	
    	public void setUsingPrayer(boolean prayer) {
    		this.usingPrayer = prayer;
    	}
    	
    	public void setUsingShield(boolean shield) {
    		this.usingShield = shield;
    	}
    	
    	public void setPotionEffect(boolean potion) {
    		this.potionEffect = potion;
    	}
    
    }
    If it looks weird, I took all the info from the Oldschool Runescape Wiki. One behaviour that you will note on their article that I did not implement here is that the protect from magic prayer only reduces the damage from chromatic dragons (and not metallic ones), whereas my design makes no such adjustment. The King Black Dragon's specific dragon breath attacks would each be a subset of this class, while having its applySpecialEffects() fields overwritten to deal with their corresponding effects.

    This class relies on a couple of things to work. It assumes that the Protect from Magic prayer is handled as a CombatListener, same goes for the effects of the Antifire Potion and having an (anti)dragonfire shield equipped. All of this makes sense, because this is what the system was designed for.

    I'm still working on this design because there are some very very important details that I want to get out of the way before expanding it. I hope you all enjoyed this read and maybe one day I'll get to implement this into a functioning server.

    Again, if you have any questions, shoot 'em.
    Reply With Quote  
     

  2. #2  
    Software Developer

    Tyrant's Avatar
    Join Date
    Jul 2013
    Age
    24
    Posts
    1,562
    Thanks given
    678
    Thanks received
    423
    Rep Power
    1060
    Code:
    public class CombatListener {
    	
    	public void buffAsAttacker(Hit hit) {
    		
    	}
    	
    	public void buffAsDefender(Hit hit) {
    		
    	}
    	
    	public void modifyAsAttacker(Hit hit) {
    		
    	}
    	
    	public void modifyAsDefender(Hit hit) {
    		
    	}
    	
    	public void onHitAsAttacker(Hit hit) {
    		
    	}
    	
    	public void onHitAsDefender(Hit hit) {
    		
    	}
    	
    	public void onDeath(Hit hit) {
    		
    	}
    
    }
    Could be
    Code:
    public interface CombatListener {
    	
    	 void buffAsAttacker(Hit hit);
    	 void buffAsDefender(Hit hit);
    	 void modifyAsAttacker(Hit hit);
    	 void modifyAsDefender(Hit hit);
    	 void onHitAsAttacker(Hit hit);
    	 void onHitAsDefender(Hit hit);
    	 void onDeath(Hit hit);
    
    }
    Code:
    public class Veng implements CombatListener {
    	@Override
    	public void onHitAsDefender(Hit hit) {
    		if (hit.getType() != HitType.POISON && hit.getType() != HitType.TRUE_DAMAGE) {
    			if (hit.getAttacker() != null) {
    				TrueHit veng = new TrueHit(hit.getAttacker(), (int)(hit.getDamage()*0.75));
    				Player def = (Player)hit.getDefender();
    				def.sendMessage("Taste Vengeance!");
    				hit.getDefender().removeCombatListener("vengeance");
    				veng.process();
    			}
    		}
    	}		
        ...
    }
    Don't really like the way its designed, however there is alot to improve but surely to work on from, so goodjob.


    -----
    EDIT; Also the Hit class is abstract, yet none of the methods are abstracted, what is entirely the point of doing so?
    Reply With Quote  
     

  3. Thankful users:


  4. #3  
    Java Programmer
    _Jon's Avatar
    Join Date
    Jan 2015
    Age
    30
    Posts
    206
    Thanks given
    36
    Thanks received
    63
    Rep Power
    47
    You could also use generics to reflect entity objects player and npcs instead of casting from hit class
    Github - Here
    Reply With Quote  
     

  5. Thankful users:


  6. #4  
    Registered Member
    hc747's Avatar
    Join Date
    Dec 2013
    Age
    26
    Posts
    1,474
    Thanks given
    3,312
    Thanks received
    691
    Rep Power
    1098
    Quote Originally Posted by Tyrant View Post
    Code:
    public class CombatListener {
        
        public void buffAsAttacker(Hit hit) {
            
        }
        
        public void buffAsDefender(Hit hit) {
            
        }
        
        public void modifyAsAttacker(Hit hit) {
            
        }
        
        public void modifyAsDefender(Hit hit) {
            
        }
        
        public void onHitAsAttacker(Hit hit) {
            
        }
        
        public void onHitAsDefender(Hit hit) {
            
        }
        
        public void onDeath(Hit hit) {
            
        }
    
    }
    Could be
    Code:
    public abstract class CombatListener {
        
        public abstract void buffAsAttacker(Hit hit);
        public abstract void buffAsDefender(Hit hit);
        public abstract void modifyAsAttacker(Hit hit);
        public abstract void modifyAsDefender(Hit hit);
        public abstract void onHitAsAttacker(Hit hit);
        public abstract void onHitAsDefender(Hit hit);
        public abstract void onDeath(Hit hit);
    
    }
    Code:
    public class Veng extends CombatListener {
        @Override
        public void onHitAsDefender(Hit hit) {
            if (hit.getType() != HitType.POISON && hit.getType() != HitType.TRUE_DAMAGE) {
                if (hit.getAttacker() != null) {
                    TrueHit veng = new TrueHit(hit.getAttacker(), (int)(hit.getDamage()*0.75));
                    Player def = (Player)hit.getDefender();
                    def.sendMessage("Taste Vengeance!");
                    hit.getDefender().removeCombatListener("vengeance");
                    veng.process();
                }
            }
        }        
        ...
    }
    Don't really like the way its designed, however there is alot to improve but surely to work on from, so goodjob.


    -----
    EDIT; Also the Hit class is abstract, yet none of the methods are abstracted, what is entirely the point of doing so?
    Declaring a class abstract means that only subclasses can be instantiated, so I suppose it was to prevent instantiation? It was a bad usage none-the-less. Also - this
    Code:
    public abstract class CombatListener {
        
        public abstract void buffAsAttacker(Hit hit);
        public abstract void buffAsDefender(Hit hit);
        public abstract void modifyAsAttacker(Hit hit);
        public abstract void modifyAsDefender(Hit hit);
        public abstract void onHitAsAttacker(Hit hit);
        public abstract void onHitAsDefender(Hit hit);
        public abstract void onDeath(Hit hit);
    
    }
    should be

    Code:
    public interface CombatListener {
    
    void buffAsAttacker(Hit hit);
    void buffAsDefender(Hit hit);
    void modifyAsAttacker(Hit hit);
    void modifyAsDefender(Hit hit);
    void onHitAsAttacker(Hit hit);
    void onHitAsDefender(Hit hit);
    void onDeath(Hit hit);
    
    }
    Also I agree with you; it could be designed in a much better way. Gj none-the-less.
    Reply With Quote  
     

  7. Thankful users:


  8. #5  
    (Official) Thanksgiver

    Arham's Avatar
    Join Date
    Jan 2013
    Age
    23
    Posts
    3,415
    Thanks given
    7,254
    Thanks received
    1,938
    Rep Power
    3905
    Learn a bit more polymorphism before doing this.
    Attached image
    Attached image
    Quote Originally Posted by MrClassic View Post
    Arham is the official thanker!
    List of my work here!
    Reply With Quote  
     

  9. #6  
    Software Developer

    Tyrant's Avatar
    Join Date
    Jul 2013
    Age
    24
    Posts
    1,562
    Thanks given
    678
    Thanks received
    423
    Rep Power
    1060
    Quote Originally Posted by hc747 View Post
    Declaring a class abstract means that only subclasses can be instantiated, so I suppose it was to prevent instantiation? It was a bad usage none-the-less. Also - this
    Code:
    public abstract class CombatListener {
        
        public abstract void buffAsAttacker(Hit hit);
        public abstract void buffAsDefender(Hit hit);
        public abstract void modifyAsAttacker(Hit hit);
        public abstract void modifyAsDefender(Hit hit);
        public abstract void onHitAsAttacker(Hit hit);
        public abstract void onHitAsDefender(Hit hit);
        public abstract void onDeath(Hit hit);
    
    }
    should be

    Code:
    public interface CombatListener {
    
    void buffAsAttacker(Hit hit);
    void buffAsDefender(Hit hit);
    void modifyAsAttacker(Hit hit);
    void modifyAsDefender(Hit hit);
    void onHitAsAttacker(Hit hit);
    void onHitAsDefender(Hit hit);
    void onDeath(Hit hit);
    
    }
    Also I agree with you; it could be designed in a much better way. Gj none-the-less.
    Oh defiantly, there are bunch more ways to do that, anyhow, @OP, you should even check some existing combat systems from released servers (make sure they're proper ones)
    and get some ideas off there.
    Reply With Quote  
     

  10. #7  
    Donator

    Jason's Avatar
    Join Date
    Aug 2009
    Posts
    6,092
    Thanks given
    2,402
    Thanks received
    2,823
    Rep Power
    4550
    Quote Originally Posted by hc747 View Post
    lalalalalalalal

    Code:
    public abstract class CombatListener {
        
        public abstract void buffAsAttacker(Hit hit);
        public abstract void buffAsDefender(Hit hit);
        public abstract void modifyAsAttacker(Hit hit);
        public abstract void modifyAsDefender(Hit hit);
        public abstract void onHitAsAttacker(Hit hit);
        public abstract void onHitAsDefender(Hit hit);
        public abstract void onDeath(Hit hit);
    
    }
    should be

    Code:
    public interface CombatListener {
    
    void buffAsAttacker(Hit hit);
    void buffAsDefender(Hit hit);
    void modifyAsAttacker(Hit hit);
    void modifyAsDefender(Hit hit);
    void onHitAsAttacker(Hit hit);
    void onHitAsDefender(Hit hit);
    void onDeath(Hit hit);
    
    }
    hi
    This could also be;

    Code:
    interface CombatListener {
    
        void buff(Hit hit);
    
        void modify(Hit hit);
    
        void onHit(Hit hit);
    
        void onDeath(Hit hit);
    }
    
    class AttackerCombatListener implements CombatListener {
    
        @Override void buff(Hit hit) {}
    
        @Override void modify(Hit hit) {}
    
        @Override void onHit(Hit hit) {}
    
        @Override void onDeath(Hit hit) {}
    }
    
    class DefenderCombatListener implements CombatListener {
    
        @Override void buff(Hit hit) {}
    
        @Override void modify(Hit hit) {}
    
        @Override void onHit(Hit hit) {}
    
        @Override void onDeath(Hit hit) {}
    }
    it goes on and on really
    Reply With Quote  
     

  11. Thankful users:


  12. #8  
    Registered Member
    Join Date
    Aug 2010
    Posts
    9
    Thanks given
    0
    Thanks received
    1
    Rep Power
    0
    Honestly, the only reason I went with that design was so that I could fill in 1 void and leave the other blank which I don't know if you can do (it's just too verbose). (also it's been a while since I learned anything new so I appreciate the feedback)

    But yes it should be an interface. The reason there's no differentiation between attacker and defender listeners is because some effects apply on both ends of the spectrum; piety and chivalry are defensive and offensive boosts. Other reason is because I don't like overcomplicating the base design.

    CombatListener is not abstract so you can instantiate it as you wish, just doesn't have a constructor because reasons. An interface is obviously much better here but I find them to be more clunky.
    Reply With Quote  
     

  13. Thankful user:


  14. #9  
    Registered Member
    Stanaveli's Avatar
    Join Date
    Aug 2014
    Posts
    1,490
    Thanks given
    184
    Thanks received
    653
    Rep Power
    1338
    Quote Originally Posted by Tyrant View Post
    Could be
    Code:
    public abstract class CombatListener {
    	
    	public abstract void buffAsAttacker(Hit hit);
    	public abstract void buffAsDefender(Hit hit);
    	public abstract void modifyAsAttacker(Hit hit);
    	public abstract void modifyAsDefender(Hit hit);
    	public abstract void onHitAsAttacker(Hit hit);
    	public abstract void onHitAsDefender(Hit hit);
    	public abstract void onDeath(Hit hit);
    
    }
    Please don't suggest things that are still wrong, for starters, when you don't want to re-use any code (which you've shown), you'd want to use an interface over an abstract class.

    @OP When you release code for the public, you'd always want to take a second on how you name your stuff and perhaps even documentate where necessary.

    e.g you could rename onHitAsAttacker to outgoingHit and onHitAsDefender to incomingHit.

    Code:
    public class Veng extends CombatListener {
    	@Override
    	public void onHitAsDefender(Hit hit) {
    		if (hit.getType() != HitType.POISON && hit.getType() != HitType.TRUE_DAMAGE) {
    			if (hit.getAttacker() != null) {
    				TrueHit veng = new TrueHit(hit.getAttacker(), (int)(hit.getDamage()*0.75));
    				Player def = (Player)hit.getDefender();
    				def.sendMessage("Taste Vengeance!");
    				hit.getDefender().removeCombatListener("vengeance");
    				veng.process();
    			}
    		}
    	}		
        ...
    }
    Don't really like the way its designed, however there is alot to improve but surely to work on from, so goodjob.
    Well yeah! Thank you, the op can really build a better version because you said you don't like it!

    No but seriously, state what you don't like, how it can be improved etc... This is a community for gods sake, we should start helping each other rather then stating "we dont like it, improve it bye" because that's what you're basically implying.

    Quote Originally Posted by Cres View Post
    Honestly, the only reason I went with that design was so that I could fill in 1 void and leave the other blank which I don't know if you can do (it's just too verbose). (also it's been a while since I learned anything new so I appreciate the feedback)

    But yes it should be an interface. The reason there's no differentiation between attacker and defender listeners is because some effects apply on both ends of the spectrum; piety and chivalry are defensive and offensive boosts. Other reason is because I don't like overcomplicating the base design.

    CombatListener is not abstract so you can instantiate it as you wish, just doesn't have a constructor because reasons. An interface is obviously much better here but I find them to be more clunky.
    You can still use an interface, just insert the default keyword for the methods. However, you're much better off using some scripting language because java can be very verbose for writing such kind of things.
    Keep your head up.



    Reply With Quote  
     

  15. #10  
    Software Developer

    Tyrant's Avatar
    Join Date
    Jul 2013
    Age
    24
    Posts
    1,562
    Thanks given
    678
    Thanks received
    423
    Rep Power
    1060
    Quote Originally Posted by Papito a neeff View Post
    Please don't suggest things that are still wrong, for starters, when you don't want to re-use any code (which you've shown), you'd want to use an interface over an abstract class.

    @OP When you release code for the public, you'd always want to take a second on how you name your stuff and perhaps even documentate where necessary.

    e.g you could rename onHitAsAttacker to outgoingHit and onHitAsDefender to incomingHit.



    Well yeah! Thank you, the op can really build a better version because you said you don't like it!

    No but seriously, state what you don't like, how it can be improved etc... This is a community for gods sake, we should start helping each other rather then stating "we dont like it, improve it bye" because that's what you're basically implying.



    You can still use an interface, just insert the default keyword for the methods. However, you're much better off using some scripting language because java can be very verbose for writing such kind of things.
    I posted off my phone so couldn't really bother to type alot, I was going to suggest using an interface but seeing other people doing so I didn't bother, aswell as editing my current post since others
    have quoted me, however, I do agree that saying 'I dont like it' isn't going to improve anything but regardless if you wanted there are bunch of other releases around so you can always take a look on how they're done.
    Reply With Quote  
     

Page 1 of 2 12 LastLast

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: 12
    Last Post: 08-14-2016, 04:45 AM
  2. Random Event System [Object Oriented]
    By JTlr Frost in forum Snippets
    Replies: 10
    Last Post: 04-09-2016, 02:37 PM
  3. Object Oriented Concepts in Java
    By sadguy in forum Application Development
    Replies: 5
    Last Post: 08-16-2014, 10:47 PM
  4. Dophert's entity based combat system
    By WH:II:DOW in forum Tutorials
    Replies: 22
    Last Post: 01-11-2009, 12:17 AM
  5. the most basic, basic npc combat system ever.
    By ritz929 in forum Tutorials
    Replies: 19
    Last Post: 09-15-2008, 11:08 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
  •