ronancpl / HeavenMS

An improved server based on MapleSolaxia (v83 MapleStory private server)
GNU Affero General Public License v3.0
1.02k stars 732 forks source link

About raise item #466

Closed totalburn closed 5 years ago

totalburn commented 5 years ago

There is raise item can be used with quest at etc item tab. As you know, it is used in Aran's quest and Cygnus Mimiana egg. reference: http://forum.ragezone.com/f566/raise-item-v83-1106707/ capture There is Five state in thread but, because I do not know exactly I will talk about partially, BEGIN_RAISE(0xEB),//todo find client RaiseRefresh(0xEC),//struct: short nInfoNumber [parse -> QuestMan] RaiseUIState(0xED), //struct: int nItemID, byte nState RaiseIncExp(0xEF), //struct: byte nItemTI, short nSlotPosition, int nItemID END_RAISE(0xF0),//todo find client (I don't know if this is the correct opcode, but it's the recv, it's not hard to get a real value.) If you use that term, first if you OPEN the item it will be BEGIN_RAISE, if you give quest item to the this item it will be RaiseRefresh RaiseUIState RaiseIncExp or whatever, and if you CLOSE it will be END_RAISE

if you take look lithum source code, As follows.

    public static final void UseItemQuest(final LittleEndianAccessor slea, final MapleClient c) {
        final short slot = slea.readShort();
        final int itemId = slea.readInt();
        final Item item = c.getPlayer().getInventory(MapleInventoryType.ETC).getItem(slot);
        final int qid = slea.readInt();
        final MapleQuest quest = MapleQuest.getInstance(qid);
        final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
        Pair<Integer, List<Integer>> questItemInfo = null;
        boolean found = false;
        for (Item i : c.getPlayer().getInventory(MapleInventoryType.ETC)) {
            if (i.getItemId() / 10000 == 422) {
                questItemInfo = ii.questItemInfo(i.getItemId());
                if (questItemInfo != null && questItemInfo.getLeft() == qid && questItemInfo.getRight() != null && questItemInfo.getRight().contains(itemId)) {
                    found = true;
                    break; //i believe it's any order
                }
            }
        }
        if (quest != null && found && item != null && item.getQuantity() > 0 && item.getItemId() == itemId) {
            final int newData = slea.readInt();
            final MapleQuestStatus stats = c.getPlayer().getQuestNoAdd(quest);
            if (stats != null && stats.getStatus() == 1) {
                stats.setCustomData(String.valueOf(newData));
                c.getPlayer().updateQuest(stats, true);
                MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.ETC, slot, (short) 1, false);
            }
        }
    }

This is too long and there are some bugs, To sum up roughly, it is as follows.

    public static void QuestItem(final LittleEndianAccessor slea, final MapleClient c) {
        MapleQuest quest = MapleQuest.getInstance(slea.readShort());
        MapleQuestStatus mqs = c.getPlayer().getQuest(quest);
        if (quest != null) {
            String cd = mqs.getCustomData();
            if (cd == null) {
                cd = "0";
            }
            int qid = quest.getId();
            MapleQuest.getInstance(qid).forceStart(c.getPlayer(), 0, cd);
        }
    }

    public static final void UseItemQuest(final LittleEndianAccessor slea, final MapleClient c) {
        final short slot = slea.readShort();
        final int itemId = slea.readInt();
        final Item item = c.getPlayer().getInventory(MapleInventoryType.ETC).getItem(slot);
        final int qid = slea.readInt();
        final MapleQuest quest = MapleQuest.getInstance(qid);
        final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
        if (quest != null && item != null && item.getQuantity() > 0 && item.getItemId() == itemId) {
            final int newData = slea.readInt();
            final MapleQuestStatus stats = c.getPlayer().getQuestNoAdd(quest);
            if (stats != null && stats.getStatus() == 1) {
                stats.setCustomData(String.valueOf(Integer.parseInt(stats.getCustomData()) + newData));
                c.getPlayer().updateQuest(stats, true);
                MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.ETC, slot, (short) 1, false);
            }
        }
        c.getSession().write(MaplePacketCreator.enableActions());
    }

Basically it's just forcestart and adding infoquest.

