CombatInteraction.html
25sep09 CmdrZin
18oct09

ref1: Project Darkstar Server Application Tutorial, 05/29/09  

Combat Interactions or How to Kill Something

The basis for interaction with the monsters is automatic engagement. If you enter an area with one or more monstes, they will attack and you will also attack unless you leave before they get to you.

Combat Control
There will be an attribute added to the MudCharacter class call distance.
It starts at zero and is incremented every 5 seconds by a timer that is started when someone enterd the area. This can be triggered by an "Arrival" event.
If all MudUser's leave the area, the timer is deleted.
The existing timer used in the Cow and Duck classes will be used to check the Room (container) for MudUser objects. If one is found, the attack begins. If more than one is found, selection is the first one on the list being that that one has been here the longest.

Attack and defense will initially be simple values and success based on the following forumla.
    If [Attack / (Attack + Defense)] > randomA then the attack does it's damage*ramdomB.
So if your attack is 2 and its defense is 1, then you have a 67% chance to hit.

The Duck and Cow classes should be a good place to start for making  Zombie, Mutant, and Undead classes. Both of these are explained in Exercise 4 of the Lab-7400 project DarkMud. See the DarkMud-Redux documents for details.

The Duck class has the ability to follow in the direction of a GO command and the Cow class has a timer task capability. Looks like both will be needed for the new Zombie class!!
Need to look into how they work now to decide how to use them in the new class. They are both part of the com.sun.sgs.darkmud.follow package.
For a simple test, make a new Zombie class and copy the Duck class into it. Change the references to Duckling to Zombie and edit the response text to "Brains..."
Add the following to the MudMain.properties file that describes the game.

room.OfficeLL5828.inventory=door.OfficeLL5828.w, item.zombie001

item.zombie001.class=com.sun.sgs.darkmud.follow.Zombie
item.zombie001.aliases=zombie monster Fred
item.zombie001.description=Fred, a zombie looking for some fresh brains

and wall la, a zombie now follows anyone who enters that Room! Enough messing around, back to work.

Duck.java

A Duck object simulates following a Player by responding to a valid GO command. As with all objects in the Room (container), the Duck object's parseOuterCommand() is called during the parsing phase of a message received. The Duck object registers interest in the command so that it will ba called during the second phase of the message process.
When it's commitCommand() is called, the Duck object 1) schedules a GoTask using the TaskManager and passed the command (which now consists of the direction String) to the task which 2) will cause the task to run and call the Duckling.follow() method that 3) builds up a new GO command with direction used and 4) initiated a parse() of the command in the same way a Player's GO command is parsed. This will result in the Duckling going in the same direction as the Player.
One slight change will be added to the Zombie class for this whole process. The Zombie will only follow 50% of the time as they are easily confused.

Cow.java

The Cow class is also very simple and uses the parse() call to initiate movement, do an emotem, output a SAY command.
When the Cow was instatiated, it launched a periodic task that trigger's every ten seconds using the AppContext.getTaskManager().schedulePeriodicTask() call.
These mechanics will be added to the Zombie class to let the little guys wander around.

Zombie.java

Copy the Duckling class into this new class and change the duckling references to zombie.
In parseOuterCommand, add a random value check to give a 50% chance of issuing the command.registerInterest() call. (see code)
Copy the pieces of the Cow class that will be needed to let the zombie wander. The parameters can be tweaked later.
One EMOTE and one SAY command were kept and the random direction was kept.
One thing that will be added is a test in the doSomething() method to cause the zombie to check the Room for MudUser objects.

Recompiling now and testing the Zombie. Also adding a few more to the map.

Debug
Every thing seems to work. They wander and such.
NOTE: After the attack mechanics are set up, the focus will be on experience points, health regeneration, inventory mods to attack/defense, and skill mods to attack/defence.  

Finding Out Who's in the Room

This should be simple. Use the MudObject.getContainer() to return the room as MubObject room. Now use room.getInventory() to return a list of objects in the room as a Set of MudObjects. Look for an instance of a MudUser object and launch an attack. Now to code it.

Zombie.java

