• Unfortunately, we have experienced significant hard drive damage that requires urgent maintenance and rebuilding. The forum will be a state of read only until we install our new drives and rebuild all the configurations needed. Please follow our Facebook page for updates, we will be back up shortly! (The forum could go offline at any given time due to the nature of the failed drives whilst awaiting the upgrades.) When you see an Incapsula error, you know we are in the process of migration.

[Add-On] Clan System with Clan Buffs

Skilled Illusionist
Joined
Dec 16, 2010
Messages
304
Reaction score
164
First release! [v83+]
If you are not using MoopleDev, this will be a bit tricky, as you will have to use your brain (and I know how most people hate that).
This is a Clan System that has been tested with MoopleDev. Fairly easy to figure it out. Each Clan has their own skillBuff, or a certain buff that shows up at a 1/25 chance each time they get hit. All of this is just a base for whatever you want to put inside. You can easily change the clan names and skills.

Make a file called MapleClans.java inside the client package. Put this inside:
PHP:
package client;

/**
*
* @author Sharky
*/

public enum MapleClans {
None(0, "None", 0, "null"),
Eagle(100, "Eagle", 3001003, "Focus"),
Serpent(200, "Serpent", 3121002, "Sharp Eyes"),
Lion(300, "Lion", 1001003, "Iron Body"), 
Salamander(400, "Salamander", 3121008, "Concentrate"),
Roadrunner(500, "Roadrunner", 4201003, "Haste"),
Shark(600, "Shark", 1101006, "Rage");
final int clanid;
final String clanname;
final int skillBuff;
final String skillName;

private MapleClans(int id, String name, int skillB, String skillN){
clanid = id;
clanname = name;
skillBuff = skillB;
skillName = skillN;
}

public static MapleClans getById(int id) {
for (MapleClans mc : MapleClans.values()) {
if (mc.getId() == id) {
return mc;
}
}
return null;
}

public int getId(){
return clanid;
}

public String getName(){
return clanname;
}

public int getSkillBuff(){
return skillBuff;
}

public String getSkillName(){
return skillName;
}
}

Go into MapleCharacter now:
Add this at the top by the private int's
PHP:
public MapleClans mapleclan = MapleClans.None;
search for:
PHP:
ret.job = MapleJob.BEGINNER;
and put this right below it:
PHP:
ret.mapleclan = MapleClans.None;
Add this method in with the other methods:
PHP:
public MapleClans getMapleClan(){
return mapleclan;
}
search for:
PHP:
ret.name = rs.getString("name");
add this right below it:
PHP:
ret.mapleclan = MapleClans.getById(rs.getInt("mapleclan"));
Remember to use your head and follow the pattern for this next part:
Search for saveToDB. You will see a two long lines of grey text. Scroll all the way to the right of the first one, and add
PHP:
mapleclan = ?
before
PHP:
WHERE ID = ?

