I really like your posts SYJourney.
I'm sure that I speak for everyone when I say we appreciate that you explain the logic behind your code ITT. & hmm.... Try to add some ITG items? heheh
obligatory:
I changed it so the drops are now tied to this, meaning: more tries = more drops, this feels alot more natural because now having to harvest for a longer time also gives more drops.
The drops overall have been increased, to make production of elixirs/power elixirs and other potions worthwhile.
The exp rate on release will be dynamic and based off levels:
Lv. 1-29: 1x
Lv. 30-69: 2x
Lv. 70-119: 3x
Lv. 120+ : 4x
(Raise takes effect after job advancement)
The reason for that is that as it stands, there is alot more low-level content in v83 than high lvl content. So if you have low rates, leveling up at high lvls just takes too much time, but if you have high rates, all the low lvl content like all the quests, training areas etc. become lost.
lvl * (0.05 - 0.0005 * (lvl / 5))
I'm doing something very similar with my server. Only I prefer using a formula to calculate per-level, instead of having "steps" for each job. Why? Take this example: Once you reach level 30, leveling up to 31, 32, 33, etc. will be significantly easier than going from 29 to 30. It feels kind of backwards for it to be easier to level at a higher level. Here's the formula I came up with, just as a sample:
Note: Mine takes effect at level 21, and at level 200 your exp rate would be 6x with this formula.Code:lvl * (0.05 - 0.0005 * (lvl / 5))
EDIT: Just read the rest of your post. I guess if you like the easier leveling after job advance, go you. XD Anyway, your stuff looks great and refreshingly unique. Keep up the good works!
When I said 'you can be more creative than that' I meant it. Having just a solid set rate for those levels, while it isn't complex, it's been done all too many times, even by myself. it's rather bland, to be honest.
Instead, look at how GMS is doing things. Kill a monster a higher level than you, you get more exp, lower, you get less.
I coded something like this once upon a time, forgot what formula i used then, but heres a video.
You must be registered to see links
I totally understand what you mean, but just think about it: You're trying to make something really complex that should be simple. I actually said this in my previous reply, the problem here is that you're adding some 'obscure' formula to something as simple as exp, which should be clear and understandable to all players.
I'm not saying that players will have trouble figuring your formula out, I'm saying that they shouldn't have to do that in the first place.
And nexon's formula that you bring up is from post Big-Bang which has completely different gameplay from v83. In post big-bang consistency doesn't matter so much, so having varied exp also is not that important. Brining in something that destroys this consistency does not match the gamplay of oldschool maple in my opinion.
Also let me just add this, I'm naturally thankful for suggestions and ideas, but 'you can be more creative than that' is a bit patronizing, and you're also just missing the point, I'm not being uncreative, I simply don't want a complex formula.
I think the main issue here is that I don't really care alot about having an 'interesting' exp rate, while you seem to think it's important.the current rate you would be going with exp seems like it would be too boring.
NPCTALK(0.5),
HARVEST,
HEAL(1.5),
ITEMSORT,
PETFOOD,
CATCHITEM,
SPECIALMOVE(500),
ENTERHARVEST(3600 * 24, 3),
CPQ(3600 * 12, 3),
CPQ2(3600 * 12, 3),
LUDI(3600 * 12, 3),
LUDIMAZE(3600 * 12, 3),
PIRATE(3600 * 12, 3),
DOJO(3600 * 24, 5),
BPQEASY(3600 * 24, 5),
BPQMED(3600 * 24, 5),
BPQHARD(3600 * 24, 5),
BPQHELL(3600 * 24, 5),
NEOTOKYO(3600 * 12, 2),
ARMORIA(3600 * 12, 1),
ZAK(3600 * 24, 1),
HT(3600 * 24, 1),
PB(3600 * 24, 1);
private final MapleItemInformationProvider iip = MapleItemInformationProvider.getInstance();
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readInt();
int toCreate = slea.readInt();
MapleProfession prof = c.getPlayer().getProfessions().getProfession(false);
if (c.getPlayer().getProfessions().hasRecipe(toCreate)) {
Recipe recipe = ProfessionFactory.getInstance().getRecipe(toCreate);
if (!MapleInventoryManipulator.checkSpace(c, toCreate, recipe.getQty(), null) || c.getPlayer().getInventory(MapleInventoryType.ETC).isFull()) {
c.getPlayer().message("You do not have enough inventory space to craft this item.");
return;
}
boolean hasItems = recipe.getReqItems().allMatch((item) -> c.getPlayer().getInventory(iip.getInventoryType(item.getLeft())).countById(item.getLeft()) >= item.getRight());
byte distance = (byte) (recipe.getReqLevel().ordinal() - prof.getLevel().ordinal());
if (hasItems) {
if (distance <= 0 || Randomizer.nextInt(51) > distance*10) {
int expgain = distance*5 + 5;
recipe.getReqItems().forEach((item) -> MapleInventoryManipulator.removeById(c, iip.getInventoryType(item.getLeft()), item.getLeft(), item.getRight(), false, false));
MapleInventoryManipulator.addById(c, toCreate, recipe.getQty());
c.announce(MaplePacketCreator.showSpecialEffect(16));
if (prof.gainExp(expgain)) {
c.getPlayer().dropMessage(6, "Creation successful! You now have enough exp to level up your profession!");
} else {
c.getPlayer().dropMessage(6, "Creation successful! You have gained "+expgain+" "+prof.getName()+" exp!");
}
} else {
int expgain = distance*1;
recipe.getReqItems().forEach((item) -> MapleInventoryManipulator.removeById(c, iip.getInventoryType(item.getLeft()), item.getLeft(), item.getRight(), false, false));
MapleInventoryManipulator.addById(c, ProfessionFactory.getInstance().getRandomFailCraft(prof.getType()), (short) 1);
if (prof.gainExp(expgain)) {
c.getPlayer().dropMessage(6, "Creation failed. However, you have gathered enough exp to level up your profession!");
} else {
c.getPlayer().message("Creation failed. You have gained "+expgain+" "+prof.getName()+" exp.");
}
}
} else {
recipe.getReqItems().forEach((item) -> MapleInventoryManipulator.removeById(c, iip.getInventoryType(item.getLeft()), item.getLeft(), Math.min(item.getRight(), c.getPlayer().getInventory(iip.getInventoryType(item.getLeft())).countById(item.getLeft())), false, false));
MapleInventoryManipulator.addById(c, ProfessionFactory.getInstance().getRandomFailCraft(prof.getType()), (short) 1);
c.getPlayer().message("Creation failed.");
}
} else {
c.getPlayer().message("You do not have the recipe to create this item");
}
}
I am gonna bumb this with a crappy almost non game related suggestion. If you are bored then consider rewritingYou must be registered to see links/You must be registered to see links/You must be registered to see links/You must be registered to see links/ since there are a lot of interfaces that in my opinion should get removed.
It's an interesting suggestion, because I'm currently writing the "complementary" to those tools on the client side. I have one question about packets, so I might as well ask it here in my thread: Do I need all of the encryptions/aes stuff and so on for my client-server communication? Or are there some that could be replaced with something simpler or removed. I'm assuming I probably need the ecryption with recvIv/sendIv, what about the rest?
I'm asking this because I'm getting quite annoyed with writing all those cryptography things that essentially just move around the bytes in my packet. (From what I can tell atleast)
public sealed class NetworkSecurity
{
private readonly NetworkSecurityKinds _kinds;
private readonly ChiperIGCrypto _chiper; //class in Crypto folder
private readonly ChiperAESCrypto _aes; // class in crypto folder
private readonly ushort _version;
public NetworkSecurity(NetworkSecurityKinds kinds, ImmutableArray<byte> aes32, ImmutableArray<byte> shuffle256, uint defaultKey, ushort version)
{
_kinds = kinds;
_version = version;
if (kinds.HasFlag(NetworkSecurityKinds.Aes))
{
_aes = new ChiperAESCrypto(aes32);
}
if (kinds.HasFlag(NetworkSecurityKinds.InnoEncryptDecrypt) || kinds.HasFlag(NetworkSecurityKinds.InnoShuffle))
{
_chiper = new ChiperIGCrypto(shuffle256, defaultKey);
}
}
public uint Encrypt(byte[] packet, int start, int count, uint key)
{
if (_kinds.HasFlag(NetworkSecurityKinds.Server))
{
return key;
}
if (_kinds.HasFlag(NetworkSecurityKinds.Shanda))
{
ShandaCrypto.Encrypt(packet, start, count);
}
if (_kinds.HasFlag(NetworkSecurityKinds.Aes))
{
_aes.Transform(packet, key);
}
if (_kinds.HasFlag(NetworkSecurityKinds.InnoEncryptDecrypt))
{
_chiper.InnoEncrypt(packet, start, count, key);
}
if (_kinds.HasFlag(NetworkSecurityKinds.InnoShuffle))
{
return _chiper.InnoHash(key);
}
return key;
}
public uint Decrypt(byte[] packet, int start, int count, uint key)
{
if (_kinds.HasFlag(NetworkSecurityKinds.Server))
{
return key;
}
if (_kinds.HasFlag(NetworkSecurityKinds.Aes))
{
_aes.Transform(packet, key);
}
if (_kinds.HasFlag(NetworkSecurityKinds.Shanda))
{
ShandaCrypto.Decrypt(packet, start, count);
}
if (_kinds.HasFlag(NetworkSecurityKinds.InnoEncryptDecrypt))
{
_chiper.InnoDecrypt(packet, start, count, key);
}
if (_kinds.HasFlag(NetworkSecurityKinds.InnoShuffle))
{
return _chiper.InnoHash(key, System.BitConverter.ToUInt32(new byte[4] { 0xF2, 0x53, 0x50, 0xC6 } ,0));
}
return key;
}
public byte[] GeneratePacketHeader(byte[] packet, int start, int count, uint key)
{
var ret = new byte[4];
var a = (((key >> 24) & 0xFF) * 0x100 + ((key >> 16) & 0xFF) ^ -(_version + 1));
var b = a ^ count;
ret[0] = (byte)(a % 0x100);
ret[1] = (byte)((a - ret[0]) / 0x100);
ret[2] = (byte)(b ^ 0x100);
ret[3] = (byte)((b - ret[2]) / 0x100);
return ret;
}
public int GetPacketLength(byte[] packet, int start, int count, uint key)
{
return (packet[start] + (packet[1 + start] << 8)) ^ (packet[2 + start] + (packet[3 + start] << 8));
}
public void Dispose()
{
_aes?.Dispose();
}
}