package ring.combat; import ring.mobiles.*; import ring.world.*; import java.util.*; public class Battle implements TickerListener { //This class abstracts combat in the MUD. It handles the tick-by-tick autoattacks of combat. It also distributes //experience points to those who win the Battle. Combat is a bit hard to visualize through an object-oriented //programming language, but we have come up with the following guidelines: // //* A Battle is initiated when a mobile (NPC or PC) executes the "kill" command or does a hostile action. //* Once initiated, the Battle is created with the "kill" command executor as the initiator and puts both the // the initiator and his target as involved participants with 100% xp modifiers. //* Any other mobile that attacks either combatant is added into the Battle with a 0% xp modifier. // This is to prevent non-grouped powerleveling. It also doesn't matter to NPCs since they don't level. // A special case is made for groups. If, when joining a Battle, the attacker is in the initiator's group, // Their XP modifier is set to (100/# players in group)%. //* NPCs can only be involved in one Battle instance at a time, players may have infinite amounts. This will // hopefully reduce server resource usage. //* This class implements TickerListener to handle combat. //* When a combatant dies, XP is calculated based on its CR (level for players) and is distributed to all other // participants that attempted to attack that combatant. Each participant's XP modifier will change how much // XP they receive. It is then further changed based on their level relative to their target's CR. // CR > participant's level = more XP, participant's level > CR = less xp. The cap for no XP is 8 levels in both // directions. // We are currently investigating a bell curve function to model this XP change. //* If a mobile flees from combat, they are removed from the Battle. If they later return to attack the target // again, they are re-joined with the appropriate XP modifier: If they are the initiator or are part of the // initiator's group, their XP modifier is reduced by 5% (note this means changing the number by a percentage, // not directly subtracting 5 from the number). If they are not, their modifier is set to 0%. // Fleeing is only necessary when being attacked. Mobiles not being attacked may freely leave the room using // direction commands and remain in the Battle. //* If a mobile is not present in the room for a kill, they still get the XP as long as they actively attempted // to kill the target or support the people doing so. "Supporting" includes healing, buff spells, and the like. // //Constants: Useful constants. public static int MAX_PARTICIPANTS = 30; //Class variables: These are all of the variables needed by this class. private Vector<Mobile> mobList; //a list of the mobiles (i.e. map keys) private TreeMap<Mobile, Double> xpModifiers; //a list of all the mobiles' xp modifiers. private Mobile initiator; //the person who started this Battle. they may or may not be a participant. //###################### //CONSTRUCTORS //###################### //This constructor is just used to initialize the internal variables. //It is not called publicly, just by the other two public constructors... code re-use and all that. private Battle() { mobList = new Vector<Mobile>(20); xpModifiers = new TreeMap<Mobile, Double>(); } //this sets up a Battle with just an initiator. public Battle(Mobile initiator) { this(); this.initiator = initiator; //add the initiator. addParticipant(initiator, 100.0); } //this is the most common constructor, giving us an initiator and other participants (variable arg list for those) public Battle(Mobile initiator, Mobile ... others) { this(); this.initiator = initiator; //add the participants addParticipant(initiator, 100.0); for (Mobile mob : others) { addParticipant(mob, 100.0); } } //###################### //ADD AND REMOVE METHODS //###################### //addParticipant method. //This method adds a participant to the Battle with the given XP modifier. It returns true if successful, //but false if the participant was not able to be added (if the mobile is dead or already in the Battle) or //if mob is null. public boolean addParticipant(Mobile mob, double xpModifier) { if (mob == null) return false; if (mob.getBaseModel().isDead()) return false; if (mobList.contains(mob)) return false; //all of the conditions check out so add the participant and return true. mobList.addElement(mob); xpModifiers.put(mob, xpModifier); return true; } //removeParticipant method. //This method removes a participant from the Battle. It returns true if successful. Currently there are no //conditions that would prevent removal that need to be specific to this function. Things such as the mobile //not existing are handled by exceptions thrown from Vector and TreeMap. public boolean removeParticipant(Mobile mob) { mobList.removeElement(mob); xpModifiers.remove(mob); return true; } //###################### //COMBAT METHODS //###################### //processTick method. //This method handles all auto-attacking between combatants. It also checks for dead status and handles //removal and XP payout if a target is dead. public void processTick(TickerEvent e) { //first, check death states for (Mobile mob : mobList) { if (mob.getBaseModel().isDead()) { giveXP(mob); //give the xp of this mobile to all participants, factoring in the xp modifiers removeParticipant(mob); } } //then do attacks. for (Mobile mob : mobList) { boolean hit = mob.attack(mob.getCombatModel().getTarget(), true); //later factor in the weapon they have. //if (hit) mob.sendData("You hit " + mob.getCombatModel().getTarget() + " for some damage."); //implement damage here! } } //giveXP method. //This method gives XP to other participants from the passed mobile. The mobile being passed is likely dead and thus //other combatants are getting XP for his death. XP modifiers are factored in here. private void giveXP(Mobile mob) { for (Mobile m : mobList) { if (mob != m) { //make sure we're not giving a mob XP for its own death... //TODO re-add mob exp calcuation back in here. m.gainXP(xpModifiers.get(m).intValue()); } } } }