At the top of doSomething()
Added
        // Get our Room
        MudObject room = this.getContainer();

        // Get everything in the room
        Set<MudObject> invList = room.getInventory();

        Set<MudObject> victumList = new HashSet<MudObject>();

        // Add everything we can kill
        for (MudObject obj : invList) {
            if( obj.getType() == MudType.USER ) {
                victumList.add(obj);
            }
        }

        // Tell us who's here Don.
        if( !victumList.isEmpty() ) {
            boolean first = true;
            StringBuffer sbOut = new StringBuffer();

            for (MudObject obj : victumList) {
                if(first == true) {
                    sbOut.append("Me eat "+obj.getDescription());
                    first = false;
                } else {
                    sbOut.append("\n and me eat "+obj.getDescription());
                }
            }
            command.setType(MudCharacter.CommandType.SAY);
            command.replaceCommand(sbOut.toString());
        } else {
to test the mechanics. Shoud get a response when in the room with one of the zombies.
Debug
hmm..HashMap error..doesn't like isInstance test..works otherwise..
Added a type parameter to MudObject so it will be easier to find out what they are.
Use obj.getType() to find out..MudUser returns MudType.USER.

This generates a list now so that the zombie can attack one or more players if needed.

Combat

Ok, there's a list now (victumList) of who to attack. For now, Monsters go first, Players go last.
There is no Attack command yet, so the Zombie timed task will be used to trigger combat.
NOTE: The Zombie class will need to be setup as a base class for all monsters.

Attack and Defense is based on simple values and success based on the following forumla.
    If [Attack / (Attack + Defense)] > randomA then the attack does it's damage*ramdomB.
So if your attack is 2 and its defense is 1, then you have a 67% chance to hit.

The Base for Attack and Defense is Agility and the damage is based on Strength for now. Ranged attacks will be added later.
Weapon and Armor modifiers will also be added in later.

To update the Player on the results of combat, the MudUser.buildAndSendInfoMessage() method is used for now.
It will update all of the Player's lists. A specific list update method will be added later.

NOTE: Since the MudUser object is being modified, the ManagedReference mark for update stuff (ref 1, pg3) may need to done.

Using the MudObject.removeFromInventory() method as a reference, the plan will be to create a ManagedReference using
    AppContext.getDataManager().createReference(object)
    AppContext.getDataManager().getForUpdate(object).
    DataManager dataManager = AppContext.getDataManager();  (from ref 1, pg 55, code example)

So, using the Player object from the victumList, make a reference, then modify it's info lists as needed. Although there's not musch chance of other task wanting to modify the Player, this seems to be the correct procedure to follow.
Added a setValue(List, Key, Value) method to MudObject to allow modifying list element values.

MudObject.java

Added
  public void modifyListItem( ListPair.ListType list, Integer key, Integer value) {
      AppContext.getDataManager().markForUpdate(this);
      HashMap<Integer, Integer> itemList = getList( list );
      itemList.put(key, value);
  }

Zombie.java

At the end of doSomething(), adding
    DataManager dataManager = AppContext.getDataManager();
    ManagedReference pRef = dataManager.createReference(mu[0]);
    ManagedObject pObj = dataManager.getForUpdate(pRef);
// TEST CODE
  if( !victumList.isEmpty() ) {
      MudUser mu[] = victumList.toArray(new MudUser[0]);
      HashMap<Integer, Integer> list = mu[0].getList(ListType.STATS);
      Integer currentHealth = list.get(DataBase.HEALTH) - 10;
      if(currentHealth <= 0) currentHealth = 1;  // no one dies yet.
      mu[0].modifyListItem(ListType.STATS, DataBase.HEALTH, currentHealth);
      mu[0].buildAndSendInfoMessage();

      String sOut = new String("You take 10 points of damage.\n");
      mu[0].sendToUser(sOut);
  }
Works well. GUI updates and you can be eaten while offline!!
Ok then. The basic support is here for resolving combat. This test code can be expanded to modify Attack and Defense with weapons, skills, stats, etc. for more complex combat. Put on the TODO list. Checking in code and updating server.

On to resolving Death and Dying.

Update for 18oct09
Add experience variable to MudCharacter class. Experience for monsters classes is how much they are worth. Experience for the MudUser class is a running total.
MudUser class get an addToExperience() method and a buildExperienceMessage() method.

MudCharacter.java

Add protected int experience variable and getExperience() method. Value is set on creation.

MudUser.java

Add addToExperience() and buildExperienceMessage() methods.
Set experience is an "01" message. (see ClientInfo.parse()).
Add a call to send current experience when player logs on.

Debug. Works but GUI does not scroll with lots of messages being sent....hmm..

MudClient.java

Try putting a validate() at end of MudClient.appendOutput()...nope..
Try setting Prefered size for scroll pane..yup..ok now.
 

MudMain.java

Add call to user.buildExperienceMessage() in loggedIn.

Much better now..you even get exp while looged off since you can't die yet.