In the second grey line, add
PHP:
mapleclan,
before
PHP:
accountid,
and add a question mark to the list of question marks to the right of it.
Look for something like this a little bit below those two grey lines:
PHP:
if (update) {
ps.setInt(57, id);
} else {
ps.setInt(57, accountid);
ps.setString(58, name);
ps.setInt(59, world);
Your numbers probably won't read the same as mine, but that doesn't matter.
What your going to do here, is add a line of code, and push all those numbers back one. Like so:
PHP:
ps.setInt(57, mapleclan.getId());
if(update) {
ps.setInt(58, id);
} else {
ps.setInt(58, accountid);
ps.setString(59, name);
ps.setInt(60, world);
Run this in your MySQL:
PHP:
ALTER TABLE `YourTable`.`characters` ADD COLUMN `mapleclan` INTEGER NOT NULL DEFAULT 0;

This next part is ONLY IF you want to add custom rates for Clans:
Add this method in MapleCharacter:
PHP:
public int[] getMapleClanRates(){
int exprate = ServerConstants.EXP_RATE;
int mesorate = ServerConstants.EXP_RATE;
int droprate = ServerConstants.DROP_RATE;
switch(getMapleClan().getId()){
case 100: // Eagle
exprate = ServerConstants.EXP_RATE;
mesorate = ServerConstants.MESO_RATE;
droprate = ServerConstants.DROP_RATE;
break;
case 200: // Serpent
exprate = ServerConstants.EXP_RATE;
mesorate = ServerConstants.MESO_RATE;
droprate = ServerConstants.DROP_RATE;
break;
case 300: // Lion
exprate = ServerConstants.EXP_RATE;
mesorate = ServerConstants.MESO_RATE;
droprate = ServerConstants.DROP_RATE;
break;
case 400: // Salamander
exprate = ServerConstants.EXP_RATE;
mesorate = ServerConstants.MESO_RATE;
droprate = ServerConstants.DROP_RATE;
break;
case 500: // Roadrunner
exprate = ServerConstants.EXP_RATE;
mesorate = ServerConstants.MESO_RATE;
droprate = ServerConstants.DROP_RATE;
break;
case 600: // Shark
exprate = ServerConstants.EXP_RATE;
mesorate = ServerConstants.MESO_RATE;
droprate = ServerConstants.DROP_RATE;
break;
default:
exprate = ServerConstants.EXP_RATE;
mesorate = ServerConstants.MESO_RATE;
droprate = ServerConstants.DROP_RATE;
break;
} 
return new int[]{exprate, mesorate, droprate};
}
These rates are all set to 0, but it's very easy to change. Let's say I wanted Eagle's to have a +15 exp rate, I would do this:
PHP:
exprate = ServerConstants.EXP_RATE + 15;
Now search for your getExpRate(), getMesoRate(), and getDropRate() methods and replace them with these:
PHP:
public int getExpRate(){
return getMapleClanRates()[0];
}

public int getMesoRate(){
return getMapleClanRates()[1];
}

public int getDropRate(){
return getMapleClanRates()[2];
}

Now, everybody join in again.

Go into your NPCConversationManager and add these methods:
PHP:
public int getMapleClan(){
return getPlayer().getMapleClan().getId();
}

public String getMapleClanName(){
return getPlayer().getMapleClan().getName();
}

public String getMapleClanSB(){
return getPlayer().getMapleClan().getSkillName();
}

public void changeClans(MapleClans mc) {
getPlayer().mapleclan = mc;
}

public void setMapleClan(int mc) {
changeClans(MapleClans.getById(mc));
}

Now, aren't you relieved? We're finally done with the hard part.

If you are using MoopleDev:
Open up your net.channel.handler.TakeDamageHandler and replace the entire thing with this:
PHP:
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.channel.handler;

import client.ISkill;
import client.MapleBuffStat;
import client.MapleCharacter;
import client.MapleClient;
import client.MapleInventoryType;
import client.Skill;
import client.SkillFactory;
import client.status.MonsterStatus;
import constants.skills.Corsair;
import net.AbstractMaplePacketHandler;
import server.life.MapleMonster;
import server.life.MobAttackInfo;
import server.life.MobAttackInfoFactory;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;

public final class TakeDamageHandler extends AbstractMaplePacketHandler {
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter player = c.getPlayer();
slea.readInt();
int damagefrom = slea.readByte();
slea.readByte(); //Element
int damage = slea.readInt();
int oid = 0;
int monsteridfrom = 0;
int pgmr = 0;
int direction = 0;
int pos_x = 0;
int pos_y = 0;
int fake = 0;
boolean is_pgmr = false;
boolean is_pg = true;
int mpattack = 0;
MapleMonster attacker = null;
if (damagefrom != -3) {
monsteridfrom = slea.readInt();
oid = slea.readInt();
attacker = (MapleMonster) player.getMap().getMapObject(oid);
if (attacker.isBuffed(MonsterStatus.NEUTRALISE)) {
return;
}
if ((player.getMap().getMonsterById(monsteridfrom) == null || attacker == null) && monsteridfrom != 9300166) {
return;
} else if (monsteridfrom == 9300166) {
if (player.haveItem(4031868)) {
c.getPlayer().getMap().spawnItemDrop(c.getPlayer(), c.getPlayer(), player.getInventory(MapleInventoryType.ETC).findById(4031868), c.getPlayer().getPosition(), true, true);
}
}
direction = slea.readByte();
}
if (player.getMap().getId() == 922210000 && monsteridfrom == 9500182) {
player.changeMap(910000000);
return;
}
if (damagefrom != -1 && damagefrom != -2 && attacker != null) {
MobAttackInfo attackInfo = MobAttackInfoFactory.getMobAttackInfo(attacker, damagefrom);
if (attackInfo.isDeadlyAttack()) {
mpattack = player.getMp() - 1;
}
mpattack += attackInfo.getMpBurn();
MobSkill skill = MobSkillFactory.getMobSkill(attackInfo.getDiseaseSkill(), attackInfo.getDiseaseLevel());
if (skill != null && damage > 0) {
skill.applyEffect(player, attacker, false);
}
if (attacker != null) {
attacker.setMp(attacker.getMp() - attackInfo.getMpCon());
if (player.getBuffedValue(MapleBuffStat.MANA_REFLECTION) != null && damage > 0 && !attacker.isBoss()) {
int jobid = player.getJob().getId();
if (jobid == 212 || jobid == 222 || jobid == 232) {
int id = jobid * 10000 + 2;
ISkill manaReflectSkill = SkillFactory.getSkill(id);
if (player.isBuffFrom(MapleBuffStat.MANA_REFLECTION, manaReflectSkill) && player.getSkillLevel(manaReflectSkill) > 0 && manaReflectSkill.getEffect(player.getSkillLevel(manaReflectSkill)).makeChanceResult()) {
int bouncedamage = (damage * manaReflectSkill.getEffect(player.getSkillLevel(manaReflectSkill)).getX() / 100);
if (bouncedamage > attacker.getMaxHp() / 5) {
bouncedamage = attacker.getMaxHp() / 5;
}
player.getMap().damageMonster(player, attacker, bouncedamage);
player.getMap().broadcastMessage(player, MaplePacketCreator.damageMonster(oid, bouncedamage), true);
player.getClient().announce(MaplePacketCreator.showOwnBuffEffect(id, 5));
player.getMap().broadcastMessage(player, MaplePacketCreator.showBuffeffect(player.getId(), id, 5), false);
}
}
}
}
}
if (damage == -1) {
fake = 4020002 + (player.getJob().getId() / 10 - 40) * 100000;
}
if (damage == 0) {
player.getAutobanManager().addMiss();
} else {
player.getAutobanManager().resetMisses();
}
if (damage > 0 && !player.isHidden()) {
if (attacker != null && !attacker.isBoss()) {
if (damagefrom == -1 && player.getBuffedValue(MapleBuffStat.POWERGUARD) != null) {
int bouncedamage = (int) (damage * (player.getBuffedValue(MapleBuffStat.POWERGUARD).doubleValue() / 100));
bouncedamage = Math.min(bouncedamage, attacker.getMaxHp() / 10);
player.getMap().damageMonster(player, attacker, bouncedamage);
damage -= bouncedamage;
player.getMap().broadcastMessage(player, MaplePacketCreator.damageMonster(oid, bouncedamage), true, true);
player.checkMonsterAggro(attacker);
}
}
if (damagefrom != -3) {
int achilles = 0;
ISkill achilles1 = null;
int jobid = player.getJob().getId();
if (jobid < 200 && jobid % 10 == 2) {
achilles1 = SkillFactory.getSkill(jobid * 10000 + jobid == 112 ? 4 : 5);
achilles = player.getSkillLevel(achilles);
}
if (achilles != 0 && achilles1 != null) {
damage *= (int) (achilles1.getEffect(achilles).getX() / 1000.0 * damage);
}
}
Integer mesoguard = player.getBuffedValue(MapleBuffStat.MESOGUARD);
if (player.getBuffedValue(MapleBuffStat.MAGIC_GUARD) != null && mpattack == 0) {
int mploss = (int) (damage * (player.getBuffedValue(MapleBuffStat.MAGIC_GUARD).doubleValue() / 100.0));
int hploss = damage - mploss;
if (mploss > player.getMp()) {
hploss += mploss - player.getMp();
mploss = player.getMp();
}
player.addMPHP(-hploss, -mploss);
} else if (mesoguard != null) {
damage = Math.round(damage / 2);
int mesoloss = (int) (damage * (mesoguard.doubleValue() / 100.0));
if (player.getMeso() < mesoloss) {
player.gainMeso(-player.getMeso(), false);
player.cancelBuffStats(MapleBuffStat.MESOGUARD);
} else {
player.gainMeso(-mesoloss, false);
}
player.addMPHP(-damage, -mpattack);
} else if (player.getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) {
if (player.getBuffedValue(MapleBuffStat.MONSTER_RIDING).intValue() == Corsair.BATTLE_SHIP) {
player.decreaseBattleshipHp(damage);
} else {
player.addMPHP(-damage, -mpattack);
}
} else {
player.addMPHP(-damage, -mpattack);
}
}
if (!player.isHidden()) {
if(damage > 0 && c.getPlayer().getMapleClan().getSkillBuff() != 0){
int chance = (int) Math.floor(Math.random() * 25);
if(chance < 1){
ISkill skillBuff = SkillFactory.getSkill(c.getPlayer().getMapleClan().getSkillBuff());
int maxlevel = skillBuff.getMaxLevel();
if(skillBuff.getMaxLevel() > 30){
maxlevel = 30;
}
skillBuff.getEffect(maxlevel).applyTo(player);
}
}
player.getMap().broadcastMessage(player, MaplePacketCreator.damagePlayer(damagefrom, monsteridfrom, player.getId(), damage, fake, direction, is_pgmr, pgmr, is_pg, oid, pos_x, pos_y), false);
player.checkBerserk();
}
if (player.getMap().getId() >= 925020000 && player.getMap().getId() < 925030000) {
player.setDojoEnergy(player.isGM() ? 300 : player.getDojoEnergy() < 300 ? player.getDojoEnergy() + 1 : 0); 
player.getClient().announce(MaplePacketCreator.getEnergy(player.getDojoEnergy()));
}
}
}

If your NOT using MoopleDev:
Open up your net.channel.handler.TakeDamageHandler and search for this:
PHP:
if(!player.isHidden()){
if(damage > 0){
player.getMap().broadcastMessage(player, MaplePacketCreator.damagePlayer(damagefrom, monsteridfrom, player.getId(), damage, fake, direction, is_pgmr, pgmr, is_pg, oid, pos_x, pos_y), false);
And replace it with this:
PHP:
if (!player.isHidden()) {
if(damage > 0 && c.getPlayer().getMapleClan().getSkillBuff() != 0){
int chance = (int) Math.floor(Math.random() * 25);
if(chance < 1){
ISkill skillBuff = SkillFactory.getSkill(c.getPlayer().getMapleClan().getSkillBuff());
int maxlevel = skillBuff.getMaxLevel();
if(skillBuff.getMaxLevel() > 30){
maxlevel = 30;
}
skillBuff.getEffect(maxlevel).applyTo(player);
}
}
player.getMap().broadcastMessage(player, MaplePacketCreator.damagePlayer(damagefrom, monsteridfrom, player.getId(), damage, fake, direction, is_pgmr, pgmr, is_pg, oid, pos_x, pos_y), false);

Compile and your done :) Well, almost. If you want to use this, I've made a NPC for changing Clans.
Here it is:
PHP:
/**
* Clan NPC
* @author Sharky
*/

clan = [[100,200,300,400,500,600],["Eagle","Serpent","Lion","Salamander","Roadrunner","Shark"],["Focus","Sharp Eyes","Iron Body","Concentrate","Haste","Rage"]];

function start(){
var talk = "Hello! Which Clan would you like to join? Each clan comes with it's own unique skill. \r\n";
for(var i = 0; i < clan[1].length; i++)
talk += "\r\n#L"+i+"#"+clan[1][i]+", with the skill "+clan[2][i]+".#l";
cm.sendSimple(talk);
}

function action(m,t,s){
if(m > 0){
cm.setMapleClan(clan[0][s]);
cm.sendOk("You are now a part of the #b"+ cm.getMapleClanName() +" clan#k, and your skill is #b"+ cm.getMapleClanSB() +".");
cm.dispose();
}
}

Some useful commands (Added Feb 5, 2011):
Add these in MapleCharacter:
PHP:
public void changeClans(MapleClans mc) {
mapleclan = mc;
}

public void setMapleClan(int mc) {
changeClans(MapleClans.getById(mc));
}

Now add this in your GMCommand.java or wherever your GM Commands are located (assuming you don't want players to be able to change clans through a command).
PHP:
} else if (sub[0].equalsIgnoreCase("setclan")){
player.setMapleClan(Integer.parseInt(sub[1]));
player.dropMessage("You are now a part of the "+ player.getMapleClan().getName() +" Clan.");
}

Add this in PlayerCommands.java or wherever your player commands are located.
PHP:
} else if (sub[0].equalsIgnoreCase("checkclan")){
player.dropMessage("You are a member of the "+ player.getMapleClan().getName() +" Clan. Your Rates are: "+ player.getExpRate() +" / "+ player.getMesoRate() +" / "+ player.getDropRate() +". Your Clan Skill is "+ player.getMapleClan().getSkillName() +".");
}

There shouldn't be any errors if your using MoopleDev. If you have any problems using Moople or another source, post your problem, being specific, and I'll see what I can do.

** Remember to add the correct imports if you get an error. **
 
Last edited by a moderator:
Legendary Battlemage
Loyal Member
Joined
Dec 13, 2010
Messages
649
Reaction score
140
Re: [Add-On] Clan System with custom buffs

First post, very nice job Chris. I might use this, with my own customizations and practically re-do it, just for fun of course :)

+1
 
Legendary Battlemage
Loyal Member
Joined
Dec 13, 2010
Messages
649
Reaction score
140
Just as an add on, this is better for v83+

And for low/lower rate servers (under 200x/100x/2x)
 
Legendary Battlemage
Loyal Member
Joined
Dec 13, 2010
Messages
649
Reaction score
140
Its funny how I caught the v83 thing, Me and him have been MSN'ing about this release for 2 days. Lmao...
 
Legendary Battlemage
Loyal Member
Joined
Dec 13, 2010
Messages
649
Reaction score
140
It turned out to be this, cause i kept pressuring you with my releases :thumbup1:
 
may web.very maple.pls.
Loyal Member
Joined
Aug 12, 2009
Messages
1,810
Reaction score
606
I'm gonna make somn like this soon but differently when expedia my son, teaches me more on hashmap and arraylist >;[
 
Skilled Illusionist
Joined
Dec 16, 2010
Messages
304
Reaction score
164
I'm gonna make somn like this soon but differently when expedia my son, teaches me more on hashmap and arraylist >;[
I'm curious, how are you going to make it?

---------- Post added at 07:43 PM ---------- Previous post was at 07:42 PM ----------

Isn't
PHP:
public int getDropRate(){
    return getMapleClan()[2];
}
suppose to be
PHP:
public int getDropRate(){
    return getMapleClanRates()[2];
}
Yes, sorry, I'll edit that.
 
Legendary Battlemage
Loyal Member
Joined
Dec 13, 2010
Messages
649
Reaction score
140
Lol, expedia stopped watching the thread. Im scared when that dude is around, lmao.
Thanks for the correction Horrorchix
 
Skilled Illusionist
Joined
Dec 16, 2010
Messages
304
Reaction score
164
With Occupations, you generally advance up the ranks. Clans are all equal, but slightly different in their own way. These should be set up to create a separation of the society, a rivalry, and it can include features such as clan wars, different skills and mounts that create their identities. Occupations are also more for high-rates, while Clans are for lower-mid rate servers.
 
Legendary Battlemage
Loyal Member
Joined
Dec 13, 2010
Messages
649
Reaction score
140
Your next project should be to create

[*] One a Clan Wars system

[*] Two a custom user identity for the different clans

[*] Disable Certain skills from other players that arent specific to the clan.

[*] Custom skills?
 
Joined
May 23, 2010
Messages
528
Reaction score
23
With Occupations, you generally advance up the ranks. Clans are all equal, but slightly different in their own way. These should be set up to create a separation of the society, a rivalry, and it can include features such as clan wars, different skills and mounts that create their identities. Occupations are also more for high-rates, while Clans are for lower-mid rate servers.

Well I guess that makes sense, it's just that this looked a lot like occupations.
 
Skilled Illusionist
Joined
Dec 16, 2010
Messages
304
Reaction score
164
Well I guess that makes sense, it's just that this looked a lot like occupations.
It all depends on how you perceive it and how you improve upon it. This is merely a base for what could be something very similar to Occupations, or something completely different.
But that's for you to do.
 
Legendary Battlemage
Loyal Member
Joined
Dec 13, 2010
Messages
649
Reaction score
140
As complex as this seems, its still a shell, a really big shell that needs some meat!
 
Back
Top