When I developed this a few days ago, I was pretty pleased with my work, as far as I am aware, nobody else uses a system like this. I have decided to release this because this is a community, and I personally believe that things should be shared, to help develop RS private servers further.
Before we start, I know I do not strictly follow conventions, and for that I appologise, I know my coding can be sloppy and inefficient, I appologise again. But rather than calling me an 'idiot' or flaming me, suggesting how it could be made better would be nicer, and please remember I spent my own personal time on this. So, shall we begin?
Description: Implementing PvP drops based on the number of hits done by a player. Can be set so that if player1 does not hit player2 within x minutes, then their hits on player2 are removed.
How it Works: Each player has a list of 'PlayerKiller's, this contains information about the player who has done the damage, how much damage, and when they last hit the player. When a player dies, a method is called to determine who has done the most damage to that player, and they get the drop. When a player dies, their list is reset. This could easily be adapted to suit certain needs. When a player logs out, they are removed from the lists of all other players. You can choose to process each player list every server cycle, this will add to the timesincelasthit variable, if that variable is over a set level, then that player can be removed from the list.
Difficulty: 4 - Not difficult to follow, but some people may become slightly confused.
Assumed Knowledge: Basic information on how the combat works on their server, and basic information regarding the player class.
Tested Server: Pali 508, but could easily be adapted to any base.
Files/Classes Modified: Player.java - Adding a new variable, PlayerCombat.java - Inserting a method, Engine.java - Inserting a method, PlayerKiller.java - Custom class, packaged alongside PlayerCombat.java.
Procedure
Step 1: First, adding the new class. PlayerKiller.java. Remember to change your package and import declarations as necessary.
Code:
package benscape.players.combat;
import benscape.Engine;
import benscape.players.Player;
/**
*
* @author Benm121
*/
public class PlayerKiller {
int playerId;
int hits;
int timeSinceLastHit;
static final int MAX_TIME_SINCE_HIT = 1000; //10 Minutes
public PlayerKiller(int PlayerId) {
playerId = PlayerId;
hits = 0;
timeSinceLastHit = 0;
}
/**
*
* @param p1 The player for which the hits should be added
* @param p2 The player which did the damage
* @param Hits The amount of damage done by P2
*/
public static void addHits(Player p1, Player p2, int Hits) {
PlayerKiller tempPlayerKiller = new PlayerKiller(p2.playerId);
for (PlayerKiller pk : p1.killer) {
if (pk.playerId == tempPlayerKiller.playerId) {
pk.hits += Hits;
pk.timeSinceLastHit = 0;
return;
}
}
tempPlayerKiller.hits = Hits;
p1.killer.add(tempPlayerKiller);
}
/**
*
* @param p The player who has died
* @return id of the player who dealt the most damage
*/
public static int getKiller(Player p) {
int PlayerId = 0;
int maxHits = 0;
for (PlayerKiller P : p.killer) {
if (P.hits > maxHits) {
maxHits = P.hits;
PlayerId = P.playerId;
}
}
p.killer.clear();
return PlayerId;
}
/**
*
* @param p The player which is logging out
*/
public static void playerLog(Player p) {
for (Player player : Engine.players) {
player.killer.remove(p);
}
}
/**
*
* @param p The player which is being processed.
*/
public static void processList(Player p) {
for (PlayerKiller pker : p.killer) {
pker.timeSinceLastHit += 1;
if (pker.timeSinceLastHit >= MAX_TIME_SINCE_HIT) {
p.killer.remove(pker);
}
}
}
}
Step 2: Create the list in the Player.java file.
Code:
import benscape.players.combat.PlayerKiller;
import java.util.*;
Add the imports, change it to match your package.
Code:
public List<PlayerKiller> killer = new ArrayList<PlayerKiller>(100);
Create the list for the players, I've used 100 as the initial value, as it is a list, it is self expanding and contracting, I just felt like using 100 .
Code:
PlayerKiller.processList(this);
May as well add this in whilst we are here, goes in the process() method.
Step 3: Deal with when a player logs out, replace the removePlayer method in the Engine class with this: (Please note, if your removePlayer method does not look similar to this, then just add the Player p decleration and the two lines under the fileManager.save.... method.
Code:
public void removePlayer(int id) {
Player p;
if (players[id] == null) {
return;
}
if (players[id].online) {
try {
fileManager.saveCharacter(players[id]);
p = players[id];
PlayerKiller.playerLog(p);
} catch (Exception e) {}
}
players[id].destruct();
players[id] = null;
Server.socketListener.removeSocket(id);
}
Step 4: Make sure the hits are added when damage is done. Open up the PlayerCombat.java file, and add the following code in the methods where damage is done. I hope you guys are intelligent enough to work this out. Remember to change p.MeleeDamage depending on where you put it.
Code:
Player p2 = Engine.players[p.attackPlayer];
PlayerKiller.addHits(p2, p, p.MeleeDamage);
Step 5: Gimme Drops! This goes in the Player class file.
In the applyDead method, replace the two dropAllItems calls with:
Code:
dropAllItems(this);
Then replace your dropAllItems method with this:
Code:
public void dropAllItems(Player p) {
Player p2 = Engine.players[PlayerKiller.getKiller(p)];
if (p2 == null || (p2.playerId > Engine.players.length) || (p2.playerId < 1)) {
return;
}
for (int i = 0; i < items.length; i++) {
if (items[i] > 0) {
if (Engine.items.isUntradable(items[i])) {
Engine.items.createGroundItem(items[i], itemsN[i], absX,
absY, heightLevel, p2.username);
} else {
Engine.items.createGroundItem(items[i], itemsN[i], absX,
absY, heightLevel, p2.username);
}
items[i] = -1;
itemsN[i] = 0;
}
}
for (int i = 0; i < equipment.length; i++) {
if (equipment[i] > 0) {
if (Engine.items.isUntradable(equipment[i])) {
Engine.items.createGroundItem(equipment[i], equipmentN[i],
absX, absY, heightLevel, p2.username);
Misc.println(p2.username);
} else {
Engine.items.createGroundItem(equipment[i], equipmentN[i],
absX, absY, heightLevel, p2.username);
}
equipment[i] = -1;
equipmentN[i] = 0;
}
}
Engine.items.createGroundItem(526, 1, absX, absY, heightLevel, p2.username); // drop bones
deathMessage = Misc.random(8);
if (deathMessage == 0) {
p.frames.sendMessage(p2,
"With a crushing blow, you defeat " + username + ".");
} else if (deathMessage == 1) {
p.frames.sendMessage(p2,
"It's a humiliating defeat for " + username + ".");
} else if (deathMessage == 2) {
p.frames.sendMessage(p2,
username + " didn't stand a chance against you.");
} else if (deathMessage == 3) {
p.frames.sendMessage(p2, "You have defeated " + username + ".");
} else if (deathMessage == 4) {
p.frames.sendMessage(p2,
username + " regrets the day they met you in combat.");
} else if (deathMessage == 5) {
p.frames.sendMessage(p2, "It's all over for " + username + ".");
} else if (deathMessage == 6) {
p.frames.sendMessage(p2, username + " falls before your might.");
} else if (deathMessage == 7) {
p.frames.sendMessage(p2,
"Can anyone defeat you? Certainly not " + username + ".");
} else if (deathMessage == 8) {
p.frames.sendMessage(p2,
"You were clearly a better fighter than " + username + ".");
}
frames.setItems(this, 149, 0, 93, items, itemsN);
frames.setItems(this, 387, 28, 94, equipment, equipmentN);
playerWeapon.setWeapon();
calculateEquipmentBonus();
}
Credits: This tutorial is written 100% by me, I guess some credits must go to Pali for his base. Please list me in credits if you use this tutorials, as it does mean a lot.
I hope this helps a lot of people.
Remember, I released something, wouldn't it be nice for others to release their codes aswell?