In case Mimiana egg is slightly different. Because it's not raised up with items but through exp, So have to hack like it.

in QuestItem      
 if(qid == 20502 || qid == 20506 || qid == 20514 || qid == 20530){ //hack
            c.getPlayer().itemquest.add(qid); //if there is no consumeitem in wz, it levelup with exp
     }
in gainExp,
                for(int qid : itemquest){ //if in the list //public ArrayList<Integer> itemquest = new ArrayList<Integer>();
                        final MapleQuest quest = MapleQuest.getInstance((int)qid);
                        final MapleQuestStatus stats = getQuestNoAdd(quest);
                        if (stats != null && stats.getStatus() == 1) {
                            if (total > 0) {
                            setExp(needed);
                            } else if (total < 0) {
                            setExp(0);
                            }
                        stats.setCustomData(String.valueOf(Integer.parseInt(stats.getCustomData()) + total));
                        updateQuest(stats, true);
                      }
                }
and item close,
    public static void QuestItemclose(LittleEndianAccessor slea, MapleClient c) {
        MapleQuest quest = MapleQuest.getInstance(slea.readShort());
        int qid = quest.getId();
        if(qid == 20502 || qid == 20506 || qid == 20514 || qid == 20530){ //hack
        c.getPlayer().itemquest.remove(Integer.valueOf(qid));
        }

I've written code that if there's a consumeitem in wz, but I'm not sure done rightly. So it's not here. Maybe you'll do better. Sorry for my ugly english and code. I'm not good at that, But I think you know what I mean.

ronancpl commented 5 years ago

Hmm, interesting, I may take a glance on it sometime later, thanks for sharing!

totalburn commented 5 years ago

To put it simply, it's just cm.forceStart(getCustomData() + newData) :) If it done correctly, we can get rid of this phrase. } else if (status == 1) { //pretty sure there would need to have an egg EXP condition... Whatever. we just have to check infoNumber.

ronancpl commented 5 years ago

Hmm, not sure why would you put script code in there (cm.forceStart, else if (status == 1) and whatnot), since code mechanic is expected to be within Java methods...

About UI view:

image

Correct me if I'm wrong, it strikes to me the whole growing UI is related to feeding/sending the right questid-infoNumber association to the player? Seems to be like that to me, I'm not seeing another kind of update packet being sent to the player.

ronancpl commented 5 years ago

Though, ah, got it, you meant writing script-ish pseudocode to point out a quest start pattern @ cm.forceStart(getCustomData() + newData).

And there's that intertwining with the quest's current script, that runs code seeking to bypass the inexistent item UI interaction. Evidently, should the UI interaction starts working that'd be refactored.

totalburn commented 5 years ago
  1. Yes correct, they are related in quest. capture

  2. I mean instead this placeholder in 20522.js(Cygnus Mimiana egg quest), we can check quest's infonumber. like this, if(parseInt(qm.getQuestRecord(20514).getCustomData())> 170000 * 3){ (Sorry lithium form) instead //pretty sure there would need to have an egg EXP condition... Whatever.

  3. whole growing UI is related to feeding/sending the right questid-infoNumber association to the player. yes, as I know. capture3 capture4 capture5

  4. How they are related in wz? capture2 There are questid grade exp consumeItem message etc.

ronancpl commented 5 years ago

This feature has been implemented now and will show up next update. To help clear the case I also took assistance from Rien server source, which had already covered the Raise UI mechanics.

This was a nice run through in development for me though. It was already known the client handles quests' infoNumber-infoEx to tell whether the player achieved enough progress to complete a quest, only then making the "complete quest" tooltip appear on the NPC.

Now, to have finally come to learn about how the server is supposed to update the clients' infoNumber-infoEx at this stage of development was astonishing to me, haha. It actually uses the updateQuestInfo(int quest, String info) method to update the player, though infoNumber being actually an entirely different questid to be fed was somewhat out of place in my mind (still, I really should have seen that coming).

Anyway, new measures have also been done to concurrently protect the progress update (if the new value is over the expected the player cannot complete the quest, since progress checking client-side is actually a string match pattern).

Well, at last, once again, thanks for sharing!